diff options
45 files changed, 1063 insertions, 888 deletions
diff --git a/api/current.txt b/api/current.txt index 96ecad617a91..bb751cc99b44 100644 --- a/api/current.txt +++ b/api/current.txt @@ -9920,6 +9920,7 @@ package android.content.res { public static class Resources.NotFoundException extends java.lang.RuntimeException { ctor public Resources.NotFoundException(); ctor public Resources.NotFoundException(java.lang.String); + ctor public Resources.NotFoundException(java.lang.String, java.lang.Exception); } public final class Resources.Theme { diff --git a/api/system-current.txt b/api/system-current.txt index 0605851f058d..65ecdc139777 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -10261,6 +10261,7 @@ package android.content.res { public static class Resources.NotFoundException extends java.lang.RuntimeException { ctor public Resources.NotFoundException(); ctor public Resources.NotFoundException(java.lang.String); + ctor public Resources.NotFoundException(java.lang.String, java.lang.Exception); } public final class Resources.Theme { diff --git a/core/java/android/app/TimePickerDialog.java b/core/java/android/app/TimePickerDialog.java index a3b3022fc69c..aca07636e26e 100644 --- a/core/java/android/app/TimePickerDialog.java +++ b/core/java/android/app/TimePickerDialog.java @@ -23,10 +23,8 @@ import android.os.Bundle; import android.util.TypedValue; import android.view.LayoutInflater; import android.view.View; -import android.widget.Button; import android.widget.TimePicker; import android.widget.TimePicker.OnTimeChangedListener; -import android.widget.TimePicker.ValidationCallback; import com.android.internal.R; @@ -64,7 +62,7 @@ public class TimePickerDialog extends AlertDialog implements OnClickListener, * @param hourOfDay the hour that was set * @param minute the minute that was set */ - public void onTimeSet(TimePicker view, int hourOfDay, int minute); + void onTimeSet(TimePicker view, int hourOfDay, int minute); } /** @@ -115,7 +113,6 @@ public class TimePickerDialog extends AlertDialog implements OnClickListener, final TypedValue outValue = new TypedValue(); context.getTheme().resolveAttribute(R.attr.timePickerDialogTheme, outValue, true); - final int layoutResId = outValue.resourceId; final LayoutInflater inflater = LayoutInflater.from(themeContext); final View view = inflater.inflate(R.layout.time_picker_dialog, null); @@ -129,7 +126,6 @@ public class TimePickerDialog extends AlertDialog implements OnClickListener, mTimePicker.setCurrentHour(mInitialHourOfDay); mTimePicker.setCurrentMinute(mInitialMinute); mTimePicker.setOnTimeChangedListener(this); - mTimePicker.setValidationCallback(mValidationCallback); } @Override @@ -181,14 +177,4 @@ public class TimePickerDialog extends AlertDialog implements OnClickListener, mTimePicker.setCurrentHour(hour); mTimePicker.setCurrentMinute(minute); } - - private final ValidationCallback mValidationCallback = new ValidationCallback() { - @Override - public void onValidationChanged(boolean valid) { - final Button positive = getButton(BUTTON_POSITIVE); - if (positive != null) { - positive.setEnabled(valid); - } - } - }; } diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 9c880d3591c3..52ec4ccfd8f3 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -479,6 +479,14 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { public static final int PRIVATE_FLAG_ENCRYPTION_AWARE = 1 << 6; /** + * Value for {@link #privateFlags}: set to {@code true} if the application + * is AutoPlay. + * + * {@hide} + */ + public static final int PRIVATE_FLAG_AUTOPLAY = 1<<6; + + /** * Private/hidden flags. See {@code PRIVATE_FLAG_...} constants. * {@hide} */ @@ -1049,6 +1057,13 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { /** * @hide */ + public boolean isAutoPlayApp() { + return (privateFlags & ApplicationInfo.PRIVATE_FLAG_AUTOPLAY) != 0; + } + + /** + * @hide + */ @Override protected ApplicationInfo getApplicationInfo() { return this; } diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index 0606e35a081e..7b3dde4262f4 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -48,7 +48,6 @@ import android.icu.text.PluralRules; import android.os.Build; import android.os.Bundle; import android.os.Trace; -import android.util.ArrayMap; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.LocaleList; @@ -68,7 +67,6 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.io.InputStream; -import java.lang.ref.WeakReference; import java.util.Locale; /** @@ -120,9 +118,6 @@ public class Resources { private static final LongSparseArray<android.content.res.ConstantState<ColorStateList>> sPreloadedColorStateLists = new LongSparseArray<>(); - private static final String CACHE_NOT_THEMED = ""; - private static final String CACHE_NULL_THEME = "null_theme"; - // Pool of TypedArrays targeted to this Resources object. final SynchronizedPool<TypedArray> mTypedArrayPool = new SynchronizedPool<>(5); @@ -130,10 +125,11 @@ public class Resources { static Resources mSystem = null; private static boolean sPreloaded; - private static int sPreloadedDensity; - // These are protected by mAccessLock. + /** Lock object used to protect access to caches and configuration. */ private final Object mAccessLock = new Object(); + + // These are protected by mAccessLock. private final Configuration mTmpConfig = new Configuration(); private final DrawableCache mDrawableCache = new DrawableCache(this); private final DrawableCache mColorDrawableCache = new DrawableCache(this); @@ -147,7 +143,12 @@ public class Resources { /** Used to inflate drawable objects from XML. */ private DrawableInflater mDrawableInflater; + /** Lock object used to protect access to {@link #mTmpValue}. */ + private final Object mTmpValueLock = new Object(); + + /** Single-item pool used to minimize TypedValue allocations. */ private TypedValue mTmpValue = new TypedValue(); + private boolean mPreloading; private int mLastCachedXmlBlockIndex = -1; @@ -249,6 +250,10 @@ public class Resources { public NotFoundException(String name) { super(name); } + + public NotFoundException(String name, Exception cause) { + super(name, cause); + } } /** @@ -621,18 +626,15 @@ public class Resources { * @see #getDimensionPixelSize */ public float getDimension(@DimenRes int id) throws NotFoundException { - synchronized (mAccessLock) { - TypedValue value = mTmpValue; - if (value == null) { - mTmpValue = value = new TypedValue(); - } - getValue(id, value, true); + final TypedValue value = obtainTempTypedValue(id); + try { if (value.type == TypedValue.TYPE_DIMENSION) { return TypedValue.complexToDimension(value.data, mMetrics); } - throw new NotFoundException( - "Resource ID #0x" + Integer.toHexString(id) + " type #0x" - + Integer.toHexString(value.type) + " is not valid"); + throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id) + + " type #0x" + Integer.toHexString(value.type) + " is not valid"); + } finally { + releaseTempTypedValue(value); } } @@ -656,19 +658,15 @@ public class Resources { * @see #getDimensionPixelSize */ public int getDimensionPixelOffset(@DimenRes int id) throws NotFoundException { - synchronized (mAccessLock) { - TypedValue value = mTmpValue; - if (value == null) { - mTmpValue = value = new TypedValue(); - } - getValue(id, value, true); + final TypedValue value = obtainTempTypedValue(id); + try { if (value.type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimensionPixelOffset( - value.data, mMetrics); + return TypedValue.complexToDimensionPixelOffset(value.data, mMetrics); } - throw new NotFoundException( - "Resource ID #0x" + Integer.toHexString(id) + " type #0x" - + Integer.toHexString(value.type) + " is not valid"); + throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id) + + " type #0x" + Integer.toHexString(value.type) + " is not valid"); + } finally { + releaseTempTypedValue(value); } } @@ -693,19 +691,15 @@ public class Resources { * @see #getDimensionPixelOffset */ public int getDimensionPixelSize(@DimenRes int id) throws NotFoundException { - synchronized (mAccessLock) { - TypedValue value = mTmpValue; - if (value == null) { - mTmpValue = value = new TypedValue(); - } - getValue(id, value, true); + final TypedValue value = obtainTempTypedValue(id); + try { if (value.type == TypedValue.TYPE_DIMENSION) { - return TypedValue.complexToDimensionPixelSize( - value.data, mMetrics); + return TypedValue.complexToDimensionPixelSize(value.data, mMetrics); } - throw new NotFoundException( - "Resource ID #0x" + Integer.toHexString(id) + " type #0x" - + Integer.toHexString(value.type) + " is not valid"); + throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id) + + " type #0x" + Integer.toHexString(value.type) + " is not valid"); + } finally { + releaseTempTypedValue(value); } } @@ -727,18 +721,15 @@ public class Resources { * @throws NotFoundException Throws NotFoundException if the given ID does not exist. */ public float getFraction(@FractionRes int id, int base, int pbase) { - synchronized (mAccessLock) { - TypedValue value = mTmpValue; - if (value == null) { - mTmpValue = value = new TypedValue(); - } - getValue(id, value, true); + final TypedValue value = obtainTempTypedValue(id); + try { if (value.type == TypedValue.TYPE_FRACTION) { return TypedValue.complexToFraction(value.data, base, pbase); } - throw new NotFoundException( - "Resource ID #0x" + Integer.toHexString(id) + " type #0x" - + Integer.toHexString(value.type) + " is not valid"); + throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id) + + " type #0x" + Integer.toHexString(value.type) + " is not valid"); + } finally { + releaseTempTypedValue(value); } } @@ -801,24 +792,14 @@ public class Resources { * not exist. */ @Nullable - public Drawable getDrawable(@DrawableRes int id, @Nullable Theme theme) throws NotFoundException { - TypedValue value; - synchronized (mAccessLock) { - value = mTmpValue; - if (value == null) { - value = new TypedValue(); - } else { - mTmpValue = null; - } - getValue(id, value, true); - } - final Drawable res = loadDrawable(value, id, theme); - synchronized (mAccessLock) { - if (mTmpValue == null) { - mTmpValue = value; - } + public Drawable getDrawable(@DrawableRes int id, @Nullable Theme theme) + throws NotFoundException { + final TypedValue value = obtainTempTypedValue(id); + try { + return loadDrawable(value, id, theme); + } finally { + releaseTempTypedValue(value); } - return res; } /** @@ -849,7 +830,8 @@ public class Resources { */ @Deprecated @Nullable - public Drawable getDrawableForDensity(@DrawableRes int id, int density) throws NotFoundException { + public Drawable getDrawableForDensity(@DrawableRes int id, int density) + throws NotFoundException { return getDrawableForDensity(id, density, null); } @@ -869,14 +851,8 @@ public class Resources { */ @Nullable public Drawable getDrawableForDensity(@DrawableRes int id, int density, @Nullable Theme theme) { - TypedValue value; - synchronized (mAccessLock) { - value = mTmpValue; - if (value == null) { - value = new TypedValue(); - } else { - mTmpValue = null; - } + final TypedValue value = obtainTempTypedValue(id); + try { getValueForDensity(id, density, value, true); /* @@ -893,15 +869,11 @@ public class Resources { value.density = (value.density * mMetrics.densityDpi) / density; } } - } - final Drawable res = loadDrawable(value, id, theme); - synchronized (mAccessLock) { - if (mTmpValue == null) { - mTmpValue = value; - } + return loadDrawable(value, id, theme); + } finally { + releaseTempTypedValue(value); } - return res; } /** @@ -963,33 +935,21 @@ public class Resources { */ @ColorInt public int getColor(@ColorRes int id, @Nullable Theme theme) throws NotFoundException { - TypedValue value; - synchronized (mAccessLock) { - value = mTmpValue; - if (value == null) { - value = new TypedValue(); - } - getValue(id, value, true); + final TypedValue value = obtainTempTypedValue(id); + try { if (value.type >= TypedValue.TYPE_FIRST_INT && value.type <= TypedValue.TYPE_LAST_INT) { - mTmpValue = value; return value.data; } else if (value.type != TypedValue.TYPE_STRING) { - throw new NotFoundException( - "Resource ID #0x" + Integer.toHexString(id) + " type #0x" - + Integer.toHexString(value.type) + " is not valid"); + throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id) + + " type #0x" + Integer.toHexString(value.type) + " is not valid"); } - mTmpValue = null; - } - final ColorStateList csl = loadColorStateList(value, id, theme); - synchronized (mAccessLock) { - if (mTmpValue == null) { - mTmpValue = value; - } + final ColorStateList csl = loadColorStateList(value, id, theme); + return csl.getDefaultColor(); + } finally { + releaseTempTypedValue(value); } - - return csl.getDefaultColor(); } /** @@ -1043,25 +1003,12 @@ public class Resources { @Nullable public ColorStateList getColorStateList(@ColorRes int id, @Nullable Theme theme) throws NotFoundException { - TypedValue value; - synchronized (mAccessLock) { - value = mTmpValue; - if (value == null) { - value = new TypedValue(); - } else { - mTmpValue = null; - } - getValue(id, value, true); - } - - final ColorStateList res = loadColorStateList(value, id, theme); - synchronized (mAccessLock) { - if (mTmpValue == null) { - mTmpValue = value; - } + final TypedValue value = obtainTempTypedValue(id); + try { + return loadColorStateList(value, id, theme); + } finally { + releaseTempTypedValue(value); } - - return res; } /** @@ -1078,19 +1025,16 @@ public class Resources { * @return Returns the boolean value contained in the resource. */ public boolean getBoolean(@BoolRes int id) throws NotFoundException { - synchronized (mAccessLock) { - TypedValue value = mTmpValue; - if (value == null) { - mTmpValue = value = new TypedValue(); - } - getValue(id, value, true); + final TypedValue value = obtainTempTypedValue(id); + try { if (value.type >= TypedValue.TYPE_FIRST_INT - && value.type <= TypedValue.TYPE_LAST_INT) { + && value.type <= TypedValue.TYPE_LAST_INT) { return value.data != 0; } - throw new NotFoundException( - "Resource ID #0x" + Integer.toHexString(id) + " type #0x" - + Integer.toHexString(value.type) + " is not valid"); + throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id) + + " type #0x" + Integer.toHexString(value.type) + " is not valid"); + } finally { + releaseTempTypedValue(value); } } @@ -1106,19 +1050,16 @@ public class Resources { * @return Returns the integer value contained in the resource. */ public int getInteger(@IntegerRes int id) throws NotFoundException { - synchronized (mAccessLock) { - TypedValue value = mTmpValue; - if (value == null) { - mTmpValue = value = new TypedValue(); - } - getValue(id, value, true); + final TypedValue value = obtainTempTypedValue(id); + try { if (value.type >= TypedValue.TYPE_FIRST_INT - && value.type <= TypedValue.TYPE_LAST_INT) { + && value.type <= TypedValue.TYPE_LAST_INT) { return value.data; } - throw new NotFoundException( - "Resource ID #0x" + Integer.toHexString(id) + " type #0x" - + Integer.toHexString(value.type) + " is not valid"); + throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id) + + " type #0x" + Integer.toHexString(value.type) + " is not valid"); + } finally { + releaseTempTypedValue(value); } } @@ -1136,17 +1077,15 @@ public class Resources { * @hide Pending API council approval. */ public float getFloat(int id) { - synchronized (mAccessLock) { - TypedValue value = mTmpValue; - if (value == null) { - mTmpValue = value = new TypedValue(); - } - getValue(id, value, true); + final TypedValue value = obtainTempTypedValue(id); + try { if (value.type == TypedValue.TYPE_FLOAT) { return value.getFloat(); } - throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id) + " type #0x" - + Integer.toHexString(value.type) + " is not valid"); + throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id) + + " type #0x" + Integer.toHexString(value.type) + " is not valid"); + } finally { + releaseTempTypedValue(value); } } @@ -1238,22 +1177,60 @@ public class Resources { * */ public InputStream openRawResource(@RawRes int id) throws NotFoundException { - TypedValue value; - synchronized (mAccessLock) { - value = mTmpValue; - if (value == null) { - value = new TypedValue(); - } else { + final TypedValue value = obtainTempTypedValue(); + try { + return openRawResource(id, value); + } finally { + releaseTempTypedValue(value); + } + } + + /** + * Returns a TypedValue populated with data for the specified resource ID + * that's suitable for temporary use. The obtained TypedValue should be + * released using {@link #releaseTempTypedValue(TypedValue)}. + * + * @param id the resource ID for which data should be obtained + * @return a populated typed value suitable for temporary use + */ + private TypedValue obtainTempTypedValue(@AnyRes int id) { + final TypedValue value = obtainTempTypedValue(); + getValue(id, value, true); + return value; + } + + /** + * Returns a TypedValue suitable for temporary use. The obtained TypedValue + * should be released using {@link #releaseTempTypedValue(TypedValue)}. + * + * @return a typed value suitable for temporary use + */ + private TypedValue obtainTempTypedValue() { + TypedValue tmpValue = null; + synchronized (mTmpValueLock) { + if (mTmpValue != null) { + tmpValue = mTmpValue; mTmpValue = null; } } - InputStream res = openRawResource(id, value); - synchronized (mAccessLock) { + if (tmpValue == null) { + return new TypedValue(); + } + return tmpValue; + } + + /** + * Returns a TypedValue to the pool. After calling this method, the + * specified TypedValue should no longer be accessed. + * + * @param value the typed value to return to the pool + */ + private void releaseTempTypedValue(TypedValue value) { + synchronized (mTmpValueLock) { if (mTmpValue == null) { mTmpValue = value; } } - return res; } /** @@ -1307,32 +1284,14 @@ public class Resources { */ public AssetFileDescriptor openRawResourceFd(@RawRes int id) throws NotFoundException { - TypedValue value; - synchronized (mAccessLock) { - value = mTmpValue; - if (value == null) { - value = new TypedValue(); - } else { - mTmpValue = null; - } - getValue(id, value, true); - } + final TypedValue value = obtainTempTypedValue(id); try { - return mAssets.openNonAssetFd( - value.assetCookie, value.string.toString()); + return mAssets.openNonAssetFd(value.assetCookie, value.string.toString()); } catch (Exception e) { - NotFoundException rnf = new NotFoundException( - "File " + value.string.toString() - + " from drawable resource ID #0x" - + Integer.toHexString(id)); - rnf.initCause(e); - throw rnf; + throw new NotFoundException("File " + value.string.toString() + " from drawable " + + "resource ID #0x" + Integer.toHexString(id), e); } finally { - synchronized (mAccessLock) { - if (mTmpValue == null) { - mTmpValue = value; - } - } + releaseTempTypedValue(value); } } @@ -2015,8 +1974,8 @@ public class Resources { Build.VERSION.RESOURCES_SDK_INT); if (DEBUG_CONFIG) { - Slog.i(TAG, "**** Updating config of " + this + ": final config is " + mConfiguration - + " final compat is " + mCompatibilityInfo); + Slog.i(TAG, "**** Updating config of " + this + ": final config is " + + mConfiguration + " final compat is " + mCompatibilityInfo); } mDrawableCache.onConfigurationChange(configChanges); @@ -2402,8 +2361,7 @@ public class Resources { } sPreloaded = true; mPreloading = true; - sPreloadedDensity = DisplayMetrics.DENSITY_DEVICE; - mConfiguration.densityDpi = sPreloadedDensity; + mConfiguration.densityDpi = DisplayMetrics.DENSITY_DEVICE; updateConfiguration(null, null); } } @@ -2740,19 +2698,16 @@ public class Resources { /*package*/ XmlResourceParser loadXmlResourceParser(int id, String type) throws NotFoundException { - synchronized (mAccessLock) { - TypedValue value = mTmpValue; - if (value == null) { - mTmpValue = value = new TypedValue(); - } - getValue(id, value, true); + final TypedValue value = obtainTempTypedValue(id); + try { if (value.type == TypedValue.TYPE_STRING) { return loadXmlResourceParser(value.string.toString(), id, value.assetCookie, type); } - throw new NotFoundException( - "Resource ID #0x" + Integer.toHexString(id) + " type #0x" - + Integer.toHexString(value.type) + " is not valid"); + throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id) + + " type #0x" + Integer.toHexString(value.type) + " is not valid"); + } finally { + releaseTempTypedValue(value); } } diff --git a/core/java/android/widget/TimePicker.java b/core/java/android/widget/TimePicker.java index 8e5af793e2d9..a24d37f4b097 100644 --- a/core/java/android/widget/TimePicker.java +++ b/core/java/android/widget/TimePicker.java @@ -100,7 +100,7 @@ public class TimePicker extends FrameLayout { * @see #getHour() */ public void setHour(int hour) { - mDelegate.setCurrentHour(hour); + mDelegate.setHour(hour); } /** @@ -110,7 +110,7 @@ public class TimePicker extends FrameLayout { * @see #setHour(int) */ public int getHour() { - return mDelegate.getCurrentHour(); + return mDelegate.getHour(); } /** @@ -120,7 +120,7 @@ public class TimePicker extends FrameLayout { * @see #getMinute() */ public void setMinute(int minute) { - mDelegate.setCurrentMinute(minute); + mDelegate.setMinute(minute); } /** @@ -130,7 +130,7 @@ public class TimePicker extends FrameLayout { * @see #setMinute(int) */ public int getMinute() { - return mDelegate.getCurrentMinute(); + return mDelegate.getMinute(); } /** @@ -150,7 +150,7 @@ public class TimePicker extends FrameLayout { @NonNull @Deprecated public Integer getCurrentHour() { - return mDelegate.getCurrentHour(); + return mDelegate.getHour(); } /** @@ -160,7 +160,7 @@ public class TimePicker extends FrameLayout { */ @Deprecated public void setCurrentMinute(@NonNull Integer currentMinute) { - mDelegate.setCurrentMinute(currentMinute); + mDelegate.setMinute(currentMinute); } /** @@ -170,7 +170,7 @@ public class TimePicker extends FrameLayout { @NonNull @Deprecated public Integer getCurrentMinute() { - return mDelegate.getCurrentMinute(); + return mDelegate.getMinute(); } /** @@ -186,7 +186,7 @@ public class TimePicker extends FrameLayout { return; } - mDelegate.setIs24HourView(is24HourView); + mDelegate.setIs24Hour(is24HourView); } /** @@ -195,7 +195,7 @@ public class TimePicker extends FrameLayout { * @see #setIs24HourView(Boolean) */ public boolean is24HourView() { - return mDelegate.is24HourView(); + return mDelegate.is24Hour(); } /** @@ -207,16 +207,6 @@ public class TimePicker extends FrameLayout { mDelegate.setOnTimeChangedListener(onTimeChangedListener); } - /** - * Sets the callback that indicates the current time is valid. - * - * @param callback the callback, may be null - * @hide - */ - public void setValidationCallback(@Nullable ValidationCallback callback) { - mDelegate.setValidationCallback(callback); - } - @Override public void setEnabled(boolean enabled) { super.setEnabled(enabled); @@ -234,12 +224,6 @@ public class TimePicker extends FrameLayout { } @Override - protected void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - mDelegate.onConfigurationChanged(newConfig); - } - - @Override protected Parcelable onSaveInstanceState() { Parcelable superState = super.onSaveInstanceState(); return mDelegate.onSaveInstanceState(superState); @@ -269,25 +253,22 @@ public class TimePicker extends FrameLayout { * for the real behavior. */ interface TimePickerDelegate { - void setCurrentHour(int currentHour); - int getCurrentHour(); + void setHour(int hour); + int getHour(); - void setCurrentMinute(int currentMinute); - int getCurrentMinute(); + void setMinute(int minute); + int getMinute(); - void setIs24HourView(boolean is24HourView); - boolean is24HourView(); + void setIs24Hour(boolean is24Hour); + boolean is24Hour(); void setOnTimeChangedListener(OnTimeChangedListener onTimeChangedListener); - void setValidationCallback(ValidationCallback callback); void setEnabled(boolean enabled); boolean isEnabled(); int getBaseline(); - void onConfigurationChanged(Configuration newConfig); - Parcelable onSaveInstanceState(Parcelable superState); void onRestoreInstanceState(Parcelable state); @@ -295,16 +276,6 @@ public class TimePicker extends FrameLayout { void onPopulateAccessibilityEvent(AccessibilityEvent event); } - /** - * A callback interface for updating input validity when the TimePicker - * when included into a Dialog. - * - * @hide - */ - public static interface ValidationCallback { - void onValidationChanged(boolean valid); - } - static String[] getAmPmStrings(Context context) { final Locale locale = context.getResources().getConfiguration().locale; final LocaleData d = LocaleData.get(locale); @@ -319,43 +290,16 @@ public class TimePicker extends FrameLayout { * An abstract class which can be used as a start for TimePicker implementations */ abstract static class AbstractTimePickerDelegate implements TimePickerDelegate { - // The delegator - protected TimePicker mDelegator; - - // The context - protected Context mContext; + protected final TimePicker mDelegator; + protected final Context mContext; + protected final Locale mLocale; - // The current locale - protected Locale mCurrentLocale; - - // Callbacks protected OnTimeChangedListener mOnTimeChangedListener; - protected ValidationCallback mValidationCallback; - public AbstractTimePickerDelegate(TimePicker delegator, Context context) { + public AbstractTimePickerDelegate(@NonNull TimePicker delegator, @NonNull Context context) { mDelegator = delegator; mContext = context; - - // initialization based on locale - setCurrentLocale(Locale.getDefault()); - } - - public void setCurrentLocale(Locale locale) { - if (locale.equals(mCurrentLocale)) { - return; - } - mCurrentLocale = locale; - } - - @Override - public void setValidationCallback(ValidationCallback callback) { - mValidationCallback = callback; - } - - protected void onValidationChanged(boolean valid) { - if (mValidationCallback != null) { - mValidationCallback.onValidationChanged(valid); - } + mLocale = context.getResources().getConfiguration().locale; } } } diff --git a/core/java/android/widget/TimePickerClockDelegate.java b/core/java/android/widget/TimePickerClockDelegate.java index 4dc5fd3e5f5c..38ce033470b6 100644 --- a/core/java/android/widget/TimePickerClockDelegate.java +++ b/core/java/android/widget/TimePickerClockDelegate.java @@ -19,7 +19,6 @@ package android.widget; import android.annotation.Nullable; import android.content.Context; import android.content.res.ColorStateList; -import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; import android.os.Parcel; @@ -89,7 +88,7 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate impl private boolean mAllowAutoAdvance; private int mInitialHourOfDay; private int mInitialMinute; - private boolean mIs24HourView; + private boolean mIs24Hour; private boolean mIsAmPmAtStart; // Accessibility strings. @@ -200,19 +199,19 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate impl mAllowAutoAdvance = true; // Updates mHourFormat variables used below. - updateHourFormat(mCurrentLocale, mIs24HourView); + updateHourFormat(mLocale, mIs24Hour); // Update hour text field. final int minHour = mHourFormatStartsAtZero ? 0 : 1; - final int maxHour = (mIs24HourView ? 23 : 11) + minHour; + final int maxHour = (mIs24Hour ? 23 : 11) + minHour; mHourView.setRange(minHour, maxHour); mHourView.setShowLeadingZeroes(mHourFormatShowLeadingZero); // Initialize with current time. - mTempCalendar = Calendar.getInstance(mCurrentLocale); + mTempCalendar = Calendar.getInstance(mLocale); final int currentHour = mTempCalendar.get(Calendar.HOUR_OF_DAY); final int currentMinute = mTempCalendar.get(Calendar.MINUTE); - initialize(currentHour, currentMinute, mIs24HourView, HOUR_INDEX); + initialize(currentHour, currentMinute, mIs24Hour, HOUR_INDEX); } /** @@ -333,7 +332,7 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate impl private void initialize(int hourOfDay, int minute, boolean is24HourView, int index) { mInitialHourOfDay = hourOfDay; mInitialMinute = minute; - mIs24HourView = is24HourView; + mIs24Hour = is24HourView; updateUI(index); } @@ -352,17 +351,16 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate impl } private void updateRadialPicker(int index) { - mRadialTimePickerView.initialize(mInitialHourOfDay, mInitialMinute, mIs24HourView); + mRadialTimePickerView.initialize(mInitialHourOfDay, mInitialMinute, mIs24Hour); setCurrentItemShowing(index, false, true); } private void updateHeaderAmPm() { - - if (mIs24HourView) { + if (mIs24Hour) { mAmPmLayout.setVisibility(View.GONE); } else { // Ensure that AM/PM layout is in the correct position. - final String dateTimePattern = DateFormat.getBestDateTimePattern(mCurrentLocale, "hm"); + final String dateTimePattern = DateFormat.getBestDateTimePattern(mLocale, "hm"); final boolean isAmPmAtStart = dateTimePattern.startsWith("a"); setAmPmAtStart(isAmPmAtStart); @@ -395,35 +393,32 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate impl * Set the current hour. */ @Override - public void setCurrentHour(int currentHour) { - if (mInitialHourOfDay == currentHour) { - return; + public void setHour(int hour) { + if (mInitialHourOfDay != hour) { + mInitialHourOfDay = hour; + updateHeaderHour(hour, true); + updateHeaderAmPm(); + mRadialTimePickerView.setCurrentHour(hour); + mRadialTimePickerView.setAmOrPm(mInitialHourOfDay < 12 ? AM : PM); + mDelegator.invalidate(); + onTimeChanged(); } - mInitialHourOfDay = currentHour; - updateHeaderHour(currentHour, true); - updateHeaderAmPm(); - mRadialTimePickerView.setCurrentHour(currentHour); - mRadialTimePickerView.setAmOrPm(mInitialHourOfDay < 12 ? AM : PM); - mDelegator.invalidate(); - onTimeChanged(); } /** - * @return The current hour in the range (0-23). + * @return the current hour in the range (0-23) */ @Override - public int getCurrentHour() { - int currentHour = mRadialTimePickerView.getCurrentHour(); - if (mIs24HourView) { + public int getHour() { + final int currentHour = mRadialTimePickerView.getCurrentHour(); + if (mIs24Hour) { return currentHour; + } + + if (mRadialTimePickerView.getAmOrPm() == PM) { + return (currentHour % HOURS_IN_HALF_DAY) + HOURS_IN_HALF_DAY; } else { - switch(mRadialTimePickerView.getAmOrPm()) { - case PM: - return (currentHour % HOURS_IN_HALF_DAY) + HOURS_IN_HALF_DAY; - case AM: - default: - return currentHour % HOURS_IN_HALF_DAY; - } + return currentHour % HOURS_IN_HALF_DAY; } } @@ -431,48 +426,48 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate impl * Set the current minute (0-59). */ @Override - public void setCurrentMinute(int currentMinute) { - if (mInitialMinute == currentMinute) { - return; + public void setMinute(int minute) { + if (mInitialMinute != minute) { + mInitialMinute = minute; + updateHeaderMinute(minute, true); + mRadialTimePickerView.setCurrentMinute(minute); + mDelegator.invalidate(); + onTimeChanged(); } - mInitialMinute = currentMinute; - updateHeaderMinute(currentMinute, true); - mRadialTimePickerView.setCurrentMinute(currentMinute); - mDelegator.invalidate(); - onTimeChanged(); } /** * @return The current minute. */ @Override - public int getCurrentMinute() { + public int getMinute() { return mRadialTimePickerView.getCurrentMinute(); } /** - * Set whether in 24 hour or AM/PM mode. + * Sets whether time is displayed in 24-hour mode or 12-hour mode with + * AM/PM indicators. * - * @param is24HourView True = 24 hour mode. False = AM/PM. + * @param is24Hour {@code true} to display time in 24-hour mode or + * {@code false} for 12-hour mode with AM/PM */ - @Override - public void setIs24HourView(boolean is24HourView) { - if (is24HourView == mIs24HourView) { - return; - } - - mIs24HourView = is24HourView; - mInitialHourOfDay = getCurrentHour(); + public void setIs24Hour(boolean is24Hour) { + if (mIs24Hour != is24Hour) { + mIs24Hour = is24Hour; + mInitialHourOfDay = getHour(); - updateUI(mRadialTimePickerView.getCurrentItemShowing()); + updateUI(mRadialTimePickerView.getCurrentItemShowing()); + } } /** - * @return true if this is in 24 hour view else false. + * @return {@code true} if time is displayed in 24-hour mode, or + * {@code false} if time is displayed in 12-hour mode with AM/PM + * indicators */ @Override - public boolean is24HourView() { - return mIs24HourView; + public boolean is24Hour() { + return mIs24Hour; } @Override @@ -502,14 +497,9 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate impl } @Override - public void onConfigurationChanged(Configuration newConfig) { - updateUI(mRadialTimePickerView.getCurrentItemShowing()); - } - - @Override public Parcelable onSaveInstanceState(Parcelable superState) { - return new SavedState(superState, getCurrentHour(), getCurrentMinute(), - is24HourView(), getCurrentItemShowing()); + return new SavedState(superState, getHour(), getMinute(), + is24Hour(), getCurrentItemShowing()); } @Override @@ -520,12 +510,6 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate impl } @Override - public void setCurrentLocale(Locale locale) { - super.setCurrentLocale(locale); - mTempCalendar = Calendar.getInstance(locale); - } - - @Override public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { onPopulateAccessibilityEvent(event); return true; @@ -534,13 +518,13 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate impl @Override public void onPopulateAccessibilityEvent(AccessibilityEvent event) { int flags = DateUtils.FORMAT_SHOW_TIME; - if (mIs24HourView) { + if (mIs24Hour) { flags |= DateUtils.FORMAT_24HOUR; } else { flags |= DateUtils.FORMAT_12HOUR; } - mTempCalendar.set(Calendar.HOUR_OF_DAY, getCurrentHour()); - mTempCalendar.set(Calendar.MINUTE, getCurrentMinute()); + mTempCalendar.set(Calendar.HOUR_OF_DAY, getHour()); + mTempCalendar.set(Calendar.MINUTE, getMinute()); String selectedDate = DateUtils.formatDateTime(mContext, mTempCalendar.getTimeInMillis(), flags); event.getText().add(selectedDate); @@ -559,8 +543,7 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate impl private void onTimeChanged() { mDelegator.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED); if (mOnTimeChangedListener != null) { - mOnTimeChangedListener.onTimeChanged(mDelegator, - getCurrentHour(), getCurrentMinute()); + mOnTimeChangedListener.onTimeChanged(mDelegator, getHour(), getMinute()); } } @@ -666,7 +649,7 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate impl } if (mOnTimeChangedListener != null) { - mOnTimeChangedListener.onTimeChanged(mDelegator, getCurrentHour(), getCurrentMinute()); + mOnTimeChangedListener.onTimeChanged(mDelegator, getHour(), getMinute()); } } @@ -677,14 +660,14 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate impl * @return a localized hour number */ private int getLocalizedHour(int hourOfDay) { - if (!mIs24HourView) { + if (!mIs24Hour) { // Convert to hour-of-am-pm. hourOfDay %= 12; } if (!mHourFormatStartsAtZero && hourOfDay == 0) { // Convert to clock-hour (either of-day or of-am-pm). - hourOfDay = mIs24HourView ? 24 : 12; + hourOfDay = mIs24Hour ? 24 : 12; } return hourOfDay; @@ -716,8 +699,8 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate impl * separator as the character which is just after the hour marker in the returned pattern. */ private void updateHeaderSeparator() { - final String bestDateTimePattern = DateFormat.getBestDateTimePattern(mCurrentLocale, - (mIs24HourView) ? "Hm" : "hm"); + final String bestDateTimePattern = DateFormat.getBestDateTimePattern(mLocale, + (mIs24Hour) ? "Hm" : "hm"); final String separatorText; // See http://www.unicode.org/reports/tr35/tr35-dates.html for hour formats final char[] hourFormats = {'H', 'h', 'K', 'k'}; @@ -819,14 +802,14 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate impl private final Runnable mCommitHour = new Runnable() { @Override public void run() { - setCurrentHour(mHourView.getValue()); + setHour(mHourView.getValue()); } }; private final Runnable mCommitMinute = new Runnable() { @Override public void run() { - setCurrentMinute(mMinuteView.getValue()); + setMinute(mMinuteView.getValue()); } }; diff --git a/core/java/android/widget/TimePickerSpinnerDelegate.java b/core/java/android/widget/TimePickerSpinnerDelegate.java index 8741cc3b784f..2ed230bf1b35 100644 --- a/core/java/android/widget/TimePickerSpinnerDelegate.java +++ b/core/java/android/widget/TimePickerSpinnerDelegate.java @@ -17,7 +17,6 @@ package android.widget; import android.content.Context; -import android.content.res.Configuration; import android.content.res.TypedArray; import android.os.Parcel; import android.os.Parcelable; @@ -33,7 +32,6 @@ import android.view.inputmethod.InputMethodManager; import com.android.internal.R; import java.util.Calendar; -import java.util.Locale; import libcore.icu.LocaleData; @@ -92,7 +90,7 @@ class TimePickerSpinnerDelegate extends TimePicker.AbstractTimePickerDelegate { mHourSpinner.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() { public void onValueChange(NumberPicker spinner, int oldVal, int newVal) { updateInputState(); - if (!is24HourView()) { + if (!is24Hour()) { if ((oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY) || (oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1)) { mIsAm = !mIsAm; @@ -124,14 +122,14 @@ class TimePickerSpinnerDelegate extends TimePicker.AbstractTimePickerDelegate { int maxValue = mMinuteSpinner.getMaxValue(); if (oldVal == maxValue && newVal == minValue) { int newHour = mHourSpinner.getValue() + 1; - if (!is24HourView() && newHour == HOURS_IN_HALF_DAY) { + if (!is24Hour() && newHour == HOURS_IN_HALF_DAY) { mIsAm = !mIsAm; updateAmPmControl(); } mHourSpinner.setValue(newHour); } else if (oldVal == minValue && newVal == maxValue) { int newHour = mHourSpinner.getValue() - 1; - if (!is24HourView() && newHour == HOURS_IN_HALF_DAY - 1) { + if (!is24Hour() && newHour == HOURS_IN_HALF_DAY - 1) { mIsAm = !mIsAm; updateAmPmControl(); } @@ -204,8 +202,8 @@ class TimePickerSpinnerDelegate extends TimePicker.AbstractTimePickerDelegate { updateAmPmControl(); // set to current time - setCurrentHour(mTempCalendar.get(Calendar.HOUR_OF_DAY)); - setCurrentMinute(mTempCalendar.get(Calendar.MINUTE)); + setHour(mTempCalendar.get(Calendar.HOUR_OF_DAY)); + setMinute(mTempCalendar.get(Calendar.MINUTE)); if (!isEnabled()) { setEnabled(false); @@ -221,7 +219,7 @@ class TimePickerSpinnerDelegate extends TimePicker.AbstractTimePickerDelegate { } private void getHourFormatData() { - final String bestDateTimePattern = DateFormat.getBestDateTimePattern(mCurrentLocale, + final String bestDateTimePattern = DateFormat.getBestDateTimePattern(mLocale, (mIs24HourView) ? "Hm" : "hm"); final int lengthPattern = bestDateTimePattern.length(); mHourWithTwoDigit = false; @@ -241,7 +239,7 @@ class TimePickerSpinnerDelegate extends TimePicker.AbstractTimePickerDelegate { } private boolean isAmPmAtStart() { - final String bestDateTimePattern = DateFormat.getBestDateTimePattern(mCurrentLocale, + final String bestDateTimePattern = DateFormat.getBestDateTimePattern(mLocale, "hm" /* skeleton */); return bestDateTimePattern.startsWith("a"); @@ -257,7 +255,7 @@ class TimePickerSpinnerDelegate extends TimePicker.AbstractTimePickerDelegate { */ private void setDividerText() { final String skeleton = (mIs24HourView) ? "Hm" : "hm"; - final String bestDateTimePattern = DateFormat.getBestDateTimePattern(mCurrentLocale, + final String bestDateTimePattern = DateFormat.getBestDateTimePattern(mLocale, skeleton); final String separatorText; int hourIndex = bestDateTimePattern.lastIndexOf('H'); @@ -279,16 +277,16 @@ class TimePickerSpinnerDelegate extends TimePicker.AbstractTimePickerDelegate { } @Override - public void setCurrentHour(int currentHour) { - setCurrentHour(currentHour, true); + public void setHour(int hour) { + setCurrentHour(hour, true); } private void setCurrentHour(int currentHour, boolean notifyTimeChanged) { // why was Integer used in the first place? - if (currentHour == getCurrentHour()) { + if (currentHour == getHour()) { return; } - if (!is24HourView()) { + if (!is24Hour()) { // convert [0,23] ordinal to wall clock display if (currentHour >= HOURS_IN_HALF_DAY) { mIsAm = false; @@ -310,9 +308,9 @@ class TimePickerSpinnerDelegate extends TimePicker.AbstractTimePickerDelegate { } @Override - public int getCurrentHour() { + public int getHour() { int currentHour = mHourSpinner.getValue(); - if (is24HourView()) { + if (is24Hour()) { return currentHour; } else if (mIsAm) { return currentHour % HOURS_IN_HALF_DAY; @@ -322,28 +320,27 @@ class TimePickerSpinnerDelegate extends TimePicker.AbstractTimePickerDelegate { } @Override - public void setCurrentMinute(int currentMinute) { - if (currentMinute == getCurrentMinute()) { + public void setMinute(int minute) { + if (minute == getMinute()) { return; } - mMinuteSpinner.setValue(currentMinute); + mMinuteSpinner.setValue(minute); onTimeChanged(); } @Override - public int getCurrentMinute() { + public int getMinute() { return mMinuteSpinner.getValue(); } - @Override - public void setIs24HourView(boolean is24HourView) { - if (mIs24HourView == is24HourView) { + public void setIs24Hour(boolean is24Hour) { + if (mIs24HourView == is24Hour) { return; } // cache the current hour since spinner range changes and BEFORE changing mIs24HourView!! - int currentHour = getCurrentHour(); + int currentHour = getHour(); // Order is important here. - mIs24HourView = is24HourView; + mIs24HourView = is24Hour; getHourFormatData(); updateHourControl(); // set value after spinner range is updated @@ -353,7 +350,7 @@ class TimePickerSpinnerDelegate extends TimePicker.AbstractTimePickerDelegate { } @Override - public boolean is24HourView() { + public boolean is24Hour() { return mIs24HourView; } @@ -388,20 +385,15 @@ class TimePickerSpinnerDelegate extends TimePicker.AbstractTimePickerDelegate { } @Override - public void onConfigurationChanged(Configuration newConfig) { - setCurrentLocale(newConfig.locale); - } - - @Override public Parcelable onSaveInstanceState(Parcelable superState) { - return new SavedState(superState, getCurrentHour(), getCurrentMinute()); + return new SavedState(superState, getHour(), getMinute()); } @Override public void onRestoreInstanceState(Parcelable state) { SavedState ss = (SavedState) state; - setCurrentHour(ss.getHour()); - setCurrentMinute(ss.getMinute()); + setHour(ss.getHour()); + setMinute(ss.getMinute()); } @Override @@ -418,8 +410,8 @@ class TimePickerSpinnerDelegate extends TimePicker.AbstractTimePickerDelegate { } else { flags |= DateUtils.FORMAT_12HOUR; } - mTempCalendar.set(Calendar.HOUR_OF_DAY, getCurrentHour()); - mTempCalendar.set(Calendar.MINUTE, getCurrentMinute()); + mTempCalendar.set(Calendar.HOUR_OF_DAY, getHour()); + mTempCalendar.set(Calendar.MINUTE, getMinute()); String selectedDateUtterance = DateUtils.formatDateTime(mContext, mTempCalendar.getTimeInMillis(), flags); event.getText().add(selectedDateUtterance); @@ -447,7 +439,7 @@ class TimePickerSpinnerDelegate extends TimePicker.AbstractTimePickerDelegate { } private void updateAmPmControl() { - if (is24HourView()) { + if (is24Hour()) { if (mAmPmSpinner != null) { mAmPmSpinner.setVisibility(View.GONE); } else { @@ -466,27 +458,16 @@ class TimePickerSpinnerDelegate extends TimePicker.AbstractTimePickerDelegate { mDelegator.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED); } - /** - * Sets the current locale. - * - * @param locale The current locale. - */ - @Override - public void setCurrentLocale(Locale locale) { - super.setCurrentLocale(locale); - mTempCalendar = Calendar.getInstance(locale); - } - private void onTimeChanged() { mDelegator.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED); if (mOnTimeChangedListener != null) { - mOnTimeChangedListener.onTimeChanged(mDelegator, getCurrentHour(), - getCurrentMinute()); + mOnTimeChangedListener.onTimeChanged(mDelegator, getHour(), + getMinute()); } } private void updateHourControl() { - if (is24HourView()) { + if (is24Hour()) { // 'k' means 1-24 hour if (mHourFormat == 'k') { mHourSpinner.setMinValue(1); @@ -509,7 +490,7 @@ class TimePickerSpinnerDelegate extends TimePicker.AbstractTimePickerDelegate { } private void updateMinuteControl() { - if (is24HourView()) { + if (is24Hour()) { mMinuteSpinnerInput.setImeOptions(EditorInfo.IME_ACTION_DONE); } else { mMinuteSpinnerInput.setImeOptions(EditorInfo.IME_ACTION_NEXT); diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp index 703a9bd2fcd5..a805b6d1d151 100755 --- a/core/jni/android/graphics/Bitmap.cpp +++ b/core/jni/android/graphics/Bitmap.cpp @@ -1006,6 +1006,7 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) { // is disposed. int dupFd = dup(blob.fd()); if (dupFd < 0) { + ALOGE("Error allocating dup fd. Error:%d", errno); blob.release(); SkSafeUnref(ctable); doThrowRE(env, "Could not allocate dup blob fd."); diff --git a/docs/html/guide/topics/manifest/uses-feature-element.jd b/docs/html/guide/topics/manifest/uses-feature-element.jd index e22dc4a4bda2..21e3057aaf04 100644 --- a/docs/html/guide/topics/manifest/uses-feature-element.jd +++ b/docs/html/guide/topics/manifest/uses-feature-element.jd @@ -563,8 +563,8 @@ is sensitive to delays or lag in sound input or output.</td> <td rowspan="6">Camera</td> <td><code>android.hardware.camera</code></td> <td>The application uses the device's back-facing (main) camera.</td> - <td>Importantly, devices with only a front-facing camera will not list this - feature, so the <code>android.hardware.camera.any</code> feature should be + <td>Devices with only a front-facing camera do not list this feature, so the + <code>android.hardware.camera.any</code> feature should be used instead if a camera facing any direction is acceptable for the application.</td> </tr> diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp index a327614a50ef..94a11f131229 100644 --- a/libs/hwui/Caches.cpp +++ b/libs/hwui/Caches.cpp @@ -79,7 +79,6 @@ bool Caches::init() { } void Caches::initExtensions() { - mExtensions.load(); if (mExtensions.hasDebugMarker()) { eventMark = glInsertEventMarkerEXT; diff --git a/libs/hwui/DeviceInfo.cpp b/libs/hwui/DeviceInfo.cpp index 03b1706faa49..39b7ecb9a914 100644 --- a/libs/hwui/DeviceInfo.cpp +++ b/libs/hwui/DeviceInfo.cpp @@ -40,7 +40,6 @@ void DeviceInfo::initialize() { } void DeviceInfo::load() { - mExtensions.load(); glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize); } diff --git a/libs/hwui/Extensions.cpp b/libs/hwui/Extensions.cpp index e257715acaeb..02caaa49e99c 100644 --- a/libs/hwui/Extensions.cpp +++ b/libs/hwui/Extensions.cpp @@ -35,7 +35,7 @@ namespace uirenderer { #endif -void Extensions::load() { +Extensions::Extensions() { auto extensions = StringUtils::split((const char*) glGetString(GL_EXTENSIONS)); mHasNPot = extensions.has("GL_OES_texture_npot"); mHasFramebufferFetch = extensions.has("GL_NV_shader_framebuffer_fetch"); diff --git a/libs/hwui/Extensions.h b/libs/hwui/Extensions.h index 8ccfabdd2450..67cc747015e0 100644 --- a/libs/hwui/Extensions.h +++ b/libs/hwui/Extensions.h @@ -31,7 +31,7 @@ namespace uirenderer { class Extensions { public: - void load(); + Extensions(); inline bool hasNPot() const { return mHasNPot; } inline bool hasFramebufferFetch() const { return mHasFramebufferFetch; } diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/CopyTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/CopyTest.java index 6f1a89baf525..369ab7dc59ea 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/CopyTest.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/CopyTest.java @@ -32,6 +32,8 @@ import android.provider.DocumentsContract.Document; import android.test.MoreAsserts; import android.test.ServiceTestCase; import android.test.mock.MockContentResolver; +import android.test.suitebuilder.annotation.MediumTest; +import android.test.suitebuilder.annotation.SmallTest; import android.util.Log; import com.android.documentsui.model.DocumentInfo; @@ -52,6 +54,7 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +@MediumTest public class CopyTest extends ServiceTestCase<CopyService> { public CopyTest() { @@ -89,9 +92,6 @@ public class CopyTest extends ServiceTestCase<CopyService> { super.tearDown(); } - /** - * Test copying a single file. - */ public void testCopyFile() throws Exception { String srcPath = "/test0.txt"; Uri testFile = mStorage.createFile(SRC_ROOT, srcPath, "text/plain", @@ -131,9 +131,6 @@ public class CopyTest extends ServiceTestCase<CopyService> { MoreAsserts.assertEquals("Moved file contents differ", testContent.getBytes(), dstContent); } - /** - * Test copying multiple files. - */ public void testCopyMultipleFiles() throws Exception { String testContent[] = { "The five boxing wizards jump quickly", diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java index 906051640c6a..ba91c83b95e8 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java @@ -31,11 +31,13 @@ import android.support.test.uiautomator.Configurator; import android.support.test.uiautomator.UiDevice; import android.support.test.uiautomator.Until; import android.test.InstrumentationTestCase; +import android.test.suitebuilder.annotation.LargeTest; import android.util.Log; import android.view.MotionEvent; import com.android.documentsui.model.RootInfo; +@LargeTest public class FilesActivityUiTest extends InstrumentationTestCase { private static final int TIMEOUT = 5000; diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/DirectoryFragmentModelTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/DirectoryFragmentModelTest.java index 746e2117dc02..b250e5d344c4 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/DirectoryFragmentModelTest.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/DirectoryFragmentModelTest.java @@ -25,6 +25,7 @@ import android.provider.DocumentsContract.Document; import android.support.v7.widget.RecyclerView; import android.test.AndroidTestCase; import android.test.mock.MockContentResolver; +import android.test.suitebuilder.annotation.SmallTest; import android.view.ViewGroup; import com.android.documentsui.DirectoryResult; @@ -34,6 +35,7 @@ import com.android.documentsui.model.DocumentInfo; import java.util.List; +@SmallTest public class DirectoryFragmentModelTest extends AndroidTestCase { private static final int ITEM_COUNT = 5; diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManagerTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManagerTest.java index d1ce56457a87..b3d45aee48b1 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManagerTest.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManagerTest.java @@ -18,6 +18,7 @@ package com.android.documentsui.dirlist; import android.support.v7.widget.RecyclerView; import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.SmallTest; import android.util.SparseBooleanArray; import android.view.View; import android.view.ViewGroup; @@ -32,6 +33,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +@SmallTest public class MultiSelectManagerTest extends AndroidTestCase { private static final List<String> items; @@ -163,7 +165,6 @@ public class MultiSelectManagerTest extends AndroidTestCase { assertRangeSelection(14, 17); } - public void testSingleTapUp_ShiftReversesSelectionDirection() { longPress(7); shiftTap(17); diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManager_GridModelTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManager_GridModelTest.java index c4b6ce5c7f87..c856b2290ea1 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManager_GridModelTest.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManager_GridModelTest.java @@ -20,11 +20,13 @@ import android.graphics.Point; import android.graphics.Rect; import android.support.v7.widget.RecyclerView.OnScrollListener; import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.SmallTest; import android.util.SparseBooleanArray; import android.view.View; import com.android.documentsui.dirlist.MultiSelectManager.GridModel; +@SmallTest public class MultiSelectManager_GridModelTest extends AndroidTestCase { private static final int VIEW_PADDING_PX = 5; diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManager_SelectionTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManager_SelectionTest.java index 64da750b1306..72fc10814e72 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManager_SelectionTest.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManager_SelectionTest.java @@ -17,10 +17,11 @@ package com.android.documentsui.dirlist; import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.SmallTest; import com.android.documentsui.dirlist.MultiSelectManager.Selection; - +@SmallTest public class MultiSelectManager_SelectionTest extends AndroidTestCase{ private Selection selection; diff --git a/packages/PrintSpooler/res/drawable/ic_add.xml b/packages/PrintSpooler/res/drawable/ic_add.xml new file mode 100644 index 000000000000..1442b1b61f46 --- /dev/null +++ b/packages/PrintSpooler/res/drawable/ic_add.xml @@ -0,0 +1,25 @@ +<?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. +--> + +<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:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z" + android:fillColor="#FFFFFF"/> +</vector>
\ No newline at end of file diff --git a/packages/PrintSpooler/res/drawable/ic_search.xml b/packages/PrintSpooler/res/drawable/ic_search.xml deleted file mode 100644 index 991fa38b2da4..000000000000 --- a/packages/PrintSpooler/res/drawable/ic_search.xml +++ /dev/null @@ -1,43 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2014 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<selector xmlns:android="http://schemas.android.com/apk/res/android" - android:autoMirrored="true"> - - <item - android:state_checked="true"> - <bitmap - android:src="@*android:drawable/ic_menu_search" - android:tint="?android:attr/colorControlActivated"> - </bitmap> - </item> - - <item - android:state_pressed="true"> - <bitmap - android:src="@*android:drawable/ic_menu_search" - android:tint="?android:attr/colorControlActivated"> - </bitmap> - </item> - - <item> - <bitmap - android:src="@*android:drawable/ic_menu_search" - android:tint="?android:attr/colorControlNormal"> - </bitmap> - </item> - -</selector> diff --git a/packages/PrintSpooler/res/layout/printer_dropdown_prompt.xml b/packages/PrintSpooler/res/layout/printer_dropdown_prompt.xml new file mode 100644 index 000000000000..11fef2d21fde --- /dev/null +++ b/packages/PrintSpooler/res/layout/printer_dropdown_prompt.xml @@ -0,0 +1,28 @@ +<?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. +--> + +<TextView xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textIsSelectable="false" + android:textColor="?android:attr/textColorPrimary" + android:paddingStart="20dip" + android:paddingEnd="8dip" + android:minHeight="56dip" + android:orientation="horizontal" + android:text="@string/destination_default_text" + android:gravity="start|center_vertical" /> diff --git a/packages/PrintSpooler/res/menu/select_printer_activity.xml b/packages/PrintSpooler/res/menu/select_printer_activity.xml index 8da57694007b..15cc13939cf0 100644 --- a/packages/PrintSpooler/res/menu/select_printer_activity.xml +++ b/packages/PrintSpooler/res/menu/select_printer_activity.xml @@ -19,7 +19,7 @@ <item android:id="@+id/action_search" android:title="@string/search" - android:icon="@*android:drawable/ic_search" + android:icon="@*android:drawable/ic_search_api_material" android:actionViewClass="android.widget.SearchView" android:showAsAction="ifRoom|collapseActionView" android:alphabeticShortcut="f" @@ -29,7 +29,7 @@ <item android:id="@+id/action_add_printer" android:title="@string/print_add_printer" - android:icon="@*android:drawable/create_contact" + android:icon="@drawable/ic_add" android:showAsAction="ifRoom" android:alphabeticShortcut="a"> </item> diff --git a/packages/PrintSpooler/res/values/strings.xml b/packages/PrintSpooler/res/values/strings.xml index 70abdf4920bb..6d8178844fba 100644 --- a/packages/PrintSpooler/res/values/strings.xml +++ b/packages/PrintSpooler/res/values/strings.xml @@ -49,6 +49,9 @@ <!-- Label of the page selection widget. [CHAR LIMIT=20] --> <string name="label_pages">Pages</string> + <!-- Label of the destination widget. [CHAR LIMIT=20] --> + <string name="destination_default_text">Select a printer</string> + <!-- Template for the all pages option in the page selection widget. [CHAR LIMIT=20] --> <string name="template_all_pages">All <xliff:g id="page_count" example="100">%1$s</xliff:g></string> diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java index f409fd43d085..e7588359478a 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java +++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java @@ -64,6 +64,7 @@ import android.text.TextWatcher; import android.util.ArrayMap; import android.util.Log; import android.view.KeyEvent; +import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnFocusChangeListener; @@ -125,6 +126,8 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat private static final String FRAGMENT_TAG = "FRAGMENT_TAG"; + private static final String HAS_PRINTED_PREF = "has_printed"; + private static final int ORIENTATION_PORTRAIT = 0; private static final int ORIENTATION_LANDSCAPE = 1; @@ -187,6 +190,7 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat private Spinner mDestinationSpinner; private DestinationAdapter mDestinationSpinnerAdapter; + private boolean mShowDestinationPrompt; private Spinner mMediaSizeSpinner; private ArrayAdapter<SpinnerItem<MediaSize>> mMediaSizeSpinnerAdapter; @@ -1093,6 +1097,7 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat updateOptionsUi(); addCurrentPrinterToHistory(); + setUserPrinted(); PageRange[] selectedPages = computeSelectedPages(); if (!Arrays.equals(mSelectedPages, selectedPages)) { @@ -1195,6 +1200,29 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat // Print button mPrintButton = (ImageView) findViewById(R.id.print_button); mPrintButton.setOnClickListener(clickListener); + + // Special prompt instead of destination spinner for the first time the user printed + if (!hasUserEverPrinted()) { + mShowDestinationPrompt = true; + + mSummaryCopies.setEnabled(false); + mSummaryPaperSize.setEnabled(false); + + mDestinationSpinner.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + mShowDestinationPrompt = false; + mSummaryCopies.setEnabled(true); + mSummaryPaperSize.setEnabled(true); + updateOptionsUi(); + + mDestinationSpinner.setOnTouchListener(null); + mDestinationSpinnerAdapter.notifyDataSetChanged(); + + return false; + } + }); + } } /** @@ -1332,6 +1360,22 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat && printer.getStatus() != PrinterInfo.STATUS_UNAVAILABLE; } + /** + * Disable all options UI elements, beside the {@link #mDestinationSpinner} + */ + private void disableOptionsUi() { + mCopiesEditText.setEnabled(false); + mCopiesEditText.setFocusable(false); + mMediaSizeSpinner.setEnabled(false); + mColorModeSpinner.setEnabled(false); + mDuplexModeSpinner.setEnabled(false); + mOrientationSpinner.setEnabled(false); + mRangeOptionsSpinner.setEnabled(false); + mPageRangeEditText.setEnabled(false); + mPrintButton.setVisibility(View.GONE); + mMoreOptionsButton.setEnabled(false); + } + void updateOptionsUi() { // Always update the summary. updateSummary(); @@ -1346,32 +1390,14 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat if (mState != STATE_PRINTER_UNAVAILABLE) { mDestinationSpinner.setEnabled(false); } - mCopiesEditText.setEnabled(false); - mCopiesEditText.setFocusable(false); - mMediaSizeSpinner.setEnabled(false); - mColorModeSpinner.setEnabled(false); - mDuplexModeSpinner.setEnabled(false); - mOrientationSpinner.setEnabled(false); - mRangeOptionsSpinner.setEnabled(false); - mPageRangeEditText.setEnabled(false); - mPrintButton.setVisibility(View.GONE); - mMoreOptionsButton.setEnabled(false); + disableOptionsUi(); return; } // If no current printer, or it has no capabilities, or it is not // available, we disable all print options except the destination. if (mCurrentPrinter == null || !canPrint(mCurrentPrinter)) { - mCopiesEditText.setEnabled(false); - mCopiesEditText.setFocusable(false); - mMediaSizeSpinner.setEnabled(false); - mColorModeSpinner.setEnabled(false); - mDuplexModeSpinner.setEnabled(false); - mOrientationSpinner.setEnabled(false); - mRangeOptionsSpinner.setEnabled(false); - mPageRangeEditText.setEnabled(false); - mPrintButton.setVisibility(View.GONE); - mMoreOptionsButton.setEnabled(false); + disableOptionsUi(); return; } @@ -1679,6 +1705,10 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat mCopiesEditText.setText(MIN_COPIES_STRING); mCopiesEditText.requestFocus(); } + + if (mShowDestinationPrompt) { + disableOptionsUi(); + } } private void updateSummary() { @@ -1980,6 +2010,32 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat } } + + /** + * Check if the user has ever printed a document + * + * @return true iff the user has ever printed a document + */ + private boolean hasUserEverPrinted() { + SharedPreferences preferences = getSharedPreferences(HAS_PRINTED_PREF, MODE_PRIVATE); + + return preferences.getBoolean(HAS_PRINTED_PREF, false); + } + + /** + * Remember that the user printed a document + */ + private void setUserPrinted() { + SharedPreferences preferences = getSharedPreferences(HAS_PRINTED_PREF, MODE_PRIVATE); + + if (!preferences.getBoolean(HAS_PRINTED_PREF, false)) { + SharedPreferences.Editor edit = preferences.edit(); + + edit.putBoolean(HAS_PRINTED_PREF, true); + edit.apply(); + } + } + private final class DestinationAdapter extends BaseAdapter implements PrinterRegistry.OnPrintersChangeListener { private final List<PrinterHolder> mPrinterHolders = new ArrayList<>(); @@ -1988,6 +2044,11 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat private boolean mHistoricalPrintersLoaded; + /** + * Has the {@link #mDestinationSpinner} ever used a view from printer_dropdown_prompt + */ + private boolean hadPromptView; + public DestinationAdapter() { mHistoricalPrintersLoaded = mPrinterRegistry.areHistoricalPrintersLoaded(); if (mHistoricalPrintersLoaded) { @@ -2098,9 +2159,20 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat @Override public View getView(int position, View convertView, ViewGroup parent) { - if (convertView == null) { - convertView = getLayoutInflater().inflate( - R.layout.printer_dropdown_item, parent, false); + if (mShowDestinationPrompt) { + if (convertView == null) { + convertView = getLayoutInflater().inflate( + R.layout.printer_dropdown_prompt, parent, false); + hadPromptView = true; + } + + return convertView; + } else { + // We don't know if we got an recyled printer_dropdown_prompt, hence do not use it + if (hadPromptView || convertView == null) { + convertView = getLayoutInflater().inflate( + R.layout.printer_dropdown_item, parent, false); + } } CharSequence title = null; diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 9e48849226a3..a37196e48891 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -348,4 +348,7 @@ <!-- Header for items under the work user [CHAR LIMIT=30] --> <string name="category_work">Work</string> + <!-- Full package name of OEM preferred device feedback reporter. Leave this blank, overlaid in Settings/TvSettings [DO NOT TRANSLATE] --> + <string name="oem_preferred_feedback_reporter" translatable="false" /> + </resources> diff --git a/packages/SettingsLib/src/com/android/settingslib/DeviceInfoUtils.java b/packages/SettingsLib/src/com/android/settingslib/DeviceInfoUtils.java new file mode 100644 index 000000000000..ff1c8665b3d2 --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/DeviceInfoUtils.java @@ -0,0 +1,172 @@ +/* + * 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 + */ + +package com.android.settingslib; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.os.Build; +import android.text.TextUtils; +import android.text.format.DateFormat; +import android.util.Log; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; +import java.util.Locale; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class DeviceInfoUtils { + private static final String TAG = "DeviceInfoUtils"; + + private static final String FILENAME_PROC_VERSION = "/proc/version"; + private static final String FILENAME_MSV = "/sys/board_properties/soc/msv"; + + /** + * Reads a line from the specified file. + * @param filename the file to read from + * @return the first line, if any. + * @throws IOException if the file couldn't be read + */ + private static String readLine(String filename) throws IOException { + BufferedReader reader = new BufferedReader(new FileReader(filename), 256); + try { + return reader.readLine(); + } finally { + reader.close(); + } + } + + public static String getFormattedKernelVersion() { + try { + return formatKernelVersion(readLine(FILENAME_PROC_VERSION)); + } catch (IOException e) { + Log.e(TAG, "IO Exception when getting kernel version for Device Info screen", + e); + + return "Unavailable"; + } + } + + public static String formatKernelVersion(String rawKernelVersion) { + // Example (see tests for more): + // Linux version 3.0.31-g6fb96c9 (android-build@xxx.xxx.xxx.xxx.com) \ + // (gcc version 4.6.x-xxx 20120106 (prerelease) (GCC) ) #1 SMP PREEMPT \ + // Thu Jun 28 11:02:39 PDT 2012 + + final String PROC_VERSION_REGEX = + "Linux version (\\S+) " + /* group 1: "3.0.31-g6fb96c9" */ + "\\((\\S+?)\\) " + /* group 2: "x@y.com" (kernel builder) */ + "(?:\\(gcc.+? \\)) " + /* ignore: GCC version information */ + "(#\\d+) " + /* group 3: "#1" */ + "(?:.*?)?" + /* ignore: optional SMP, PREEMPT, and any CONFIG_FLAGS */ + "((Sun|Mon|Tue|Wed|Thu|Fri|Sat).+)"; /* group 4: "Thu Jun 28 11:02:39 PDT 2012" */ + + Matcher m = Pattern.compile(PROC_VERSION_REGEX).matcher(rawKernelVersion); + if (!m.matches()) { + Log.e(TAG, "Regex did not match on /proc/version: " + rawKernelVersion); + return "Unavailable"; + } else if (m.groupCount() < 4) { + Log.e(TAG, "Regex match on /proc/version only returned " + m.groupCount() + + " groups"); + return "Unavailable"; + } + return m.group(1) + "\n" + // 3.0.31-g6fb96c9 + m.group(2) + " " + m.group(3) + "\n" + // x@y.com #1 + m.group(4); // Thu Jun 28 11:02:39 PDT 2012 + } + + /** + * Returns " (ENGINEERING)" if the msv file has a zero value, else returns "". + * @return a string to append to the model number description. + */ + public static String getMsvSuffix() { + // Production devices should have a non-zero value. If we can't read it, assume it's a + // production device so that we don't accidentally show that it's an ENGINEERING device. + try { + String msv = readLine(FILENAME_MSV); + // Parse as a hex number. If it evaluates to a zero, then it's an engineering build. + if (Long.parseLong(msv, 16) == 0) { + return " (ENGINEERING)"; + } + } catch (IOException|NumberFormatException e) { + // Fail quietly, as the file may not exist on some devices, or may be unreadable + } + return ""; + } + + public static String getFeedbackReporterPackage(Context context) { + final String feedbackReporter = + context.getResources().getString(R.string.oem_preferred_feedback_reporter); + if (TextUtils.isEmpty(feedbackReporter)) { + // Reporter not configured. Return. + return feedbackReporter; + } + // Additional checks to ensure the reporter is on system image, and reporter is + // configured to listen to the intent. Otherwise, dont show the "send feedback" option. + final Intent intent = new Intent(Intent.ACTION_BUG_REPORT); + + PackageManager pm = context.getPackageManager(); + List<ResolveInfo> resolvedPackages = + pm.queryIntentActivities(intent, PackageManager.GET_RESOLVED_FILTER); + for (ResolveInfo info : resolvedPackages) { + if (info.activityInfo != null) { + if (!TextUtils.isEmpty(info.activityInfo.packageName)) { + try { + ApplicationInfo ai = + pm.getApplicationInfo(info.activityInfo.packageName, 0); + if ((ai.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { + // Package is on the system image + if (TextUtils.equals( + info.activityInfo.packageName, feedbackReporter)) { + return feedbackReporter; + } + } + } catch (PackageManager.NameNotFoundException e) { + // No need to do anything here. + } + } + } + } + return null; + } + + public static String getSecurityPatch() { + String patch = Build.VERSION.SECURITY_PATCH; + if (!"".equals(patch)) { + try { + SimpleDateFormat template = new SimpleDateFormat("yyyy-MM-dd"); + Date patchDate = template.parse(patch); + String format = DateFormat.getBestDateTimePattern(Locale.getDefault(), "dMMMMyyyy"); + patch = DateFormat.format(format, patchDate).toString(); + } catch (ParseException e) { + // broken parse; fall through and use the raw string + } + return patch; + } else { + return null; + } + } + +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index 3f0000e645d5..069279fdafe3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -62,6 +62,7 @@ import android.service.notification.NotificationListenerService; import android.service.notification.NotificationListenerService.RankingMap; import android.service.notification.StatusBarNotification; import android.text.TextUtils; +import android.util.ArraySet; import android.util.Log; import android.util.Slog; import android.util.SparseArray; @@ -175,6 +176,7 @@ public abstract class BaseStatusBar extends SystemUI implements protected boolean mDeviceInteractive; protected boolean mVisible; + protected ArraySet<Entry> mHeadsUpEntriesToRemoveOnSwitch = new ArraySet<>(); // mScreenOnFromKeyguard && mVisible. private boolean mVisibleToUser; @@ -1664,7 +1666,10 @@ public abstract class BaseStatusBar extends SystemUI implements return; } - final PendingIntent intent = sbn.getNotification().contentIntent; + Notification notification = sbn.getNotification(); + final PendingIntent intent = notification.contentIntent != null + ? notification.contentIntent + : notification.fullScreenIntent; final String notificationKey = sbn.getKey(); // Mark notification for one frame. @@ -1746,8 +1751,8 @@ public abstract class BaseStatusBar extends SystemUI implements } public void register(ExpandableNotificationRow row, StatusBarNotification sbn) { - final PendingIntent contentIntent = sbn.getNotification().contentIntent; - if (contentIntent != null) { + Notification notification = sbn.getNotification(); + if (notification.contentIntent != null || notification.fullScreenIntent != null) { row.setOnClickListener(this); } else { row.setOnClickListener(null); @@ -2013,6 +2018,8 @@ public abstract class BaseStatusBar extends SystemUI implements Entry entry = mNotificationData.get(key); if (entry == null) { return; + } else if (mHeadsUpEntriesToRemoveOnSwitch.contains(entry)) { + mHeadsUpEntriesToRemoveOnSwitch.remove(entry); } Notification n = notification.getNotification(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java index bbef1c031ff5..fbe97300aa3d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java @@ -84,10 +84,10 @@ public class NotificationGroupManager { // the close future. See b/23676310 for reference. return; } - if (notif.isGroupSummary()) { - group.summary = null; - } else { + if (notif.isGroupChild()) { group.children.remove(removed); + } else { + group.summary = null; } if (group.children.isEmpty()) { if (group.summary == null) { @@ -107,17 +107,17 @@ public class NotificationGroupManager { group = new NotificationGroup(); mGroupMap.put(groupKey, group); } - if (notif.isGroupSummary()) { + if (notif.isGroupChild()) { + group.children.add(added); + if (group.summary != null && group.children.size() == 1 && !group.expanded) { + group.summary.row.updateNotificationHeader(); + } + } else { group.summary = added; group.expanded = added.row.areChildrenExpanded(); if (!group.children.isEmpty()) { mListener.onGroupCreatedFromChildren(group); } - } else { - group.children.add(added); - if (group.summary != null && group.children.size() == 1 && !group.expanded) { - group.summary.row.updateNotificationHeader(); - } } } @@ -169,7 +169,7 @@ public class NotificationGroupManager { * @return whether a given notification is a summary in a group which has children */ public boolean isSummaryOfGroup(StatusBarNotification sbn) { - if (sbn.getNotification().isGroupChild()) { + if (!sbn.getNotification().isGroupSummary()) { return false; } NotificationGroup group = mGroupMap.get(sbn.getGroupKey()); 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 3e5251587820..9ab2a2c28793 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -617,7 +617,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, }; private HashMap<ExpandableNotificationRow, List<ExpandableNotificationRow>> mTmpChildOrderMap = new HashMap<>(); - private HashSet<Entry> mHeadsUpEntriesToRemoveOnSwitch = new HashSet<>(); private RankingMap mLatestRankingMap; private boolean mNoAnimationOnNextBarModeChange; private FalsingManager mFalsingManager; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java index dc9f5e863379..5cfd17463886 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java @@ -458,7 +458,10 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL mReleaseOnExpandFinish = false; } else { for (NotificationData.Entry entry : mEntriesToRemoveAfterExpand) { - removeHeadsUpEntry(entry); + if (isHeadsUp(entry.key)) { + // Maybe the heads-up was removed already + removeHeadsUpEntry(entry); + } } } mEntriesToRemoveAfterExpand.clear(); @@ -596,6 +599,9 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL postTime = Math.max(postTime, currentTime); } removeAutoRemovalCallbacks(); + if (mEntriesToRemoveAfterExpand.contains(entry)) { + mEntriesToRemoveAfterExpand.remove(entry); + } if (!hasFullScreenIntent(entry) && !mRemoteInputActive) { long finishTime = postTime + mHeadsUpNotificationDecay; long removeDelay = Math.max(finishTime - currentTime, mMinimumDisplayTime); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index c1525144765b..add7a982c38f 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -1956,8 +1956,7 @@ public class PackageManagerService extends IPackageManager.Stub { mUserAppDataDir = new File(dataDir, "user"); mDrmAppPrivateInstallDir = new File(dataDir, "app-private"); - sUserManager = new UserManagerService(context, this, - mInstallLock, mPackages); + sUserManager = new UserManagerService(context, this, mPackages); // Propagate permission configuration in to package manager. ArrayMap<String, SystemConfig.PermissionEntry> permConfig @@ -16353,23 +16352,26 @@ public class PackageManagerService extends IPackageManager.Stub { } /** Called by UserManagerService */ - void cleanUpUserLILPw(UserManagerService userManager, int userHandle) { - mDirtyUsers.remove(userHandle); - mSettings.removeUserLPw(userHandle); - mPendingBroadcasts.remove(userHandle); - if (mInstaller != null) { - // Technically, we shouldn't be doing this with the package lock - // held. However, this is very rare, and there is already so much - // other disk I/O going on, that we'll let it slide for now. - final StorageManager storage = mContext.getSystemService(StorageManager.class); - for (VolumeInfo vol : storage.getWritablePrivateVolumes()) { - final String volumeUuid = vol.getFsUuid(); - if (DEBUG_INSTALL) Slog.d(TAG, "Removing user data on volume " + volumeUuid); - mInstaller.removeUserDataDirs(volumeUuid, userHandle); + void cleanUpUser(UserManagerService userManager, int userHandle) { + synchronized (mPackages) { + mDirtyUsers.remove(userHandle); + mUserNeedsBadging.delete(userHandle); + mSettings.removeUserLPw(userHandle); + mPendingBroadcasts.remove(userHandle); + } + synchronized (mInstallLock) { + if (mInstaller != null) { + final StorageManager storage = mContext.getSystemService(StorageManager.class); + for (VolumeInfo vol : storage.getWritablePrivateVolumes()) { + final String volumeUuid = vol.getFsUuid(); + if (DEBUG_INSTALL) Slog.d(TAG, "Removing user data on volume " + volumeUuid); + mInstaller.removeUserDataDirs(volumeUuid, userHandle); + } + } + synchronized (mPackages) { + removeUnusedPackagesLILPw(userManager, userHandle); } } - mUserNeedsBadging.delete(userHandle); - removeUnusedPackagesLILPw(userManager, userHandle); } /** @@ -16419,12 +16421,18 @@ public class PackageManagerService extends IPackageManager.Stub { } /** Called by UserManagerService */ - void createNewUserLILPw(int userHandle) { + void createNewUser(int userHandle) { if (mInstaller != null) { - mInstaller.createUserConfig(userHandle); - mSettings.createNewUserLILPw(this, mInstaller, userHandle); - applyFactoryDefaultBrowserLPw(userHandle); - primeDomainVerificationsLPw(userHandle); + synchronized (mInstallLock) { + synchronized (mPackages) { + mInstaller.createUserConfig(userHandle); + mSettings.createNewUserLILPw(this, mInstaller, userHandle); + } + } + synchronized (mPackages) { + applyFactoryDefaultBrowserLPw(userHandle); + primeDomainVerificationsLPw(userHandle); + } } } diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java index 5d8b1d281d54..903d12bcc217 100644 --- a/services/core/java/com/android/server/pm/SELinuxMMAC.java +++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java @@ -103,6 +103,9 @@ public final class SELinuxMMAC { // Append privapp to existing seinfo label private static final String PRIVILEGED_APP_STR = ":privapp"; + // Append autoplay to existing seinfo label + private static final String AUTOPLAY_APP_STR = ":autoplayapp"; + /** * Load the mac_permissions.xml file containing all seinfo assignments used to * label apps. The loaded mac_permissions.xml file is determined by the @@ -316,6 +319,9 @@ public final class SELinuxMMAC { } } + if (pkg.applicationInfo.isAutoPlayApp()) + pkg.applicationInfo.seinfo += AUTOPLAY_APP_STR; + if (pkg.applicationInfo.isPrivilegedApp()) pkg.applicationInfo.seinfo += PRIVILEGED_APP_STR; diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 3a1d2de0d37c..ab0b182762e1 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -97,8 +97,7 @@ import libcore.io.IoUtils; * * Method naming convention: * <ul> - * <li> Methods suffixed with "LILP" should be called within {@link #mInstallLock} and - * {@link #mPackagesLock} locks obtained in the respective order. + * <li> Methods suffixed with "LP" should be called within the {@link #mPackagesLock} lock. * <li> Methods suffixed with "LR" should be called within the {@link #mRestrictionsLock} lock. * <li> Methods suffixed with "LU" should be called within the {@link #mUsersLock} lock. * </ul> @@ -164,7 +163,6 @@ public class UserManagerService extends IUserManager.Stub { private final Context mContext; private final PackageManagerService mPm; - private final Object mInstallLock; private final Object mPackagesLock; // Short-term lock for internal state, when interaction/sync with PM is not required private final Object mUsersLock = new Object(); @@ -215,6 +213,7 @@ public class UserManagerService extends IUserManager.Stub { @GuardedBy("mRestrictionsLock") private final SparseArray<Bundle> mAppliedUserRestrictions = new SparseArray<>(); + @GuardedBy("mGuestRestrictions") private final Bundle mGuestRestrictions = new Bundle(); /** @@ -226,6 +225,7 @@ public class UserManagerService extends IUserManager.Stub { @GuardedBy("mUsersLock") private int[] mUserIds; + @GuardedBy("mPackagesLock") private int mNextSerialNumber; private int mUserVersion = 0; @@ -245,11 +245,9 @@ public class UserManagerService extends IUserManager.Stub { } } - /** - * Available for testing purposes. - */ - UserManagerService(File dataDir, File baseUserPath) { - this(null, null, new Object(), new Object(), dataDir, baseUserPath); + @VisibleForTesting + UserManagerService(File dataDir) { + this(null, null, new Object(), dataDir); } /** @@ -257,68 +255,53 @@ public class UserManagerService extends IUserManager.Stub { * associated with the package manager, and the given lock is the * package manager's own lock. */ - UserManagerService(Context context, PackageManagerService pm, - Object installLock, Object packagesLock) { - this(context, pm, installLock, packagesLock, - Environment.getDataDirectory(), - new File(Environment.getDataDirectory(), "user")); + UserManagerService(Context context, PackageManagerService pm, Object packagesLock) { + this(context, pm, packagesLock, Environment.getDataDirectory()); } - /** - * Available for testing purposes. - */ private UserManagerService(Context context, PackageManagerService pm, - Object installLock, Object packagesLock, - File dataDir, File baseUserPath) { + Object packagesLock, File dataDir) { mContext = context; mPm = pm; - mInstallLock = installLock; mPackagesLock = packagesLock; mHandler = new MainHandler(); - synchronized (mInstallLock) { - synchronized (mPackagesLock) { - mUsersDir = new File(dataDir, USER_INFO_DIR); - mUsersDir.mkdirs(); - // Make zeroth user directory, for services to migrate their files to that location - File userZeroDir = new File(mUsersDir, String.valueOf(UserHandle.USER_SYSTEM)); - userZeroDir.mkdirs(); - FileUtils.setPermissions(mUsersDir.toString(), - FileUtils.S_IRWXU|FileUtils.S_IRWXG - |FileUtils.S_IROTH|FileUtils.S_IXOTH, - -1, -1); - mUserListFile = new File(mUsersDir, USER_LIST_FILENAME); - initDefaultGuestRestrictions(); - readUserListLILP(); - sInstance = this; - } + synchronized (mPackagesLock) { + mUsersDir = new File(dataDir, USER_INFO_DIR); + mUsersDir.mkdirs(); + // Make zeroth user directory, for services to migrate their files to that location + File userZeroDir = new File(mUsersDir, String.valueOf(UserHandle.USER_SYSTEM)); + userZeroDir.mkdirs(); + FileUtils.setPermissions(mUsersDir.toString(), + FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IROTH | FileUtils.S_IXOTH, + -1, -1); + mUserListFile = new File(mUsersDir, USER_LIST_FILENAME); + initDefaultGuestRestrictions(); + readUserListLP(); + sInstance = this; } mLocalService = new LocalService(); LocalServices.addService(UserManagerInternal.class, mLocalService); } void systemReady() { - synchronized (mInstallLock) { - synchronized (mPackagesLock) { - synchronized (mUsersLock) { - // Prune out any partially created/partially removed users. - ArrayList<UserInfo> partials = new ArrayList<UserInfo>(); - final int userSize = mUsers.size(); - for (int i = 0; i < userSize; i++) { - UserInfo ui = mUsers.valueAt(i); - if ((ui.partial || ui.guestToRemove) && i != 0) { - partials.add(ui); - } - } - final int partialsSize = partials.size(); - for (int i = 0; i < partialsSize; i++) { - UserInfo ui = partials.get(i); - Slog.w(LOG_TAG, "Removing partially created user " + ui.id - + " (name=" + ui.name + ")"); - removeUserStateLILP(ui.id); - } + // Prune out any partially created/partially removed users. + ArrayList<UserInfo> partials = new ArrayList<>(); + synchronized (mUsersLock) { + final int userSize = mUsers.size(); + for (int i = 0; i < userSize; i++) { + UserInfo ui = mUsers.valueAt(i); + if ((ui.partial || ui.guestToRemove) && i != 0) { + partials.add(ui); } } } + final int partialsSize = partials.size(); + for (int i = 0; i < partialsSize; i++) { + UserInfo ui = partials.get(i); + Slog.w(LOG_TAG, "Removing partially created user " + ui.id + + " (name=" + ui.name + ")"); + removeUserState(ui.id); + } onUserForeground(UserHandle.USER_SYSTEM); mAppOpsService = IAppOpsService.Stub.asInterface( ServiceManager.getService(Context.APP_OPS_SERVICE)); @@ -627,17 +610,22 @@ public class UserManagerService extends IUserManager.Stub { public void makeInitialized(int userId) { checkManageUsersPermission("makeInitialized"); - synchronized (mPackagesLock) { - UserInfo info = getUserInfoNoChecks(userId); + boolean scheduleWriteUser = false; + UserInfo info; + synchronized (mUsersLock) { + info = mUsers.get(userId); if (info == null || info.partial) { Slog.w(LOG_TAG, "makeInitialized: unknown user #" + userId); - // TODO Check if we should return here instead of a null check below + return; } - if (info != null && (info.flags&UserInfo.FLAG_INITIALIZED) == 0) { + if ((info.flags & UserInfo.FLAG_INITIALIZED) == 0) { info.flags |= UserInfo.FLAG_INITIALIZED; - scheduleWriteUser(info); + scheduleWriteUser = true; } } + if (scheduleWriteUser) { + scheduleWriteUser(info); + } } /** @@ -645,17 +633,18 @@ public class UserManagerService extends IUserManager.Stub { * restrictions. */ private void initDefaultGuestRestrictions() { - if (mGuestRestrictions.isEmpty()) { - mGuestRestrictions.putBoolean(UserManager.DISALLOW_OUTGOING_CALLS, true); - mGuestRestrictions.putBoolean(UserManager.DISALLOW_SMS, true); + synchronized (mGuestRestrictions) { + if (mGuestRestrictions.isEmpty()) { + mGuestRestrictions.putBoolean(UserManager.DISALLOW_OUTGOING_CALLS, true); + mGuestRestrictions.putBoolean(UserManager.DISALLOW_SMS, true); + } } } @Override public Bundle getDefaultGuestRestrictions() { checkManageUsersPermission("getDefaultGuestRestrictions"); - // TODO Switch to mGuestRestrictions for locking - synchronized (mPackagesLock) { + synchronized (mGuestRestrictions) { return new Bundle(mGuestRestrictions); } } @@ -663,12 +652,12 @@ public class UserManagerService extends IUserManager.Stub { @Override public void setDefaultGuestRestrictions(Bundle restrictions) { checkManageUsersPermission("setDefaultGuestRestrictions"); - synchronized (mInstallLock) { - synchronized (mPackagesLock) { - mGuestRestrictions.clear(); - mGuestRestrictions.putAll(restrictions); - writeUserListLILP(); - } + synchronized (mGuestRestrictions) { + mGuestRestrictions.clear(); + mGuestRestrictions.putAll(restrictions); + } + synchronized (mPackagesLock) { + writeUserListLP(); } } @@ -775,7 +764,7 @@ public class UserManagerService extends IUserManager.Stub { Preconditions.checkState(mCachedEffectiveUserRestrictions.get(userId) != newRestrictions); mBaseUserRestrictions.put(userId, newRestrictions); - scheduleWriteUser(mUsers.get(userId)); + scheduleWriteUser(getUserInfoNoChecks(userId)); } final Bundle effective = computeEffectiveUserRestrictionsLR(userId); @@ -996,9 +985,9 @@ public class UserManagerService extends IUserManager.Stub { } } - private void readUserListLILP() { + private void readUserListLP() { if (!mUserListFile.exists()) { - fallbackToSingleUserLILP(); + fallbackToSingleUserLP(); return; } FileInputStream fis = null; @@ -1015,7 +1004,7 @@ public class UserManagerService extends IUserManager.Stub { if (type != XmlPullParser.START_TAG) { Slog.e(LOG_TAG, "Unable to read user list"); - fallbackToSingleUserLILP(); + fallbackToSingleUserLP(); return; } @@ -1036,7 +1025,7 @@ public class UserManagerService extends IUserManager.Stub { final String name = parser.getName(); if (name.equals(TAG_USER)) { String id = parser.getAttributeValue(null, ATTR_ID); - UserInfo user = readUserLILP(Integer.parseInt(id)); + UserInfo user = readUserLP(Integer.parseInt(id)); if (user != null) { synchronized (mUsersLock) { @@ -1051,8 +1040,10 @@ public class UserManagerService extends IUserManager.Stub { && type != XmlPullParser.END_TAG) { if (type == XmlPullParser.START_TAG) { if (parser.getName().equals(TAG_RESTRICTIONS)) { - UserRestrictionsUtils - .readRestrictions(parser, mGuestRestrictions); + synchronized (mGuestRestrictions) { + UserRestrictionsUtils + .readRestrictions(parser, mGuestRestrictions); + } } break; } @@ -1061,25 +1052,18 @@ public class UserManagerService extends IUserManager.Stub { } } updateUserIds(); - upgradeIfNecessaryLILP(); - } catch (IOException ioe) { - fallbackToSingleUserLILP(); - } catch (XmlPullParserException pe) { - fallbackToSingleUserLILP(); + upgradeIfNecessaryLP(); + } catch (IOException | XmlPullParserException e) { + fallbackToSingleUserLP(); } finally { - if (fis != null) { - try { - fis.close(); - } catch (IOException e) { - } - } + IoUtils.closeQuietly(fis); } } /** * Upgrade steps between versions, either for fixing bugs or changing the data format. */ - private void upgradeIfNecessaryLILP() { + private void upgradeIfNecessaryLP() { int userVersion = mUserVersion; if (userVersion < 1) { // Assign a proper name for the owner, if not initialized correctly before @@ -1132,11 +1116,11 @@ public class UserManagerService extends IUserManager.Stub { + USER_VERSION); } else { mUserVersion = userVersion; - writeUserListLILP(); + writeUserListLP(); } } - private void fallbackToSingleUserLILP() { + private void fallbackToSingleUserLP() { int flags = UserInfo.FLAG_INITIALIZED; // In split system user mode, the admin and primary flags are assigned to the first human // user. @@ -1161,7 +1145,7 @@ public class UserManagerService extends IUserManager.Stub { updateUserIds(); initDefaultGuestRestrictions(); - writeUserListLILP(); + writeUserListLP(); writeUserLP(system); } @@ -1247,8 +1231,7 @@ public class UserManagerService extends IUserManager.Stub { * <user id="2"></user> * </users> */ - private void writeUserListLILP() { - // TODO Investigate removing a dependency on mInstallLock + private void writeUserListLP() { FileOutputStream fos = null; AtomicFile userListFile = new AtomicFile(mUserListFile); try { @@ -1266,8 +1249,10 @@ public class UserManagerService extends IUserManager.Stub { serializer.attribute(null, ATTR_USER_VERSION, Integer.toString(mUserVersion)); serializer.startTag(null, TAG_GUEST_RESTRICTIONS); - UserRestrictionsUtils - .writeRestrictions(serializer, mGuestRestrictions, TAG_RESTRICTIONS); + synchronized (mGuestRestrictions) { + UserRestrictionsUtils + .writeRestrictions(serializer, mGuestRestrictions, TAG_RESTRICTIONS); + } serializer.endTag(null, TAG_GUEST_RESTRICTIONS); int[] userIdsToWrite; synchronized (mUsersLock) { @@ -1293,7 +1278,7 @@ public class UserManagerService extends IUserManager.Stub { } } - private UserInfo readUserLILP(int id) { + private UserInfo readUserLP(int id) { int flags = 0; int serialNumber = id; String name = null; @@ -1468,8 +1453,7 @@ public class UserManagerService extends IUserManager.Stub { } private UserInfo createUserInternal(String name, int flags, int parentId) { - if (getUserRestrictions(UserHandle.getCallingUserId()).getBoolean( - UserManager.DISALLOW_ADD_USER, false)) { + if (hasUserRestriction(UserManager.DISALLOW_ADD_USER, UserHandle.getCallingUserId())) { Log.w(LOG_TAG, "Cannot add user. DISALLOW_ADD_USER is enabled."); return null; } @@ -1480,120 +1464,114 @@ public class UserManagerService extends IUserManager.Stub { final boolean isManagedProfile = (flags & UserInfo.FLAG_MANAGED_PROFILE) != 0; final boolean isRestricted = (flags & UserInfo.FLAG_RESTRICTED) != 0; final long ident = Binder.clearCallingIdentity(); - UserInfo userInfo = null; + UserInfo userInfo; final int userId; try { - synchronized (mInstallLock) { - synchronized (mPackagesLock) { - UserInfo parent = null; - if (parentId != UserHandle.USER_NULL) { - synchronized (mUsersLock) { - parent = getUserInfoLU(parentId); - } - if (parent == null) return null; - } - if (isManagedProfile && !canAddMoreManagedProfiles(parentId, false)) { - Log.e(LOG_TAG, "Cannot add more managed profiles for user " + parentId); - return null; - } - if (!isGuest && !isManagedProfile && isUserLimitReached()) { - // If we're not adding a guest user or a managed profile and the limit has - // been reached, cannot add a user. - return null; + synchronized (mPackagesLock) { + UserInfo parent = null; + if (parentId != UserHandle.USER_NULL) { + synchronized (mUsersLock) { + parent = getUserInfoLU(parentId); } - // If we're adding a guest and there already exists one, bail. - if (isGuest && findCurrentGuestUser() != null) { + if (parent == null) return null; + } + if (isManagedProfile && !canAddMoreManagedProfiles(parentId, false)) { + Log.e(LOG_TAG, "Cannot add more managed profiles for user " + parentId); + return null; + } + if (!isGuest && !isManagedProfile && isUserLimitReached()) { + // If we're not adding a guest user or a managed profile and the limit has + // been reached, cannot add a user. + return null; + } + // If we're adding a guest and there already exists one, bail. + if (isGuest && findCurrentGuestUser() != null) { + return null; + } + // In legacy mode, restricted profile's parent can only be the owner user + if (isRestricted && !UserManager.isSplitSystemUser() + && (parentId != UserHandle.USER_SYSTEM)) { + Log.w(LOG_TAG, "Cannot add restricted profile - parent user must be owner"); + return null; + } + if (isRestricted && UserManager.isSplitSystemUser()) { + if (parent == null) { + Log.w(LOG_TAG, "Cannot add restricted profile - parent user must be " + + "specified"); return null; } - // In legacy mode, restricted profile's parent can only be the owner user - if (isRestricted && !UserManager.isSplitSystemUser() - && (parentId != UserHandle.USER_SYSTEM)) { - Log.w(LOG_TAG, "Cannot add restricted profile - parent user must be owner"); + if (!parent.canHaveProfile()) { + Log.w(LOG_TAG, "Cannot add restricted profile - profiles cannot be " + + "created for the specified parent user id " + parentId); return null; } - if (isRestricted && UserManager.isSplitSystemUser()) { - if (parent == null) { - Log.w(LOG_TAG, "Cannot add restricted profile - parent user must be " - + "specified"); - return null; - } - if (!parent.canHaveProfile()) { - Log.w(LOG_TAG, "Cannot add restricted profile - profiles cannot be " - + "created for the specified parent user id " + parentId); - return null; - } - } - // In split system user mode, we assign the first human user the primary flag. - // And if there is no device owner, we also assign the admin flag to primary - // user. - if (UserManager.isSplitSystemUser() - && !isGuest && !isManagedProfile && getPrimaryUser() == null) { - flags |= UserInfo.FLAG_PRIMARY; - DevicePolicyManager devicePolicyManager = (DevicePolicyManager) - mContext.getSystemService(Context.DEVICE_POLICY_SERVICE); - if (devicePolicyManager == null - || devicePolicyManager.getDeviceOwner() == null) { - flags |= UserInfo.FLAG_ADMIN; - } + } + // In split system user mode, we assign the first human user the primary flag. + // And if there is no device owner, we also assign the admin flag to primary user. + if (UserManager.isSplitSystemUser() + && !isGuest && !isManagedProfile && getPrimaryUser() == null) { + flags |= UserInfo.FLAG_PRIMARY; + DevicePolicyManager devicePolicyManager = (DevicePolicyManager) + mContext.getSystemService(Context.DEVICE_POLICY_SERVICE); + if (devicePolicyManager == null + || devicePolicyManager.getDeviceOwner() == null) { + flags |= UserInfo.FLAG_ADMIN; } - userId = getNextAvailableId(); - userInfo = new UserInfo(userId, name, null, flags); - userInfo.serialNumber = mNextSerialNumber++; - long now = System.currentTimeMillis(); - userInfo.creationTime = (now > EPOCH_PLUS_30_YEARS) ? now : 0; - userInfo.partial = true; - Environment.getUserSystemDirectory(userInfo.id).mkdirs(); + } + userId = getNextAvailableId(); + userInfo = new UserInfo(userId, name, null, flags); + userInfo.serialNumber = mNextSerialNumber++; + long now = System.currentTimeMillis(); + userInfo.creationTime = (now > EPOCH_PLUS_30_YEARS) ? now : 0; + userInfo.partial = true; + Environment.getUserSystemDirectory(userInfo.id).mkdirs(); + synchronized (mUsersLock) { mUsers.put(userId, userInfo); - writeUserListLILP(); - if (parent != null) { - if (isManagedProfile) { - if (parent.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID) { - parent.profileGroupId = parent.id; - scheduleWriteUser(parent); - } - userInfo.profileGroupId = parent.profileGroupId; - } else if (isRestricted) { - if (!parent.canHaveProfile()) { - Log.w(LOG_TAG, "Cannot add restricted profile - parent user must be owner"); - } - if (parent.restrictedProfileParentId == UserInfo.NO_PROFILE_GROUP_ID) { - parent.restrictedProfileParentId = parent.id; - scheduleWriteUser(parent); - } - userInfo.restrictedProfileParentId = parent.restrictedProfileParentId; + } + writeUserListLP(); + if (parent != null) { + if (isManagedProfile) { + if (parent.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID) { + parent.profileGroupId = parent.id; + writeUserLP(parent); } - } - - final StorageManager storage = mContext.getSystemService(StorageManager.class); - storage.createUserKey(userId, userInfo.serialNumber); - for (VolumeInfo vol : storage.getWritablePrivateVolumes()) { - final String volumeUuid = vol.getFsUuid(); - try { - final File userDir = Environment.getDataUserDirectory(volumeUuid, - userId); - storage.prepareUserStorage(volumeUuid, userId, userInfo.serialNumber); - enforceSerialNumber(userDir, userInfo.serialNumber); - } catch (IOException e) { - Log.wtf(LOG_TAG, "Failed to create user directory on " + volumeUuid, e); + userInfo.profileGroupId = parent.profileGroupId; + } else if (isRestricted) { + if (parent.restrictedProfileParentId == UserInfo.NO_PROFILE_GROUP_ID) { + parent.restrictedProfileParentId = parent.id; + writeUserLP(parent); } - } - mPm.createNewUserLILPw(userId); - userInfo.partial = false; - scheduleWriteUser(userInfo); - updateUserIds(); - Bundle restrictions = new Bundle(); - synchronized (mRestrictionsLock) { - mBaseUserRestrictions.append(userId, restrictions); + userInfo.restrictedProfileParentId = parent.restrictedProfileParentId; } } } - mPm.newUserCreated(userId); - if (userInfo != null) { - Intent addedIntent = new Intent(Intent.ACTION_USER_ADDED); - addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userInfo.id); - mContext.sendBroadcastAsUser(addedIntent, UserHandle.ALL, - android.Manifest.permission.MANAGE_USERS); + final StorageManager storage = mContext.getSystemService(StorageManager.class); + storage.createUserKey(userId, userInfo.serialNumber); + for (VolumeInfo vol : storage.getWritablePrivateVolumes()) { + final String volumeUuid = vol.getFsUuid(); + try { + final File userDir = Environment.getDataUserDirectory(volumeUuid, userId); + storage.prepareUserStorage(volumeUuid, userId, userInfo.serialNumber); + enforceSerialNumber(userDir, userInfo.serialNumber); + } catch (IOException e) { + Log.wtf(LOG_TAG, "Failed to create user directory on " + volumeUuid, e); + } } + mPm.createNewUser(userId); + userInfo.partial = false; + synchronized (mPackagesLock) { + writeUserLP(userInfo); + } + updateUserIds(); + Bundle restrictions = new Bundle(); + synchronized (mRestrictionsLock) { + mBaseUserRestrictions.append(userId, restrictions); + } + mPm.newUserCreated(userId); + Intent addedIntent = new Intent(Intent.ACTION_USER_ADDED); + addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId); + mContext.sendBroadcastAsUser(addedIntent, UserHandle.ALL, + android.Manifest.permission.MANAGE_USERS); } finally { Binder.restoreCallingIdentity(ident); } @@ -1782,11 +1760,7 @@ public class UserManagerService extends IUserManager.Stub { // Clean up any ActivityManager state LocalServices.getService(ActivityManagerInternal.class) .onUserRemoved(userHandle); - synchronized (mInstallLock) { - synchronized (mPackagesLock) { - removeUserStateLILP(userHandle); - } - } + removeUserState(userHandle); } }.start(); } @@ -1798,10 +1772,10 @@ public class UserManagerService extends IUserManager.Stub { } } - private void removeUserStateLILP(final int userHandle) { + private void removeUserState(final int userHandle) { mContext.getSystemService(StorageManager.class).destroyUserKey(userHandle); // Cleanup package manager settings - mPm.cleanUpUserLILPw(this, userHandle); + mPm.cleanUpUser(this, userHandle); // Remove this user from the list synchronized (mUsersLock) { @@ -1811,7 +1785,9 @@ public class UserManagerService extends IUserManager.Stub { AtomicFile userFile = new AtomicFile(new File(mUsersDir, userHandle + XML_SUFFIX)); userFile.delete(); // Update the user list - writeUserListLILP(); + synchronized (mPackagesLock) { + writeUserListLP(); + } updateUserIds(); removeDirectoryRecursive(Environment.getUserSystemDirectory(userHandle)); } @@ -2146,17 +2122,15 @@ public class UserManagerService extends IUserManager.Stub { * @param userId the user that was just foregrounded */ public void onUserForeground(int userId) { - synchronized (mPackagesLock) { - UserInfo user = getUserInfoNoChecks(userId); - long now = System.currentTimeMillis(); - if (user == null || user.partial) { - Slog.w(LOG_TAG, "userForeground: unknown user #" + userId); - return; - } - if (now > EPOCH_PLUS_30_YEARS) { - user.lastLoggedInTime = now; - scheduleWriteUser(user); - } + UserInfo user = getUserInfoNoChecks(userId); + if (user == null || user.partial) { + Slog.w(LOG_TAG, "userForeground: unknown user #" + userId); + return; + } + long now = System.currentTimeMillis(); + if (now > EPOCH_PLUS_30_YEARS) { + user.lastLoggedInTime = now; + scheduleWriteUser(user); } } @@ -2164,7 +2138,6 @@ public class UserManagerService extends IUserManager.Stub { * Returns the next available user id, filling in any holes in the ids. * TODO: May not be a good idea to recycle ids, in case it results in confusion * for data and battery stats collection, or unexpected cross-talk. - * @return */ private int getNextAvailableId() { synchronized (mUsersLock) { @@ -2348,7 +2321,9 @@ public class UserManagerService extends IUserManager.Stub { } pw.println(); pw.println("Guest restrictions:"); - UserRestrictionsUtils.dumpRestrictions(pw, " ", mGuestRestrictions); + synchronized (mGuestRestrictions) { + UserRestrictionsUtils.dumpRestrictions(pw, " ", mGuestRestrictions); + } } } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index e264c437241c..4e38f67511dd 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -55,17 +55,6 @@ class DisplayContent { * from mDisplayWindows; */ private final WindowList mWindows = new WindowList(); - // This protects the following display size properties, so that - // getDisplaySize() doesn't need to acquire the global lock. This is - // needed because the window manager sometimes needs to use ActivityThread - // while it has its global state locked (for example to load animation - // resources), but the ActivityThread also needs get the current display - // size sometimes when it has its package lock held. - // - // These will only be modified with both mWindowMap and mDisplaySizeLock - // held (in that order) so the window manager doesn't need to acquire this - // lock when needing these values in its normal operation. - final Object mDisplaySizeLock = new Object(); int mInitialDisplayWidth = 0; int mInitialDisplayHeight = 0; int mInitialDisplayDensity = 0; @@ -202,18 +191,16 @@ class DisplayContent { } void initializeDisplayBaseInfo() { - synchronized(mDisplaySizeLock) { - // Bootstrap the default logical display from the display manager. - final DisplayInfo newDisplayInfo = - mService.mDisplayManagerInternal.getDisplayInfo(mDisplayId); - if (newDisplayInfo != null) { - mDisplayInfo.copyFrom(newDisplayInfo); - } - mBaseDisplayWidth = mInitialDisplayWidth = mDisplayInfo.logicalWidth; - mBaseDisplayHeight = mInitialDisplayHeight = mDisplayInfo.logicalHeight; - mBaseDisplayDensity = mInitialDisplayDensity = mDisplayInfo.logicalDensityDpi; - mBaseDisplayRect.set(0, 0, mBaseDisplayWidth, mBaseDisplayHeight); + // Bootstrap the default logical display from the display manager. + final DisplayInfo newDisplayInfo = + mService.mDisplayManagerInternal.getDisplayInfo(mDisplayId); + if (newDisplayInfo != null) { + mDisplayInfo.copyFrom(newDisplayInfo); } + mBaseDisplayWidth = mInitialDisplayWidth = mDisplayInfo.logicalWidth; + mBaseDisplayHeight = mInitialDisplayHeight = mDisplayInfo.logicalHeight; + mBaseDisplayDensity = mInitialDisplayDensity = mDisplayInfo.logicalDensityDpi; + mBaseDisplayRect.set(0, 0, mBaseDisplayWidth, mBaseDisplayHeight); } void getLogicalDisplayRect(Rect out) { diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 92eacd6b750f..4293f0a48e5d 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -6908,27 +6908,25 @@ public class WindowManagerService extends IWindowManager.Stub final int appWidth = mPolicy.getNonDecorDisplayWidth(dw, dh, mRotation); final int appHeight = mPolicy.getNonDecorDisplayHeight(dw, dh, mRotation); final DisplayInfo displayInfo = displayContent.getDisplayInfo(); - synchronized(displayContent.mDisplaySizeLock) { - displayInfo.rotation = mRotation; - displayInfo.logicalWidth = dw; - displayInfo.logicalHeight = dh; - displayInfo.logicalDensityDpi = displayContent.mBaseDisplayDensity; - displayInfo.appWidth = appWidth; - displayInfo.appHeight = appHeight; - displayInfo.getLogicalMetrics(mRealDisplayMetrics, - CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null); - displayInfo.getAppMetrics(mDisplayMetrics); - if (displayContent.mDisplayScalingDisabled) { - displayInfo.flags |= Display.FLAG_SCALING_DISABLED; - } else { - displayInfo.flags &= ~Display.FLAG_SCALING_DISABLED; - } + displayInfo.rotation = mRotation; + displayInfo.logicalWidth = dw; + displayInfo.logicalHeight = dh; + displayInfo.logicalDensityDpi = displayContent.mBaseDisplayDensity; + displayInfo.appWidth = appWidth; + displayInfo.appHeight = appHeight; + displayInfo.getLogicalMetrics(mRealDisplayMetrics, + CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null); + displayInfo.getAppMetrics(mDisplayMetrics); + if (displayContent.mDisplayScalingDisabled) { + displayInfo.flags |= Display.FLAG_SCALING_DISABLED; + } else { + displayInfo.flags &= ~Display.FLAG_SCALING_DISABLED; + } - mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager( - displayContent.getDisplayId(), displayInfo); + mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager( + displayContent.getDisplayId(), displayInfo); - displayContent.mBaseDisplayRect.set(0, 0, dw, dh); - } + displayContent.mBaseDisplayRect.set(0, 0, dw, dh); if (false) { Slog.i(TAG, "Set app display size: " + appWidth + " x " + appHeight); } @@ -8060,10 +8058,8 @@ public class WindowManagerService extends IWindowManager.Stub synchronized (mWindowMap) { final DisplayContent displayContent = getDisplayContentLocked(displayId); if (displayContent != null && displayContent.hasAccess(Binder.getCallingUid())) { - synchronized(displayContent.mDisplaySizeLock) { - size.x = displayContent.mInitialDisplayWidth; - size.y = displayContent.mInitialDisplayHeight; - } + size.x = displayContent.mInitialDisplayWidth; + size.y = displayContent.mInitialDisplayHeight; } } } @@ -8073,10 +8069,8 @@ public class WindowManagerService extends IWindowManager.Stub synchronized (mWindowMap) { final DisplayContent displayContent = getDisplayContentLocked(displayId); if (displayContent != null && displayContent.hasAccess(Binder.getCallingUid())) { - synchronized(displayContent.mDisplaySizeLock) { - size.x = displayContent.mBaseDisplayWidth; - size.y = displayContent.mBaseDisplayHeight; - } + size.x = displayContent.mBaseDisplayWidth; + size.y = displayContent.mBaseDisplayHeight; } } } @@ -8145,13 +8139,9 @@ public class WindowManagerService extends IWindowManager.Stub } } - private void setForcedDisplayScalingModeLocked(DisplayContent displayContent, - int mode) { + private void setForcedDisplayScalingModeLocked(DisplayContent displayContent, int mode) { Slog.i(TAG, "Using display scaling mode: " + (mode == 0 ? "auto" : "off")); - - synchronized(displayContent.mDisplaySizeLock) { - displayContent.mDisplayScalingDisabled = (mode != 0); - } + displayContent.mDisplayScalingDisabled = (mode != 0); reconfigureDisplayLocked(displayContent); } @@ -8169,13 +8159,11 @@ public class WindowManagerService extends IWindowManager.Stub try { width = Integer.parseInt(sizeStr.substring(0, pos)); height = Integer.parseInt(sizeStr.substring(pos+1)); - synchronized(displayContent.mDisplaySizeLock) { - if (displayContent.mBaseDisplayWidth != width - || displayContent.mBaseDisplayHeight != height) { - Slog.i(TAG, "FORCED DISPLAY SIZE: " + width + "x" + height); - displayContent.mBaseDisplayWidth = width; - displayContent.mBaseDisplayHeight = height; - } + if (displayContent.mBaseDisplayWidth != width + || displayContent.mBaseDisplayHeight != height) { + Slog.i(TAG, "FORCED DISPLAY SIZE: " + width + "x" + height); + displayContent.mBaseDisplayWidth = width; + displayContent.mBaseDisplayHeight = height; } } catch (NumberFormatException ex) { } @@ -8192,11 +8180,9 @@ public class WindowManagerService extends IWindowManager.Stub int density; try { density = Integer.parseInt(densityStr); - synchronized(displayContent.mDisplaySizeLock) { - if (displayContent.mBaseDisplayDensity != density) { - Slog.i(TAG, "FORCED DISPLAY DENSITY: " + density); - displayContent.mBaseDisplayDensity = density; - } + if (displayContent.mBaseDisplayDensity != density) { + Slog.i(TAG, "FORCED DISPLAY DENSITY: " + density); + displayContent.mBaseDisplayDensity = density; } } catch (NumberFormatException ex) { } @@ -8206,21 +8192,16 @@ public class WindowManagerService extends IWindowManager.Stub int mode = Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.DISPLAY_SCALING_FORCE, 0); if (mode != 0) { - synchronized(displayContent.mDisplaySizeLock) { - Slog.i(TAG, "FORCED DISPLAY SCALING DISABLED"); - displayContent.mDisplayScalingDisabled = true; - } + Slog.i(TAG, "FORCED DISPLAY SCALING DISABLED"); + displayContent.mDisplayScalingDisabled = true; } } // displayContent must not be null private void setForcedDisplaySizeLocked(DisplayContent displayContent, int width, int height) { Slog.i(TAG, "Using new display size: " + width + "x" + height); - - synchronized(displayContent.mDisplaySizeLock) { - displayContent.mBaseDisplayWidth = width; - displayContent.mBaseDisplayHeight = height; - } + displayContent.mBaseDisplayWidth = width; + displayContent.mBaseDisplayHeight = height; reconfigureDisplayLocked(displayContent); } @@ -8256,9 +8237,7 @@ public class WindowManagerService extends IWindowManager.Stub synchronized (mWindowMap) { final DisplayContent displayContent = getDisplayContentLocked(displayId); if (displayContent != null && displayContent.hasAccess(Binder.getCallingUid())) { - synchronized(displayContent.mDisplaySizeLock) { - return displayContent.mInitialDisplayDensity; - } + return displayContent.mInitialDisplayDensity; } } return -1; @@ -8269,9 +8248,7 @@ public class WindowManagerService extends IWindowManager.Stub synchronized (mWindowMap) { final DisplayContent displayContent = getDisplayContentLocked(displayId); if (displayContent != null && displayContent.hasAccess(Binder.getCallingUid())) { - synchronized(displayContent.mDisplaySizeLock) { - return displayContent.mBaseDisplayDensity; - } + return displayContent.mBaseDisplayDensity; } } return -1; @@ -8306,10 +8283,7 @@ public class WindowManagerService extends IWindowManager.Stub // displayContent must not be null private void setForcedDisplayDensityLocked(DisplayContent displayContent, int density) { Slog.i(TAG, "Using new display density: " + density); - - synchronized(displayContent.mDisplaySizeLock) { - displayContent.mBaseDisplayDensity = density; - } + displayContent.mBaseDisplayDensity = density; reconfigureDisplayLocked(displayContent); } @@ -8400,12 +8374,10 @@ public class WindowManagerService extends IWindowManager.Stub private void setOverscanLocked(DisplayContent displayContent, int left, int top, int right, int bottom) { final DisplayInfo displayInfo = displayContent.getDisplayInfo(); - synchronized (displayContent.mDisplaySizeLock) { - displayInfo.overscanLeft = left; - displayInfo.overscanTop = top; - displayInfo.overscanRight = right; - displayInfo.overscanBottom = bottom; - } + displayInfo.overscanLeft = left; + displayInfo.overscanTop = top; + displayInfo.overscanRight = right; + displayInfo.overscanBottom = bottom; mDisplaySettings.setOverscanLocked(displayInfo.uniqueId, displayInfo.name, left, top, right, bottom); @@ -9992,14 +9964,11 @@ public class WindowManagerService extends IWindowManager.Stub DisplayInfo displayInfo = displayContent.getDisplayInfo(); final Rect rect = new Rect(); mDisplaySettings.getOverscanLocked(displayInfo.name, displayInfo.uniqueId, rect); - synchronized (displayContent.mDisplaySizeLock) { - displayInfo.overscanLeft = rect.left; - displayInfo.overscanTop = rect.top; - displayInfo.overscanRight = rect.right; - displayInfo.overscanBottom = rect.bottom; - mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager( - displayId, displayInfo); - } + displayInfo.overscanLeft = rect.left; + displayInfo.overscanTop = rect.top; + displayInfo.overscanRight = rect.right; + displayInfo.overscanBottom = rect.bottom; + mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(displayId, displayInfo); configureDisplayPolicyLocked(displayContent); // TODO: Create an input channel for each display with touch capability. diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp index d3c3c1044ae7..b1a4c7dee73f 100644 --- a/tools/aapt2/ResourceUtils.cpp +++ b/tools/aapt2/ResourceUtils.cpp @@ -23,22 +23,28 @@ namespace aapt { namespace ResourceUtils { -void extractResourceName(const StringPiece16& str, StringPiece16* outPackage, +bool extractResourceName(const StringPiece16& str, StringPiece16* outPackage, StringPiece16* outType, StringPiece16* outEntry) { + bool hasPackageSeparator = false; + bool hasTypeSeparator = false; const char16_t* start = str.data(); const char16_t* end = start + str.size(); const char16_t* current = start; while (current != end) { if (outType->size() == 0 && *current == u'/') { + hasTypeSeparator = true; outType->assign(start, current - start); start = current + 1; } else if (outPackage->size() == 0 && *current == u':') { + hasPackageSeparator = true; outPackage->assign(start, current - start); start = current + 1; } current++; } outEntry->assign(start, end - start); + + return !(hasPackageSeparator && outPackage->empty()) && !(hasTypeSeparator && outType->empty()); } bool tryParseReference(const StringPiece16& str, ResourceNameRef* outRef, bool* outCreate, @@ -62,26 +68,34 @@ bool tryParseReference(const StringPiece16& str, ResourceNameRef* outRef, bool* StringPiece16 package; StringPiece16 type; StringPiece16 entry; - extractResourceName(trimmedStr.substr(offset, trimmedStr.size() - offset), &package, &type, - &entry); + if (!extractResourceName(trimmedStr.substr(offset, trimmedStr.size() - offset), + &package, &type, &entry)) { + return false; + } const ResourceType* parsedType = parseResourceType(type); if (!parsedType) { return false; } + if (entry.empty()) { + return false; + } + if (create && *parsedType != ResourceType::kId) { return false; } - if (outRef != nullptr) { + if (outRef) { outRef->package = package; outRef->type = *parsedType; outRef->entry = entry; } + if (outCreate) { *outCreate = create; } + if (outPrivate) { *outPrivate = priv; } @@ -104,20 +118,33 @@ bool tryParseAttributeReference(const StringPiece16& str, ResourceNameRef* outRe StringPiece16 package; StringPiece16 type; StringPiece16 entry; - extractResourceName(trimmedStr.substr(1, trimmedStr.size() - 1), &package, &type, &entry); + if (!extractResourceName(trimmedStr.substr(1, trimmedStr.size() - 1), + &package, &type, &entry)) { + return false; + } if (!type.empty() && type != u"attr") { return false; } - outRef->package = package; - outRef->type = ResourceType::kAttr; - outRef->entry = entry; + if (entry.empty()) { + return false; + } + + if (outRef) { + outRef->package = package; + outRef->type = ResourceType::kAttr; + outRef->entry = entry; + } return true; } return false; } +bool isAttributeReference(const StringPiece16& str) { + return tryParseAttributeReference(str, nullptr); +} + /* * Style parent's are a bit different. We accept the following formats: * diff --git a/tools/aapt2/ResourceUtils.h b/tools/aapt2/ResourceUtils.h index 851edc89fa90..34daa66b1546 100644 --- a/tools/aapt2/ResourceUtils.h +++ b/tools/aapt2/ResourceUtils.h @@ -34,8 +34,9 @@ namespace ResourceUtils { * * where the package can be empty. Validation must be performed on each * individual extracted piece to verify that the pieces are valid. + * Returns false if there was no package but a ':' was present. */ -void extractResourceName(const StringPiece16& str, StringPiece16* outPackage, +bool extractResourceName(const StringPiece16& str, StringPiece16* outPackage, StringPiece16* outType, StringPiece16* outEntry); /* @@ -54,12 +55,17 @@ bool tryParseReference(const StringPiece16& str, ResourceNameRef* outReference, bool isReference(const StringPiece16& str); /* - * Returns true if the string was parsed as an attribute reference (?[package:]type/name), + * Returns true if the string was parsed as an attribute reference (?[package:][type/]name), * with `outReference` set to the parsed reference. */ bool tryParseAttributeReference(const StringPiece16& str, ResourceNameRef* outReference); /** + * Returns true if the string is in the form of an attribute reference(?[package:][type/]name). + */ +bool isAttributeReference(const StringPiece16& str); + +/** * Returns true if the value is a boolean, putting the result in `outValue`. */ bool tryParseBool(const StringPiece16& str, bool* outValue); diff --git a/tools/aapt2/ResourceUtils_test.cpp b/tools/aapt2/ResourceUtils_test.cpp index 7de8f4130316..3d2a6e18e2bc 100644 --- a/tools/aapt2/ResourceUtils_test.cpp +++ b/tools/aapt2/ResourceUtils_test.cpp @@ -90,6 +90,26 @@ TEST(ResourceUtilsTest, FailToParseAutoCreateNonIdReference) { &privateRef)); } +TEST(ResourceUtilsTest, ParseAttributeReferences) { + EXPECT_TRUE(ResourceUtils::isAttributeReference(u"?android")); + EXPECT_TRUE(ResourceUtils::isAttributeReference(u"?android:foo")); + EXPECT_TRUE(ResourceUtils::isAttributeReference(u"?attr/foo")); + EXPECT_TRUE(ResourceUtils::isAttributeReference(u"?android:attr/foo")); +} + +TEST(ResourceUtilsTest, FailParseIncompleteReference) { + EXPECT_FALSE(ResourceUtils::isAttributeReference(u"?style/foo")); + EXPECT_FALSE(ResourceUtils::isAttributeReference(u"?android:style/foo")); + EXPECT_FALSE(ResourceUtils::isAttributeReference(u"?android:")); + EXPECT_FALSE(ResourceUtils::isAttributeReference(u"?android:attr/")); + EXPECT_FALSE(ResourceUtils::isAttributeReference(u"?:attr/")); + EXPECT_FALSE(ResourceUtils::isAttributeReference(u"?:attr/foo")); + EXPECT_FALSE(ResourceUtils::isAttributeReference(u"?:/")); + EXPECT_FALSE(ResourceUtils::isAttributeReference(u"?:/foo")); + EXPECT_FALSE(ResourceUtils::isAttributeReference(u"?attr/")); + EXPECT_FALSE(ResourceUtils::isAttributeReference(u"?/foo")); +} + TEST(ResourceUtilsTest, ParseStyleParentReference) { const ResourceName kAndroidStyleFooName = { u"android", ResourceType::kStyle, u"foo" }; const ResourceName kStyleFooName = { {}, ResourceType::kStyle, u"foo" }; diff --git a/tools/aapt2/XmlDom.h b/tools/aapt2/XmlDom.h index 9a46bcb9fc5c..721bf5bd6320 100644 --- a/tools/aapt2/XmlDom.h +++ b/tools/aapt2/XmlDom.h @@ -215,10 +215,13 @@ public: for (auto iter = mPackageDecls.rbegin(); iter != rend; ++iter) { if (name.package == iter->prefix) { if (iter->package.empty()) { - return ResourceName{ localPackage.toString(), name.type, name.entry }; - } else { + if (localPackage != name.package) { + return ResourceName{ localPackage.toString(), name.type, name.entry }; + } + } else if (iter->package != name.package) { return ResourceName{ iter->package, name.type, name.entry }; } + break; } } return {}; diff --git a/tools/aapt2/link/ReferenceLinkerVisitor.h b/tools/aapt2/link/ReferenceLinkerVisitor.h index c70531ba8296..a4cb596eb5ec 100644 --- a/tools/aapt2/link/ReferenceLinkerVisitor.h +++ b/tools/aapt2/link/ReferenceLinkerVisitor.h @@ -80,7 +80,7 @@ public: return; } - DiagMessage errorMsg; + DiagMessage errorMsg(reference->getSource()); errorMsg << "reference to " << reference->name.value(); if (realName) { errorMsg << " (aka " << realName.value() << ")"; @@ -92,7 +92,7 @@ public: } if (!mSymbols->findById(reference->id.value())) { - mContext->getDiagnostics()->error(DiagMessage() + mContext->getDiagnostics()->error(DiagMessage(reference->getSource()) << "reference to " << reference->id.value() << " was not found"); mError = true; diff --git a/tools/aapt2/link/XmlReferenceLinker.cpp b/tools/aapt2/link/XmlReferenceLinker.cpp index 147b9bf16248..caab9b8a4684 100644 --- a/tools/aapt2/link/XmlReferenceLinker.cpp +++ b/tools/aapt2/link/XmlReferenceLinker.cpp @@ -33,6 +33,7 @@ class XmlReferenceLinkerVisitor : public xml::PackageAwareVisitor { private: IAaptContext* mContext; ISymbolTable* mSymbols; + Source mSource; std::set<int>* mSdkLevelsFound; ReferenceLinkerVisitor mReferenceLinkerVisitor; bool mError = false; @@ -40,13 +41,14 @@ private: public: using xml::PackageAwareVisitor::visit; - XmlReferenceLinkerVisitor(IAaptContext* context, ISymbolTable* symbols, + XmlReferenceLinkerVisitor(IAaptContext* context, ISymbolTable* symbols, const Source& source, std::set<int>* sdkLevelsFound) : - mContext(context), mSymbols(symbols), mSdkLevelsFound(sdkLevelsFound), + mContext(context), mSymbols(symbols), mSource(source), mSdkLevelsFound(sdkLevelsFound), mReferenceLinkerVisitor(context, symbols, this) { } void visit(xml::Element* el) override { + const Source source = mSource.withLine(el->lineNumber); for (xml::Attribute& attr : el->attributes) { Maybe<std::u16string> maybePackage = util::extractPackageFromNamespace(attr.namespaceUri); @@ -76,15 +78,16 @@ public: !(attribute->typeMask & android::ResTable_map::TYPE_STRING)) { // We won't be able to encode this as a string. mContext->getDiagnostics()->error( - DiagMessage() << "'" << attr.value << "' " - << "is incompatible with attribute " - << package << ":" << attr.name << " " << *attribute); + DiagMessage(source) << "'" << attr.value << "' " + << "is incompatible with attribute " + << package << ":" << attr.name << " " + << *attribute); mError = true; } } else { - mContext->getDiagnostics()->error( - DiagMessage() << "attribute '" << package << ":" << attr.name - << "' was not found"); + mContext->getDiagnostics()->error(DiagMessage(source) + << "attribute '" << package << ":" + << attr.name << "' was not found"); mError = true; } @@ -95,6 +98,7 @@ public: if (attr.compiledValue) { // With a compiledValue, we must resolve the reference and assign it an ID. + attr.compiledValue->setSource(source); attr.compiledValue->accept(&mReferenceLinkerVisitor); } } @@ -123,7 +127,8 @@ public: bool XmlReferenceLinker::consume(IAaptContext* context, XmlResource* resource) { mSdkLevelsFound.clear(); - XmlReferenceLinkerVisitor visitor(context, context->getExternalSymbols(), &mSdkLevelsFound); + XmlReferenceLinkerVisitor visitor(context, context->getExternalSymbols(), resource->file.source, + &mSdkLevelsFound); if (resource->root) { resource->root->accept(&visitor); return !visitor.hasError(); diff --git a/tools/aapt2/process/SymbolTable.cpp b/tools/aapt2/process/SymbolTable.cpp index 9a8b263f5f97..bb33ea78f496 100644 --- a/tools/aapt2/process/SymbolTable.cpp +++ b/tools/aapt2/process/SymbolTable.cpp @@ -77,16 +77,8 @@ const ISymbolTable::Symbol* SymbolTableWrapper::findByName(const ResourceName& n } -static std::shared_ptr<ISymbolTable::Symbol> lookupIdInTable(const android::ResTable& table, - ResourceId id) { - android::Res_value val = {}; - ssize_t block = table.getResource(id.id, &val, true); - if (block >= 0) { - std::shared_ptr<ISymbolTable::Symbol> s = std::make_shared<ISymbolTable::Symbol>(); - s->id = id; - return s; - } - +static std::shared_ptr<ISymbolTable::Symbol> lookupAttributeInTable(const android::ResTable& table, + ResourceId id) { // Try as a bag. const android::ResTable::bag_entry* entry; ssize_t count = table.lockBag(id.id, &entry); @@ -148,14 +140,23 @@ const ISymbolTable::Symbol* AssetManagerSymbolTableBuilder::AssetManagerSymbolTa for (const auto& asset : mAssets) { const android::ResTable& table = asset->getResources(false); StringPiece16 typeStr = toString(name.type); + uint32_t typeSpecFlags = 0; ResourceId resId = table.identifierForName(name.entry.data(), name.entry.size(), typeStr.data(), typeStr.size(), - name.package.data(), name.package.size()); + name.package.data(), name.package.size(), + &typeSpecFlags); if (!resId.isValid()) { continue; } - std::shared_ptr<Symbol> s = lookupIdInTable(table, resId); + std::shared_ptr<Symbol> s; + if (name.type == ResourceType::kAttr) { + s = lookupAttributeInTable(table, resId); + } else { + s = std::make_shared<Symbol>(); + s->id = resId; + } + if (s) { mCache.put(name, s); return s.get(); @@ -173,7 +174,28 @@ const ISymbolTable::Symbol* AssetManagerSymbolTableBuilder::AssetManagerSymbolTa for (const auto& asset : mAssets) { const android::ResTable& table = asset->getResources(false); - std::shared_ptr<Symbol> s = lookupIdInTable(table, id); + android::ResTable::resource_name name; + if (!table.getResourceName(id.id, true, &name)) { + continue; + } + + bool isAttr = false; + if (name.type) { + if (const ResourceType* t = parseResourceType(StringPiece16(name.type, name.typeLen))) { + isAttr = (*t == ResourceType::kAttr); + } + } else if (name.type8) { + isAttr = (StringPiece(name.type8, name.typeLen) == "attr"); + } + + std::shared_ptr<Symbol> s; + if (isAttr) { + s = lookupAttributeInTable(table, id); + } else { + s = std::make_shared<Symbol>(); + s->id = id; + } + if (s) { mIdCache.put(id, s); return s.get(); diff --git a/tools/aapt2/unflatten/BinaryResourceParser.cpp b/tools/aapt2/unflatten/BinaryResourceParser.cpp index 0d17e8467d32..304833481be0 100644 --- a/tools/aapt2/unflatten/BinaryResourceParser.cpp +++ b/tools/aapt2/unflatten/BinaryResourceParser.cpp @@ -304,6 +304,11 @@ bool BinaryResourceParser::parsePackage(const ResChunk_header* chunk) { return false; } + // There can be multiple packages in a table, so + // clear the type and key pool in case they were set from a previous package. + mTypePool.uninit(); + mKeyPool.uninit(); + ResChunkPullParser parser(getChunkData(&packageHeader->header), getChunkDataLen(&packageHeader->header)); while (ResChunkPullParser::isGoodEvent(parser.next())) { |