summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt1
-rw-r--r--api/system-current.txt1
-rw-r--r--core/java/android/app/TimePickerDialog.java16
-rw-r--r--core/java/android/content/pm/ApplicationInfo.java15
-rw-r--r--core/java/android/content/res/Resources.java337
-rw-r--r--core/java/android/widget/TimePicker.java96
-rw-r--r--core/java/android/widget/TimePickerClockDelegate.java143
-rw-r--r--core/java/android/widget/TimePickerSpinnerDelegate.java85
-rwxr-xr-xcore/jni/android/graphics/Bitmap.cpp1
-rw-r--r--docs/html/guide/topics/manifest/uses-feature-element.jd4
-rw-r--r--libs/hwui/Caches.cpp1
-rw-r--r--libs/hwui/DeviceInfo.cpp1
-rw-r--r--libs/hwui/Extensions.cpp2
-rw-r--r--libs/hwui/Extensions.h2
-rw-r--r--packages/DocumentsUI/tests/src/com/android/documentsui/CopyTest.java9
-rw-r--r--packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java2
-rw-r--r--packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/DirectoryFragmentModelTest.java2
-rw-r--r--packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManagerTest.java3
-rw-r--r--packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManager_GridModelTest.java2
-rw-r--r--packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManager_SelectionTest.java3
-rw-r--r--packages/PrintSpooler/res/drawable/ic_add.xml25
-rw-r--r--packages/PrintSpooler/res/drawable/ic_search.xml43
-rw-r--r--packages/PrintSpooler/res/layout/printer_dropdown_prompt.xml28
-rw-r--r--packages/PrintSpooler/res/menu/select_printer_activity.xml4
-rw-r--r--packages/PrintSpooler/res/values/strings.xml3
-rw-r--r--packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java118
-rw-r--r--packages/SettingsLib/res/values/strings.xml3
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/DeviceInfoUtils.java172
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java8
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java52
-rw-r--r--services/core/java/com/android/server/pm/SELinuxMMAC.java6
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java413
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java31
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java125
-rw-r--r--tools/aapt2/ResourceUtils.cpp43
-rw-r--r--tools/aapt2/ResourceUtils.h10
-rw-r--r--tools/aapt2/ResourceUtils_test.cpp20
-rw-r--r--tools/aapt2/XmlDom.h7
-rw-r--r--tools/aapt2/link/ReferenceLinkerVisitor.h4
-rw-r--r--tools/aapt2/link/XmlReferenceLinker.cpp23
-rw-r--r--tools/aapt2/process/SymbolTable.cpp48
-rw-r--r--tools/aapt2/unflatten/BinaryResourceParser.cpp5
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())) {