Merge "Fix crash in getNativeCanvas when canvasHandle is NULL" into lmp-mr1-dev
diff --git a/api/current.txt b/api/current.txt
index 5a0c67e..27b107a 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -238,8 +238,8 @@
field public static final int accessibilityFeedbackType = 16843650; // 0x1010382
field public static final int accessibilityFlags = 16843652; // 0x1010384
field public static final int accessibilityLiveRegion = 16843758; // 0x10103ee
- field public static final int accessibilityTraversalAfter = 16844036; // 0x1010504
- field public static final int accessibilityTraversalBefore = 16844035; // 0x1010503
+ field public static final int accessibilityTraversalAfter = 16843986; // 0x10104d2
+ field public static final int accessibilityTraversalBefore = 16843985; // 0x10104d1
field public static final int accountPreferences = 16843423; // 0x101029f
field public static final int accountType = 16843407; // 0x101028f
field public static final int action = 16842797; // 0x101002d
@@ -403,7 +403,7 @@
field public static final int closeIcon = 16843905; // 0x1010481
field public static final int codes = 16843330; // 0x1010242
field public static final int collapseColumns = 16843083; // 0x101014b
- field public static final int collapseContentDescription = 16844034; // 0x1010502
+ field public static final int collapseContentDescription = 16843984; // 0x10104d0
field public static final int color = 16843173; // 0x10101a5
field public static final int colorAccent = 16843829; // 0x1010435
field public static final int colorActivatedHighlight = 16843664; // 0x1010390
@@ -476,7 +476,7 @@
field public static final int dialogLayout = 16843255; // 0x10101f7
field public static final int dialogMessage = 16843251; // 0x10101f3
field public static final int dialogPreferenceStyle = 16842897; // 0x1010091
- field public static final int dialogPreferredPadding = 16844037; // 0x1010505
+ field public static final int dialogPreferredPadding = 16843987; // 0x10104d3
field public static final int dialogTheme = 16843528; // 0x1010308
field public static final int dialogTitle = 16843250; // 0x10101f2
field public static final int digits = 16843110; // 0x1010166
@@ -1007,7 +1007,7 @@
field public static final int requiredForAllUsers = 16843728; // 0x10103d0
field public static final int requiresFadingEdge = 16843685; // 0x10103a5
field public static final int requiresSmallestWidthDp = 16843620; // 0x1010364
- field public static final int resizeClip = 16844033; // 0x1010501
+ field public static final int resizeClip = 16843983; // 0x10104cf
field public static final int resizeMode = 16843619; // 0x1010363
field public static final int resizeable = 16843405; // 0x101028d
field public static final int resource = 16842789; // 0x1010025
@@ -1017,6 +1017,7 @@
field public static final int restrictionType = 16843923; // 0x1010493
field public static final int resumeWhilePausing = 16843954; // 0x10104b2
field public static final int reversible = 16843851; // 0x101044b
+ field public static final int revisionCode = 16843989; // 0x10104d5
field public static final int right = 16843183; // 0x10101af
field public static final int ringtonePreferenceStyle = 16842899; // 0x1010093
field public static final int ringtoneType = 16843257; // 0x10101f9
@@ -1056,7 +1057,7 @@
field public static final int scrollbars = 16842974; // 0x10100de
field public static final int scrollingCache = 16843006; // 0x10100fe
field public static final deprecated int searchButtonText = 16843269; // 0x1010205
- field public static final int searchHintIcon = 16844038; // 0x1010506
+ field public static final int searchHintIcon = 16843988; // 0x10104d4
field public static final int searchIcon = 16843907; // 0x1010483
field public static final int searchMode = 16843221; // 0x10101d5
field public static final int searchSettingsDescription = 16843402; // 0x101028a
@@ -7276,6 +7277,7 @@
field public static final java.lang.String TEXT_SERVICES_MANAGER_SERVICE = "textservices";
field public static final java.lang.String TV_INPUT_SERVICE = "tv_input";
field public static final java.lang.String UI_MODE_SERVICE = "uimode";
+ field public static final java.lang.String USAGE_STATS_SERVICE = "usagestats";
field public static final java.lang.String USB_SERVICE = "usb";
field public static final java.lang.String USER_SERVICE = "user";
field public static final java.lang.String VIBRATOR_SERVICE = "vibrator";
@@ -8545,6 +8547,7 @@
field public static final int REQUESTED_PERMISSION_REQUIRED = 1; // 0x1
field public android.content.pm.ActivityInfo[] activities;
field public android.content.pm.ApplicationInfo applicationInfo;
+ field public int baseRevisionCode;
field public android.content.pm.ConfigurationInfo[] configPreferences;
field public android.content.pm.FeatureGroupInfo[] featureGroups;
field public long firstInstallTime;
@@ -8564,6 +8567,7 @@
field public int sharedUserLabel;
field public android.content.pm.Signature[] signatures;
field public java.lang.String[] splitNames;
+ field public int[] splitRevisionCodes;
field public int versionCode;
field public java.lang.String versionName;
}
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 475d540..2ea1d4d 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -47,6 +47,7 @@
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
+import android.os.SELinux;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
@@ -741,13 +742,14 @@
if (mProfileFile != null) {
try {
- fd = ParcelFileDescriptor.open(
+ fd = openForSystemServer(
new File(mProfileFile),
ParcelFileDescriptor.MODE_CREATE |
ParcelFileDescriptor.MODE_TRUNCATE |
ParcelFileDescriptor.MODE_READ_WRITE);
} catch (FileNotFoundException e) {
System.err.println("Error: Unable to open file: " + mProfileFile);
+ System.err.println("Consider using a file under /data/local/tmp/");
return;
}
profilerInfo = new ProfilerInfo(mProfileFile, fd, mSamplingInterval, mAutoStop);
@@ -1053,13 +1055,14 @@
if (start) {
profileFile = nextArgRequired();
try {
- fd = ParcelFileDescriptor.open(
+ fd = openForSystemServer(
new File(profileFile),
ParcelFileDescriptor.MODE_CREATE |
ParcelFileDescriptor.MODE_TRUNCATE |
ParcelFileDescriptor.MODE_READ_WRITE);
} catch (FileNotFoundException e) {
System.err.println("Error: Unable to open file: " + profileFile);
+ System.err.println("Consider using a file under /data/local/tmp/");
return;
}
profilerInfo = new ProfilerInfo(profileFile, fd, 0, false);
@@ -1113,12 +1116,13 @@
try {
File file = new File(heapFile);
file.delete();
- fd = ParcelFileDescriptor.open(file,
+ fd = openForSystemServer(file,
ParcelFileDescriptor.MODE_CREATE |
ParcelFileDescriptor.MODE_TRUNCATE |
ParcelFileDescriptor.MODE_READ_WRITE);
} catch (FileNotFoundException e) {
System.err.println("Error: Unable to open file: " + heapFile);
+ System.err.println("Consider using a file under /data/local/tmp/");
return;
}
@@ -1855,4 +1859,18 @@
} catch (RemoteException e) {
}
}
+
+ /**
+ * Open the given file for sending into the system process. This verifies
+ * with SELinux that the system will have access to the file.
+ */
+ private static ParcelFileDescriptor openForSystemServer(File file, int mode)
+ throws FileNotFoundException {
+ final ParcelFileDescriptor fd = ParcelFileDescriptor.open(file, mode);
+ final String tcon = SELinux.getFileContext(file.getAbsolutePath());
+ if (!SELinux.checkSELinuxAccess("u:r:system_server:s0", tcon, "file", "read")) {
+ throw new FileNotFoundException("System server has no access to file context " + tcon);
+ }
+ return fd;
+ }
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 0daa8e2..e6bb09f 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2877,11 +2877,10 @@
/**
* Use with {@link #getSystemService} to retrieve a {@link
- * android.app.usage.UsageStatsManager} for interacting with the status bar.
+ * android.app.usage.UsageStatsManager} for querying device usage stats.
*
* @see #getSystemService
* @see android.app.usage.UsageStatsManager
- * @hide
*/
public static final String USAGE_STATS_SERVICE = "usagestats";
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index b66bd01..9223269 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -41,14 +41,30 @@
* attribute.
*/
public int versionCode;
-
+
/**
* The version name of this package, as specified by the <manifest>
* tag's {@link android.R.styleable#AndroidManifest_versionName versionName}
* attribute.
*/
public String versionName;
-
+
+ /**
+ * The revision number of the base APK for this package, as specified by the
+ * <manifest> tag's
+ * {@link android.R.styleable#AndroidManifest_revisionCode revisionCode}
+ * attribute.
+ */
+ public int baseRevisionCode;
+
+ /**
+ * The revision number of any split APKs for this package, as specified by
+ * the <manifest> tag's
+ * {@link android.R.styleable#AndroidManifest_revisionCode revisionCode}
+ * attribute. Indexes are a 1:1 mapping against {@link #splitNames}.
+ */
+ public int[] splitRevisionCodes;
+
/**
* The shared user ID name of this package, as specified by the <manifest>
* tag's {@link android.R.styleable#AndroidManifest_sharedUserId sharedUserId}
@@ -257,21 +273,26 @@
public PackageInfo() {
}
+ @Override
public String toString() {
return "PackageInfo{"
+ Integer.toHexString(System.identityHashCode(this))
+ " " + packageName + "}";
}
+ @Override
public int describeContents() {
return 0;
}
+ @Override
public void writeToParcel(Parcel dest, int parcelableFlags) {
dest.writeString(packageName);
dest.writeStringArray(splitNames);
dest.writeInt(versionCode);
dest.writeString(versionName);
+ dest.writeInt(baseRevisionCode);
+ dest.writeIntArray(splitRevisionCodes);
dest.writeString(sharedUserId);
dest.writeInt(sharedUserLabel);
if (applicationInfo != null) {
@@ -305,10 +326,12 @@
public static final Parcelable.Creator<PackageInfo> CREATOR
= new Parcelable.Creator<PackageInfo>() {
+ @Override
public PackageInfo createFromParcel(Parcel source) {
return new PackageInfo(source);
}
+ @Override
public PackageInfo[] newArray(int size) {
return new PackageInfo[size];
}
@@ -316,9 +339,11 @@
private PackageInfo(Parcel source) {
packageName = source.readString();
- splitNames = source.readStringArray();
+ splitNames = source.createStringArray();
versionCode = source.readInt();
versionName = source.readString();
+ baseRevisionCode = source.readInt();
+ splitRevisionCodes = source.createIntArray();
sharedUserId = source.readString();
sharedUserLabel = source.readInt();
int hasApp = source.readInt();
diff --git a/core/java/android/content/pm/PackageInfoLite.java b/core/java/android/content/pm/PackageInfoLite.java
index e336c5f..1efe082 100644
--- a/core/java/android/content/pm/PackageInfoLite.java
+++ b/core/java/android/content/pm/PackageInfoLite.java
@@ -19,6 +19,8 @@
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.internal.content.PackageHelper;
+
/**
* Basic information about a package as specified in its manifest.
* Utility class used in PackageManager methods
@@ -31,11 +33,19 @@
*/
public String packageName;
+ /** Names of any split APKs, ordered by parsed splitName */
+ public String[] splitNames;
+
/**
* The android:versionCode of the package.
*/
public int versionCode;
+ /** Revision code of base APK */
+ public int baseRevisionCode;
+ /** Revision codes of any split APKs, ordered by parsed splitName */
+ public int[] splitRevisionCodes;
+
/**
* The android:multiArch flag from the package manifest. If set,
* we will extract all native libraries for the given app, not just those
@@ -70,7 +80,10 @@
public void writeToParcel(Parcel dest, int parcelableFlags) {
dest.writeString(packageName);
+ dest.writeStringArray(splitNames);
dest.writeInt(versionCode);
+ dest.writeInt(baseRevisionCode);
+ dest.writeIntArray(splitRevisionCodes);
dest.writeInt(recommendedInstallLocation);
dest.writeInt(installLocation);
dest.writeInt(multiArch ? 1 : 0);
@@ -96,7 +109,10 @@
private PackageInfoLite(Parcel source) {
packageName = source.readString();
+ splitNames = source.createStringArray();
versionCode = source.readInt();
+ baseRevisionCode = source.readInt();
+ splitRevisionCodes = source.createIntArray();
recommendedInstallLocation = source.readInt();
installLocation = source.readInt();
multiArch = (source.readInt() != 0);
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 8515520..82da7c5 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -261,11 +261,16 @@
/** Paths of any split APKs, ordered by parsed splitName */
public final String[] splitCodePaths;
+ /** Revision code of base APK */
+ public final int baseRevisionCode;
+ /** Revision codes of any split APKs, ordered by parsed splitName */
+ public final int[] splitRevisionCodes;
+
public final boolean coreApp;
public final boolean multiArch;
public PackageLite(String codePath, ApkLite baseApk, String[] splitNames,
- String[] splitCodePaths) {
+ String[] splitCodePaths, int[] splitRevisionCodes) {
this.packageName = baseApk.packageName;
this.versionCode = baseApk.versionCode;
this.installLocation = baseApk.installLocation;
@@ -274,6 +279,8 @@
this.codePath = codePath;
this.baseCodePath = baseApk.codePath;
this.splitCodePaths = splitCodePaths;
+ this.baseRevisionCode = baseApk.revisionCode;
+ this.splitRevisionCodes = splitRevisionCodes;
this.coreApp = baseApk.coreApp;
this.multiArch = baseApk.multiArch;
}
@@ -296,6 +303,7 @@
public final String packageName;
public final String splitName;
public final int versionCode;
+ public final int revisionCode;
public final int installLocation;
public final VerifierInfo[] verifiers;
public final Signature[] signatures;
@@ -303,12 +311,13 @@
public final boolean multiArch;
public ApkLite(String codePath, String packageName, String splitName, int versionCode,
- int installLocation, List<VerifierInfo> verifiers, Signature[] signatures,
- boolean coreApp, boolean multiArch) {
+ int revisionCode, int installLocation, List<VerifierInfo> verifiers,
+ Signature[] signatures, boolean coreApp, boolean multiArch) {
this.codePath = codePath;
this.packageName = packageName;
this.splitName = splitName;
this.versionCode = versionCode;
+ this.revisionCode = revisionCode;
this.installLocation = installLocation;
this.verifiers = verifiers.toArray(new VerifierInfo[verifiers.size()]);
this.signatures = signatures;
@@ -409,6 +418,8 @@
pi.packageName = p.packageName;
pi.splitNames = p.splitNames;
pi.versionCode = p.mVersionCode;
+ pi.baseRevisionCode = p.baseRevisionCode;
+ pi.splitRevisionCodes = p.splitRevisionCodes;
pi.versionName = p.mVersionName;
pi.sharedUserId = p.mSharedUserId;
pi.sharedUserLabel = p.mSharedUserLabel;
@@ -647,7 +658,7 @@
throws PackageParserException {
final ApkLite baseApk = parseApkLite(packageFile, flags);
final String packagePath = packageFile.getAbsolutePath();
- return new PackageLite(packagePath, baseApk, null, null);
+ return new PackageLite(packagePath, baseApk, null, null, null);
}
private static PackageLite parseClusterPackageLite(File packageDir, int flags)
@@ -704,20 +715,24 @@
String[] splitNames = null;
String[] splitCodePaths = null;
+ int[] splitRevisionCodes = null;
if (size > 0) {
splitNames = new String[size];
splitCodePaths = new String[size];
+ splitRevisionCodes = new int[size];
splitNames = apks.keySet().toArray(splitNames);
Arrays.sort(splitNames, sSplitNameComparator);
for (int i = 0; i < size; i++) {
splitCodePaths[i] = apks.get(splitNames[i]).codePath;
+ splitRevisionCodes[i] = apks.get(splitNames[i]).revisionCode;
}
}
final String codePath = packageDir.getAbsolutePath();
- return new PackageLite(codePath, baseApk, splitNames, splitCodePaths);
+ return new PackageLite(codePath, baseApk, splitNames, splitCodePaths,
+ splitRevisionCodes);
}
/**
@@ -782,6 +797,7 @@
final int num = lite.splitNames.length;
pkg.splitNames = lite.splitNames;
pkg.splitCodePaths = lite.splitCodePaths;
+ pkg.splitRevisionCodes = lite.splitRevisionCodes;
pkg.splitFlags = new int[num];
for (int i = 0; i < num; i++) {
@@ -1249,25 +1265,21 @@
int installLocation = PARSE_DEFAULT_INSTALL_LOCATION;
int versionCode = 0;
+ int revisionCode = 0;
boolean coreApp = false;
boolean multiArch = false;
- int numFound = 0;
for (int i = 0; i < attrs.getAttributeCount(); i++) {
- String attr = attrs.getAttributeName(i);
+ final String attr = attrs.getAttributeName(i);
if (attr.equals("installLocation")) {
installLocation = attrs.getAttributeIntValue(i,
PARSE_DEFAULT_INSTALL_LOCATION);
- numFound++;
} else if (attr.equals("versionCode")) {
versionCode = attrs.getAttributeIntValue(i, 0);
- numFound++;
+ } else if (attr.equals("revisionCode")) {
+ revisionCode = attrs.getAttributeIntValue(i, 0);
} else if (attr.equals("coreApp")) {
coreApp = attrs.getAttributeBooleanValue(i, false);
- numFound++;
- }
- if (numFound >= 3) {
- break;
}
}
@@ -1301,7 +1313,7 @@
}
return new ApkLite(codePath, packageSplit.first, packageSplit.second, versionCode,
- installLocation, verifiers, signatures, coreApp, multiArch);
+ revisionCode, installLocation, verifiers, signatures, coreApp, multiArch);
}
/**
@@ -1359,6 +1371,8 @@
com.android.internal.R.styleable.AndroidManifest);
pkg.mVersionCode = pkg.applicationInfo.versionCode = sa.getInteger(
com.android.internal.R.styleable.AndroidManifest_versionCode, 0);
+ pkg.baseRevisionCode = sa.getInteger(
+ com.android.internal.R.styleable.AndroidManifest_revisionCode, 0);
pkg.mVersionName = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifest_versionName, 0);
if (pkg.mVersionName != null) {
@@ -4168,6 +4182,7 @@
public final static class Package {
public String packageName;
+
/** Names of any split APKs, ordered by parsed splitName */
public String[] splitNames;
@@ -4185,6 +4200,11 @@
/** Paths of any split APKs, ordered by parsed splitName */
public String[] splitCodePaths;
+ /** Revision code of base APK */
+ public int baseRevisionCode;
+ /** Revision codes of any split APKs, ordered by parsed splitName */
+ public int[] splitRevisionCodes;
+
/** Flags of any split APKs; ordered by parsed splitName */
public int[] splitFlags;
@@ -4222,7 +4242,7 @@
// The version code declared for this package.
public int mVersionCode;
-
+
// The version name declared for this package.
public String mVersionName;
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 0145e05..78d3e9c 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -747,7 +747,7 @@
*/
public Drawable getDrawable(int id) throws NotFoundException {
final Drawable d = getDrawable(id, null);
- if (d.canApplyTheme()) {
+ if (d != null && d.canApplyTheme()) {
Log.w(TAG, "Drawable " + getResourceName(id) + " has unresolved theme "
+ "attributes! Consider using Resources.getDrawable(int, Theme) or "
+ "Context.getDrawable(int).", new RuntimeException());
diff --git a/core/java/android/hardware/camera2/legacy/ParameterUtils.java b/core/java/android/hardware/camera2/legacy/ParameterUtils.java
index 98adcea..3b10eb5 100644
--- a/core/java/android/hardware/camera2/legacy/ParameterUtils.java
+++ b/core/java/android/hardware/camera2/legacy/ParameterUtils.java
@@ -918,7 +918,9 @@
convertCameraAreaToActiveArrayRectangle(activeArray, zoomData, fakeArea);
Point leftEye = face.leftEye, rightEye = face.rightEye, mouth = face.mouth;
- if (leftEye != null && rightEye != null && mouth != null) {
+ if (leftEye != null && rightEye != null && mouth != null && leftEye.x != -2000 &&
+ leftEye.y != -2000 && rightEye.x != -2000 && rightEye.y != -2000 &&
+ mouth.x != -2000 && mouth.y != -2000) {
leftEye = convertCameraPointToActiveArrayPoint(activeArray, zoomData,
leftEye, /*usePreviewCrop*/true);
rightEye = convertCameraPointToActiveArrayPoint(activeArray, zoomData,
diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java
index ad54912..78b9c18 100644
--- a/core/java/android/net/VpnService.java
+++ b/core/java/android/net/VpnService.java
@@ -296,9 +296,9 @@
*
* This method only needs to be called if the VPN has explicitly bound its underlying
* communications channels — such as the socket(s) passed to {@link #protect(int)} —
- * to a {@code Network} using APIs such as {@link Network#bindSocket} or {@link
- * Network#bindDatagramSocket}. The VPN should call this method every time the set of {@code
- * Network}s it is using changes.
+ * to a {@code Network} using APIs such as {@link Network#bindSocket(Socket)} or
+ * {@link Network#bindSocket(DatagramSocket)}. The VPN should call this method every time
+ * the set of {@code Network}s it is using changes.
*
* {@code networks} is one of the following:
* <ul>
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index a040aae..9496b53 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -538,7 +538,7 @@
out.print(prefix); out.print("mType="); out.print(mType);
out.print(" mWindowFlags="); out.print(mWindowFlags);
out.print(" mCurWindowFlags="); out.println(mCurWindowFlags);
- out.print(" mWindowPrivateFlags="); out.print(mWindowPrivateFlags);
+ out.print(prefix); out.print("mWindowPrivateFlags="); out.print(mWindowPrivateFlags);
out.print(" mCurWindowPrivateFlags="); out.println(mCurWindowPrivateFlags);
out.print(prefix); out.print("mVisibleInsets=");
out.print(mVisibleInsets.toShortString());
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java
index 3e4eb02..0ca08e1 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -51,12 +51,19 @@
import libcore.icu.ICU;
/**
- * This class is a widget for selecting a date. The date can be selected by a
- * year, month, and day spinners or a {@link CalendarView}. The set of spinners
- * and the calendar view are automatically synchronized. The client can
- * customize whether only the spinners, or only the calendar view, or both to be
- * displayed. Also the minimal and maximal date from which dates to be selected
- * can be customized.
+ * Provides a widget for selecting a date.
+ * <p>
+ * When the {@link android.R.styleable#DatePicker_datePickerMode} attribute is
+ * set to {@code spinner}, the date can be selected using year, month, and day
+ * spinners or a {@link CalendarView}. The set of spinners and the calendar
+ * view are automatically synchronized. The client can customize whether only
+ * the spinners, or only the calendar view, or both to be displayed.
+ * </p>
+ * <p>
+ * When the {@link android.R.styleable#DatePicker_datePickerMode} attribute is
+ * set to {@code calendar}, the month and day can be selected using a
+ * calendar-style view while the year can be selected separately using a list.
+ * </p>
* <p>
* See the <a href="{@docRoot}guide/topics/ui/controls/pickers.html">Pickers</a>
* guide.
@@ -80,6 +87,7 @@
* @attr ref android.R.styleable#DatePicker_yearListItemTextAppearance
* @attr ref android.R.styleable#DatePicker_yearListSelectorColor
* @attr ref android.R.styleable#DatePicker_calendarTextColor
+ * @attr ref android.R.styleable#DatePicker_datePickerMode
*/
@Widget
public class DatePicker extends FrameLayout {
@@ -357,6 +365,10 @@
/**
* Gets the {@link CalendarView}.
+ * <p>
+ * This method returns {@code null} when the
+ * {@link android.R.styleable#DatePicker_datePickerMode} attribute is set
+ * to {@code calendar}.
*
* @return The calendar view.
* @see #getCalendarViewShown()
@@ -367,6 +379,10 @@
/**
* Sets whether the {@link CalendarView} is shown.
+ * <p>
+ * Calling this method has no effect when the
+ * {@link android.R.styleable#DatePicker_datePickerMode} attribute is set
+ * to {@code calendar}.
*
* @param shown True if the calendar view is to be shown.
*/
diff --git a/core/java/com/android/internal/app/ProcessStats.java b/core/java/com/android/internal/app/ProcessStats.java
index 1b25486..70fb510 100644
--- a/core/java/com/android/internal/app/ProcessStats.java
+++ b/core/java/com/android/internal/app/ProcessStats.java
@@ -1079,7 +1079,9 @@
ProcessDataCollection totals = new ProcessDataCollection(screenStates,
memStates, procStates);
computeProcessData(proc, totals, now);
- if (totals.totalTime != 0 || totals.numPss != 0) {
+ double percentage = (double) totals.totalTime / (double) totalTime * 100;
+ // We don't print percentages < .01, so just drop those.
+ if (percentage >= 0.005 || totals.numPss != 0) {
if (prefix != null) {
pw.print(prefix);
}
@@ -2470,7 +2472,7 @@
totalMem.totalTime, totalPss, totalMem.sysMemSamples);
totalPss = printMemoryCategory(pw, " ", "Free ", totalMem.sysMemFreeWeight,
totalMem.totalTime, totalPss, totalMem.sysMemSamples);
- totalPss = printMemoryCategory(pw, " ", "Z-Ram ", totalMem.sysMemZRamWeight,
+ totalPss = printMemoryCategory(pw, " ", "Z-Ram ", totalMem.sysMemZRamWeight,
totalMem.totalTime, totalPss, totalMem.sysMemSamples);
pw.print(" TOTAL : ");
printSizeValue(pw, totalPss);
diff --git a/core/res/res/layout/input_method_switch_dialog_title.xml b/core/res/res/layout/input_method_switch_dialog_title.xml
index 37bceb6..856a7af 100644
--- a/core/res/res/layout/input_method_switch_dialog_title.xml
+++ b/core/res/res/layout/input_method_switch_dialog_title.xml
@@ -24,21 +24,26 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginBottom="9dip"
- android:layout_marginStart="20dip"
- android:layout_marginEnd="10dip"
- android:layout_marginTop="6dip"
- android:gravity="center_vertical"
- android:orientation="vertical" >
-
- <com.android.internal.widget.DialogTitle
- android:id="@+id/alertTitle"
- style="@android:style/DialogWindowTitle.DeviceDefault"
+ android:orientation="vertical">
+ <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:ellipsize="end"
- android:singleLine="true"
- android:text="@string/select_input_method" />
+ android:orientation="horizontal"
+ android:gravity="center_vertical|start"
+ android:paddingStart="?attr/dialogPreferredPadding"
+ android:paddingEnd="?attr/dialogPreferredPadding"
+ android:paddingTop="@dimen/dialog_padding_top_material">
+
+ <com.android.internal.widget.DialogTitle
+ android:id="@+id/alertTitle"
+ style="?attr/windowTitleStyle"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAlignment="viewStart"
+ android:text="@string/select_input_method" />
+ </LinearLayout>
</LinearLayout>
<!-- Hard keyboard switch -->
@@ -55,34 +60,34 @@
android:orientation="horizontal" >
<LinearLayout
- android:layout_width="0dip"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
- android:background="?android:attr/selectableItemBackground"
+ android:background="?attr/selectableItemBackground"
android:ellipsize="marquee"
android:gravity="center_vertical"
- android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:minHeight="?attr/listPreferredItemHeightSmall"
android:orientation="vertical"
- android:paddingBottom="5dip"
- android:paddingStart="16dip"
- android:paddingEnd="0dip"
- android:paddingTop="5dip" >
+ android:paddingBottom="5dp"
+ android:paddingStart="?attr/listPreferredItemPaddingStart"
+ android:paddingEnd="0dp"
+ android:paddingTop="5dp" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:text="@string/hardware"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:textColor="?android:attr/textColorAlertDialogListItem" />
+ android:textAppearance="?attr/textAppearanceMedium"
+ android:textColor="?attr/textColorAlertDialogListItem" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:text="@string/show_ime"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="?android:attr/textColorAlertDialogListItem" />
+ android:textAppearance="?attr/textAppearanceSmall"
+ android:textColor="?attr/textColorAlertDialogListItem" />
</LinearLayout>
<Switch
@@ -90,12 +95,12 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
- android:layout_marginEnd="12dip" />
+ android:layout_marginEnd="?attr/listPreferredItemPaddingEnd" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
- android:background="?android:attr/listDividerAlertDialog" />
+ android:background="?attr/listDividerAlertDialog" />
</LinearLayout>
</LinearLayout>
diff --git a/core/res/res/layout/input_method_switch_item.xml b/core/res/res/layout/input_method_switch_item.xml
index cfa8b31..6185ed6 100644
--- a/core/res/res/layout/input_method_switch_item.xml
+++ b/core/res/res/layout/input_method_switch_item.xml
@@ -17,44 +17,38 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:minHeight="?attr/listPreferredItemHeightSmall"
android:orientation="horizontal"
android:gravity="center_vertical"
- android:paddingStart="16dip"
- android:paddingEnd="12dip"
- android:minHeight="?attr/listPreferredItemHeightSmall"
- android:background="@color/transparent">
+ android:paddingStart="?attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?attr/listPreferredItemPaddingEnd">
- <RadioButton
- android:id="@+id/radio"
- android:layout_width="35dip"
+ <RadioButton android:id="@+id/radio"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:paddingEnd="12dip"
+ android:paddingEnd="16dp"
android:gravity="center_vertical"
android:focusable="false"
android:clickable="false" />
<LinearLayout
- android:layout_width="wrap_content"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical"
android:gravity="center_vertical">
- <TextView android:id="@android:id/text1"
+ <TextView android:id="@id/text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?attr/textAppearanceListItem"
- android:textColor="?attr/textColorAlertDialogListItem"
- android:gravity="center_vertical|start"
android:singleLine="true"
android:ellipsize="marquee" />
- <TextView android:id="@android:id/text2"
+ <TextView android:id="@id/text2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?attr/textAppearanceListItemSecondary"
- android:textColor="?attr/textColorAlertDialogListItem"
- android:gravity="center_vertical|start"
android:singleLine="true"
android:ellipsize="marquee" />
diff --git a/core/res/res/layout/simple_list_item_2.xml b/core/res/res/layout/simple_list_item_2.xml
index b47a346..bcbc6fc 100644
--- a/core/res/res/layout/simple_list_item_2.xml
+++ b/core/res/res/layout/simple_list_item_2.xml
@@ -17,22 +17,22 @@
<TwoLineListItem xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:minHeight="?android:attr/listPreferredItemHeight"
+ android:minHeight="?attr/listPreferredItemHeight"
android:mode="twoLine"
- android:paddingStart="?android:attr/listPreferredItemPaddingStart"
- android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+ android:paddingStart="?attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?attr/listPreferredItemPaddingEnd">
- <TextView android:id="@android:id/text1"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="8dip"
- android:textAppearance="?android:attr/textAppearanceListItem" />
+ <TextView android:id="@id/text1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:textAppearance="?attr/textAppearanceListItem" />
- <TextView android:id="@android:id/text2"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_below="@android:id/text1"
- android:layout_alignStart="@android:id/text1"
- android:textAppearance="?android:attr/textAppearanceListItemSecondary" />
+ <TextView android:id="@id/text2"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/text1"
+ android:layout_alignStart="@id/text1"
+ android:textAppearance="?attr/textAppearanceListItemSecondary" />
</TwoLineListItem>
diff --git a/core/res/res/values-mcc204-mnc04/config.xml b/core/res/res/values-mcc204-mnc04/config.xml
old mode 100644
new mode 100755
index 2cc7374..dbda6af
--- a/core/res/res/values-mcc204-mnc04/config.xml
+++ b/core/res/res/values-mcc204-mnc04/config.xml
@@ -63,4 +63,6 @@
<item>Vodafone NL,live.vodafone.com,,,vodafone,vodafone,,,,,204,04,,DUN</item>
<item>[ApnSettingV3]SaskTel Tethering,inet.stm.sk.ca,,,,,,,,,204,04,,DUN,,,true,0,,,,,,,gid,5A</item>
</string-array>
+
+ <string translatable="false" name="prohibit_manual_network_selection_in_gobal_mode">true;BAE0000000000000</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc004/config.xml b/core/res/res/values-mcc310-mnc004/config.xml
old mode 100644
new mode 100755
index 6a34a3d..cbe5145
--- a/core/res/res/values-mcc310-mnc004/config.xml
+++ b/core/res/res/values-mcc310-mnc004/config.xml
@@ -38,5 +38,5 @@
<string-array translatable="false" name="config_sms_convert_destination_number_support">
<item>true</item>
</string-array>
-
+ <string translatable="false" name="prohibit_manual_network_selection_in_gobal_mode">true</string>
</resources>
diff --git a/core/res/res/values-mcc311-mnc480/config.xml b/core/res/res/values-mcc311-mnc480/config.xml
old mode 100644
new mode 100755
index 2022d12..be68d57
--- a/core/res/res/values-mcc311-mnc480/config.xml
+++ b/core/res/res/values-mcc311-mnc480/config.xml
@@ -60,4 +60,5 @@
<string-array translatable="false" name="config_sms_convert_destination_number_support">
<item>true</item>
</string-array>
+ <string translatable="false" name="prohibit_manual_network_selection_in_gobal_mode">true</string>
</resources>
diff --git a/core/res/res/values-sw600dp/dimens_material.xml b/core/res/res/values-sw600dp/dimens_material.xml
index 2547b7a..dffe8e7 100644
--- a/core/res/res/values-sw600dp/dimens_material.xml
+++ b/core/res/res/values-sw600dp/dimens_material.xml
@@ -23,5 +23,11 @@
<dimen name="action_bar_default_height_material">64dp</dimen>
<!-- Default padding of an action bar. -->
<dimen name="action_bar_default_padding_material">4dp</dimen>
+ <!-- Default content inset of an action bar. -->
+ <dimen name="action_bar_content_inset_material">24dp</dimen>
+ <!-- Padding to add to the start of the overflow action button. -->
+ <dimen name="action_bar_navigation_padding_start_material">8dp</dimen>
+ <!-- Padding to add to the end of the overflow action button. -->
+ <dimen name="action_bar_overflow_padding_end_material">18dp</dimen>
</resources>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 10c2518e..0c3fb9a 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -260,9 +260,18 @@
released, or define it however else you want, as long as each
successive version has a higher number. This is not a version
number generally shown to the user, that is usually supplied
- with {@link android.R.attr#versionName}. -->
+ with {@link android.R.attr#versionName}. When an app is delivered
+ as multiple split APKs, each APK must have the exact same versionCode. -->
<attr name="versionCode" format="integer" />
-
+
+ <!-- Internal revision code. This number is the number used to determine
+ whether one APK is more recent than another: it has no other meaning
+ than that higher numbers are more recent. This value is only meaningful
+ when the two {@link android.R.attr#versionCode} values are already
+ identical. When an app is delivered as multiple split APKs, each
+ APK may have a different revisionCode value. -->
+ <attr name="revisionCode" format="integer" />
+
<!-- The text shown to the user to indicate the version they have. This
is used for no other purpose than display to the user; the actual
significant version number is given by {@link android.R.attr#versionCode}. -->
@@ -1025,6 +1034,7 @@
<declare-styleable name="AndroidManifest">
<attr name="versionCode" />
<attr name="versionName" />
+ <attr name="revisionCode" />
<attr name="sharedUserId" />
<attr name="sharedUserLabel" />
<attr name="installLocation" />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
old mode 100644
new mode 100755
index bb2ee48..cdbee61
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1950,4 +1950,6 @@
<!-- The maximum bitmap size that can be written to a MediaMetadata object. This value
is the max width/height allowed in dips.-->
<dimen name="config_mediaMetadataBitmapMaxSize">320dp</dimen>
+
+ <string translatable="false" name="prohibit_manual_network_selection_in_gobal_mode">false</string>
</resources>
diff --git a/core/res/res/values/dimens_material.xml b/core/res/res/values/dimens_material.xml
index 54f483d..f7ebbe4 100644
--- a/core/res/res/values/dimens_material.xml
+++ b/core/res/res/values/dimens_material.xml
@@ -37,6 +37,8 @@
<dimen name="action_bar_default_height_material">56dp</dimen>
<!-- Default padding of an action bar. -->
<dimen name="action_bar_default_padding_material">4dp</dimen>
+ <!-- Default content inset of an action bar. -->
+ <dimen name="action_bar_content_inset_material">16dp</dimen>
<!-- Vertical padding around action bar icons. -->
<dimen name="action_bar_icon_vertical_padding_material">16dp</dimen>
<!-- Top margin for action bar subtitles -->
@@ -44,6 +46,17 @@
<!-- Bottom margin for action bar subtitles -->
<dimen name="action_bar_subtitle_bottom_margin_material">5dp</dimen>
+ <!-- Default padding for list items. This should match the action bar
+ content inset so that ListActivity items line up correctly. -->
+ <dimen name="list_item_padding_horizontal_material">@dimen/action_bar_content_inset_material</dimen>
+
+ <!-- Padding to add to the start of the overflow action button. -->
+ <dimen name="action_bar_navigation_padding_start_material">0dp</dimen>
+ <!-- Padding to add to the start of the overflow action button. -->
+ <dimen name="action_bar_overflow_padding_start_material">6dp</dimen>
+ <!-- Padding to add to the end of the overflow action button. -->
+ <dimen name="action_bar_overflow_padding_end_material">10dp</dimen>
+
<dimen name="action_button_min_width_overflow_material">36dp</dimen>
<dimen name="action_button_min_width_material">48dp</dimen>
<dimen name="action_button_min_height_material">48dp</dimen>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 1123813..2bb9aa8 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2583,24 +2583,17 @@
<public type="raw" name="nodomain" id="0x01100001" />
<!-- ===============================================================
- Insert space from public API 21 attributes to private attributes
- =============================================================== -->
- <eat-comment />
-
- <!-- @hide -->
- <public-padding type="attr" name="private_resource_pad" end="0x01010500" />
-
- <!-- ===============================================================
Resources added in version 22 of the platform
=============================================================== -->
<eat-comment />
- <public type="attr" name="resizeClip"/>
- <public type="attr" name="collapseContentDescription"/>
- <public type="attr" name="accessibilityTraversalBefore" />
- <public type="attr" name="accessibilityTraversalAfter" />
- <public type="attr" name="dialogPreferredPadding" />
- <public type="attr" name="searchHintIcon" />
+ <public type="attr" name="resizeClip" id="0x010104cf" />
+ <public type="attr" name="collapseContentDescription" id="0x010104d0" />
+ <public type="attr" name="accessibilityTraversalBefore" id="0x010104d1" />
+ <public type="attr" name="accessibilityTraversalAfter" id="0x010104d2" />
+ <public type="attr" name="dialogPreferredPadding" id="0x010104d3" />
+ <public type="attr" name="searchHintIcon" id="0x010104d4" />
+ <public type="attr" name="revisionCode" />
<public type="style" name="Theme.DeviceDefault.Dialog.Alert" />
<public type="style" name="Theme.DeviceDefault.Light.Dialog.Alert" />
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index c7e9ec9..d3d6d70 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -815,6 +815,7 @@
<style name="Widget.Material.Toolbar.Button.Navigation" parent="Widget.Toolbar.Button.Navigation">
<item name="background">?attr/selectableItemBackgroundBorderless</item>
+ <item name="paddingStart">@dimen/action_bar_navigation_padding_start_material</item>
</style>
<style name="Widget.Material.WebTextView" parent="Widget.WebTextView"/>
@@ -874,8 +875,8 @@
<item name="contentDescription">@string/action_menu_overflow_description</item>
<item name="minWidth">@dimen/action_button_min_width_overflow_material</item>
<item name="minHeight">@dimen/action_button_min_height_material</item>
- <item name="paddingStart">6dp</item>
- <item name="paddingEnd">10dp</item>
+ <item name="paddingStart">@dimen/action_bar_overflow_padding_start_material</item>
+ <item name="paddingEnd">@dimen/action_bar_overflow_padding_end_material</item>
</style>
<style name="Widget.Material.ActionBar.TabView" parent="Widget.ActionBar.TabView">
@@ -910,11 +911,12 @@
<item name="subtitleTextStyle">@style/TextAppearance.Material.Widget.ActionBar.Subtitle</item>
<item name="progressBarStyle">?attr/progressBarStyleHorizontal</item>
<item name="indeterminateProgressStyle">?attr/progressBarStyle</item>
- <item name="progressBarPadding">32dip</item>
- <item name="itemPadding">8dip</item>
+ <item name="progressBarPadding">32dp</item>
+ <item name="itemPadding">8dp</item>
<item name="homeLayout">@layout/action_bar_home_material</item>
<item name="gravity">center_vertical</item>
- <item name="contentInsetStart">16dp</item>
+ <item name="contentInsetStart">@dimen/action_bar_content_inset_material</item>
+ <item name="contentInsetEnd">@dimen/action_bar_content_inset_material</item>
<item name="elevation">8dp</item>
<item name="popupTheme">?attr/actionBarPopupTheme</item>
</style>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
old mode 100644
new mode 100755
index 7bb57ce..1880351
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2127,6 +2127,7 @@
<java-symbol type="id" name="scrollIndicatorUp" />
<java-symbol type="id" name="scrollIndicatorDown" />
<java-symbol type="array" name="config_sms_convert_destination_number_support" />
+ <java-symbol type="string" name="prohibit_manual_network_selection_in_gobal_mode" />
<!-- From SignalStrength -->
<java-symbol type="integer" name="config_LTE_RSRP_threshold_type" />
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index 3f2062dc..9690238 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -115,10 +115,10 @@
<item name="textAppearanceListItem">@style/TextAppearance.Material.Subhead</item>
<item name="textAppearanceListItemSmall">@style/TextAppearance.Material.Subhead</item>
<item name="textAppearanceListItemSecondary">@style/TextAppearance.Material.Body1</item>
- <item name="listPreferredItemPaddingLeft">16dip</item>
- <item name="listPreferredItemPaddingRight">16dip</item>
- <item name="listPreferredItemPaddingStart">16dip</item>
- <item name="listPreferredItemPaddingEnd">16dip</item>
+ <item name="listPreferredItemPaddingLeft">@dimen/list_item_padding_horizontal_material</item>
+ <item name="listPreferredItemPaddingRight">@dimen/list_item_padding_horizontal_material</item>
+ <item name="listPreferredItemPaddingStart">@dimen/list_item_padding_horizontal_material</item>
+ <item name="listPreferredItemPaddingEnd">@dimen/list_item_padding_horizontal_material</item>
<!-- @hide -->
<item name="searchResultListItemHeight">58dip</item>
@@ -467,10 +467,10 @@
<item name="textAppearanceListItem">@style/TextAppearance.Material.Subhead</item>
<item name="textAppearanceListItemSmall">@style/TextAppearance.Material.Subhead</item>
<item name="textAppearanceListItemSecondary">@style/TextAppearance.Material.Body1</item>
- <item name="listPreferredItemPaddingLeft">16dip</item>
- <item name="listPreferredItemPaddingRight">16dip</item>
- <item name="listPreferredItemPaddingStart">16dip</item>
- <item name="listPreferredItemPaddingEnd">16dip</item>
+ <item name="listPreferredItemPaddingLeft">@dimen/list_item_padding_horizontal_material</item>
+ <item name="listPreferredItemPaddingRight">@dimen/list_item_padding_horizontal_material</item>
+ <item name="listPreferredItemPaddingStart">@dimen/list_item_padding_horizontal_material</item>
+ <item name="listPreferredItemPaddingEnd">@dimen/list_item_padding_horizontal_material</item>
<!-- @hide -->
<item name="searchResultListItemHeight">58dip</item>
diff --git a/docs/html-intl/intl/zh-cn/about/versions/android-5.0.jd b/docs/html-intl/intl/zh-cn/about/versions/android-5.0.jd
new file mode 100644
index 0000000..9c3dd5c
--- /dev/null
+++ b/docs/html-intl/intl/zh-cn/about/versions/android-5.0.jd
@@ -0,0 +1,631 @@
+page.title=Android 5.0 API
+excludeFromSuggestions=true
+sdk.platform.version=5.0
+sdk.platform.apiLevel=21
+@jd:body
+
+
+<div id="qv-wrapper">
+<div id="qv">
+
+<h2>在本文档中 <a href="#" onclick="hideNestedItems('#toc44',this);return false;" class="header-toggle"> <span class="more">展开</span><span class="less" style="display:none">合拢</span></a></h2>
+
+<ol id="toc44" class="hide-nested">
+ <li><a href="#ApiLevel">更新目标 API 级别</a></li>
+ <li><a href="#Behaviors">重要的行为变更</a>
+ <ol>
+ <li><a href="#ART">如果您尚未针对新的 Android 运行时 (ART) 测试您的应用…</a></li>
+ <li><a href="#BehaviorNotifications">如果您的应用实施通知…</a></li>
+ <li><a href="#BehaviorMediaControl">如果您的应用使用 RemoteControlClient…</a></li>
+<li><a href="#BehaviorGetRecentTasks">如果您的应用使用 getRecentTasks()…</a></li>
+<li><a href="#64BitSupport">如果您使用的是 Android 原生开发工具包 (NDK)…</a></li>
+<li><a href="#BindService">如果您的应用绑定到某项服务…</a></li>
+<li><a href="#BehaviorWebView">如果您的应用使用 WebView…</a></li>
+ </ol>
+ </li>
+ <li><a href="#UI">界面</a>
+ <ol>
+ <li><a href="#MaterialDesign">Material Design 设计支持</a></li>
+ <li><a href="#Recents">“最近用过”屏幕中的并行文档和活动</a></li>
+ <li><a href="#WebView">WebView 更新</a></li>
+ <li><a href="#ScreenCapture">屏幕截图和共享</a></li>
+ </ol>
+ </li>
+ <li><a href="#Notifications">通知</a>
+ <ol>
+ <li><a href="#LockscreenNotifications">锁定屏幕通知</a></li>
+ <li><a href="#NotificationsMetadata">通知元数据</a></li>
+ </ol>
+ </li>
+ <li><a href="#Graphics">图形</a>
+ <ol>
+ <li><a href="#OpenGLES-3-1">支持 OpenGL ES 3.1</a></li>
+ <li><a href="#AndroidExtensionPack">Android 扩展程序包</a></li>
+ </ol>
+ </li>
+ <li><a href="#Media">媒体</a>
+ <ol>
+ <li><a href="#Camera-v2">提供高级摄像头功能的摄像头 API</a></li>
+ <li><a href="#AudioPlayback">音频播放</a></li>
+ <li><a href="#MediaPlaybackControl">媒体播放控件</a></li>
+ <li><a href="#MediaBrowsing">媒体浏览</a></li>
+ </ol>
+ </li>
+ <li><a href="#Storage">存储</a>
+ <ol>
+ <li><a href="#DirectorySelection">目录选择</a></li>
+ </ol>
+ </li>
+ <li><a href="#Wireless">无线和连接</a>
+ <ol>
+ <li><a href="#Multinetwork">多个网络连接</a></li>
+ <li><a href="#BluetoothBroadcasting">蓝牙广播</a></li>
+ <li><a href="#NFCEnhancements">NFC 增强功能</a></li>
+ </ol>
+ </li>
+ <li><a href="#Power">Project Volta</a>
+ <ol>
+ <li><a href="#JobScheduler">安排作业</a></li>
+ <li><a href="#PowerMeasurementTools">用于查询耗电信息的开发者工具</a>
+ </ol>
+ </li>
+ <li><a href="#Enterprise">Android 在办公和教育中的应用</a>
+ <ol>
+ <li><a href="#ManagedProvisioning">托管配置</a></li>
+ <li><a href="#DeviceOwner">设备所有者</a></li>
+ <li><a href="#ScreenPinning">固定屏幕</a></li>
+ </ol>
+ </li>
+ <li><a href="#System">系统</a>
+ <ol>
+ <li><a href="#AppUsageStatistics">应用使用情况统计信息</a></li>
+ </ol>
+ </li>
+ <li><a href="#Printing">打印框架</a>
+ <ol>
+ <li><a href="#PDFRender">使用位图来呈现 PDF 文件</a></li>
+ </ol>
+ </li>
+ <li><a href="#TestingA11y">测试和辅助功能</a>
+ <ol>
+ <li><a href="#TestingA11yImprovements">测试和辅助功能改进</a></li>
+ </ol>
+ </li>
+ <li><a href="#IME">IME</a>
+ <ol>
+ <li><a href="#Switching">更轻松地切换输入语言</a></li>
+ </ol>
+ </li>
+ <li><a href="#Manifest">清单声明</a>
+ <ol>
+ <li><a href="#ManifestFeatures">应声明的必需功能</a></li>
+ <li><a href="#Permissions">用户权限</a></li>
+ </ol>
+ </li>
+</ol>
+
+<h2>API 区别</h2>
+<ol>
+<li><a href="{@docRoot}sdk/api_diff/21/changes.html">API 级别 20 对比 21 »</a> </li>
+<li><a href="{@docRoot}sdk/api_diff/preview-21/changes.html">L Developer Preview 对比级别 21 »</a> </li>
+</ol>
+
+</div>
+</div>
+
+<p>API 级别:{@sdkPlatformApiLevel}</p>
+
+<p>Android 5.0 (<a href="{@docRoot}reference/android/os/Build.VERSION_CODES.html#LOLLIPOP">LOLLIPOP</a>) 为用户和应用开发者提供了新的功能。本文档介绍了最值得注意的新 API。</p>
+
+<p>有关新平台功能的扼要介绍,另请参阅 <a href="{@docRoot}about/versions/lollipop.html">Android Lollipop 集锦</a>。</p>
+
+
+<h3 id="Start">开始开发</h3>
+
+<p>要构建 Android 5.0 版应用,您必须先<a href="{@docRoot}sdk/index.html">下载 Android SDK</a>,然后使用 <a href="{@docRoot}tools/help/sdk-manager.html">SDK 管理器</a>下载 Android 5.0 SDK 平台和系统映像。</p>
+
+
+
+<h3 id="ApiLevel">更新您的目标 API 级别</h3>
+
+<p>要进一步针对运行 Android {@sdkPlatformVersion} 的设备优化您的应用,请将 <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code targetSdkVersion}</a> 设置为 <code>"{@sdkPlatformApiLevel}"</code>,在 Android {@sdkPlatformVersion} 系统映像上安装该应用并予以测试,然后将更改后的新版应用发布出去。</p>
+
+<p>您既可以使用 {@sdkPlatformVersion} API,也可以支持旧版本,方法是在代码中构建条件,确保先检查系统 API 级别再执行 <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code minSdkVersion}</a> 不支持的 API。要详细了解有关保持向后兼容的信息,请参阅<a href="{@docRoot}training/basics/supporting-devices/platforms.html">支持不同平台版本</a>。</p>
+
+<p>要详细了解有关各个 API 级别运行方式的信息,请参阅<a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#ApiLevels">什么是 API 级别?</a></p>
+
+<h2 id="Behaviors">重要的行为变更</h2>
+
+<p>请注意,如果您之前发布了 Android 版应用,那么您的应用可能会受到 Android 5.0 变化的影响。</p>
+
+<h3 id="ART">如果您尚未针对新的 Android 运行时 (ART) 测试您的应用…</h3>
+
+<p>第 4.4 版引入了一个全新的实验性功能,即 Android 运行时 (ART)。在第 4.4 版中,ART 是可选的,默认运行时仍为 Dalvik。对于 Android 5.0,默认运行时现在是 ART。</p>
+
+<p>有关 ART 新功能的概述,请参阅 <a href="https://source.android.com/devices/tech/dalvik/art.html">ART 简介</a>。主要包括下面的一些新功能:</p>
+
+<ul>
+ <li>预先 (AOT) 编译</li>
+ <li>经过改进的垃圾回收 (GC)</li>
+ <li>经过改进的调试支持</li>
+</ul>
+
+<p>大多数 Android 应用应该不用做出任何更改即可直接在 ART 下运行。不过,Dalvik 上运行的部分技术在 ART 上无法运行。要了解最重要的问题,请参阅<a href="{@docRoot}guide/practices/verifying-apps-art.html">在 Android 运行时 (ART) 上验证应用行为</a>。请特别注意以下情况:</p>
+
+<ul>
+ <li>您的应用使用 Java 原生接口 (JNI) 运行 C/C++ 代码。</li>
+ <li>您使用的开发工具可生成非标准代码(例如某些混淆代码)。</li>
+ <li>您使用的技术与垃圾回收压缩不兼容。(虽然 ART 当前没有实施垃圾回收压缩,但在 Android 开放源代码项目中,垃圾回收压缩正处于开发阶段)。</li>
+</ul>
+
+<h3 id="BehaviorNotifications">如果您的应用实施通知…</h3>
+
+<p>请确保您的通知能够反映出 Android 5.0 的这些变化。要详细了解如何设计适用于 Android 5.0 及更高版本的通知,请参阅<a href="{@docRoot}design/patterns/notifications.html">通知设计指南</a>。
+</p>
+
+<h4 id="NotificationsMaterialDesignStyle">Material Design 设计样式</h4>
+<p>通知是在白色(或颜色非常浅的)背景上绘制的深色文本,以便与新的 Material Design 设计小部件协调一致。请确保所有通知在新的配色方案下都能够正常显示。如果通知无法正常显示,请予以修复:</p>
+
+<ul>
+ <li>使用 {@link android.app.Notification.Builder#setColor(int) setColor()} 在图标图片后面的圆圈中设置一种强调色彩。 </li>
+ <li>更新或移除有颜色的资源。系统会忽略操作图标和主通知图标中的所有非 Alpha 通道。您应当假定这些图标仅包含 Alpha 通道。系统会用白色来绘制通知图标,而用深灰色来绘制操作图标。</li>
+</ul>
+
+<h4 id="NotificationsSoundVibration">声音和振动</h4>
+<p>如果您当前在使用 {@link android.media.Ringtone}、{@link android.media.MediaPlayer} 或 {@link android.os.Vibrator} 类为通知添加声音和振动,请移除该代码,这样系统才能以优先模式正常呈现通知。<em></em>若要添加声音和振动,请改用 {@link android.app.Notification.Builder} 方法。</p>
+
+<p>将设备设置为 {@link android.media.AudioManager#RINGER_MODE_SILENT RINGER_MODE_SILENT} 会导致它进入新的优先模式。如果将设备设置为 {@link android.media.AudioManager#RINGER_MODE_NORMAL RINGER_MODE_NORMAL} 或 {@link android.media.AudioManager#RINGER_MODE_NORMAL RINGER_MODE_VIBRATE},则会使其退出优先模式。</p>
+
+<p>以前,Android 将 {@link android.media.AudioManager#STREAM_MUSIC STREAM_MUSIC} 用作主音量流,以此来控制平板电脑设备的音量。在 Android 5.0 中,手机和平板电脑设备的主音量流现已统一,均由 {@link android.media.AudioManager#STREAM_RING STREAM_RING} 或 {@link android.media.AudioManager#STREAM_NOTIFICATION STREAM_NOTIFICATION} 控制。</p>
+
+<h4 id="NotificationsLockscreenVisibility">通知在锁定屏幕上的公开程度</h4>
+<p>在 Android 5.0 中,通知现在默认显示在用户的锁定屏幕上。用户可以选择不公开敏感信息,这样的话系统会自动修改通知显示文本。要自定义这种经过修改的通知,请使用 {@link android.app.Notification.Builder#setPublicVersion(android.app.Notification) setPublicVersion()}。</p>
+<p>如果通知不包含个人信息,或者您希望在通知中显示媒体播放控件,请调用 {@link android.app.Notification.Builder#setVisibility(int) setVisibility()} 方法,并将通知的公开程度的级别设置为 {@link android.app.Notification#VISIBILITY_PUBLIC VISIBILITY_PUBLIC}。
+</p>
+
+<h4 id="NotificationsMediaPlayback">媒体播放</h4>
+<p>如果您要实施可呈现媒体播放状态或传输控件的通知,请考虑使用新的 {@link android.app.Notification.MediaStyle} 模板,而不是自定义 {@link android.widget.RemoteViews.RemoteView} 对象。无论您选择使用哪种方法,请务必将通知的公开程度设置为 {@link android.app.Notification#VISIBILITY_PUBLIC VISIBILITY_PUBLIC},以便用户可以在锁定屏幕中使用您的控件。请注意,从 Android 5.0 开始,系统不再在锁定屏幕中显示 {@link android.media.RemoteControlClient} 对象。有关详情,请参阅<a href="#BehaviorMediaControl">如果您的应用使用 RemoteControlClient</a>。</p>
+
+<h4 id="NotificationsHeadsup">提醒通知</h4>
+<p>现在,当设备处于活动状态(即设备未锁定且屏幕亮起)时,通知可以显示在小型浮动窗口中(也称为提醒通知)。此类通知采用的显示形式与紧凑型通知采用的形式类似,不同的是提醒通知还会显示操作按钮。用户无需退出当前应用,即可根据提醒通知执行操作或关闭提醒通知。</p>
+
+<p>可以触发提醒通知的情景示例包括:</p>
+
+<ul>
+ <li>用户在全屏模式下执行操作(应用使用 {@link android.app.Notification#fullScreenIntent})</li>
+ <li>通知的优先级较高,并且使用铃声或振动</li>
+</ul>
+
+<p>如果您的应用在上述任意一种情景下实施通知,请确保提醒通知能够正常呈现。</p>
+
+<h3 id="BehaviorMediaControl">如果您的应用使用 RemoteControlClient…</h3>
+<p>{@link android.media.RemoteControlClient} 类现已被弃用。请尽快改用全新的 {@link android.media.session.MediaSession} API。</p>
+
+<p>对于 {@link android.media.session.MediaSession} 或 {@link android.media.RemoteControlClient},Android 5.0 中的锁定屏幕不会显示传输控件。相反,您的应用可以通过通知在锁定屏幕中提供媒体播放控件。这样,您的应用就能够更好地控制媒体按钮的展现方式,同时还能让用户无论在设备处于锁定还是解锁状态下都可以执行同样的操作。</p>
+
+<p>为此,Android 5.0 引入了一个新的 {@link android.app.Notification.MediaStyle} 模板。{@link android.app.Notification.MediaStyle} 将您通过 {@link android.app.Notification.Builder#addAction(int, java.lang.CharSequence, android.app.PendingIntent) Notification.Builder.addAction()} 添加的通知操作转换到在您应用的媒体播放通知中内嵌的紧凑型按钮中。将会话令牌传递到 {@link android.app.Notification.MediaStyle#setMediaSession(android.media.session.MediaSession.Token) setSession()} 方法,指示系统此通知控制正在进行的媒体会话。</p>
+
+<p>请务必将通知的公开程度设置为 {@link android.app.Notification#VISIBILITY_PUBLIC VISIBILITY_PUBLIC},将通知标为安全通知,以便在任何锁定屏幕(安全屏幕或其他屏幕)上显示。有关详情,请参阅<a href="#LockscreenNotifications">在锁定屏幕上显示通知</a>。</p>
+
+<p>如果您的应用是在 Android <a href="{@docRoot}tv/index.html">TV</a> 或 <a href="{@docRoot}wear/index.html">Wear</a> 平台上运行,请通过实施 {@link android.media.session.MediaSession} 类来显示媒体播放控件。如果您的应用需要在 Android 设备上接收媒体按钮事件,您还应当实施 {@link android.media.session.MediaSession}。</p>
+
+<h3 id="BehaviorGetRecentTasks">如果您的应用使用 getRecentTasks()…</h3>
+
+<p><em></em>由于 Android 5.0 中引入了并行文档和活动任务这一新功能(请参阅下文中的<a href="#Recents">“最近用过”屏幕中的并行文档和活动</a>),{@link android.app.ActivityManager#getRecentTasks ActivityManager.getRecentTasks()} 方法现已被弃用,以更好地保护用户隐私。为了实现向后兼容性,此方法仍会返回它的一小部分数据,包括调用应用自身的任务,同时还可能包括其他一些非敏感任务(例如“首页”)。如果您的应用在使用此方法检索它自身的任务,请改用 {@link android.app.ActivityManager#getAppTasks() getAppTasks()} 检索该信息。</p>
+
+<h3 id="64BitSupport">如果您使用的是 Android 原生开发工具包 (NDK)…</h3>
+
+<p>Android 5.0 支持 64 位系统。64 位增强功能增加了寻址空间并提高了性能,同时仍能全面支持现有的 32 位应用。64 位支持还提高了 OpenSSL 的加密性能。另外,此版本还引入了新的原生媒体 NDK API 以及原生 OpenGL ES (GLES) 3.1 支持。</p>
+
+<p>要利用 Android 5.0 中提供的 64 位支持,请从 <a href="{@docRoot}tools/sdk/ndk/index.html">Android NDK 页面</a>下载并安装 NDK 10c 版。要详细了解 NDK 的重要变化和错误修复,请参阅 10c 版<a href="{@docRoot}tools/sdk/ndk/index.html#Revisions">版本说明</a>。</p>
+
+<h3 id="BindService">如果您的应用绑定到某项服务…</h3>
+
+<p>{@link android.content.Context#bindService(android.content.Intent, android.content.ServiceConnection, int) Context.bindService()} 方法现在要求获取显式 {@link android.content.Intent};如果获取的是隐式 intent,则会引发异常。为了确保您的应用是安全的,请在启动或绑定 {@link android.app.Service} 时使用显式 intent,并且不要为此服务声明 intent 过滤器。</p>
+
+<h3 id="BehaviorWebView">如果您的应用使用 WebView…</h3>
+
+<p>Android 5.0 会更改您应用的默认行为。</p>
+<ul>
+<li><strong>如果您的应用的目标 API 级别不低于 21</strong>:
+ <ul>
+ <li>默认情况下,系统会屏蔽<a href="https://developer.mozilla.org/en-US/docs/Security/MixedContent" class="external-link">混合内容</a>和第三方 Cookie。要让系统不屏蔽混合内容和第三方 Cookie,请分别使用 {@link android.webkit.WebSettings#setMixedContentMode(int) setMixedContentMode()} 和 {@link android.webkit.CookieManager#setAcceptThirdPartyCookies(android.webkit.WebView, boolean) setAcceptThirdPartyCookies()} 方法。</li>
+ <li>系统现在可以智能选择要绘制的 HTML 文档部分。这一新的默认行为有助于降低内存占用量并提高性能。如果您要一次性呈现整个文档,请调用 {@link android.webkit.WebView#enableSlowWholeDocumentDraw()} 停用此优化行为。</li>
+ </ul>
+</li>
+<li><strong>如果您的应用的目标 API 级别低于 21</strong>:系统不会屏蔽混合内容和第三方 Cookie,并且始终会一次性呈现整个文档。</li>
+</ul>
+
+<h2 id="UI">界面</h2>
+
+<h3 id="MaterialDesign">Material Design 设计支持</h3>
+
+<p><em></em>即将发布的版本新增了对 Android 新 Material Design 设计样式的支持。您可以借助 Material Design 设计样式创建应用,使其呈现动态的视觉效果并为用户提供自然的界面元素过渡效果。此支持包括:</p>
+
+<ul>
+
+ <li>素材主题背景</li>
+ <li>视图阴影</li>
+ <li>{@link android.support.v7.widget.RecyclerView} 小部件</li>
+ <li>可绘制的动画和样式效果</li>
+ <li>Material Design 设计动画和活动过渡效果</li>
+ <li>基于视图状态的视图属性动画生成器</li>
+ <li>可自定义的界面小部件和应用栏(含您可以控制的调色板)</li>
+ <li>基于 XML 矢量图形的动画和非动画图形内容</li>
+</ul>
+
+<p>要详细了解如何向您的应用添加 Material Design 设计功能,请参阅 <a href="{@docRoot}training/material/index.html">Material Design 设计</a>。</p>
+
+<h3 id="Recents">“最近用过”屏幕中的并行文档和活动</h3>
+
+<p>在以前的版本中,对于用户最近与之互动的每个应用,<a href="{@docRoot}guide/components/recents.html">“最近用过”屏幕</a>都只能显示一个任务。现在,您的应用可以视需要针对其他并行活动或文档打开多个任务。通过此功能,用户可以在“最近用过”屏幕中快速切换各个活动和文档,并能在所有应用之间获得一致的切换体验,从而实现多任务处理。此类并行任务示例可能包括:网络浏览器应用中打开的标签页、效率类应用中的文档、游戏中的并行对局或信息应用中的聊天。您的应用可以通过 {@link android.app.ActivityManager.AppTask} 类管理任务。</p>
+
+<p>要插入逻辑中断,以便系统能够将您的活动视为一个新任务,请在通过 {@link android.app.Activity#startActivity(android.content.Intent) startActivity()} 启动活动时使用 {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT}。您还可以在清单中将 <a href="{@docRoot}guide/topics/manifest/activity-element.html"><activity></a> 元素的 {@code documentLaunchMode} 属性设置为 {@code "intoExisting"} 或 {@code "always"},从而实现该行为。</p>
+
+<p>为了避免“最近用过”屏幕过于混乱,您可以设置应用能够在该屏幕中显示的任务数上限。为此,请设置 <a href="{@docRoot}guide/topics/manifest/application-element.html"><application></a> 的 {@link android.R.attr#maxRecents android:maxRecents} 属性。目前可以指定的上限为,每个用户 50 个任务(对于 RAM 较小的设备,则为每个用户 25 个任务)。</a></p>
+
+<p>您可以将“最近用过”屏幕中的任务设置为在重新启动过程中保持不变。要控制这一持续行为,请使用 <a href="{@docRoot}reference/android/R.attr.html#persistableMode">android:persistableMode</a> 属性。您也可以通过调用 {@link android.app.Activity#setTaskDescription(android.app.ActivityManager.TaskDescription) setTaskDescription()} 方法,来更改“最近用过”屏幕中活动的视觉属性,如活动的颜色、标签和图标。</p>
+
+<h3 id="WebView">WebView 更新</h3>
+<p>Android 5.0 将 {@link android.webkit.WebView} 实施更新为 Chromium M37,不仅提高了安全性和稳定性,还修复了多项错误。Android 5.0 上运行的 {@link android.webkit.WebView} 的默认用户代理字符串已更新为采用 37.0.0.0 作为版本号。</p>
+
+<p>此版本引入了 {@link android.webkit.PermissionRequest} 类,该类允许您的应用 <a href="https://developer.mozilla.org/en-US/docs/NavigatorUserMedia.getUserMedia" class="external-link">getUserMedia()</a> 之类的 Web API 向 {@link android.webkit.WebView} 授予对受保护资源(例如摄像头和麦克风)的访问权限。您的应用必须拥有对这些资源的相应 Android 权限,才能向 {@link android.webkit.WebView} 授予权限。</p>
+
+<p>借助新的 <code><a href="{@docRoot}reference/android/webkit/WebChromeClient.html#onShowFileChooser(android.webkit.WebView, android.webkit.ValueCallback<android.net.Uri[]>, android.webkit.WebChromeClient.FileChooserParams)">onShowFileChooser()</a></code> 方法,您现在不但可以在 {@link android.webkit.WebView} 中使用输入表单字段,而且可以启动文件选择器从 Android 设备中选择图片和文件。</p>
+
+<p>另外,此版本还支持 <a href="http://webaudio.github.io/web-audio-api/" class="external-link">WebAudio</a>、<a href="https://www.khronos.org/webgl/" class="external-link">WebGL</a> 和 <a href="http://www.webrtc.org/" class="external-link">WebRTC</a> 等开放标准。要详细了解此版本中的新增功能,请参阅 <a href="https://developer.chrome.com/multidevice/webview/overview" class="external-link">Android 版 WebView</a>。</p>
+
+<h3 id="ScreenCapture">屏幕截图和共享</h3>
+<p>通过 Android 5.0,您可以使用新的 {@link android.media.projection} API 向您的应用添加屏幕截图和屏幕共享功能。例如,如果您希望在视频会议应用中启用屏幕共享,则会发现此功能非常实用。</p>
+
+<p>通过新的 {@link android.media.projection.MediaProjection#createVirtualDisplay(java.lang.String, int, int, int, int, android.view.Surface, android.hardware.display.VirtualDisplay.Callback, android.os.Handler) createVirtualDisplay()} 方法,您的应用可以将主屏幕内容(默认显示屏)捕获到 {@link android.view.Surface} 对象中,然后通过网络发送该对象。该 API 仅允许捕获非安全的屏幕内容,不允许捕获系统音频。要开始屏幕截图,您的应用必须先使用通过 {@link android.media.projection.MediaProjectionManager#createScreenCaptureIntent()} 方法获取的 {@link android.content.Intent} 启动屏幕截图对话框,来请求用户向其授予相应权限。</p>
+
+<p>有关新 API 的使用示例,请参阅示例项目中的 {@code MediaProjectionDemo} 类。</p>
+
+<h2 id="Notifications">通知</h2>
+
+<h3 id="LockscreenNotifications">锁定屏幕通知</h3>
+<p>Android 5.0 中的锁定屏幕能够呈现通知。<em></em>用户可以通过“设置”来选择是否允许在安全的锁定屏幕上显示敏感的通知内容。</p>
+
+<p>您的应用可以控制其通知在安全的锁定屏幕上显示时的具体公开程度。要控制公开程度的级别,请调用 {@link android.app.Notification.Builder#setVisibility(int) setVisibility()} 并指定下列值之一:</p>
+
+<ul>
+<li>{@link android.app.Notification#VISIBILITY_PRIVATE VISIBILITY_PRIVATE}:显示基本信息(例如通知图标),但隐藏通知的全部内容。</li>
+<li>{@link android.app.Notification#VISIBILITY_PUBLIC VISIBILITY_PUBLIC}:显示通知的全部内容。</li>
+<li>{@link android.app.Notification#VISIBILITY_SECRET VISIBILITY_SECRET}:不显示任何内容,甚至连通知图标也不显示。</li>
+</ul>
+
+<p>如果公开程度级别为 {@link android.app.Notification#VISIBILITY_PRIVATE VISIBILITY_PRIVATE},您还可以提供隐藏了个人详细信息的通知内容修改版本。例如,短信应用的通知可能会显示“您有 3 条新短信”,但隐藏短信内容和发送者。要提供此备用通知,请先使用 {@link android.app.Notification.Builder} 创建替代通知。当创建不公开的通知对象时,请通过 {@link android.app.Notification.Builder#setPublicVersion(android.app.Notification) setPublicVersion()} 方法为其附加替代通知。</p>
+
+<h3 id="NotificationsMetadata">通知元数据</h3>
+<p>Android 5.0 使用与您的应用通知关联的元数据更智能地对通知进行排序。要设置元数据,请在构建通知时调用 {@link android.app.Notification.Builder} 中的以下方法:</p>
+
+<ul>
+<li>{@link android.app.Notification.Builder#setCategory(java.lang.String) setCategory()}:告诉系统当设备处于“优先”<em></em>模式时如何处理您的应用通知(例如,当通知表示来电、即时消息或警报时)。
+<li>{@link android.app.Notification.Builder#setPriority(int) setPriority()}:将通知标记为重要性高于或低于普通通知。如果还带有声音或振动,则优先级字段设置为 {@link android.app.Notification#PRIORITY_MAX PRIORITY_MAX} 或 {@link android.app.Notification#PRIORITY_HIGH PRIORITY_HIGH} 的通知将出现在一个小的浮动窗口中。</li>
+<li>{@link android.app.Notification.Builder#addPerson(java.lang.String) addPerson()}:允许您向通知添加一个或多个相关的人员。利用此方法,您的应用可指示系统将来自指定人员的通知归成一组,或者将来自这些人员的通知归类为重要性高于普通通知。</li>
+</ul>
+
+<h2 id="Graphics">显卡</h2>
+
+<h3 id="OpenGLES-3-1">支持 OpenGL ES 3.1</h3>
+<p>Android 5.0 针对 OpenGL ES 3.1 添加了 Java 接口和原生支持。OpenGL ES 3.1 中提供的主要新功能包括:</p>
+
+<ul>
+<li>对着色器进行计算
+<li>单独的着色器对象
+<li>间接绘图命令
+<li>多样本和模版纹理
+<li>对着色语言的改进
+<li>用于高级混合模式和调试的扩展
+<li>对 OpenGL ES 2.0 和 3.0 的向后兼容性
+</ul>
+
+<p>Android 上 OpenGL ES 3.1 的 Java 接口是通过 {@link android.opengl.GLES31} 提供的。当使用 OpenGL ES 3.1 时,请务必在您的清单文件中使用 <a href="{@docRoot}guide/topics/manifest/uses-feature-element.html">{@code <uses-feature>}</a> 标记和 {@code android:glEsVersion} 属性声明它。例如:</p>
+
+<pre>
+<manifest>
+ <uses-feature android:glEsVersion="0x00030001" />
+ ...
+</manifest>
+</pre>
+
+<p>有关使用 OpenGL ES 的更多信息,包括如何在运行时检查设备支持的 OpenGL ES 版本,请参阅 <a href="{@docRoot}guide/topics/graphics/opengl.html">OpenGL ES API 指南</a>。</p>
+
+<h3 id="AndroidExtensionPack">Android 扩展程序包</h3>
+
+<p>除了 OpenGL ES 3.1 之外,本版本还提供了一个扩展程序包,该程序包具有适用于高级显卡功能的 Java 接口和原生支持。Android 将这些扩展程序视为一个程序包。(如果存在 {@code ANDROID_extension_pack_es31a} 扩展程序,则您的应用可以假定该程序包中的所有扩展都存在,并可通过单个 {@code #extension} 语句启用着色语言功能。)</p>
+
+<p>该扩展程序包支持:</p>
+
+<ul>
+<li>对着色器存储缓冲区、图片和原子的有保证片段着色器支持(片段着色器支持在 OpenGL ES 3.1 中是可选的。)</li>
+<li>曲面细分和几何着色器</li>
+<li>ASTC (LDR) 纹理压缩格式</li>
+<li>按样本插值和着色</li>
+<li>为帧缓冲区中的每个颜色附件使用不同的混合模式</li>
+</ul>
+
+<p>该扩展程序包的 Java 接口是通过 {@link android.opengl.GLES31Ext} 提供的。在您的应用清单中,您可以声明您的应用必须仅安装在支持该扩展程序包的设备上。例如:</p>
+
+<pre>
+<manifest>
+ <uses-feature android:name=“android.hardware.opengles.aep”
+ android:required="true" />
+ ...
+</manifest>
+</pre>
+
+<h2 id="Media">媒体</h2>
+
+<h3 id="Camera-v2">用于高级摄像头功能的摄像头 API</h3>
+
+<p>Android 5.0 引入了新的 <a href="{@docRoot}reference/android/hardware/camera2/package-summary.html">android.hardware.camera2</a> API 来帮助执行精细的照片拍摄和图片处理。您现在可以通过 {@link android.hardware.camera2.CameraManager#getCameraIdList() getCameraIdList()} 以编程方式访问可供系统使用的摄像头设备,并通过 {@link android.hardware.camera2.CameraManager#openCamera(java.lang.String, android.hardware.camera2.CameraDevice.StateCallback, android.os.Handler) openCamera()} 连接到特定设备。要开始拍摄图片,请创建一个 {@link android.hardware.camera2.CameraCaptureSession} 并指定 {@link android.view.Surface} 对象来发送所拍摄的图片。您可以将 {@link android.hardware.camera2.CameraCaptureSession} 配置为单拍或连拍。</p>
+
+<p>要想在拍摄了新图片时收到通知,请实施 {@link android.hardware.camera2.CameraCaptureSession.CaptureCallback} 监听器并将其设置在您的拍摄请求中。现在,当系统完成图片拍摄请求时,您的 {@link android.hardware.camera2.CameraCaptureSession.CaptureCallback} 监听器将收到对 {@link android.hardware.camera2.CameraCaptureSession.CaptureCallback#onCaptureCompleted(android.hardware.camera2.CameraCaptureSession, android.hardware.camera2.CaptureRequest, android.hardware.camera2.TotalCaptureResult) onCaptureCompleted()} 的调用,在 {@link android.hardware.camera2.CaptureResult} 中向您提供图片拍摄元数据。</p>
+
+<p>{@link android.hardware.camera2.CameraCharacteristics} 类允许您的应用检测设备上有哪些摄像头功能可用。对象的 {@link android.hardware.camera2.CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL INFO_SUPPORTED_HARDWARE_LEVEL} 属性表示摄像头的功能级别。</p>
+
+<ul>
+ <li>所有设备都至少支持 {@link android.hardware.camera2.CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY} 硬件级别,该级别的功能大致等同于已弃用的 {@link android.hardware.Camera} API 的功能。</li>
+ <li>支持 {@link android.hardware.camera2.CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_FULL INFO_SUPPORTED_HARDWARE_LEVEL_FULL} 硬件级别的设备不仅能让用户手动控制拍摄和后期处理,而且能够以高帧速率拍摄高分辨率图片。</li>
+</ul>
+
+<p>要了解如何使用更新后的<a href="{@docRoot}reference/android/hardware/camera2/package-summary.html">摄像头</a> API,请参考本版本中的 {@code Camera2Basic} 和 {@code Camera2Video} 实施样本。</p>
+
+<h3 id="AudioPlayback">音频播放</h3>
+<p>此版本对 {@link android.media.AudioTrack} 进行了以下更改:</p>
+<ul>
+ <li>您的应用现在能够以浮点格式 ({@link android.media.AudioFormat#ENCODING_PCM_FLOAT ENCODING_PCM_FLOAT}) 提供音频数据。这允许更大的动态范围、更一致的精度以及更大的动态余量。浮点算法在中间计算期间尤其有用。播放端点为音频数据使用整数格式,并且具有较低的位深度。(在 Android 5.0 中,内部管线的某些部分尚未采用浮点格式。)
+ <li>您的应用现在可以将音频数据提供为 {@link java.nio.ByteBuffer},所用格式与 {@link android.media.MediaCodec} 提供的格式相同。
+ <li>{@link android.media.AudioTrack#WRITE_NON_BLOCKING WRITE_NON_BLOCKING} 选项可以简化某些应用的缓冲和多线程处理。
+</ul>
+
+<h3 id="MediaPlaybackControl">媒体播放控件</h3>
+<p>使用新的通知和媒体 API,可确保系统界面能够了解您的媒体播放并提取和显示专辑封面。而借助新的 {@link android.media.session.MediaSession} 和 {@link android.media.session.MediaController} 类,您现在则可更轻松地跨界面和服务控制媒体播放。</p>
+
+<p>新的 {@link android.media.session.MediaSession} 类替代了被弃用的 {@link android.media.RemoteControlClient} 类,并且提供了一组用于处理传输控件和媒体按钮的回调方法。如果您的应用提供媒体播放并且在 Android <a href="{@docRoot}tv/index.html">TV</a> 或 <a href="{@docRoot}wear/index.html">Wear</a> 平台上运行,请使用 {@link android.media.session.MediaSession} 类和相同的回调方法来处理您的传输控件。</p>
+
+<p>您现在可以通过新的 {@link android.media.session.MediaController} 类构建您自己的媒体控制器应用。此类提供了一种线程安全方法,可让您通过自己的应用界面流程来监控媒体播放。在创建控制器时,请指定一个 {@link android.media.session.MediaSession.Token} 对象,以便您的应用可以与给定的 {@link android.media.session.MediaSession} 进行互动。通过使用 {@link android.media.session.MediaController.TransportControls} 方法,您可以发送诸如 {@link android.media.session.MediaController.TransportControls#play() play()}、{@link android.media.session.MediaController.TransportControls#stop() stop()}、{@link android.media.session.MediaController.TransportControls#skipToNext() skipToNext()} 和 {@link android.media.session.MediaController.TransportControls#setRating(android.media.Rating) setRating()} 之类的命令来控制该会话中的媒体播放。创建好控制器之后,您还可以注册一个 {@link android.media.session.MediaController.Callback} 对象来监听该会话中的元数据和状态更改。</p>
+
+<p>此外,您还可以通过新的 {@link android.app.Notification.MediaStyle} 类创建允许将播放控件关联到媒体会话的丰富通知。</p>
+
+<h3 id="MediaBrowsing">媒体浏览</h3>
+<p>Android 5.0 通过新的 <a href="{@docRoot}reference/android/media/browse/package-summary.html">android.media.browse</a> API 引入了允许应用浏览其他应用的媒体内容库的功能。要公开您的应用中的媒体内容,请扩展 {@link android.service.media.MediaBrowserService} 类。您的 {@link android.service.media.MediaBrowserService} 实施应当提供对 {@link android.media.session.MediaSession.Token} 的访问权限,以便应用可以播放通过您的服务提供的媒体内容。</p>
+<p>要与媒体浏览器服务进行互动,请使用 {@link android.media.browse.MediaBrowser} 类。在创建 {@link android.media.browse.MediaBrowser} 实例时,请为 {@link android.media.session.MediaSession} 指定组件名称。然后,您的应用可以使用该浏览器实例连接到关联的服务,并获取 {@link android.media.session.MediaSession.Token} 对象来播放通过该服务公开的内容。</p>
+
+<h2 id="Storage">存储</h2>
+
+<h3 id="DirectorySelection">目录选择</h3>
+
+<p>Android 5.0 对<a href="{@docRoot}guide/topics/providers/document-provider.html">存储访问框架</a>进行了扩展,允许用户选择整个目录子树,并向应用授予对其中包含的所有文档的读/写访问权限(无需请求用户逐项确认)。</p>
+
+<p>要选择目录子树,请构建并发送 {@link android.content.Intent#ACTION_OPEN_DOCUMENT_TREE OPEN_DOCUMENT_TREE} intent。系统将显示支持子树选择的所有 {@link android.provider.DocumentsProvider} 实例,允许用户进行浏览并选择目录。返回的 URI 表示对所选子树的访问权限。然后,您可以使用 {@link android.provider.DocumentsContract#buildChildDocumentsUriUsingTree(android.net.Uri, java.lang.String) buildChildDocumentsUriUsingTree()} 和 {@link android.provider.DocumentsContract#buildDocumentUriUsingTree(android.net.Uri, java.lang.String) buildDocumentUriUsingTree()} 以及 {@link android.content.ContentResolver#query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String) query()} 来深入了解此子树。</p>
+
+<p>新的 {@link android.provider.DocumentsContract#createDocument(android.content.ContentResolver, android.net.Uri, java.lang.String, java.lang.String) createDocument()} 方法允许您在子树下的任何位置创建新的文档或目录。要管理现有文档,请使用 {@link android.provider.DocumentsContract#renameDocument(android.content.ContentResolver, android.net.Uri, java.lang.String) renameDocument()} 和 {@link android.provider.DocumentsProvider#deleteDocument(java.lang.String) deleteDocument()}。在发出这些调用之前,请检查 {@link android.provider.DocumentsContract.Document#COLUMN_FLAGS COLUMN_FLAGS} 以验证服务提供方是否支持这些调用。</p>
+
+<p>如果您在实施 {@link android.provider.DocumentsProvider} 并希望支持子树选择,请实施 {@link android.provider.DocumentsProvider#isChildDocument(java.lang.String, java.lang.String) isChildDocument()} 并在您的 {@link android.provider.DocumentsContract.Root#COLUMN_FLAGS COLUMN_FLAGS} 中添加 {@link android.provider.DocumentsContract.Root#FLAG_SUPPORTS_IS_CHILD FLAG_SUPPORTS_IS_CHILD}。</p>
+
+<p>Android 5.0 还在共享的存储空间内引入了程序包专用的新目录,您的应用可将要添加到 {@link android.provider.MediaStore} 中的媒体文件放置在这些目录中。新的 {@link android.content.Context#getExternalMediaDirs()} 将返回这些目录在所有共享的存储设备上的路径。与 {@link android.content.Context#getExternalFilesDir(java.lang.String) getExternalFilesDir()} 类似,您的应用不需要额外的权限即可访问所返回的路径。平台将定期扫描这些目录中的新媒体,但您也可使用 {@link android.media.MediaScannerConnection} 对新内容进行显式扫描。</p>
+
+<h2 id="Wireless">无线和连接</h2>
+
+<h3 id="Multinetwork">多个网络连接</h3>
+<p>Android 5.0 提供了新的多网络 API,它们允许您的应用动态扫描具有特定功能的可用网络,然后与这些网络建立连接。如果您的应用需要一个专用网络(例如 SUPL、MMS 或运营商计费的网络),或者如果您希望使用某种特定类型的传输协议来发送数据,则此功能非常有用。</p>
+
+<p>要从您的应用动态选择并连接到某个网络,请执行以下步骤:</p>
+
+<ol>
+ <li>创建一个 {@link android.net.ConnectivityManager}。</li>
+ <li>使用 {@link android.net.NetworkRequest.Builder} 类创建一个 {@link android.net.NetworkRequest} 对象并指定您的应用感兴趣的网络功能和传输类型。</li>
+<li>要扫描合适的网络,请调用 {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback) requestNetwork()} 或 {@link android.net.ConnectivityManager#registerNetworkCallback(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback) registerNetworkCallback()},并传入 {@link android.net.NetworkRequest} 对象和一个 {@link android.net.ConnectivityManager.NetworkCallback} 实施。如果希望在检测到合适的网络后主动切换到该网络,请使用 {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback) requestNetwork()} 方法;如果希望仅接收网络扫描结果通知而不主动切换,请使用 {@link android.net.ConnectivityManager#registerNetworkCallback(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback) registerNetworkCallback()} 方法。</li>
+</ol>
+
+<p>当系统检测到合适的网络时,它将连接到该网络并调用 {@link android.net.ConnectivityManager.NetworkCallback#onAvailable(android.net.Network) onAvailable()} 回调。您可以在回调中使用 {@link android.net.Network} 对象来获取关于该网络的更多信息,或者指示通信使用选定的网络。</p>
+
+<h3 id="BluetoothBroadcasting">低功耗蓝牙</h3>
+<p>Android 4.3 中作为重头戏引入了对<a href="{@docRoot}guide/topics/connectivity/bluetooth-le.html">低功耗蓝牙</a>(“低功耗蓝牙”<em></em>)的平台支持。在 Android 5.0 中,Android 设备现在可以用作低功耗蓝牙<em>外围设备</em>。应用可以使用此功能使附近的设备知道它的存在。例如,您可以构建相应的应用来允许设备用作计步器或健康检测器并与另一低功耗蓝牙设备交换其数据。</p>
+<p>新的 {@link android.bluetooth.le} API 允许您的应用对公告进行广播,扫描响应,以及与附近的低功能蓝牙设备建立连接。要使用新的公告和扫描功能,请在您的清单中添加 {@link android.Manifest.permission#BLUETOOTH_ADMIN BLUETOOTH_ADMIN} 权限。当用户从 Play 商店更新或下载您的应用时,会要求他们向您的应用授予以下权限:“蓝牙连接信息:允许应用控制蓝牙,包括向附近的蓝牙设备进行广播以及获取关于这些设备的信息。”</p>
+
+<p>要开始低功耗蓝牙公告以便其他设备可以发现您的应用,请调用 {@link android.bluetooth.le.BluetoothLeAdvertiser#startAdvertising(android.bluetooth.le.AdvertiseSettings, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseCallback) startAdvertising()} 并传入 {@link android.bluetooth.le.AdvertiseCallback} 类的一个实施。回调对象将收到关于公告操作成功或失败的报告。</p>
+
+<p> Android 5.0 引入了 {@link android.bluetooth.le.ScanFilter} 类,以便您的应用可以仅扫描它感兴趣的特定类型的设备。要开始扫描低功耗蓝牙设备,请调用 {@link android.bluetooth.le.BluetoothLeScanner#startScan(android.bluetooth.le.ScanCallback) startScan()} 并传入一个过滤器列表。在该方法调用中,您还必须提供 {@link android.bluetooth.le.ScanCallback} 的一个实施以便在发现低功耗蓝牙公告时进行报告。 </p>
+
+<h3 id="NFCEnhancements">NFC 增强功能</h3>
+<p>Android 5.0 添加了以下增强功能以实现更广泛和更灵活的 NFC 使用:</p>
+
+<ul>
+<li>“分享”<em></em>菜单中现在提供了 Android Beam。</li>
+<li>您的应用可以通过调用 {@link android.nfc.NfcAdapter#invokeBeam(android.app.Activity) invokeBeam()} 在用户的设备上调用 Android Beam 来分享数据。这不需要用户对着另一个具有 NFC 功能的设备手动点按设备即可完成数据传输。</li>
+<li>您可以使用新的 {@link android.nfc.NdefRecord#createTextRecord(java.lang.String, java.lang.String) createTextRecord()} 方法来创建包含 UTF-8 文本数据的 NDEF 记录。</li>
+<li>如果您在开发支付应用,则您现在能够通过调用 <code><a href="{@docRoot}reference/android/nfc/cardemulation/CardEmulation.html#registerAidsForService(android.content.ComponentName, java.lang.String, java.util.List<java.lang.String>)">registerAidsForService()</a></code> 动态注册 NFC 应用 ID (AID)。您还可以使用 {@link android.nfc.cardemulation.CardEmulation#setPreferredService(android.app.Activity, android.content.ComponentName) setPreferredService()} 设置当某个特定的活动处于前台时应当使用的首选卡仿真服务。</li>
+</ul>
+
+<h2 id="Power">Project Volta</h2>
+
+<p>除了新功能之前,Android 5.0 还重点对电池寿命进行了改进。可以使用新的 API 和工具来了解并优化您的应用的功耗。</p>
+
+<h3 id="JobScheduler">调度作业</h3>
+<p>Android 5.0 提供了一个新的 {@link android.app.job.JobScheduler} API,它允许您通过为系统定义要在以后的某个时间或在指定的条件下(例如,当设备在充电时)异步运行的作业来优化电池寿命。作业调度在下列情况下非常有用:</p>
+<ul>
+ <li>应用具有您可以推迟的非面向用户的工作。</li>
+ <li>应用具有当插入设备时您希望优先执行的工作。</li>
+ <li>应用具有需要访问网络或 Wi-Fi 连接的任务。</li>
+ <li>应用具有您希望作为一个批次定期运行的许多任务。</li>
+
+</ul>
+
+<p>工作单元由一个 {@link android.app.job.JobInfo} 对象进行封装。此对象指定了调度条件。</p>
+
+<p>使用 {@link android.app.job.JobInfo.Builder} 类配置调度的任务应当如何运行。您可以将任务调度为在特定的条件下运行,例如:</p>
+
+<ul>
+ <li>当设备充电时启动</li>
+ <li>当设备连接到不限流量网络时启动</li>
+ <li>当设备空闲时启动</li>
+ <li>在特定的截止期限之前或以最小的延迟完成</li>
+</ul>
+
+<p>例如,您可以添加如下代码以在不限流量网络上运行您的任务:</p>
+
+<pre>
+JobInfo uploadTask = new JobInfo.Builder(mJobId,
+ mServiceComponent /* JobService component */)
+ .setRequiredNetworkCapabilities(JobInfo.NetworkType.UNMETERED)
+ .build();
+JobScheduler jobScheduler =
+ (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
+jobScheduler.schedule(uploadTask);
+</pre>
+
+<p>如果设备具有稳定的电源(也就是说,它已插入了 2 分钟以上并且电池处于<a href="{@docRoot}reference/android/content/Intent.html#ACTION_BATTERY_OKAY">健康水平</a>),则系统将运行任何已就绪可运行的已调度作业,即使作业的截止期限尚未到期也是如此。</p>
+
+<p>要查看有关如何使用 {@link android.app.job.JobScheduler} API 的示例,请参考本版本中的 {@code JobSchedulerSample} 实施样本。</p>
+
+<h3 id="PowerMeasurementTools">用于查询耗电量信息的开发者工具</h3>
+
+<p>新的 {@code dumpsys batterystats} 命令将生成关于设备上耗电量信息的有趣统计数据,该数据按唯一用户 ID (UID) 进行组织。该统计信息包括:</p>
+
+<ul>
+<li>与电池相关的事件的历史记录
+<li>设备的全局统计信息
+<li>每个 UID 和系统组件的大致用电量
+<li>每个数据包的每个手机应用毫秒数
+<li>系统 UID 汇总统计信息
+<li>应用 UID 汇总统计信息
+</ul>
+
+<p>可使用 {@code --help} 选项了解用于对输出进行自定义的各种选项。例如,要输出自设备上次充电后给定应用包的耗电量统计信息,请运行以下命令:
+<pre>
+$ adb shell dumpsys batterystats --charged <package-name>
+</pre>
+
+<p>您可以对 {@code dumpsys} 命令的输出使用 <a href="https://github.com/google/battery-historian" class="external-link">Battery Historian</a> 工具从日志中生成与电量相关的事件的 HTML 可视形式。此信息使您可以更轻松地了解和诊断任何与电池相关的问题。</p>
+
+<h2 id="Enterprise">办公场所和教育环境中的 Android</h2>
+<h3 id="ManagedProvisioning">托管配置</h3>
+
+<p>Android 5.0 提供了用于在企业环境内运行应用的新功能。如果用户具有现有的个人帐户,则<a href="{@docRoot}guide/topics/admin/device-admin.html">设备管理员</a>可以启动托管配置流程来向设备添加一个共存但单独的“托管配置文件”<em></em>。与托管配置文件关联的应用将与非托管应用一起出现在用户的启动器、“最近用过”屏幕以及通知中。</p>
+
+<p>要启动托管配置流程,请在 {@link android.content.Intent} 中发送 {@link android.app.admin.DevicePolicyManager#ACTION_PROVISION_MANAGED_PROFILE ACTION_PROVISION_MANAGED_PROFILE}。如果调用成功,则系统将触发 {@link android.app.admin.DeviceAdminReceiver#onProfileProvisioningComplete(android.content.Context, android.content.Intent) onProfileProvisioningComplete()} 回调。然后,您可以调用 {@link android.app.admin.DevicePolicyManager#setProfileEnabled(android.content.ComponentName) setProfileEnabled()} 来启用此托管配置文件。</p>
+
+<p>默认情况下,在托管配置文件中只会启用一小部分应用。您可以通过调用 {@link android.app.admin.DevicePolicyManager#enableSystemApp(android.content.ComponentName, android.content.Intent) enableSystemApp()} 在托管配置文件中安装额外的应用。</p>
+
+<p>如果您在开发启动器应用,则可以使用新的 {@link android.content.pm.LauncherApps} 类来获取当前用户的可启动活动的列表以及任何关联的托管配置文件。您的启动器可以通过向图标绘图添加一个工作标记,使托管应用更加惹人注目。要检索带标记的图标,请调用 {@link android.content.pm.PackageManager#getUserBadgedIcon(android.graphics.drawable.Drawable, android.os.UserHandle) getUserBadgedIcon()}。</p>
+
+<p>要查看如何使用此新功能,请参考本版本中的 {@code BasicManagedProfile} 实施样本。</p>
+
+<h3 id="DeviceOwner">设备所有者</h3>
+<p>Android 5.0 引入了部署设备所有者应用的功能。“设备所有者”<em></em>是一类特殊的<a href="{@docRoot}guide/topics/admin/device-admin.html">设备管理员</a>,具有在设备上创建和移除辅助用户以及配置全局设置的额外能力。您的设备所有者应用可以使用 {@link android.app.admin.DevicePolicyManager} 类中的方法来对托管设备上的配置、安全性和应用进行精细控制。一个设备在任一时刻只能有一个处于活动状态的设备所有者。</p>
+
+<p>要部署并激活设备所有者,您必须在设备处于未配置状态时执行从编程应用到设备的 NFC 数据传输。此数据传输发送的信息与<a href="#ManagedProvisioning">托管配置</a>中描述的配置 intent 中的信息相同。</p>
+
+<h3 id="ScreenPinning">屏幕固定</h3>
+
+<p>Android 5.0 引入了一个新的屏幕固定 API,它允许您暂时防止用户离开您的任务或被通知打扰。例如,如果您在开发一个教育应用以在 Android 或单用途或资讯服务应用上支持高风险评估需求,则可以使用此功能。一旦您的应用激活了屏幕固定,则用户将无法看到通知、访问其他应用或者返回到主屏幕,直到您的应用退出该模式。</p>
+
+<p>有两种方法用来激活屏幕固定:</p>
+
+<ul>
+<li><strong>手动</strong>:用户可以在“设置”>“安全性”>“屏幕固定”<em></em>中启用屏幕固定,并通过在“最近用过”屏幕上点触屏幕固定图标来选择他们要固定的任务。</li> <li><strong>以编程方式</strong>:要以编程方式激活屏幕固定,请在您的应用中调用 {@link android.app.Activity#startLockTask() startLockTask()}。如果请求方应用不是设备所有者,则会提示用户确认。设备所有者应用可以调用 {@link android.app.admin.DevicePolicyManager#setLockTaskPackages(android.content.ComponentName, java.lang.String[]) setLockTaskPackages()} 方法来使应用成为可固定的,不需要经历用户确认步骤。</li>
+</ul>
+
+<p>当任务锁定处于活动状态时,会发生以下行为:</p>
+
+<ul>
+<li>状态栏为空,并且用户通知和状态信息被隐藏。</li>
+<li>首页和“最近的应用”按钮被隐藏。</li>
+<li>其他应用无法启动新活动。</li>
+<li>当前应用可以启动新活动,只要这样做不会创建新任务即可。</li>
+<li>当屏幕固定是由设备所有者调用的时,用户将保持锁定到您的应用,直到该应用调用 {@link android.app.Activity#stopLockTask() stopLockTask()}。</li>
+<li>如果屏幕固定是由设备所有者之外另一应用或者是由用户直接调用的活动时,用户可以通过同时按住“返回”和“最近”按钮退出。</li>
+
+</ul>
+
+<h2 id="Printing">打印框架</h2>
+
+<h3 id="PDFRender">使用位图来呈现 PDF 文件</h3>
+<p>您现在可以使用新的 {@link android.graphics.pdf.PdfRenderer} 类将 PDF 文档页呈现为位图图片以便打印。您必须指定系统将可打印内容写入其中的一个可查找的(也就是说,可以随机访问内容){@link android.os.ParcelFileDescriptor}。您的应用可以通过 {@link android.graphics.pdf.PdfRenderer#openPage(int) openPage()} 获取页面进行呈现,然后调用 {@link android.graphics.pdf.PdfRenderer.Page#render(android.graphics.Bitmap, android.graphics.Rect, android.graphics.Matrix, int) render()} 将已打开的 {@link android.graphics.pdf.PdfRenderer.Page} 转变为位图。如果您只希望将文档的一部分转变为位图图片(例如,要实施<a href="http://en.wikipedia.org/wiki/Tiled_rendering" class="external-link">平铺渲染</a>以放大文档),则还可以设置其他参数。</p>
+
+<p>有关如何使用新 API 的示例,请参阅 {@code PdfRendererBasic} 样本。</p>
+
+<h2 id="System">系统</h2>
+<h3 id="AppUsageStatistics">应用使用情况统计信息</h3>
+<p>您现在可以通过新的 {@link android.app.usage} API 访问 Android 设备上的应用使用情况历史记录。此 API 提供了比被弃用的 {@link android.app.ActivityManager#getRecentTasks(int, int) getRecentTasks()} 方法更详细的使用情况信息。要使用此 API,必须先在您的清单中声明 {@code "android.permission.PACKAGE_USAGE_STATS"} 权限。用户还必须通过“设置”>“安全性”>“应用”<em></em>使用“使用情况访问”启用对此应用的访问权限。</p>
+
+<p>系统将以每个应用为单位收集使用情况数据,并按每天、每周、每月和每年时间间隔对数据进行汇总。系统保留此数据的最大持续时间如下所述:</p>
+
+<ul>
+ <li>每天数据:7 天</li>
+ <li>每周数据:4 周</li>
+ <li>每月数据:6 个月</li>
+ <li>每年数据:2 年</li>
+</ul>
+
+<p>对于每个应用,系统将记录以下数据:</p>
+<ul>
+<li>上次使用应用的时间</li>
+<li>应用在该时间间隔内(按天、周、月或年)处于前台的总时间长度</li>
+<li>组件(由程序包和活动名称予以标识)在一天中移动到前台或后台时的时间戳捕获</li>
+<li>设备配置更改时(例如当设备配置因为旋转而更改时)的时间戳捕获</li>
+</ul>
+
+<h2 id="TestingA11y">测试和辅助功能 </h2>
+
+<h3 id="TestingA11yImprovements">测试和辅助功能改进</h3>
+<p>Android 5.0 对测试和辅助功能增加了以下支持:</p>
+
+<ul>
+<li>新的 {@link android.app.UiAutomation#getWindowAnimationFrameStats() getWindowAnimationFrameStats()} 和 {@link android.app.UiAutomation#getWindowContentFrameStats(int) getWindowContentFrameStats()} 方法将捕获窗口动画和内容的帧统计信息。这些方法允许您编写仪器测试来评估某个应用是否在以足够的刷新频率来呈现帧以提供流畅的用户体验。</li>
+
+<li>新的 {@link android.app.UiAutomation#executeShellCommand(java.lang.String) executeShellCommand()} 方法允许您通过仪器测试执行 shell 命令。命令执行类似于从连接到设备的主机运行 {@code adb shell},允许您使用基于 shell 的工具,例如 {@code dumpsys}、{@code am} {@code content} 和 {@code pm}。</li>
+
+<li>使用辅助功能 API(例如 <a href="{@docRoot}tools/help/uiautomator/index.html">{@code UiAutomator}</a>)的辅助服务和测试工具现在可以检索有视力的用户可以与之进行互动的屏幕上窗口的属性的详细信息。要检索 {@link android.view.accessibility.AccessibilityWindowInfo} 对象的列表,请调用新的 {@link android.accessibilityservice.AccessibilityService#getWindows() getWindows()} 方法。</li>
+
+<li>新的 {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction} 类允许您定义可以对 {@link android.view.accessibility.AccessibilityNodeInfo} 执行的标准或自定义操作。新的 {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction} 类替换了以前在 {@link android.view.accessibility.AccessibilityNodeInfo} 中提供的与操作相关的 API。</li>
+
+<li>Android 5.0 对您的应用中的文本到语音合成提供了更细粒度的控制。新的 {@link android.speech.tts.Voice} 类允许您的应用使用与特定区域设置、质量和延迟评级以及特定于文本到语音引擎的参数关联的语音配置文件。</li>
+</ul>
+
+<h2 id="IME">IME</h2>
+
+<h3 id="Switching">更轻松地在输入语言之间切换</h3>
+
+<p>从 Android 5.0 开始,用户可以更轻松地在该平台支持的所有<a href="{@docRoot}guide/topics/text/creating-input-method.html">输入法编辑器 (IME)</a> 之间进行切换。执行指定的切换操作(通常是触摸软键盘上的一个地球图标)将循环经过所有此类 IME。此行为更改是由 {@link android.view.inputmethod.InputMethodManager#shouldOfferSwitchingToNextInputMethod(android.os.IBinder) shouldOfferSwitchingToNextInputMethod()} 方法实施的。</p>
+
+<p>此外,框架现在还将检查下一个 IME 究竟是否包括切换机制(并因此检查该 IME 是否支持切换到它之后的 IME)。具有切换机制的 IME 不会切换到没有切换机制的 IME。此行为更改是由 {@link android.view.inputmethod.InputMethodManager#switchToNextInputMethod(android.os.IBinder, boolean) switchToNextInputMethod()} 方法实施的。
+
+<p>要查看有关如何使用更新的 IME 切换 API 的示例,请参考本版本中更新的软键盘实施样本。要详细了解如何实施 IME 切换,请参阅<a href="{@docRoot}guide/topics/text/creating-input-method.html">创建输入法</a>。
+</p>
+
+<h2 id="Manifest">清单声明</h2>
+
+<h3 id="ManifestFeatures">声明必需的功能</h3>
+<p><a href="{@docRoot}guide/topics/manifest/uses-feature-element.html">{@code <uses-feature>}</a> 元素中现在支持下列值,因此,您可以确保您的应用仅安装在提供了您的应用所需功能的设备上。</p>
+
+<ul>
+<li>{@link android.content.pm.PackageManager#FEATURE_AUDIO_OUTPUT}</li>
+<li>{@link android.content.pm.PackageManager#FEATURE_CAMERA_CAPABILITY_MANUAL_POST_PROCESSING}</li>
+<li>{@link android.content.pm.PackageManager#FEATURE_CAMERA_CAPABILITY_MANUAL_SENSOR}</li>
+<li>{@link android.content.pm.PackageManager#FEATURE_CAMERA_CAPABILITY_RAW}</li>
+<li>{@link android.content.pm.PackageManager#FEATURE_CAMERA_LEVEL_FULL}</li>
+<li>{@link android.content.pm.PackageManager#FEATURE_GAMEPAD}</li>
+<li>{@link android.content.pm.PackageManager#FEATURE_LIVE_TV}</li>
+<li>{@link android.content.pm.PackageManager#FEATURE_MANAGED_USERS}</li>
+<li>{@link android.content.pm.PackageManager#FEATURE_LEANBACK}</li>
+<li>{@link android.content.pm.PackageManager#FEATURE_OPENGLES_EXTENSION_PACK}</li>
+<li>{@link android.content.pm.PackageManager#FEATURE_SECURELY_REMOVES_USERS}</li>
+<li>{@link android.content.pm.PackageManager#FEATURE_SENSOR_AMBIENT_TEMPERATURE}</li>
+<li>{@link android.content.pm.PackageManager#FEATURE_SENSOR_HEART_RATE_ECG}</li>
+<li>{@link android.content.pm.PackageManager#FEATURE_SENSOR_RELATIVE_HUMIDITY}</li>
+<li>{@link android.content.pm.PackageManager#FEATURE_VERIFIED_BOOT}</li>
+<li>{@link android.content.pm.PackageManager#FEATURE_WEBVIEW}</li>
+</ul>
+
+<h3 id="Permissions">用户权限</h3>
+
+<p><a href="{@docRoot}guide/topics/manifest/uses-permission-element.html">{@code <uses-permission>}</a> 元素中现在支持以下权限来声明您的应用访问特定 API 时所需的权限。</p>
+
+<ul>
+<li>{@link android.Manifest.permission#BIND_DREAM_SERVICE}:当针对 API 级别 21 和更高级别时,<a href="{@docRoot}about/versions/android-4.2.html#Daydream">Daydream</a> 服务需要此权限来确保只有系统可以绑定到它。</li>
+</ul>
\ No newline at end of file
diff --git a/docs/html-intl/intl/zh-cn/about/versions/lollipop.jd b/docs/html-intl/intl/zh-cn/about/versions/lollipop.jd
new file mode 100644
index 0000000..3403630
--- /dev/null
+++ b/docs/html-intl/intl/zh-cn/about/versions/lollipop.jd
@@ -0,0 +1,253 @@
+page.title=Android Lollipop
+@jd:body
+
+
+
+
+
+
+
+
+ <div style="padding:0px 0px 0px 20px;float:right;margin:0 -10px 0 0">
+ <img src="{@docRoot}images/home/l-hero_2x.png" srcset="{@docRoot}images/home/l-hero.png 1x, {@docRoot}images/home/l-hero_2x.png 2x" width="460" height="300" >
+ </div>
+
+ <div class="landing-docs" style="float:right;clear:both;margin:68px 0 2em 3em;">
+ <div class="col-4 normal-links highlights" style="font-size:12px;">
+ <h3 id="thisd" >面向开发者的主要功能</h3>
+ <ul style="list-style-type:none;">
+ <li><a href="#Material">Material Design 设计</a></li>
+ <li><a href="#Perf">注重性能</a></li>
+ <li><a href="#Notifications">通知</a></li>
+ <li><a href="#TV">以大屏幕呈现</a></li>
+ <li><a href="#Documents">以文档为中心</a></li>
+ <li><a href="#Connectivity">连接性能再上一级</a></li>
+ <li><a href="#Graphics">高性能图形</a></li>
+ <li><a href="#Audio">音频处理功能更强</a></li>
+ <li><a href="#Camera">摄像头和视频处理进一步完善</a></li>
+ <li><a href="#Work">Android 在办公中的应用</a></li>
+ <li><a href="#ScreenCapture">屏幕截图和共享</a></li>
+ <li><a href="#Sensors">新型传感器</a></li>
+ <li><a href="#WebView">Chromium WebView</a></li>
+ <li><a href="#Accessibility">辅助功能和输入</a></li>
+ <li><a href="#Battery">构建低电耗应用</a></li>
+ </ul>
+ </div>
+</div>
+
+
+
+
+
+
+
+<p>欢迎使用 Android 5.0 Lollipop,这是迄今为止规模最大最为雄心勃勃的 Android 版本!</p>
+
+<p>此版本为用户推出了各种崭新的新功能,为开发者则提供了数千个新的 API。不仅如此,它将 Android 的疆土扩展得更远,小到手机、平板电脑和穿戴式设备,大到电视和汽车,都可以是它活跃的领地。</p>
+
+<p>要深入了解面向开发者的新 API,请参阅 <a href="{@docRoot}about/versions/android-5.0.html">Android 5.0 API 概述</a>。有关适合消费者阅读的更多 Android 5.0 内容,请访问 <a href="http://www.android.com/versions/lollipop-5-0/">www.android.com</a>。</p>
+
+
+
+<h2 id="Material">Material design 设计</h2>
+
+<p>Android 5.0 将 <a href="http://www.google.com/design/spec">Material design 设计</a>引入 Android 系统,它自带一个扩展的界面工具包,可以让您轻松地将新的设计图案集成到自己的应用中。 </p>
+
+
+
+<p>在新的 <strong>3D 视图</strong>中,您可以设置 z 轴让元素从视图层面上“站”起来,甚至可以随着元素的移动投下<strong>实时阴影</strong>。</p>
+
+
+<p>内置的 <strong>Activity Transitions</strong> API 可以通过优美的动画天衣无缝地让用户从一种状态切换到另一种状态。素材主题为您的活动提供了各种过渡方式,包括在各个活动中使用<strong>共享的可视化元素</strong>功能。</p>
+
+
+
+<div style="width:290px;margin-right:35px;float:left">
+ <div class="framed-nexus5-port-span-5">
+ <video class="play-on-hover" autoplay="">
+ <source src="/design/material/videos/ContactsAnim.mp4">
+ <source src="/design/videos/ContactsAnim.webm">
+ <source src="/design/videos/ContactsAnim.ogv">
+ </video>
+ </div>
+ <div style="font-size:10pt;margin-left:20px;margin-bottom:30px">
+ <em>要重播影片,请单击设备屏幕</em>
+ </div>
+</div>
+
+
+<p>您可以对按钮、复选框以及应用中的其他触摸控件加上涟漪动画。
+
+<p>您还可以在 XML 文件中定义可动的矢量图,让它们展现出各种不同的动画效果。矢量图在自由缩放的同时不会失真,非常适合用作应用中的纯色图标。</p>
+
+<p><strong>RenderThread</strong> 则是新推出的由系统管理的渲染线程,即使主界面线程出现延迟,它也能保持动画运行流畅。 </p>
+
+
+<h2 id="Perf">注重性能</h2>
+
+<p>Android 5.0 为用户带来更快、更流畅、更强大的计算体验。</p>
+
+<p>Android 现在完全运行在全新构建的 <strong>ART 运行时</strong>上,支持混合使用预先编译 (AOT)、即时编译 (JIT) 和编译好的代码。它可以在 ARM、x86 和 MIPS 架构上正常运行,并且完全兼容 64 位。</p>
+
+<p>ART 改进了应用性能和响应能力。它采用高效的垃圾回收方式,减少了垃圾回收事件的次数和暂停时间,贴合搭配垂直同步窗口,因此您的应用不会出现掉帧现象。ART 还会动态移动内存,针对前台使用情况优化内存。 </p>
+
+<p>Android 5.0 引入了对 <strong>64 位架构</strong>(已应用于 Nexus 9 的 NVIDIA Tegra K1)的平台支持,经过优化后提供更大的寻址空间,提升了针对特定的计算工作负载的性能。以 Java 编写的应用可自动作为 64 位应用运行,无需任何修改。如果您的应用使用原生代码,也不用担心。我们扩展了 NDK,可支持适用于 ARM v8、x86-64 和 MIPS-64 的新 ABI。</p>
+
+<p>Android 一如既往注重实现更流畅的性能,并在 5.0 中改进了声音/视频同步方式。音频和图形管道都经过调校,时间戳更为精准,因此视频应用和游戏都能显示流畅的同步内容。</p>
+
+
+<h2 id="Notifications">通知</h2>
+
+<p>在 Android 5.0 中,用户可以更加方便地查看、使用和配置通知。 </p>
+
+<img src="{@docRoot}images/versions/notification-headsup.png" style="float:right; margin:0 0 40px 60px" width="300" height="224" />
+
+<p>用户可以根据需在<strong>锁屏上</strong>显示不同的通知详细信息,例如,他们可以选择在安全锁屏上完全不显示通知,也可以选择显示部分或全部通知。 </p>
+
+<p>像来电这样的重要通知警报会显示在<strong>浮动通知</strong>中,这是一个浮在上方显示的小窗口,方便用户无需退出当前应用就能响应或关闭通知。</p>
+
+<p>您现在可以在通知中通过<strong>新的元数据</strong>来收集相关的联系人(用于评级)、类别和优先级。</p>
+
+<p>内置新的媒体通知模板可以设置多达 6 个控制按钮(包括“翘拇指”之类的自定义控件),以通知方式实现一致的媒体控制,再也用不着 RemoteViews 了!</p>
+
+
+
+<h2 id="TV">以大屏幕呈现</h2>
+
+<p><a href="http://developer.android.com/tv/index.html">Android TV</a> 打造了一个完整的电视平台,让您的应用在大屏幕上驰骋。它以简洁的主屏幕体验为核心,提供个性化推荐和语音搜索功能,方便客户轻松发现内容。</p>
+
+<p>有了 Android TV,您现在可以为应用或游戏内容<strong>营造宏大且引人入胜的氛围</strong>,并支持与游戏控制器和其他输入设备互动。Android 还在 <a href="{@docRoot}tools/support-library/features.html#v17-leanback">v17 支持库</a>中提供了 <strong>Leanback 界面框架</strong>,帮助您构建在 10 英尺处观看的电视界面,给用户一种恍若看电影的感觉。</p>
+
+<p><strong>Android TV 输入框架</strong> (TIF) 能够让电视应用处理来自 HDMI 输入、电视调谐器和 IPTV 接收器等等的视频流。它还通过 TV 输入框架发布的元数据支持直播电视搜索和推荐功能,并且内置 HDMI-CEC 控制服务,可以让用户通过单独一个遥控器操控多个设备。 </p>
+
+<p>TV 输入框架能够让您广泛使用大量直播电视输入来源,并将它们汇总在同一个界面中供用户浏览、查看和欣赏内容。如果您为内容构建电视输入服务,用户就能更便利地在电视设备上观看这些内容。</p>
+
+
+
+<img src="{@docRoot}images/versions/recents_screen_2x.png" srcset="{@docRoot}images/versions/recents_screen.png 1x, {@docRoot}images/versions/recents_screen_2x.png 2x" style="float:right; margin:0 0 40px 60px" width="300" height="521" />
+
+<h2 id="Documents">以文档为中心</h2>
+
+<p>Android 5.0 引入了重新设计的“概览”空间(以前称为“最近事项”),在多任务处理上更为强大,用途更加广泛。</p>
+
+<p>新的 API 让应用中的活动化身成为独立的文档,与其他最近活动的屏幕并排显示,</p>
+
+<p>这样您可以充分利用并行文档,让用户即时访问您的更多内容或服务。例如,您可以使用并行文档展现工作提效应用中的文件、游戏中的玩家比赛,或者即时消息应用的聊天。 </p>
+
+
+
+<h2 id="Connectivity">连接性能更上一级</h2>
+
+<p>Android 5.0 新开放的 API 可以让应用与<strong>低功耗蓝牙</strong> (BLE) 的扫描(內围模式)和广告(外围模式)实现并行操作。</p>
+
+<p>新的<strong>多重联网</strong>功能允许应用查询可用网络来查找可用功能,例如它们是 Wi-Fi 网络、蜂窝网络还是不限流量网络,以及它们是否提供了特定的网络功能。然后,应用可以请求联网,并在连接断开或出现其他的网络变动时做出响应。</p>
+
+<p><strong>NFC</strong> API 现在允许应用动态注册 NFC 应用 ID (AID)。它们还可以根据活动状态下的服务设置首选的卡仿真服务并创建 NDEF 记录(采用 UTF-8 文本数据)。</p>
+
+
+
+<h2 id="Graphics">高性能图形</h2>
+
+<p>现已支持 <strong><a href="http://www.khronos.org/opengles/3_X/">Khronos OpenGL ES 3.1</a></strong>,因此您可以在受支持的设备上为游戏和其他应用采用最高性能的 2D 和 3D 图形功能。 </p>
+
+<p>OpenGL ES 3.1 增加了计算着色器、模版纹理、加速的视觉效果、优质 ETC2/EAC 纹理压缩、高级纹理渲染、标准化纹理尺寸以及渲染缓冲区格式等功能。</p>
+
+
+<div class="figure" style="width:350px; margin:0 0 0 60px">
+<img src="{@docRoot}images/versions/rivalknights.png" style="float:right;" width="350" height="525" />
+<p class="img-caption">Gameloft 开发的《骑士对决》采用了 AEP 中的 ASTC(自适应可伸缩纹理压缩)和 ES 3.1 中的计算着色器,不仅打造出 HDR(高动态范围)泛光效果,而且图形细节更加清楚。</p>
+</div>
+
+<p>Android 5.0 还引入了 <strong>Android 扩展程序包</strong> (AEP),这是一组 OpenGL ES 扩展程序,可让您使用镶嵌图案着色器、几何图形着色器、ASTC 纹理压缩、按样本插入和着色以及其他高级渲染功能。有了 AEP,您就可以通过一系列 GPU 运用高性能图形。</p>
+
+
+<h2 id="Audio">音频处理功能更强</h2>
+
+<p>采用全新的音频捕捉设计,提供<strong>低延迟音频输入</strong>功能。在此设计中,快速音频捕捉线程只要不是读取则永远不会阻塞,高速客户端采用本地取样频率、通道数和位深度;普通客户端具备重新采样、上/下行通道混合以及上/下位深度的特点。</p>
+
+<p>借助多通道<strong>音频流混合</strong>,专业音频应用可以混音多达八个通道,包括 5.1 和 7.1 通道在内。</p>
+
+<p>应用现在可以公开自己的媒体内容,并<strong>浏览其他应用的媒体</strong>,然后请求播放。内容通过具有查询功能的界面公开,不需要驻留在设备上。</p>
+
+<p>应用可以通过与特定区域设置、质量和延迟评级关联的语音配置文件精准控制<strong>从文本到语音的合成</strong>。新的 API 还增强了对合成错误检查、网络合成、语言搜索和网络回退的支持。</p>
+
+<p>Android 现在支持标准 <strong>USB 音频</strong>外设,因此用户可以连接 USB 耳机、扬声器、麦克风和其他高性能数字外设。Android 5.0 更是支持 <strong>Opus</strong> 音频编解码器。</p>
+
+<p>在媒体播放控制领域内新开放的 <strong>{@link android.media.session.MediaSession}</strong> API,能够在不同屏幕和不同控制器之间保持一致的媒体控制。</p>
+
+
+<h2 id="Camera">摄像头和视频处理进一步完善</h2>
+
+<p>Android 5.0 引入了<strong>全新的摄像头 API</strong>,支持您以 YUV 和 Bayer RAW 等原始格式拍照,并针对每个独立帧控制曝光时间、ISO 感光度和帧持续时间。新的完全同步的摄像头管道允许您在受支持的设备上以每秒 30 帧的速率捕获未压缩的全分辨率 YUV 图片。</p>
+
+<p>除了图片之外,您还可以通过摄像头捕获元数据,例如噪声模型和光学信息。</p>
+
+<p>视频如果是通过网络发送视频流,则现在可以利用 H.265 <strong>高效视频编码 (HEVC)</strong> 实现视频数据的最佳编码和解码。 </p>
+
+<p>Android 5.0 还新支持<strong>多媒体隧道</strong>,以便针对超高清 (4K) 内容提供最佳体验,并且能够将压缩的音频和视频数据一起播放。 </p>
+
+
+
+<div class="figure" style="width:320px; margin:1em 0 0 20px;padding-left:2em;">
+<img style="float:right; margin:0 1em 1em 2em" src="{@docRoot}images/android-5.0/managed_apps_launcher@2x.png" srcset="{@docRoot}images/android-5.0/managed_apps_launcher@2x.png 2x" alt="" width="300" />
+<p class="img-caption">用户可以通过一致的方式查看其个人应用和工作应用,这些应用带有标记,可以轻松识别。</p>
+</div>
+
+
+<h2 id="Work">工作场所中的 Android</h2>
+
+<p>为了在企业环境中实现自带设备办公,开发者可以借助全新的<a href="{@docRoot}about/versions/android-5.0.html#Enterprise">托管配置流程</a>在设备上创建安全的工作配置文件。在启动器中,应用如果带有工作标记,则表示该应用及其数据是由 IT 管理员在工作配置文件内管理的。</p>
+
+<p>针对个人配置文件和工作配置文件的通知都显示在一个统一视图中。每个配置文件的数据始终彼此分开,即使当两个配置文件使用同一应用时也是如此。</p>
+
+<p>对于公司自有的设备,IT 管理员可以通过<a href="{@docRoot}about/versions/android-5.0.html#DeviceOwner">设备所有者</a>着手处理新设备并予以配置。雇主可以针对已安装并可配置全局设备设置的设备所有者应用发放这些设备。</p>
+
+
+
+<h2 id="ScreenCapture">屏幕截图和共享</h2>
+
+<p>Android 5.0 允许您在应用中使用屏幕捕获和屏幕共享功能。 </p>
+
+<p>只要拥有用户权限,您就可以从显示屏捕获非安全视频,并可以选择通过网络发送。</p>
+
+
+<h2 id="Sensors">新型传感器</h2>
+
+<p>在 Android 5.0 中,新的<strong>倾斜检测</strong>传感器能够帮助改善受支持设备上对活动的识别情况,<strong>心率传感器</strong>则可报告触摸设备的人员的心率。 </p>
+
+<p>新的<strong>互动复合传感器</strong>现在可用来检测特殊互动,例如“唤醒”<em></em>手势、<em></em>“拾取”手势和<em></em>“扫视”手势。</p>
+
+
+
+<h2 id="WebView">Chromium WebView</h2>
+
+<div style="float:right;margin:1em 2em 1em 2em;">
+ <img src="/images/kk-chromium-icon.png" alt="" height="160" style="margin-bottom:0em;">
+</div>
+
+<p>Android 5.0 的初始版本包括一个适用于 {@link android.webkit.WebView} 的 Chromium 版本,该版本基于 Chromium M37 版本,增加了对 <strong>WebRTC</strong>、<strong>WebAudio</strong> 和 <strong>WebGL</strong> 的支持。 </p>
+
+<p>Chromium M37 还包括对所有 <strong>Web 组件</strong>规范的原生支持:定制元素、Shadow DOM、HTML 导入以及模板。也就是说,您可以在 WebView 中使用 <a href="http://polymer-project.org/">Polymer</a> 及其<a href="https://www.polymer-project.org/docs/elements/material.html">Material design 设计元素</a>,无需 polyfills。</p>
+
+<p>虽然自 Android 4.4 以来 WebView 已基于 Chromium ,但现在可以从 Google Play 更新 Chromium 层。</p>
+
+<p>当有新的 Chromium 版本可用时,用户可以从 Google Play 进行更新以确保获得针对 WebView 的最新增强功能和错误修复,以便为在 Android 5.0 和更高版本上使用 WebView 的应用提供最新的 Web API 和错误修复。</p>
+
+
+
+<h2 id="Accessibility">辅助功能和输入</h2>
+
+<p>新的辅助功能 API 可以检索屏幕(普通用户可见并可与之互动)上窗口的属性的详细信息,并且可以为界面元素定义标准或定制的输入操作。</p>
+
+<p>新的输入法编辑器 (IME) API 支持从输入法更快地直接切换到其他 IME。</p>
+
+
+
+<h2 id="Battery">可构建低电耗应用</h2>
+
+<p>新的<strong>作业调度</strong> API 支持您将系统作业推迟到后期或特定情况(例如,设备在充电或已连接到 Wi-Fi),从而最大延长电池寿命。</p>
+
+<p>新的 <code>dumpsys batterystats</code> 命令会生成<strong>耗电量统计信息</strong>,方便您了解整个系统的用电情况以及应用对设备电池的影响。您可以查看电源事件的历史记录、每个 UID 和系统组件的大致用电量,等等。</p>
+
+<img src="{@docRoot}images/versions/battery_historian.png" srcset="{@docRoot}images/versions/battery_historian@2x.png 2x" alt="" width="760" height="462" />
+<p class="img-caption">Battery Historian 是一个新工具,可以将 <code>dumpsys batterystats</code> 的统计信息转成直观的信息,方便就电池进行调试。该工具位于 <a href="https://github.com/google/battery-historian">https://github.com/google/battery-historian</a>。</p>
diff --git a/docs/html/auto/index.jd b/docs/html/auto/index.jd
index 129478a..7d9400f 100644
--- a/docs/html/auto/index.jd
+++ b/docs/html/auto/index.jd
@@ -78,7 +78,7 @@
<div class="landing-section-header">
<div class="landing-h1 hero">Android Auto</div>
<div class="landing-subhead hero">Audio entertainment and
- messaging services in the car</div>
+ messaging services in the car.</div>
<div class="landing-hero-description">
<p style="width:450px">Let drivers listen to and control
content in your music and other audio apps. Allow drivers to
@@ -86,12 +86,15 @@
car's controls and screen.</p>
</div>
- <div class="landing-body">
+ <div class="landing-body" style="margin-top:40px;">
<a href="{@docRoot}training/auto/index.html"
- class="landing-button landing-primary"
- style="margin-top:40px;">
+ class="landing-button landing-primary">
Get Started
</a>
+ <a href="https://www.youtube.com/watch?v=ctiaVxgclsg" class="video-shadowbox-button white"
+ style="margin-left:40px">
+ Watch the Intro Video
+ </a>
</div>
</div>
</div>
@@ -110,7 +113,7 @@
<div class="landing-section landing-gray-background" id="android-in-car">
<div class="wrap">
- <div class="landing-section-h1">
+ <div class="landing-section-header">
<div class="landing-h1">Extending Android to Cars</div>
<div class="landing-subhead">Android Auto brings the Android
platform into the car with a user interface that's optimized for driving.</div>
@@ -131,10 +134,6 @@
</div>
</div>
</div>
- <p>Before you start building, check out the
- <a href="http://youtu.be/ctiaVxgclsg" class="external-link">Introduction to Android Auto</a> video to understand how
- users see and interact with your app in Android Auto.
- </p>
</div>
</div> <!-- end .wrap -->
</div> <!-- end .landing-section -->
diff --git a/docs/html/training/sync-adapters/running-sync-adapter.jd b/docs/html/training/sync-adapters/running-sync-adapter.jd
index 8fb7e80c..194e94b 100644
--- a/docs/html/training/sync-adapters/running-sync-adapter.jd
+++ b/docs/html/training/sync-adapters/running-sync-adapter.jd
@@ -396,13 +396,11 @@
// Account
public static final String ACCOUNT = "default_account";
// Sync interval constants
- public static final long MILLISECONDS_PER_SECOND = 1000L;
public static final long SECONDS_PER_MINUTE = 60L;
public static final long SYNC_INTERVAL_IN_MINUTES = 60L;
public static final long SYNC_INTERVAL =
SYNC_INTERVAL_IN_MINUTES *
- SECONDS_PER_MINUTE *
- MILLISECONDS_PER_SECOND;
+ SECONDS_PER_MINUTE;
// Global variables
// A content resolver for accessing the provider
ContentResolver mResolver;
@@ -419,7 +417,7 @@
ContentResolver.addPeriodicSync(
ACCOUNT,
AUTHORITY,
- null,
+ Bundle.EMPTY,
SYNC_INTERVAL);
...
}
diff --git a/docs/html/wear/index.jd b/docs/html/wear/index.jd
index c372395..27e8098 100644
--- a/docs/html/wear/index.jd
+++ b/docs/html/wear/index.jd
@@ -28,6 +28,8 @@
+
+
<div class="landing-body-content">
<div class="landing-hero-container">
<div class="landing-section wear-hero">
@@ -39,7 +41,7 @@
<div class="col-10">
<div class="landing-section-header">
<div class="landing-h1 hero">Android Wear</div>
- <div class="landing-subhead hero">Information that moves with you</div>
+ <div class="landing-subhead hero">Information that moves with you.</div>
</div>
<div class="landing-hero-description">
@@ -54,6 +56,10 @@
<a href="{@docRoot}training/building-wearables.html" class="landing-button landing-primary" style="margin-top: 40px;">
Get Started
</a>
+ <a href="https://www.youtube.com/watch?v=Bl4Qne-RpcM" class="video-shadowbox-button white"
+ style="margin-left:40px">
+ Watch the Intro Video
+ </a>
</div>
</div>
diff --git a/graphics/java/android/graphics/drawable/RotateDrawable.java b/graphics/java/android/graphics/drawable/RotateDrawable.java
index 1ee44fb..e1991fe 100644
--- a/graphics/java/android/graphics/drawable/RotateDrawable.java
+++ b/graphics/java/android/graphics/drawable/RotateDrawable.java
@@ -216,7 +216,7 @@
* @attr ref android.R.styleable#RotateDrawable_pivotX
*/
public void setPivotX(float pivotX) {
- if (mState.mPivotX == pivotX) {
+ if (mState.mPivotX != pivotX) {
mState.mPivotX = pivotX;
invalidateSelf();
}
@@ -242,7 +242,7 @@
* @see #isPivotXRelative()
*/
public void setPivotXRelative(boolean relative) {
- if (mState.mPivotXRel == relative) {
+ if (mState.mPivotXRel != relative) {
mState.mPivotXRel = relative;
invalidateSelf();
}
@@ -270,7 +270,7 @@
* @attr ref android.R.styleable#RotateDrawable_pivotY
*/
public void setPivotY(float pivotY) {
- if (mState.mPivotY == pivotY) {
+ if (mState.mPivotY != pivotY) {
mState.mPivotY = pivotY;
invalidateSelf();
}
@@ -296,7 +296,7 @@
* @see #isPivotYRelative()
*/
public void setPivotYRelative(boolean relative) {
- if (mState.mPivotYRel == relative) {
+ if (mState.mPivotYRel != relative) {
mState.mPivotYRel = relative;
invalidateSelf();
}
diff --git a/libs/hwui/ResourceCache.cpp b/libs/hwui/ResourceCache.cpp
index 12d4928..717ce9a 100644
--- a/libs/hwui/ResourceCache.cpp
+++ b/libs/hwui/ResourceCache.cpp
@@ -185,10 +185,9 @@
if (ref == NULL) {
// If we're not tracking this resource, just delete it
if (Caches::hasInstance()) {
- Caches::getInstance().textureCache.removeDeferred(resource);
- } else {
- delete resource;
+ Caches::getInstance().textureCache.releaseTexture(resource);
}
+ delete resource;
return;
}
ref->destroyed = true;
@@ -238,6 +237,9 @@
bool ResourceCache::recycleLocked(SkBitmap* resource) {
ssize_t index = mCache->indexOfKey(resource);
if (index < 0) {
+ if (Caches::hasInstance()) {
+ Caches::getInstance().textureCache.releaseTexture(resource);
+ }
// not tracking this resource; just recycle the pixel data
resource->setPixels(NULL, NULL);
return true;
@@ -262,17 +264,20 @@
*/
void ResourceCache::deleteResourceReferenceLocked(const void* resource, ResourceReference* ref) {
if (ref->recycled && ref->resourceType == kBitmap) {
- ((SkBitmap*) resource)->setPixels(NULL, NULL);
+ SkBitmap* bitmap = (SkBitmap*) resource;
+ if (Caches::hasInstance()) {
+ Caches::getInstance().textureCache.releaseTexture(bitmap);
+ }
+ bitmap->setPixels(NULL, NULL);
}
if (ref->destroyed) {
switch (ref->resourceType) {
case kBitmap: {
SkBitmap* bitmap = (SkBitmap*) resource;
if (Caches::hasInstance()) {
- Caches::getInstance().textureCache.removeDeferred(bitmap);
- } else {
- delete bitmap;
+ Caches::getInstance().textureCache.releaseTexture(bitmap);
}
+ delete bitmap;
}
break;
case kPath: {
diff --git a/libs/hwui/ResourceCache.h b/libs/hwui/ResourceCache.h
index a922d53..f12f2a4 100644
--- a/libs/hwui/ResourceCache.h
+++ b/libs/hwui/ResourceCache.h
@@ -43,7 +43,6 @@
class ResourceReference {
public:
- ResourceReference() { refCount = 0; recycled = false; destroyed = false;}
ResourceReference(ResourceType type) {
refCount = 0; recycled = false; destroyed = false; resourceType = type;
}
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
index 5fcb194..5bad2fc 100644
--- a/libs/hwui/TextureCache.cpp
+++ b/libs/hwui/TextureCache.cpp
@@ -20,6 +20,7 @@
#include <GLES2/gl2.h>
#include <SkCanvas.h>
+#include <SkPixelRef.h>
#include <utils/Mutex.h>
@@ -36,7 +37,7 @@
///////////////////////////////////////////////////////////////////////////////
TextureCache::TextureCache():
- mCache(LruCache<const SkPixelRef*, Texture*>::kUnlimitedCapacity),
+ mCache(LruCache<uint32_t, Texture*>::kUnlimitedCapacity),
mSize(0), mMaxSize(MB(DEFAULT_TEXTURE_CACHE_SIZE)),
mFlushRate(DEFAULT_TEXTURE_CACHE_FLUSH_RATE) {
char property[PROPERTY_VALUE_MAX];
@@ -60,7 +61,7 @@
}
TextureCache::TextureCache(uint32_t maxByteSize):
- mCache(LruCache<const SkPixelRef*, Texture*>::kUnlimitedCapacity),
+ mCache(LruCache<uint32_t, Texture*>::kUnlimitedCapacity),
mSize(0), mMaxSize(maxByteSize) {
init();
}
@@ -105,7 +106,7 @@
// Callbacks
///////////////////////////////////////////////////////////////////////////////
-void TextureCache::operator()(const SkPixelRef*&, Texture*& texture) {
+void TextureCache::operator()(uint32_t&, Texture*& texture) {
// This will be called already locked
if (texture) {
mSize -= texture->bitmapSize;
@@ -124,7 +125,7 @@
///////////////////////////////////////////////////////////////////////////////
void TextureCache::resetMarkInUse() {
- LruCache<const SkPixelRef*, Texture*>::Iterator iter(mCache);
+ LruCache<uint32_t, Texture*>::Iterator iter(mCache);
while (iter.next()) {
iter.value()->isInUse = false;
}
@@ -142,7 +143,7 @@
// Returns a prepared Texture* that either is already in the cache or can fit
// in the cache (and is thus added to the cache)
Texture* TextureCache::getCachedTexture(const SkBitmap* bitmap) {
- Texture* texture = mCache.get(bitmap->pixelRef());
+ Texture* texture = mCache.get(bitmap->pixelRef()->getStableID());
if (!texture) {
if (!canMakeTextureFromBitmap(bitmap)) {
@@ -172,7 +173,7 @@
if (mDebugEnabled) {
ALOGD("Texture created, size = %d", size);
}
- mCache.put(bitmap->pixelRef(), texture);
+ mCache.put(bitmap->pixelRef()->getStableID(), texture);
}
} else if (!texture->isInUse && bitmap->getGenerationID() != texture->generation) {
// Texture was in the cache but is dirty, re-upload
@@ -219,22 +220,19 @@
return texture;
}
-void TextureCache::remove(const SkBitmap* bitmap) {
- mCache.remove(bitmap->pixelRef());
-}
+void TextureCache::releaseTexture(const SkBitmap* bitmap) {
+ if (!bitmap || !bitmap->pixelRef()) return;
-void TextureCache::removeDeferred(const SkBitmap* bitmap) {
Mutex::Autolock _l(mLock);
- mGarbage.push(bitmap);
+ mGarbage.push(bitmap->pixelRef()->getStableID());
}
void TextureCache::clearGarbage() {
Mutex::Autolock _l(mLock);
size_t count = mGarbage.size();
for (size_t i = 0; i < count; i++) {
- const SkBitmap* bitmap = mGarbage.itemAt(i);
- mCache.remove(bitmap->pixelRef());
- delete bitmap;
+ uint32_t pixelRefId = mGarbage.itemAt(i);
+ mCache.remove(pixelRefId);
}
mGarbage.clear();
}
diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h
index 61db5b0..3e94d1f 100644
--- a/libs/hwui/TextureCache.h
+++ b/libs/hwui/TextureCache.h
@@ -49,7 +49,7 @@
* Any texture added to the cache causing the cache to grow beyond the maximum
* allowed size will also cause the oldest texture to be kicked out.
*/
-class TextureCache: public OnEntryRemoved<const SkPixelRef*, Texture*> {
+class TextureCache: public OnEntryRemoved<uint32_t, Texture*> {
public:
TextureCache();
TextureCache(uint32_t maxByteSize);
@@ -59,7 +59,7 @@
* Used as a callback when an entry is removed from the cache.
* Do not invoke directly.
*/
- void operator()(const SkPixelRef*& pixelRef, Texture*& texture);
+ void operator()(uint32_t&, Texture*& texture);
/**
* Resets all Textures to not be marked as in use
@@ -83,16 +83,12 @@
* texture is not kept in the cache. The caller must destroy the texture.
*/
Texture* getTransient(const SkBitmap* bitmap);
- /**
- * Removes the texture associated with the specified bitmap.
- * Upon remove the texture is freed.
- */
- void remove(const SkBitmap* bitmap);
+
/**
* Removes the texture associated with the specified bitmap. This is meant
* to be called from threads that are not the EGL context thread.
*/
- void removeDeferred(const SkBitmap* bitmap);
+ void releaseTexture(const SkBitmap* bitmap);
/**
* Process deferred removals.
*/
@@ -147,7 +143,7 @@
void init();
- LruCache<const SkPixelRef*, Texture*> mCache;
+ LruCache<uint32_t, Texture*> mCache;
uint32_t mSize;
uint32_t mMaxSize;
@@ -157,7 +153,7 @@
bool mDebugEnabled;
- Vector<const SkBitmap*> mGarbage;
+ Vector<uint32_t> mGarbage;
mutable Mutex mLock;
}; // class TextureCache
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 645681a..3f08305 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -17,6 +17,7 @@
package android.media;
import android.Manifest;
+import android.annotation.NonNull;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemApi;
@@ -2318,14 +2319,25 @@
return status;
}
- // when adding new flags, add them to AUDIOFOCUS_FLAGS_ALL
+ // when adding new flags, add them to the relevant AUDIOFOCUS_FLAGS_APPS or SYSTEM masks
/** @hide */
+ @SystemApi
public static final int AUDIOFOCUS_FLAG_DELAY_OK = 0x1 << 0;
/** @hide */
- public static final int AUDIOFOCUS_FLAGS_ALL = AUDIOFOCUS_FLAG_DELAY_OK;
+ @SystemApi
+ public static final int AUDIOFOCUS_FLAG_LOCK = 0x1 << 1;
+ /** @hide */
+ public static final int AUDIOFOCUS_FLAGS_APPS = AUDIOFOCUS_FLAG_DELAY_OK;
+ /** @hide */
+ public static final int AUDIOFOCUS_FLAGS_SYSTEM = AUDIOFOCUS_FLAG_DELAY_OK
+ | AUDIOFOCUS_FLAG_LOCK;
/**
* @hide
+ * Request audio focus.
+ * Send a request to obtain the audio focus. This method differs from
+ * {@link #requestAudioFocus(OnAudioFocusChangeListener, int, int)} in that it can express
+ * that the requester accepts delayed grants of audio focus.
* @param l the listener to be notified of audio focus changes. It is not allowed to be null
* when the request is flagged with {@link #AUDIOFOCUS_FLAG_DELAY_OK}.
* @param requestAttributes non null {@link AudioAttributes} describing the main reason for
@@ -2340,24 +2352,70 @@
* usecases such as voice memo recording, or speech recognition.
* Use {@link #AUDIOFOCUS_GAIN} for a focus request of unknown duration such
* as the playback of a song or a video.
- * @param flags use 0 when not using any flags for the request, which behaves like
- * {@link #requestAudioFocus(OnAudioFocusChangeListener, int, int)}, where either audio
- * focus is granted immediately, or the grant request fails because the system is in a
- * state where focus cannot change (e.g. a phone call).
- * Use {link #AUDIOFOCUS_FLAG_DELAY_OK} if it is ok for the requester to not be granted
- * audio focus immediately (as indicated by {@link #AUDIOFOCUS_REQUEST_DELAYED}) when
- * the system is in a state where focus cannot change, but be granted focus later when
- * this condition ends.
+ * @param flags 0 or {link #AUDIOFOCUS_FLAG_DELAY_OK}.
+ * <br>Use 0 when not using any flags for the request, which behaves like
+ * {@link #requestAudioFocus(OnAudioFocusChangeListener, int, int)}, where either audio
+ * focus is granted immediately, or the grant request fails because the system is in a
+ * state where focus cannot change (e.g. a phone call).
+ * <br>Use {link #AUDIOFOCUS_FLAG_DELAY_OK} if it is ok for the requester to not be granted
+ * audio focus immediately (as indicated by {@link #AUDIOFOCUS_REQUEST_DELAYED}) when
+ * the system is in a state where focus cannot change, but be granted focus later when
+ * this condition ends.
* @return {@link #AUDIOFOCUS_REQUEST_FAILED}, {@link #AUDIOFOCUS_REQUEST_GRANTED}
* or {@link #AUDIOFOCUS_REQUEST_DELAYED}.
* The return value is never {@link #AUDIOFOCUS_REQUEST_DELAYED} when focus is requested
* without the {@link #AUDIOFOCUS_FLAG_DELAY_OK} flag.
* @throws IllegalArgumentException
*/
+ @SystemApi
public int requestAudioFocus(OnAudioFocusChangeListener l,
- AudioAttributes requestAttributes,
+ @NonNull AudioAttributes requestAttributes,
int durationHint,
int flags) throws IllegalArgumentException {
+ if (flags != (flags & AUDIOFOCUS_FLAGS_APPS)) {
+ throw new IllegalArgumentException("Invalid flags 0x"
+ + Integer.toHexString(flags).toUpperCase());
+ }
+ return requestAudioFocus(l, requestAttributes, durationHint,
+ flags & AUDIOFOCUS_FLAGS_APPS,
+ null /* no AudioPolicy*/);
+ }
+
+ /**
+ * @hide
+ * Request or lock audio focus.
+ * This method is to be used by system components that have registered an
+ * {@link android.media.audiopolicy.AudioPolicy} to request audio focus, but also to "lock" it
+ * so focus granting is temporarily disabled.
+ * @param l see the description of the same parameter in
+ * {@link #requestAudioFocus(OnAudioFocusChangeListener, AudioAttributes, int, int)}
+ * @param requestAttributes non null {@link AudioAttributes} describing the main reason for
+ * requesting audio focus.
+ * @param durationHint see the description of the same parameter in
+ * {@link #requestAudioFocus(OnAudioFocusChangeListener, AudioAttributes, int, int)}
+ * @param flags 0 or a combination of {link #AUDIOFOCUS_FLAG_DELAY_OK},
+ * {@link #AUDIOFOCUS_FLAG_LOCK}
+ * <br>Use 0 when not using any flags for the request, which behaves like
+ * {@link #requestAudioFocus(OnAudioFocusChangeListener, int, int)}, where either audio
+ * focus is granted immediately, or the grant request fails because the system is in a
+ * state where focus cannot change (e.g. a phone call).
+ * <br>Use {link #AUDIOFOCUS_FLAG_DELAY_OK} if it is ok for the requester to not be granted
+ * audio focus immediately (as indicated by {@link #AUDIOFOCUS_REQUEST_DELAYED}) when
+ * the system is in a state where focus cannot change, but be granted focus later when
+ * this condition ends.
+ * <br>Use {@link #AUDIOFOCUS_FLAG_LOCK} when locking audio focus so granting is
+ * temporarily disabled.
+ * @param ap a registered {@link android.media.audiopolicy.AudioPolicy} instance when locking
+ * focus, or null.
+ * @return see the description of the same return value in
+ * {@link #requestAudioFocus(OnAudioFocusChangeListener, AudioAttributes, int, int)}
+ * @throws IllegalArgumentException
+ */
+ public int requestAudioFocus(OnAudioFocusChangeListener l,
+ @NonNull AudioAttributes requestAttributes,
+ int durationHint,
+ int flags,
+ AudioPolicy ap) throws IllegalArgumentException {
// parameter checking
if (requestAttributes == null) {
throw new IllegalArgumentException("Illegal null AudioAttributes argument");
@@ -2366,7 +2424,7 @@
(durationHint > AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE)) {
throw new IllegalArgumentException("Invalid duration hint");
}
- if (flags != (flags & AUDIOFOCUS_FLAGS_ALL)) {
+ if (flags != (flags & AUDIOFOCUS_FLAGS_SYSTEM)) {
throw new IllegalArgumentException("Illegal flags 0x"
+ Integer.toHexString(flags).toUpperCase());
}
@@ -2374,6 +2432,10 @@
throw new IllegalArgumentException(
"Illegal null focus listener when flagged as accepting delayed focus grant");
}
+ if (((flags & AUDIOFOCUS_FLAG_LOCK) == AUDIOFOCUS_FLAG_LOCK) && (ap == null)) {
+ throw new IllegalArgumentException(
+ "Illegal null audio policy when locking audio focus");
+ }
int status = AUDIOFOCUS_REQUEST_FAILED;
registerAudioFocusListener(l);
@@ -2381,9 +2443,10 @@
try {
status = service.requestAudioFocus(requestAttributes, durationHint, mICallBack,
mAudioFocusDispatcher, getIdForAudioFocusListener(l),
- mContext.getOpPackageName() /* package name */, flags);
+ mContext.getOpPackageName() /* package name */, flags,
+ ap != null ? ap.token() : null);
} catch (RemoteException e) {
- Log.e(TAG, "Can't call requestAudioFocus() on AudioService due to "+e);
+ Log.e(TAG, "Can't call requestAudioFocus() on AudioService:", e);
}
return status;
}
@@ -2405,9 +2468,11 @@
.setInternalLegacyStreamType(streamType).build(),
durationHint, mICallBack, null,
MediaFocusControl.IN_VOICE_COMM_FOCUS_ID,
- mContext.getOpPackageName(), 0 /* flags, legacy behavior*/ );
+ mContext.getOpPackageName(),
+ AUDIOFOCUS_FLAG_LOCK,
+ null /* policy token */);
} catch (RemoteException e) {
- Log.e(TAG, "Can't call requestAudioFocusForCall() on AudioService due to "+e);
+ Log.e(TAG, "Can't call requestAudioFocusForCall() on AudioService:", e);
}
}
@@ -2420,9 +2485,10 @@
public void abandonAudioFocusForCall() {
IAudioService service = getService();
try {
- service.abandonAudioFocus(null, MediaFocusControl.IN_VOICE_COMM_FOCUS_ID);
+ service.abandonAudioFocus(null, MediaFocusControl.IN_VOICE_COMM_FOCUS_ID,
+ null /*AudioAttributes, legacy behavior*/);
} catch (RemoteException e) {
- Log.e(TAG, "Can't call abandonAudioFocusForCall() on AudioService due to "+e);
+ Log.e(TAG, "Can't call abandonAudioFocusForCall() on AudioService:", e);
}
}
@@ -2432,19 +2498,30 @@
* @return {@link #AUDIOFOCUS_REQUEST_FAILED} or {@link #AUDIOFOCUS_REQUEST_GRANTED}
*/
public int abandonAudioFocus(OnAudioFocusChangeListener l) {
+ return abandonAudioFocus(l, null /*AudioAttributes, legacy behavior*/);
+ }
+
+ /**
+ * @hide
+ * Abandon audio focus. Causes the previous focus owner, if any, to receive focus.
+ * @param l the listener with which focus was requested.
+ * @param aa the {@link AudioAttributes} with which audio focus was requested
+ * @return {@link #AUDIOFOCUS_REQUEST_FAILED} or {@link #AUDIOFOCUS_REQUEST_GRANTED}
+ */
+ @SystemApi
+ public int abandonAudioFocus(OnAudioFocusChangeListener l, AudioAttributes aa) {
int status = AUDIOFOCUS_REQUEST_FAILED;
unregisterAudioFocusListener(l);
IAudioService service = getService();
try {
status = service.abandonAudioFocus(mAudioFocusDispatcher,
- getIdForAudioFocusListener(l));
+ getIdForAudioFocusListener(l), aa);
} catch (RemoteException e) {
- Log.e(TAG, "Can't call abandonAudioFocus() on AudioService due to "+e);
+ Log.e(TAG, "Can't call abandonAudioFocus() on AudioService:", e);
}
return status;
}
-
//====================================================================
// Remote Control
/**
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index c70ac55..b6b24a4 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -49,6 +49,7 @@
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
import android.media.audiopolicy.AudioMix;
+import android.media.audiopolicy.AudioPolicy;
import android.media.audiopolicy.AudioPolicyConfig;
import android.os.Binder;
import android.os.Build;
@@ -5018,13 +5019,34 @@
// Audio Focus
//==========================================================================================
public int requestAudioFocus(AudioAttributes aa, int durationHint, IBinder cb,
- IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags) {
+ IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags,
+ IBinder policyToken) {
+ // permission checks
+ if ((flags & AudioManager.AUDIOFOCUS_FLAG_LOCK) == AudioManager.AUDIOFOCUS_FLAG_LOCK) {
+ if (mMediaFocusControl.IN_VOICE_COMM_FOCUS_ID.equals(clientId)) {
+ if (PackageManager.PERMISSION_GRANTED != mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.MODIFY_PHONE_STATE)) {
+ Log.e(TAG, "Invalid permission to (un)lock audio focus", new Exception());
+ return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
+ }
+ } else {
+ // only a registered audio policy can be used to lock focus
+ synchronized (mAudioPolicies) {
+ if (!mAudioPolicies.containsKey(policyToken)) {
+ Log.e(TAG, "Invalid unregistered AudioPolicy to (un)lock audio focus",
+ new Exception());
+ return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
+ }
+ }
+ }
+ }
+
return mMediaFocusControl.requestAudioFocus(aa, durationHint, cb, fd,
clientId, callingPackageName, flags);
}
- public int abandonAudioFocus(IAudioFocusDispatcher fd, String clientId) {
- return mMediaFocusControl.abandonAudioFocus(fd, clientId);
+ public int abandonAudioFocus(IAudioFocusDispatcher fd, String clientId, AudioAttributes aa) {
+ return mMediaFocusControl.abandonAudioFocus(fd, clientId, aa);
}
public void unregisterAudioFocusClient(String clientId) {
@@ -5725,6 +5747,9 @@
// TODO implement clearing mix attribute matching info in native audio policy
}
+ //======================
+ // Audio policy proxy
+ //======================
/**
* This internal class inherits from AudioPolicyConfig which contains all the mixes and
* their configurations.
@@ -5742,8 +5767,8 @@
public void binderDied() {
synchronized (mAudioPolicies) {
Log.i(TAG, "audio policy " + mToken + " died");
- mAudioPolicies.remove(mToken);
disconnectMixes();
+ mAudioPolicies.remove(mToken);
}
}
diff --git a/media/java/android/media/FocusRequester.java b/media/java/android/media/FocusRequester.java
index 682d54c..0d675fc 100644
--- a/media/java/android/media/FocusRequester.java
+++ b/media/java/android/media/FocusRequester.java
@@ -83,6 +83,10 @@
}
}
+ boolean isLockedFocusOwner() {
+ return ((mGrantFlags & AudioManager.AUDIOFOCUS_FLAG_LOCK) != 0);
+ }
+
boolean hasSameBinder(IBinder ib) {
return (mSourceRef != null) && mSourceRef.equals(ib);
}
@@ -99,6 +103,9 @@
return mCallingUid == uid;
}
+ String getClientId() {
+ return mClientId;
+ }
int getGainRequest() {
return mFocusGainRequest;
@@ -144,12 +151,20 @@
return focusChangeToString(mFocusLossReceived);
}
+ private static String flagsToString(int flags) {
+ String msg = new String();
+ if ((flags & AudioManager.AUDIOFOCUS_FLAG_DELAY_OK) != 0) { msg += "DELAY_OK"; }
+ if (!msg.isEmpty()) { msg += "|"; }
+ if ((flags & AudioManager.AUDIOFOCUS_FLAG_LOCK) != 0) { msg += "LOCK"; }
+ return msg;
+ }
+
void dump(PrintWriter pw) {
pw.println(" source:" + mSourceRef
+ " -- pack: " + mPackageName
+ " -- client: " + mClientId
+ " -- gain: " + focusGainToString()
- + " -- grant: " + mGrantFlags
+ + " -- flags: " + flagsToString(mGrantFlags)
+ " -- loss: " + focusLossToString()
+ " -- uid: " + mCallingUid
+ " -- attr: " + mAttributes);
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 47a5291..b691447 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -118,9 +118,10 @@
boolean isBluetoothA2dpOn();
int requestAudioFocus(in AudioAttributes aa, int durationHint, IBinder cb,
- IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags);
+ IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags,
+ IBinder policyToken);
- int abandonAudioFocus(IAudioFocusDispatcher fd, String clientId);
+ int abandonAudioFocus(IAudioFocusDispatcher fd, String clientId, in AudioAttributes aa);
void unregisterAudioFocusClient(String clientId);
diff --git a/media/java/android/media/MediaFocusControl.java b/media/java/android/media/MediaFocusControl.java
index c495106..8d96970 100644
--- a/media/java/android/media/MediaFocusControl.java
+++ b/media/java/android/media/MediaFocusControl.java
@@ -390,7 +390,8 @@
// AudioFocus
//==========================================================================================
- /* constant to identify focus stack entry that is used to hold the focus while the phone
+ /**
+ * Constant to identify a focus stack entry that is used to hold the focus while the phone
* is ringing or during a call. Used by com.android.internal.telephony.CallManager when
* entering and exiting calls.
*/
@@ -539,40 +540,40 @@
* Helper function:
* Returns true if the system is in a state where the focus can be reevaluated, false otherwise.
* The implementation guarantees that a state where focus cannot be immediately reassigned
- * implies that an "exclusive" focus owner is at the top of the focus stack.
+ * implies that an "locked" focus owner is at the top of the focus stack.
* Modifications to the implementation that break this assumption will cause focus requests to
* misbehave when honoring the AudioManager.AUDIOFOCUS_FLAG_DELAY_OK flag.
*/
private boolean canReassignAudioFocus() {
// focus requests are rejected during a phone call or when the phone is ringing
// this is equivalent to IN_VOICE_COMM_FOCUS_ID having the focus
- if (!mFocusStack.isEmpty() && isExclusiveFocusOwner(mFocusStack.peek())) {
+ if (!mFocusStack.isEmpty() && isLockedFocusOwner(mFocusStack.peek())) {
return false;
}
return true;
}
- private boolean isExclusiveFocusOwner(FocusRequester fr) {
- return fr.hasSameClient(IN_VOICE_COMM_FOCUS_ID);
+ private boolean isLockedFocusOwner(FocusRequester fr) {
+ return (fr.hasSameClient(IN_VOICE_COMM_FOCUS_ID) || fr.isLockedFocusOwner());
}
/**
* Helper function
- * Pre-conditions: focus stack is not empty, there is one or more exclusive focus owner
+ * Pre-conditions: focus stack is not empty, there is one or more locked focus owner
* at the top of the focus stack
* Push the focus requester onto the audio focus stack at the first position immediately
- * following the exclusive focus owners.
+ * following the locked focus owners.
* @return {@link AudioManager#AUDIOFOCUS_REQUEST_GRANTED} or
* {@link AudioManager#AUDIOFOCUS_REQUEST_DELAYED}
*/
- private int pushBelowExclusiveFocusOwners(FocusRequester nfr) {
- int lastExclusiveFocusOwnerIndex = mFocusStack.size();
+ private int pushBelowLockedFocusOwners(FocusRequester nfr) {
+ int lastLockedFocusOwnerIndex = mFocusStack.size();
for (int index = mFocusStack.size()-1; index >= 0; index--) {
- if (isExclusiveFocusOwner(mFocusStack.elementAt(index))) {
- lastExclusiveFocusOwnerIndex = index;
+ if (isLockedFocusOwner(mFocusStack.elementAt(index))) {
+ lastLockedFocusOwnerIndex = index;
}
}
- if (lastExclusiveFocusOwnerIndex == mFocusStack.size()) {
+ if (lastLockedFocusOwnerIndex == mFocusStack.size()) {
// this should not happen, but handle it and log an error
Log.e(TAG, "No exclusive focus owner found in propagateFocusLossFromGain_syncAf()",
new Exception());
@@ -581,7 +582,7 @@
mFocusStack.push(nfr);
return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
} else {
- mFocusStack.insertElementAt(nfr, lastExclusiveFocusOwnerIndex);
+ mFocusStack.insertElementAt(nfr, lastLockedFocusOwnerIndex);
return AudioManager.AUDIOFOCUS_REQUEST_DELAYED;
}
}
@@ -687,7 +688,7 @@
if (focusGrantDelayed) {
// focusGrantDelayed being true implies we can't reassign focus right now
// which implies the focus stack is not empty.
- return pushBelowExclusiveFocusOwners(nfr);
+ return pushBelowLockedFocusOwners(nfr);
} else {
// propagate the focus change through the stack
if (!mFocusStack.empty()) {
@@ -703,8 +704,11 @@
return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
}
- /** @see AudioManager#abandonAudioFocus(AudioManager.OnAudioFocusChangeListener) */
- protected int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId) {
+ /**
+ * @see AudioManager#abandonAudioFocus(AudioManager.OnAudioFocusChangeListener, AudioAttributes)
+ * */
+ protected int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId, AudioAttributes aa) {
+ // AudioAttributes are currently ignored, to be used for zones
Log.i(TAG, " AudioFocus abandonAudioFocus() from " + clientId);
try {
// this will take care of notifying the new focus owner if needed
diff --git a/media/java/android/service/media/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java
index 0754fd4..779d486 100644
--- a/media/java/android/service/media/MediaBrowserService.java
+++ b/media/java/android/service/media/MediaBrowserService.java
@@ -189,8 +189,10 @@
} else {
try {
mConnections.put(b, connection);
- callbacks.onConnect(connection.root.getRootId(),
- mSession, connection.root.getExtras());
+ if (mSession != null) {
+ callbacks.onConnect(connection.root.getRootId(),
+ mSession, connection.root.getExtras());
+ }
} catch (RemoteException ex) {
Log.w(TAG, "Calling onConnect() failed. Dropping client. "
+ "pkg=" + pkg);
@@ -319,16 +321,34 @@
/**
* Call to set the media session.
* <p>
- * This must be called before onCreate returns.
+ * This should be called as soon as possible during the service's startup.
+ * It may only be called once.
*
* @return The media session token, must not be null.
*/
- public void setSessionToken(MediaSession.Token token) {
+ public void setSessionToken(final MediaSession.Token token) {
if (token == null) {
- throw new IllegalStateException(this.getClass().getName()
- + ".onCreateSession() set invalid MediaSession.Token");
+ throw new IllegalArgumentException("Session token may not be null.");
}
- mSession = token;
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (mSession != null) {
+ throw new IllegalStateException("The session token has already been set.");
+ }
+ mSession = token;
+ for (IBinder key : mConnections.keySet()) {
+ ConnectionRecord connection = mConnections.get(key);
+ try {
+ connection.callbacks.onConnect(connection.root.getRootId(), mSession,
+ connection.root.getExtras());
+ } catch (RemoteException e) {
+ Log.w(TAG, "Connection for " + connection.pkg + " is no longer valid.");
+ mConnections.remove(key);
+ }
+ }
+ }
+ });
}
/**
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index 1f28324..cc1e01a 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -180,7 +180,10 @@
}
ret.packageName = pkg.packageName;
+ ret.splitNames = pkg.splitNames;
ret.versionCode = pkg.versionCode;
+ ret.baseRevisionCode = pkg.baseRevisionCode;
+ ret.splitRevisionCodes = pkg.splitRevisionCodes;
ret.installLocation = pkg.installLocation;
ret.verifiers = pkg.verifiers;
ret.recommendedInstallLocation = PackageHelper.resolveInstallLocation(context,
diff --git a/packages/DocumentsUI/res/values/dimens.xml b/packages/DocumentsUI/res/values/dimens.xml
index d4d7ede..06f65e2 100644
--- a/packages/DocumentsUI/res/values/dimens.xml
+++ b/packages/DocumentsUI/res/values/dimens.xml
@@ -18,10 +18,10 @@
<dimen name="icon_size">40dp</dimen>
<dimen name="root_icon_size">24dp</dimen>
- <dimen name="grid_width">160dp</dimen>
+ <dimen name="grid_width">152dp</dimen>
<dimen name="grid_height">176dp</dimen>
- <dimen name="grid_item_width">160dp</dimen>
+ <dimen name="grid_item_width">152dp</dimen>
<dimen name="grid_item_height">176dp</dimen>
<dimen name="grid_item_padding">4dp</dimen>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index 219de38..8778f11 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -386,6 +386,7 @@
if (mDrawerToggle != null) {
mDrawerToggle.syncState();
}
+ updateActionBar();
}
public void setRootsDrawerOpen(boolean open) {
@@ -724,7 +725,6 @@
@Override
protected void onRestoreInstanceState(Bundle state) {
super.onRestoreInstanceState(state);
- updateActionBar();
}
private BaseAdapter mStackAdapter = new BaseAdapter() {
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java b/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java
index 882b364..90875c0 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java
@@ -106,12 +106,26 @@
mRenderer.close(callback);
}
- public void destroy() {
+ public void destroy(final Runnable callback) {
+ if (mState == STATE_OPENED) {
+ close(new Runnable() {
+ @Override
+ public void run() {
+ destroy(callback);
+ }
+ });
+ return;
+ }
+
mState = STATE_DESTROYED;
if (DEBUG) {
Log.i(LOG_TAG, "STATE_DESTROYED");
}
mRenderer.destroy();
+
+ if (callback != null) {
+ callback.run();
+ }
}
public void startPreload(int firstShownPage, int lastShownPage) {
@@ -158,7 +172,7 @@
try {
if (mState != STATE_DESTROYED) {
mCloseGuard.warnIfOpen();
- destroy();
+ destroy(null);
}
} finally {
super.finalize();
@@ -412,6 +426,8 @@
@GuardedBy("mLock")
private IPdfRenderer mRenderer;
+ private OpenTask mOpenTask;
+
private boolean mBoundToService;
private boolean mDestroyed;
@@ -439,80 +455,24 @@
}
}
- public void open(final ParcelFileDescriptor source, final OpenDocumentCallback callback) {
+ public void open(ParcelFileDescriptor source, OpenDocumentCallback callback) {
// Opening a new document invalidates the cache as it has pages
// from the last document. We keep the cache even when the document
// is closed to show pages while the other side is writing the new
// document.
mPageContentCache.invalidate();
- new AsyncTask<Void, Void, Integer>() {
- @Override
- protected void onPreExecute() {
- if (mDestroyed) {
- cancel(true);
- return;
- }
- Intent intent = new Intent(PdfManipulationService.ACTION_GET_RENDERER);
- intent.setClass(mContext, PdfManipulationService.class);
- intent.setData(Uri.fromParts("fake-scheme", String.valueOf(
- AsyncRenderer.this.hashCode()), null));
- mContext.bindService(intent, AsyncRenderer.this, Context.BIND_AUTO_CREATE);
- mBoundToService = true;
- }
-
- @Override
- protected Integer doInBackground(Void... params) {
- synchronized (mLock) {
- while (mRenderer == null) {
- try {
- mLock.wait();
- } catch (InterruptedException ie) {
- /* ignore */
- }
- }
- try {
- return mRenderer.openDocument(source);
- } catch (RemoteException re) {
- Log.e(LOG_TAG, "Cannot open PDF document");
- return PdfManipulationService.ERROR_MALFORMED_PDF_FILE;
- } finally {
- // Close the fd as we passed it to another process
- // which took ownership.
- IoUtils.closeQuietly(source);
- }
- }
- }
-
- @Override
- public void onPostExecute(Integer pageCount) {
- switch (pageCount) {
- case PdfManipulationService.ERROR_MALFORMED_PDF_FILE: {
- mPageCount = PrintDocumentInfo.PAGE_COUNT_UNKNOWN;
- if (callback != null) {
- callback.onFailure(OpenDocumentCallback.ERROR_MALFORMED_PDF_FILE);
- }
- } break;
- case PdfManipulationService.ERROR_SECURE_PDF_FILE: {
- mPageCount = PrintDocumentInfo.PAGE_COUNT_UNKNOWN;
- if (callback != null) {
- callback.onFailure(OpenDocumentCallback.ERROR_SECURE_PDF_FILE);
- }
- } break;
- default: {
- mPageCount = pageCount;
- if (callback != null) {
- callback.onSuccess();
- }
- } break;
- }
- }
- }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);
+ mOpenTask = new OpenTask(source, callback);
+ mOpenTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);
}
public void close(final Runnable callback) {
cancelAllRendering();
+ if (mOpenTask != null) {
+ mOpenTask.cancel();
+ }
+
new AsyncTask<Void, Void, Void>() {
@Override
protected void onPreExecute() {
@@ -549,6 +509,7 @@
mBoundToService = false;
mContext.unbindService(AsyncRenderer.this);
}
+
mPageContentCache.invalidate();
mPageContentCache.clear();
mDestroyed = true;
@@ -687,6 +648,91 @@
}
}
+ private final class OpenTask extends AsyncTask<Void, Void, Integer> {
+ private final ParcelFileDescriptor mSource;
+ private final OpenDocumentCallback mCallback;
+
+ public OpenTask(ParcelFileDescriptor source, OpenDocumentCallback callback) {
+ mSource = source;
+ mCallback = callback;
+ }
+
+ @Override
+ protected void onPreExecute() {
+ if (mDestroyed) {
+ cancel(true);
+ return;
+ }
+ Intent intent = new Intent(PdfManipulationService.ACTION_GET_RENDERER);
+ intent.setClass(mContext, PdfManipulationService.class);
+ intent.setData(Uri.fromParts("fake-scheme", String.valueOf(
+ AsyncRenderer.this.hashCode()), null));
+ mContext.bindService(intent, AsyncRenderer.this, Context.BIND_AUTO_CREATE);
+ mBoundToService = true;
+ }
+
+ @Override
+ protected Integer doInBackground(Void... params) {
+ synchronized (mLock) {
+ while (mRenderer == null && !isCancelled()) {
+ try {
+ mLock.wait();
+ } catch (InterruptedException ie) {
+ /* ignore */
+ }
+ }
+ try {
+ return mRenderer.openDocument(mSource);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Cannot open PDF document");
+ return PdfManipulationService.ERROR_MALFORMED_PDF_FILE;
+ } finally {
+ // Close the fd as we passed it to another process
+ // which took ownership.
+ IoUtils.closeQuietly(mSource);
+ }
+ }
+ }
+
+ @Override
+ public void onPostExecute(Integer pageCount) {
+ switch (pageCount) {
+ case PdfManipulationService.ERROR_MALFORMED_PDF_FILE: {
+ mPageCount = PrintDocumentInfo.PAGE_COUNT_UNKNOWN;
+ if (mCallback != null) {
+ mCallback.onFailure(OpenDocumentCallback.ERROR_MALFORMED_PDF_FILE);
+ }
+ } break;
+ case PdfManipulationService.ERROR_SECURE_PDF_FILE: {
+ mPageCount = PrintDocumentInfo.PAGE_COUNT_UNKNOWN;
+ if (mCallback != null) {
+ mCallback.onFailure(OpenDocumentCallback.ERROR_SECURE_PDF_FILE);
+ }
+ } break;
+ default: {
+ mPageCount = pageCount;
+ if (mCallback != null) {
+ mCallback.onSuccess();
+ }
+ } break;
+ }
+
+ mOpenTask = null;
+ }
+
+ @Override
+ protected void onCancelled(Integer integer) {
+ mOpenTask = null;
+ }
+
+ public void cancel() {
+ cancel(true);
+ synchronized(mLock) {
+ mLock.notifyAll();
+ }
+ }
+ }
+
private final class RenderPageTask extends AsyncTask<Void, Void, RenderedPage> {
final int mPageIndex;
final RenderSpec mRenderSpec;
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java b/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java
index 02d2715..22a7f86 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java
@@ -346,7 +346,6 @@
private List<PrinterInfo> mHistoricalPrinters = new ArrayList<>();
private boolean mReadHistoryCompleted;
- private boolean mReadHistoryInProgress;
private ReadTask mReadTask;
@@ -358,7 +357,7 @@
}
public boolean isReadHistoryInProgress() {
- return mReadHistoryInProgress;
+ return mReadTask != null;
}
public boolean isReadHistoryCompleted() {
@@ -366,9 +365,7 @@
}
public boolean stopReadPrinterHistory() {
- final boolean cancelled = mReadTask.cancel(true);
- mReadTask = null;
- return cancelled;
+ return mReadTask.cancel(true);
}
public void readPrinterHistory() {
@@ -376,7 +373,6 @@
Log.i(LOG_TAG, "read history started "
+ FusedPrintersProvider.this.hashCode());
}
- mReadHistoryInProgress = true;
mReadTask = new ReadTask();
mReadTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
}
@@ -534,15 +530,20 @@
mFavoritePrinters.clear();
mFavoritePrinters.addAll(computeFavoritePrinters(mHistoricalPrinters));
- mReadHistoryInProgress = false;
mReadHistoryCompleted = true;
// Deliver the printers.
updatePrinters(mDiscoverySession.getPrinters(), mFavoritePrinters);
+ // We are done.
+ mReadTask = null;
+
// Loading the available printers if needed.
loadInternal();
+ }
+ @Override
+ protected void onCancelled(List<PrinterInfo> printerInfos) {
// We are done.
mReadTask = null;
}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java
index 0d2e736..2757b81 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java
@@ -493,13 +493,13 @@
return selectedPages;
}
- public void destroy() {
- mPageContentRepository.destroy();
+ public void destroy(Runnable callback) {
mCloseGuard.close();
mState = STATE_DESTROYED;
if (DEBUG) {
Log.i(LOG_TAG, "STATE_DESTROYED");
}
+ mPageContentRepository.destroy(callback);
}
@Override
@@ -507,7 +507,7 @@
try {
if (mState != STATE_DESTROYED) {
mCloseGuard.warnIfOpen();
- destroy();
+ destroy(null);
}
} finally {
super.finalize();
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index b76a9cd..352b545 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -291,6 +291,9 @@
if (isFinishing() || (isFinalState(mState) && !mPrintedDocument.isUpdating())) {
return;
}
+ if (mPrintedDocument.isUpdating()) {
+ mPrintedDocument.cancel();
+ }
setState(STATE_PRINT_CANCELED);
doFinish();
}
@@ -558,7 +561,9 @@
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- mPrintPreviewController.onOrientationChanged();
+ if (mPrintPreviewController != null) {
+ mPrintPreviewController.onOrientationChanged();
+ }
}
@Override
@@ -1630,9 +1635,15 @@
mSpoolerProvider.destroy();
mPrintedDocument.finish();
mPrintedDocument.destroy();
- mPrintPreviewController.destroy();
+ mPrintPreviewController.destroy(new Runnable() {
+ @Override
+ public void run() {
+ finish();
+ }
+ });
+ } else {
+ finish();
}
- finish();
}
private final class SpinnerItem<T> {
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintPreviewController.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintPreviewController.java
index 15342ae..8716fd2 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintPreviewController.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintPreviewController.java
@@ -192,10 +192,10 @@
});
}
- public void destroy() {
+ public void destroy(Runnable callback) {
mHandler.cancelQueuedOperations();
mRecyclerView.setAdapter(null);
- mPageAdapter.destroy();
+ mPageAdapter.destroy(callback);
}
@Override
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 4f54bb3..0d56e47 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -301,7 +301,7 @@
<string name="interruption_level_none" msgid="3831278883136066646">"Ingen"</string>
<string name="interruption_level_priority" msgid="6517366750688942030">"Prioritet"</string>
<string name="interruption_level_all" msgid="1330581184930945764">"Alle"</string>
- <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Opladning (fuldt opladet om <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
+ <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Oplader (fuldt opladet om <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
<string name="accessibility_multi_user_switch_switcher" msgid="7305948938141024937">"Skift bruger"</string>
<string name="accessibility_multi_user_switch_switcher_with_current" msgid="8434880595284601601">"Skift bruger. Nuværende bruger er <xliff:g id="CURRENT_USER_NAME">%s</xliff:g>"</string>
<string name="accessibility_multi_user_switch_quick_contact" msgid="3020367729287990475">"Vis profil"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 611d596..ff75cbb 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -32,9 +32,9 @@
<string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Hakuna arifa"</string>
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Inaendelea"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Arifa"</string>
- <string name="battery_low_title" msgid="6456385927409742437">"Betri imeisha"</string>
- <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> iliyosalia"</string>
- <string name="battery_low_percent_format_saver_started" msgid="6859235584035338833">"<xliff:g id="PERCENTAGE">%s</xliff:g> iliyosalia. Kiokoa betri kimewashwa."</string>
+ <string name="battery_low_title" msgid="6456385927409742437">"Betri inaisha"</string>
+ <string name="battery_low_percent_format" msgid="2900940511201380775">"Imebakisha <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
+ <string name="battery_low_percent_format_saver_started" msgid="6859235584035338833">"Imebakisha <xliff:g id="PERCENTAGE">%s</xliff:g>. Kiokoa betri kimewashwa."</string>
<string name="invalid_charger" msgid="4549105996740522523">"Chaji ya USB haihamiliwi.\n Tumia chaka iliyopeanwa."</string>
<string name="invalid_charger_title" msgid="3515740382572798460">"Kuchaji kwa kutumia USB hakutumiki."</string>
<string name="invalid_charger_text" msgid="5474997287953892710">"Tumia chaja iliyonunuliwa pamoja na kifaa pekee."</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index de69a86..8152d00 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -267,7 +267,7 @@
<string name="quick_settings_tethering_label" msgid="7153452060448575549">"網絡共享"</string>
<string name="quick_settings_hotspot_label" msgid="6046917934974004879">"熱點"</string>
<string name="quick_settings_notifications_label" msgid="4818156442169154523">"通知"</string>
- <string name="quick_settings_flashlight_label" msgid="2133093497691661546">"閃光燈"</string>
+ <string name="quick_settings_flashlight_label" msgid="2133093497691661546">"手電筒"</string>
<string name="quick_settings_cellular_detail_title" msgid="8575062783675171695">"流動數據"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"數據用量"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"剩餘資料"</string>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index b28b0c5..1eb04d0 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -645,7 +645,7 @@
<!-- QuickSettings: Cellular detail panel, remaining data title [CHAR LIMIT=NONE] -->
<string name="quick_settings_cellular_detail_remaining_data">Remaining data</string>
<!-- QuickSettings: Cellular detail panel, over limit title [CHAR LIMIT=NONE] -->
- <string name="quick_settings_cellular_detail_over_limit">Limit reached – data usage paused</string>
+ <string name="quick_settings_cellular_detail_over_limit">Over limit</string>
<!-- QuickSettings: Cellular detail panel, data used format string [CHAR LIMIT=NONE] -->
<string name="quick_settings_cellular_detail_data_used"><xliff:g id="data_used" example="2.0 GB">%s</xliff:g> used</string>
<!-- QuickSettings: Cellular detail panel, data limit format string [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index de99a82..08844f31 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -23,6 +23,9 @@
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.Configuration;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.MathUtils;
import android.view.MotionEvent;
@@ -52,6 +55,8 @@
View.OnClickListener, NotificationStackScrollLayout.OnOverscrollTopChangedListener,
KeyguardAffordanceHelper.Callback, NotificationStackScrollLayout.OnEmptySpaceClickListener {
+ private static final boolean DEBUG = false;
+
// Cap and total height of Roboto font. Needs to be adjusted when font for the big clock is
// changed.
private static final int CAP_HEIGHT = 1456;
@@ -169,6 +174,7 @@
public NotificationPanelView(Context context, AttributeSet attrs) {
super(context, attrs);
+ setWillNotDraw(!DEBUG);
}
public void setStatusBar(PhoneStatusBar bar) {
@@ -503,11 +509,11 @@
}
if (Math.abs(h) > mTouchSlop && Math.abs(h) > Math.abs(x - mInitialTouchX)
&& shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, h)) {
+ mQsTracking = true;
onQsExpansionStarted();
mInitialHeightOnTouch = mQsExpansionHeight;
mInitialTouchY = y;
mInitialTouchX = x;
- mQsTracking = true;
mIntercepting = false;
mNotificationStackScroller.removeLongPressCallback();
return true;
@@ -747,11 +753,11 @@
public void onOverscrolled(float lastTouchX, float lastTouchY, int amount) {
if (mIntercepting && shouldQuickSettingsIntercept(lastTouchX, lastTouchY,
-1 /* yDiff: Not relevant here */)) {
+ mQsTracking = true;
onQsExpansionStarted(amount);
mInitialHeightOnTouch = mQsExpansionHeight;
mInitialTouchY = mLastTouchY;
mInitialTouchX = mLastTouchX;
- mQsTracking = true;
}
}
@@ -798,6 +804,7 @@
}
mScrollView.scrollTo(0, 0);
setQsExpansion(height);
+ requestPanelHeightUpdate();
}
private void setQsExpanded(boolean expanded) {
@@ -1077,6 +1084,9 @@
R.string.accessibility_desc_quick_settings));
mLastAnnouncementWasQuickSettings = true;
}
+ if (DEBUG) {
+ invalidate();
+ }
}
private String getKeyguardOrLockScreenString() {
@@ -1239,11 +1249,9 @@
@Override
protected boolean isScrolledToBottom() {
- if (mStatusBar.getBarState() == StatusBarState.KEYGUARD) {
- return true;
- }
if (!isInSettings()) {
- return mNotificationStackScroller.isScrolledToBottom();
+ return mStatusBar.getBarState() == StatusBarState.KEYGUARD
+ || mNotificationStackScroller.isScrolledToBottom();
} else {
return mScrollView.isScrolledToBottom();
}
@@ -1260,7 +1268,7 @@
}
int maxHeight;
if (mTwoFingerQsExpand || mQsExpanded || mIsExpanding && mQsExpandedWhenExpandingStarted) {
- maxHeight = Math.max(calculatePanelHeightQsExpanded(), calculatePanelHeightShade());
+ maxHeight = calculatePanelHeightQsExpanded();
} else {
maxHeight = calculatePanelHeightShade();
}
@@ -1289,8 +1297,7 @@
// In Shade, interpolate linearly such that QS is closed whenever panel height is
// minimum QS expansion + minStackHeight
float panelHeightQsCollapsed = mNotificationStackScroller.getIntrinsicPadding()
- + mNotificationStackScroller.getMinStackHeight()
- + mNotificationStackScroller.getNotificationTopPadding();
+ + mNotificationStackScroller.getMinStackHeight();
float panelHeightQsExpanded = calculatePanelHeightQsExpanded();
t = (expandedHeight - panelHeightQsCollapsed)
/ (panelHeightQsExpanded - panelHeightQsCollapsed);
@@ -1302,6 +1309,9 @@
updateHeader();
updateUnlockIcon();
updateNotificationTranslucency();
+ if (DEBUG) {
+ invalidate();
+ }
}
/**
@@ -1340,7 +1350,9 @@
}
float totalHeight = Math.max(
mQsMaxExpansionHeight + mNotificationStackScroller.getNotificationTopPadding(),
- mClockPositionResult.stackScrollerPadding - mTopPaddingAdjustment)
+ mStatusBarState == StatusBarState.KEYGUARD
+ ? mClockPositionResult.stackScrollerPadding - mTopPaddingAdjustment
+ : 0)
+ notificationHeight;
if (totalHeight > mNotificationStackScroller.getHeight()) {
float fullyCollapsedHeight = mQsMaxExpansionHeight
@@ -1353,7 +1365,7 @@
}
private int getScrollViewScrollY() {
- if (mScrollYOverride != -1) {
+ if (mScrollYOverride != -1 && !mQsTracking) {
return mScrollYOverride;
} else {
return mScrollView.getScrollY();
@@ -1874,4 +1886,30 @@
public void onEmptySpaceClicked(float x, float y) {
onEmptySpaceClick(x);
}
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ super.dispatchDraw(canvas);
+ if (DEBUG) {
+ Paint p = new Paint();
+ p.setColor(Color.RED);
+ p.setStrokeWidth(2);
+ p.setStyle(Paint.Style.STROKE);
+ canvas.drawLine(0, getMaxPanelHeight(), getWidth(), getMaxPanelHeight(), p);
+ p.setColor(Color.BLUE);
+ canvas.drawLine(0, getExpandedHeight(), getWidth(), getExpandedHeight(), p);
+ p.setColor(Color.GREEN);
+ canvas.drawLine(0, calculatePanelHeightQsExpanded(), getWidth(),
+ calculatePanelHeightQsExpanded(), p);
+ p.setColor(Color.YELLOW);
+ canvas.drawLine(0, calculatePanelHeightShade(), getWidth(),
+ calculatePanelHeightShade(), p);
+ p.setColor(Color.MAGENTA);
+ canvas.drawLine(0, calculateQsTopPadding(), getWidth(),
+ calculateQsTopPadding(), p);
+ p.setColor(Color.CYAN);
+ canvas.drawLine(0, mNotificationStackScroller.getTopPadding(), getWidth(),
+ mNotificationStackScroller.getTopPadding(), p);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index f0c599d..47ec08d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -168,6 +168,7 @@
if (mNotificationPanel.isFullyExpanded()
&& mStackScrollLayout.getVisibility() == View.VISIBLE
&& mService.getBarState() == StatusBarState.KEYGUARD
+ && !mService.isQsExpanded()
&& !mService.isBouncerShowing()) {
intercept = mDragDownHelper.onInterceptTouchEvent(ev);
// wake up on a touch down event, if dozing
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index c5f1161..16ff3dc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -464,7 +464,8 @@
int newStackHeight = (int) height;
int minStackHeight = getMinStackHeight();
int stackHeight;
- if (newStackHeight - mTopPadding >= minStackHeight || getNotGoneChildCount() == 0) {
+ if (newStackHeight - mTopPadding - mTopPaddingOverflow >= minStackHeight
+ || getNotGoneChildCount() == 0) {
setTranslationY(mTopPaddingOverflow);
stackHeight = newStackHeight;
} else {
@@ -474,7 +475,8 @@
int translationY = (newStackHeight - minStackHeight);
// A slight parallax effect is introduced in order for the stack to catch up with
// the top card.
- float partiallyThere = (float) (newStackHeight - mTopPadding) / minStackHeight;
+ float partiallyThere = (newStackHeight - mTopPadding - mTopPaddingOverflow)
+ / minStackHeight;
partiallyThere = Math.max(0, partiallyThere);
translationY += (1 - partiallyThere) * (mBottomStackPeekSize +
mCollapseSecondCardPadding);
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 2d79425..8b3739d 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -2776,7 +2776,7 @@
}
/**
- * Prepare for a VPN application. This method is used by system-privileged apps.
+ * Prepare for a VPN application.
* Permissions are checked in Vpn class.
* @hide
*/
@@ -3541,6 +3541,7 @@
private final HashMap<Messenger, NetworkAgentInfo> mNetworkAgentInfos =
new HashMap<Messenger, NetworkAgentInfo>();
+ // Note: if mDefaultRequest is changed, NetworkMonitor needs to be updated.
private final NetworkRequest mDefaultRequest;
private boolean isDefaultNetwork(NetworkAgentInfo nai) {
@@ -3552,10 +3553,12 @@
int currentScore, NetworkMisc networkMisc) {
enforceConnectivityInternalPermission();
+ // TODO: Instead of passing mDefaultRequest, provide an API to determine whether a Network
+ // satisfies mDefaultRequest.
NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(),
new NetworkInfo(networkInfo), new LinkProperties(linkProperties),
new NetworkCapabilities(networkCapabilities), currentScore, mContext, mTrackerHandler,
- new NetworkMisc(networkMisc));
+ new NetworkMisc(networkMisc), mDefaultRequest);
synchronized (this) {
nai.networkMonitor.systemReady = mSystemReady;
}
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index edea274..4d0868a 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -66,6 +66,7 @@
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.database.ContentObserver;
+import android.graphics.drawable.Drawable;
import android.inputmethodservice.InputMethodService;
import android.net.Uri;
import android.os.Binder;
@@ -2830,23 +2831,30 @@
}
}
}
- final Context themedContext = new ContextThemeWrapper(context,
- android.R.style.Theme_DeviceDefault_Settings);
- mDialogBuilder = new AlertDialog.Builder(themedContext);
- final TypedArray a = themedContext.obtainStyledAttributes(null,
- com.android.internal.R.styleable.DialogPreference,
- com.android.internal.R.attr.alertDialogStyle, 0);
- mDialogBuilder.setIcon(a.getDrawable(
- com.android.internal.R.styleable.DialogPreference_dialogIcon));
- a.recycle();
+
+ final Context settingsContext = new ContextThemeWrapper(context,
+ com.android.internal.R.style.Theme_DeviceDefault_Settings);
+
+ mDialogBuilder = new AlertDialog.Builder(settingsContext);
mDialogBuilder.setOnCancelListener(new OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
hideInputMethodMenu();
}
});
- final LayoutInflater inflater =
- (LayoutInflater)themedContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+ final Context dialogContext = mDialogBuilder.getContext();
+ final TypedArray a = dialogContext.obtainStyledAttributes(null,
+ com.android.internal.R.styleable.DialogPreference,
+ com.android.internal.R.attr.alertDialogStyle, 0);
+ final Drawable dialogIcon = a.getDrawable(
+ com.android.internal.R.styleable.DialogPreference_dialogIcon);
+ a.recycle();
+
+ mDialogBuilder.setIcon(dialogIcon);
+
+ final LayoutInflater inflater = (LayoutInflater) dialogContext.getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
final View tv = inflater.inflate(
com.android.internal.R.layout.input_method_switch_dialog_title, null);
mDialogBuilder.setCustomTitle(tv);
@@ -2857,7 +2865,7 @@
.findViewById(com.android.internal.R.id.hard_keyboard_section)
.setVisibility(mWindowManagerService.isHardKeyboardAvailable()
? View.VISIBLE : View.GONE);
- final Switch hardKeySwitch = (Switch)mSwitchingDialogTitleView.findViewById(
+ final Switch hardKeySwitch = (Switch) mSwitchingDialogTitleView.findViewById(
com.android.internal.R.id.hard_keyboard_switch);
hardKeySwitch.setChecked(mShowImeWithHardKeyboard);
hardKeySwitch.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@@ -2870,7 +2878,7 @@
}
});
- final ImeSubtypeListAdapter adapter = new ImeSubtypeListAdapter(themedContext,
+ final ImeSubtypeListAdapter adapter = new ImeSubtypeListAdapter(dialogContext,
com.android.internal.R.layout.input_method_switch_item, imList, checkedItem);
final OnClickListener choiceListener = new OnClickListener() {
@Override
@@ -2926,10 +2934,11 @@
public ImeSubtypeListAdapter(Context context, int textViewResourceId,
List<ImeSubtypeListItem> itemsList, int checkedItem) {
super(context, textViewResourceId, itemsList);
+
mTextViewResourceId = textViewResourceId;
mItemsList = itemsList;
mCheckedItem = checkedItem;
- mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
@Override
diff --git a/services/core/java/com/android/server/IntentResolver.java b/services/core/java/com/android/server/IntentResolver.java
index 387eabc..cea1ebe 100644
--- a/services/core/java/com/android/server/IntentResolver.java
+++ b/services/core/java/com/android/server/IntentResolver.java
@@ -23,7 +23,6 @@
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
-import java.util.Map;
import java.util.Set;
import android.net.Uri;
@@ -31,6 +30,7 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
+import android.util.MutableInt;
import android.util.PrintWriterPrinter;
import android.util.Slog;
import android.util.LogPrinter;
@@ -213,36 +213,65 @@
}
boolean dumpMap(PrintWriter out, String titlePrefix, String title,
- String prefix, Map<String, F[]> map, String packageName,
- boolean printFilter) {
- String eprefix = prefix + " ";
- String fprefix = prefix + " ";
+ String prefix, ArrayMap<String, F[]> map, String packageName,
+ boolean printFilter, boolean collapseDuplicates) {
+ final String eprefix = prefix + " ";
+ final String fprefix = prefix + " ";
+ final ArrayMap<Object, MutableInt> found = new ArrayMap<>();
boolean printedSomething = false;
Printer printer = null;
- for (Map.Entry<String, F[]> e : map.entrySet()) {
- F[] a = e.getValue();
+ for (int mapi=0; mapi<map.size(); mapi++) {
+ F[] a = map.valueAt(mapi);
final int N = a.length;
boolean printedHeader = false;
F filter;
- for (int i=0; i<N && (filter=a[i]) != null; i++) {
- if (packageName != null && !isPackageForFilter(packageName, filter)) {
- continue;
- }
- if (title != null) {
- out.print(titlePrefix); out.println(title);
- title = null;
- }
- if (!printedHeader) {
- out.print(eprefix); out.print(e.getKey()); out.println(":");
- printedHeader = true;
- }
- printedSomething = true;
- dumpFilter(out, fprefix, filter);
- if (printFilter) {
- if (printer == null) {
- printer = new PrintWriterPrinter(out);
+ if (collapseDuplicates) {
+ found.clear();
+ for (int i=0; i<N && (filter=a[i]) != null; i++) {
+ if (packageName != null && !isPackageForFilter(packageName, filter)) {
+ continue;
}
- filter.dump(printer, fprefix + " ");
+ Object label = filterToLabel(filter);
+ int index = found.indexOfKey(label);
+ if (index < 0) {
+ found.put(label, new MutableInt(1));
+ } else {
+ found.valueAt(index).value++;
+ }
+ }
+ for (int i=0; i<found.size(); i++) {
+ if (title != null) {
+ out.print(titlePrefix); out.println(title);
+ title = null;
+ }
+ if (!printedHeader) {
+ out.print(eprefix); out.print(map.keyAt(mapi)); out.println(":");
+ printedHeader = true;
+ }
+ printedSomething = true;
+ dumpFilterLabel(out, fprefix, found.keyAt(i), found.valueAt(i).value);
+ }
+ } else {
+ for (int i=0; i<N && (filter=a[i]) != null; i++) {
+ if (packageName != null && !isPackageForFilter(packageName, filter)) {
+ continue;
+ }
+ if (title != null) {
+ out.print(titlePrefix); out.println(title);
+ title = null;
+ }
+ if (!printedHeader) {
+ out.print(eprefix); out.print(map.keyAt(mapi)); out.println(":");
+ printedHeader = true;
+ }
+ printedSomething = true;
+ dumpFilter(out, fprefix, filter);
+ if (printFilter) {
+ if (printer == null) {
+ printer = new PrintWriterPrinter(out);
+ }
+ filter.dump(printer, fprefix + " ");
+ }
}
}
}
@@ -250,32 +279,32 @@
}
public boolean dump(PrintWriter out, String title, String prefix, String packageName,
- boolean printFilter) {
+ boolean printFilter, boolean collapseDuplicates) {
String innerPrefix = prefix + " ";
String sepPrefix = "\n" + prefix;
String curPrefix = title + "\n" + prefix;
if (dumpMap(out, curPrefix, "Full MIME Types:", innerPrefix,
- mTypeToFilter, packageName, printFilter)) {
+ mTypeToFilter, packageName, printFilter, collapseDuplicates)) {
curPrefix = sepPrefix;
}
if (dumpMap(out, curPrefix, "Base MIME Types:", innerPrefix,
- mBaseTypeToFilter, packageName, printFilter)) {
+ mBaseTypeToFilter, packageName, printFilter, collapseDuplicates)) {
curPrefix = sepPrefix;
}
if (dumpMap(out, curPrefix, "Wild MIME Types:", innerPrefix,
- mWildTypeToFilter, packageName, printFilter)) {
+ mWildTypeToFilter, packageName, printFilter, collapseDuplicates)) {
curPrefix = sepPrefix;
}
if (dumpMap(out, curPrefix, "Schemes:", innerPrefix,
- mSchemeToFilter, packageName, printFilter)) {
+ mSchemeToFilter, packageName, printFilter, collapseDuplicates)) {
curPrefix = sepPrefix;
}
if (dumpMap(out, curPrefix, "Non-Data Actions:", innerPrefix,
- mActionToFilter, packageName, printFilter)) {
+ mActionToFilter, packageName, printFilter, collapseDuplicates)) {
curPrefix = sepPrefix;
}
if (dumpMap(out, curPrefix, "MIME Typed Actions:", innerPrefix,
- mTypedActionToFilter, packageName, printFilter)) {
+ mTypedActionToFilter, packageName, printFilter, collapseDuplicates)) {
curPrefix = sepPrefix;
}
return curPrefix == sepPrefix;
@@ -479,6 +508,14 @@
out.print(prefix); out.println(filter);
}
+ protected Object filterToLabel(F filter) {
+ return "IntentFilter";
+ }
+
+ protected void dumpFilterLabel(PrintWriter out, String prefix, Object label, int count) {
+ out.print(prefix); out.print(label); out.print(": "); out.println(count);
+ }
+
private final void addFilter(ArrayMap<String, F[]> map, String name, F filter) {
F[] array = map.get(name);
if (array == null) {
diff --git a/services/core/java/com/android/server/MmsServiceBroker.java b/services/core/java/com/android/server/MmsServiceBroker.java
index da50751..b1c4955 100644
--- a/services/core/java/com/android/server/MmsServiceBroker.java
+++ b/services/core/java/com/android/server/MmsServiceBroker.java
@@ -20,6 +20,7 @@
import android.app.AppOpsManager;
import android.app.PendingIntent;
import android.content.ComponentName;
+import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
@@ -33,11 +34,15 @@
import android.os.Message;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.os.UserHandle;
+import android.provider.Telephony;
import android.telephony.TelephonyManager;
import android.util.Slog;
import com.android.internal.telephony.IMms;
+import java.util.List;
+
/**
* This class is a proxy for MmsService APIs. We need this because MmsService runs
* in phone process and may crash anytime. This manages a connection to the actual
@@ -226,6 +231,8 @@
// Service API calls implementation, proxied to the real MmsService in "com.android.mms.service"
private final class BinderService extends IMms.Stub {
+ private static final String PHONE_PACKAGE_NAME = "com.android.phone";
+
@Override
public void sendMessage(int subId, String callingPkg, Uri contentUri,
String locationUrl, Bundle configOverrides, PendingIntent sentIntent)
@@ -236,6 +243,9 @@
callingPkg) != AppOpsManager.MODE_ALLOWED) {
return;
}
+ contentUri = adjustUriForUserAndGrantPermission(contentUri,
+ Telephony.Mms.Intents.MMS_SEND_ACTION,
+ Intent.FLAG_GRANT_READ_URI_PERMISSION);
getServiceGuarded().sendMessage(subId, callingPkg, contentUri, locationUrl,
configOverrides, sentIntent);
}
@@ -251,6 +261,10 @@
callingPkg) != AppOpsManager.MODE_ALLOWED) {
return;
}
+ contentUri = adjustUriForUserAndGrantPermission(contentUri,
+ Telephony.Mms.Intents.MMS_DOWNLOAD_ACTION,
+ Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+
getServiceGuarded().downloadMessage(subId, callingPkg, locationUrl, contentUri,
configOverrides, downloadedIntent);
}
@@ -397,5 +411,40 @@
public boolean getAutoPersisting() throws RemoteException {
return getServiceGuarded().getAutoPersisting();
}
+
+ /**
+ * Modifies the Uri to contain the caller's userId, if necessary.
+ * Grants the phone package on primary user permission to access the contentUri,
+ * even if the caller is not in the primary user.
+ *
+ * @param contentUri The Uri to adjust
+ * @param action The intent action used to find the associated carrier app
+ * @param permission The permission to add
+ * @return The adjusted Uri containing the calling userId.
+ */
+ private Uri adjustUriForUserAndGrantPermission(Uri contentUri, String action,
+ int permission) {
+ final int callingUserId = UserHandle.getCallingUserId();
+ if (callingUserId != UserHandle.USER_OWNER) {
+ contentUri = ContentProvider.maybeAddUserId(contentUri, callingUserId);
+ }
+ long token = Binder.clearCallingIdentity();
+ try {
+ mContext.grantUriPermission(PHONE_PACKAGE_NAME, contentUri, permission);
+
+ // Grant permission for the carrier app.
+ Intent intent = new Intent(action);
+ TelephonyManager telephonyManager =
+ (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ List<String> carrierPackages = telephonyManager.getCarrierPackageNamesForIntent(
+ intent);
+ if (carrierPackages != null && carrierPackages.size() == 1) {
+ mContext.grantUriPermission(carrierPackages.get(0), contentUri, permission);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ return contentUri;
+ }
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 779e132..7e17043 100755
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -13232,7 +13232,7 @@
if (mReceiverResolver.dump(pw, needSep ?
"\n Receiver Resolver Table:" : " Receiver Resolver Table:",
- " ", dumpPackage, false)) {
+ " ", dumpPackage, false, false)) {
needSep = true;
printedAnything = true;
}
diff --git a/services/core/java/com/android/server/am/ProcessStatsService.java b/services/core/java/com/android/server/am/ProcessStatsService.java
index bffb541..d05910b 100644
--- a/services/core/java/com/android/server/am/ProcessStatsService.java
+++ b/services/core/java/com/android/server/am/ProcessStatsService.java
@@ -582,9 +582,10 @@
pw.println("Process stats (procstats) dump options:");
pw.println(" [--checkin|-c|--csv] [--csv-screen] [--csv-proc] [--csv-mem]");
pw.println(" [--details] [--full-details] [--current] [--hours N] [--last N]");
- pw.println(" [--active] [--commit] [--reset] [--clear] [--write] [-h] [<package.name>]");
+ pw.println(" [--max N] --active] [--commit] [--reset] [--clear] [--write] [-h]");
+ pw.println(" [<package.name>]");
pw.println(" --checkin: perform a checkin: print and delete old committed states.");
- pw.println(" --c: print only state in checkin format.");
+ pw.println(" -c: print only state in checkin format.");
pw.println(" --csv: output data suitable for putting in a spreadsheet.");
pw.println(" --csv-screen: on, off.");
pw.println(" --csv-mem: norm, mod, low, crit.");
@@ -595,6 +596,7 @@
pw.println(" --current: only dump current state.");
pw.println(" --hours: aggregate over about N last hours.");
pw.println(" --last: only show the last committed stats at index N (starting at 1).");
+ pw.println(" --max: for -a, max num of historical batches to print.");
pw.println(" --active: only show currently active processes/services.");
pw.println(" --commit: commit current stats to disk and reset to start new stats.");
pw.println(" --reset: reset current stats, without committing.");
@@ -636,6 +638,7 @@
boolean dumpAll = false;
int aggregateHours = 0;
int lastIndex = 0;
+ int maxNum = 2;
boolean activeOnly = false;
String reqPackage = null;
boolean csvSepScreenStats = false;
@@ -734,6 +737,20 @@
dumpHelp(pw);
return;
}
+ } else if ("--max".equals(arg)) {
+ i++;
+ if (i >= args.length) {
+ pw.println("Error: argument required for --max");
+ dumpHelp(pw);
+ return;
+ }
+ try {
+ maxNum = Integer.parseInt(args[i]);
+ } catch (NumberFormatException e) {
+ pw.println("Error: --max argument not an int -- " + args[i]);
+ dumpHelp(pw);
+ return;
+ }
} else if ("--active".equals(arg)) {
activeOnly = true;
currentOnly = true;
@@ -892,7 +909,11 @@
try {
ArrayList<String> files = getCommittedFiles(0, false, !isCheckin);
if (files != null) {
- for (int i=0; i<files.size(); i++) {
+ int start = isCheckin ? 0 : (files.size() - maxNum);
+ if (start < 0) {
+ start = 0;
+ }
+ for (int i=start; i<files.size(); i++) {
if (DEBUG) Slog.d(TAG, "Retrieving state: " + files.get(i));
try {
AtomicFile file = new AtomicFile(new File(files.get(i)));
@@ -947,19 +968,6 @@
}
}
if (!isCheckin) {
- if (!currentOnly) {
- if (sepNeeded) {
- pw.println();
- }
- pw.println("AGGREGATED OVER LAST 24 HOURS:");
- dumpAggregatedStats(pw, 24, now, reqPackage, isCompact,
- dumpDetails, dumpFullDetails, dumpAll, activeOnly);
- pw.println();
- pw.println("AGGREGATED OVER LAST 3 HOURS:");
- dumpAggregatedStats(pw, 3, now, reqPackage, isCompact,
- dumpDetails, dumpFullDetails, dumpAll, activeOnly);
- sepNeeded = true;
- }
synchronized (mAm) {
if (isCompact) {
mProcessStats.dumpCheckinLocked(pw, reqPackage);
@@ -977,8 +985,21 @@
} else {
mProcessStats.dumpSummaryLocked(pw, reqPackage, now, activeOnly);
}
+ sepNeeded = true;
}
}
+ if (!currentOnly) {
+ if (sepNeeded) {
+ pw.println();
+ }
+ pw.println("AGGREGATED OVER LAST 24 HOURS:");
+ dumpAggregatedStats(pw, 24, now, reqPackage, isCompact,
+ dumpDetails, dumpFullDetails, dumpAll, activeOnly);
+ pw.println();
+ pw.println("AGGREGATED OVER LAST 3 HOURS:");
+ dumpAggregatedStats(pw, 3, now, reqPackage, isCompact,
+ dumpDetails, dumpFullDetails, dumpAll, activeOnly);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 4cf2a4a..12da5c3 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -68,7 +68,7 @@
public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, NetworkInfo info,
LinkProperties lp, NetworkCapabilities nc, int score, Context context, Handler handler,
- NetworkMisc misc) {
+ NetworkMisc misc, NetworkRequest defaultRequest) {
this.messenger = messenger;
asyncChannel = ac;
network = null;
@@ -76,7 +76,7 @@
linkProperties = lp;
networkCapabilities = nc;
currentScore = score;
- networkMonitor = new NetworkMonitor(context, handler, this);
+ networkMonitor = new NetworkMonitor(context, handler, this, defaultRequest);
networkMisc = misc;
created = false;
validated = false;
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index 593a28a..78f3705 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -27,6 +27,7 @@
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
+import android.net.NetworkRequest;
import android.net.TrafficStats;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
@@ -222,6 +223,7 @@
private final TelephonyManager mTelephonyManager;
private final WifiManager mWifiManager;
private final AlarmManager mAlarmManager;
+ private final NetworkRequest mDefaultRequest;
private String mServer;
private boolean mIsCaptivePortalCheckEnabled = false;
@@ -239,7 +241,8 @@
private State mCaptivePortalState = new CaptivePortalState();
private State mLingeringState = new LingeringState();
- public NetworkMonitor(Context context, Handler handler, NetworkAgentInfo networkAgentInfo) {
+ public NetworkMonitor(Context context, Handler handler, NetworkAgentInfo networkAgentInfo,
+ NetworkRequest defaultRequest) {
// Add suffix indicating which NetworkMonitor we're talking about.
super(TAG + networkAgentInfo.name());
@@ -249,6 +252,7 @@
mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+ mDefaultRequest = defaultRequest;
addState(mDefaultState);
addState(mOfflineState, mDefaultState);
@@ -369,14 +373,25 @@
case CMD_REEVALUATE:
if (message.arg1 != mReevaluateToken)
return HANDLED;
- if (mNetworkAgentInfo.isVPN()) {
- transitionTo(mValidatedState);
- return HANDLED;
- }
- // If network provides no internet connectivity adjust evaluation.
- if (!mNetworkAgentInfo.networkCapabilities.hasCapability(
- NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
- // TODO: Try to verify something works. Do all gateways respond to pings?
+ // Don't bother validating networks that don't satisify the default request.
+ // This includes:
+ // - VPNs which can be considered explicitly desired by the user and the
+ // user's desire trumps whether the network validates.
+ // - Networks that don't provide internet access. It's unclear how to
+ // validate such networks.
+ // - Untrusted networks. It's unsafe to prompt the user to sign-in to
+ // such networks and the user didn't express interest in connecting to
+ // such networks (an app did) so the user may be unhappily surprised when
+ // asked to sign-in to a network they didn't want to connect to in the
+ // first place. Validation could be done to adjust the network scores
+ // however these networks are app-requested and may not be intended for
+ // general usage, in which case general validation may not be an accurate
+ // measure of the network's quality. Only the app knows how to evaluate
+ // the network so don't bother validating here. Furthermore sending HTTP
+ // packets over the network may be undesirable, for example an extremely
+ // expensive metered network, or unwanted leaking of the User Agent string.
+ if (!mDefaultRequest.networkCapabilities.satisfiedByNetworkCapabilities(
+ mNetworkAgentInfo.networkCapabilities)) {
transitionTo(mValidatedState);
return HANDLED;
}
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 6da186f..f08a652 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -216,20 +216,11 @@
* @return true if the operation is succeeded.
*/
public synchronized boolean prepare(String oldPackage, String newPackage) {
- // Return false if the package does not match.
if (oldPackage != null && getAppUid(oldPackage, mUserHandle) != mOwnerUID) {
- // The package doesn't match. If this VPN was not previously authorized, return false
- // to force user authorization. Otherwise, revoke the VPN anyway.
+ // The package doesn't match. We return false (to obtain user consent) unless the user
+ // has already consented to that VPN package.
if (!oldPackage.equals(VpnConfig.LEGACY_VPN) && isVpnUserPreConsented(oldPackage)) {
- long token = Binder.clearCallingIdentity();
- try {
- // This looks bizarre, but it is what ConfirmDialog in VpnDialogs is doing when
- // the user clicks through to allow the VPN to consent. So we are emulating the
- // action of the dialog without actually showing it.
- prepare(null, oldPackage);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ prepareInternal(oldPackage);
return true;
}
return false;
@@ -244,54 +235,58 @@
// Check if the caller is authorized.
enforceControlPermission();
- // Reset the interface.
- if (mInterface != null) {
- mStatusIntent = null;
- agentDisconnect();
- jniReset(mInterface);
- mInterface = null;
- mVpnUsers = null;
- }
+ prepareInternal(newPackage);
+ return true;
+ }
- // Revoke the connection or stop LegacyVpnRunner.
- if (mConnection != null) {
- try {
- mConnection.mService.transact(IBinder.LAST_CALL_TRANSACTION,
- Parcel.obtain(), null, IBinder.FLAG_ONEWAY);
- } catch (Exception e) {
- // ignore
- }
- mContext.unbindService(mConnection);
- mConnection = null;
- } else if (mLegacyVpnRunner != null) {
- mLegacyVpnRunner.exit();
- mLegacyVpnRunner = null;
- }
-
+ /** Prepare the VPN for the given package. Does not perform permission checks. */
+ private void prepareInternal(String newPackage) {
long token = Binder.clearCallingIdentity();
try {
- mNetd.denyProtect(mOwnerUID);
- } catch (Exception e) {
- Log.wtf(TAG, "Failed to disallow UID " + mOwnerUID + " to call protect() " + e);
+ // Reset the interface.
+ if (mInterface != null) {
+ mStatusIntent = null;
+ agentDisconnect();
+ jniReset(mInterface);
+ mInterface = null;
+ mVpnUsers = null;
+ }
+
+ // Revoke the connection or stop LegacyVpnRunner.
+ if (mConnection != null) {
+ try {
+ mConnection.mService.transact(IBinder.LAST_CALL_TRANSACTION,
+ Parcel.obtain(), null, IBinder.FLAG_ONEWAY);
+ } catch (Exception e) {
+ // ignore
+ }
+ mContext.unbindService(mConnection);
+ mConnection = null;
+ } else if (mLegacyVpnRunner != null) {
+ mLegacyVpnRunner.exit();
+ mLegacyVpnRunner = null;
+ }
+
+ try {
+ mNetd.denyProtect(mOwnerUID);
+ } catch (Exception e) {
+ Log.wtf(TAG, "Failed to disallow UID " + mOwnerUID + " to call protect() " + e);
+ }
+
+ Log.i(TAG, "Switched from " + mPackage + " to " + newPackage);
+ mPackage = newPackage;
+ mOwnerUID = getAppUid(newPackage, mUserHandle);
+ try {
+ mNetd.allowProtect(mOwnerUID);
+ } catch (Exception e) {
+ Log.wtf(TAG, "Failed to allow UID " + mOwnerUID + " to call protect() " + e);
+ }
+ mConfig = null;
+
+ updateState(DetailedState.IDLE, "prepare");
} finally {
Binder.restoreCallingIdentity(token);
}
-
- Log.i(TAG, "Switched from " + mPackage + " to " + newPackage);
- mPackage = newPackage;
- mOwnerUID = getAppUid(newPackage, mUserHandle);
- token = Binder.clearCallingIdentity();
- try {
- mNetd.allowProtect(mOwnerUID);
- } catch (Exception e) {
- Log.wtf(TAG, "Failed to allow UID " + mOwnerUID + " to call protect() " + e);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- mConfig = null;
-
- updateState(DetailedState.IDLE, "prepare");
- return true;
}
/**
diff --git a/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java b/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java
index 9593a9c..59d5605 100644
--- a/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java
+++ b/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java
@@ -89,11 +89,8 @@
tv.updateActiveSource(current);
invokeCallback(HdmiControlManager.RESULT_SUCCESS);
} else {
- HdmiCecMessage routingChange = HdmiCecMessageBuilder.buildRoutingChange(
- getSourceAddress(), newActive.physicalAddress, current.physicalAddress);
- mService.sendCecCommand(routingChange);
- tv.addAndStartAction(
- new RoutingControlAction(tv, current.physicalAddress, true, mCallback));
+ tv.startRoutingControl(newActive.physicalAddress, current.physicalAddress, true,
+ mCallback);
}
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index ec38124..3f78949 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -362,11 +362,22 @@
return;
}
int newPath = mService.portIdToPath(portId);
+ startRoutingControl(oldPath, newPath, true, callback);
+ }
+
+ @ServiceThreadOnly
+ void startRoutingControl(int oldPath, int newPath, boolean queryDevicePowerStatus,
+ IHdmiControlCallback callback) {
+ assertRunOnServiceThread();
+ if (oldPath == newPath) {
+ return;
+ }
HdmiCecMessage routingChange =
HdmiCecMessageBuilder.buildRoutingChange(mAddress, oldPath, newPath);
mService.sendCecCommand(routingChange);
removeAction(RoutingControlAction.class);
- addAndStartAction(new RoutingControlAction(this, newPath, true, callback));
+ addAndStartAction(
+ new RoutingControlAction(this, newPath, queryDevicePowerStatus, callback));
}
@ServiceThreadOnly
@@ -587,12 +598,9 @@
private boolean handleNewDeviceAtTheTailOfActivePath(int path) {
// Seq #22
if (isTailOfActivePath(path, getActivePath())) {
- removeAction(RoutingControlAction.class);
int newPath = mService.portIdToPath(getActivePortId());
setActivePath(newPath);
- mService.sendCecCommand(HdmiCecMessageBuilder.buildRoutingChange(
- mAddress, getActivePath(), newPath));
- addAndStartAction(new RoutingControlAction(this, newPath, false, null));
+ startRoutingControl(getActivePath(), newPath, false, null);
return true;
}
return false;
@@ -1346,12 +1354,8 @@
assertRunOnServiceThread();
// Seq #23
if (isTailOfActivePath(path, getActivePath())) {
- removeAction(RoutingControlAction.class);
int newPath = mService.portIdToPath(getActivePortId());
- mService.sendCecCommand(HdmiCecMessageBuilder.buildRoutingChange(
- mAddress, getActivePath(), newPath));
- mActiveSource.invalidate();
- addAndStartAction(new RoutingControlAction(this, getActivePath(), true, null));
+ startRoutingControl(getActivePath(), newPath, true, null);
}
}
@@ -1367,13 +1371,9 @@
// Seq #24
if (getActivePortId() != Constants.INVALID_PORT_ID) {
if (!routingForBootup && !isProhibitMode()) {
- removeAction(RoutingControlAction.class);
int newPath = mService.portIdToPath(getActivePortId());
setActivePath(newPath);
- mService.sendCecCommand(HdmiCecMessageBuilder.buildRoutingChange(mAddress,
- getActivePath(), newPath));
- addAndStartAction(new RoutingControlAction(this, getActivePortId(),
- routingForBootup, null));
+ startRoutingControl(getActivePath(), newPath, routingForBootup, null);
}
} else {
int activePath = mService.getPhysicalAddress();
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 6958ff8..d60c57f 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1869,8 +1869,8 @@
final Notification notification = record.sbn.getNotification();
// Should this notification make noise, vibe, or use the LED?
- final boolean canInterrupt = (record.score >= SCORE_INTERRUPTION_THRESHOLD) &&
- !record.isIntercepted();
+ final boolean aboveThreshold = record.score >= SCORE_INTERRUPTION_THRESHOLD;
+ final boolean canInterrupt = aboveThreshold && !record.isIntercepted();
if (DBG || record.isIntercepted())
Slog.v(TAG,
"pkg=" + record.sbn.getPackageName() + " canInterrupt=" + canInterrupt +
@@ -2009,7 +2009,7 @@
if (mLedNotification != null && record.getKey().equals(mLedNotification.getKey())) {
mLedNotification = null;
}
- if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 && canInterrupt) {
+ if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 && aboveThreshold) {
mLights.add(record.getKey());
updateLightsLocked();
if (mUseAttentionLight) {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index fb0e357..cc1b3ad 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -497,12 +497,15 @@
// haven't been overridden.
if (params.mode == SessionParams.MODE_INHERIT_EXISTING) {
try {
- if (stageCid != null) {
+ final List<File> fromFiles = mResolvedInheritedFiles;
+ final File toDir = resolveStageDir();
+
+ if (isLinkPossible(fromFiles, toDir)) {
+ linkFiles(fromFiles, toDir);
+ } else {
// TODO: this should delegate to DCS so the system process
// avoids holding open FDs into containers.
- copyFiles(mResolvedInheritedFiles, resolveStageDir());
- } else {
- linkFiles(mResolvedInheritedFiles, resolveStageDir());
+ copyFiles(fromFiles, toDir);
}
} catch (IOException e) {
throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
@@ -721,7 +724,7 @@
// This is kind of hacky; we're creating a half-parsed package that is
// straddled between the inherited and staged APKs.
final PackageLite pkg = new PackageLite(null, baseApk, null,
- splitPaths.toArray(new String[splitPaths.size()]));
+ splitPaths.toArray(new String[splitPaths.size()]), null);
final boolean isForwardLocked =
(params.installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0;
@@ -733,6 +736,26 @@
}
}
+ /**
+ * Determine if creating hard links between source and destination is
+ * possible. That is, do they all live on the same underlying device.
+ */
+ private boolean isLinkPossible(List<File> fromFiles, File toDir) {
+ try {
+ final StructStat toStat = Os.stat(toDir.getAbsolutePath());
+ for (File fromFile : fromFiles) {
+ final StructStat fromStat = Os.stat(fromFile.getAbsolutePath());
+ if (fromStat.st_dev != toStat.st_dev) {
+ return false;
+ }
+ }
+ } catch (ErrnoException e) {
+ Slog.w(TAG, "Failed to detect if linking possible: " + e);
+ return false;
+ }
+ return true;
+ }
+
private static void linkFiles(List<File> fromFiles, File toDir) throws IOException {
for (File fromFile : fromFiles) {
final File toFile = new File(toDir, fromFile.getName());
@@ -760,7 +783,11 @@
if (!FileUtils.copyFile(fromFile, tmpFile)) {
throw new IOException("Failed to copy " + fromFile + " to " + tmpFile);
}
-
+ try {
+ Os.chmod(tmpFile.getAbsolutePath(), 0644);
+ } catch (ErrnoException e) {
+ throw new IOException("Failed to chmod " + tmpFile);
+ }
final File toFile = new File(toDir, fromFile.getName());
if (LOGD) Slog.d(TAG, "Renaming " + tmpFile + " to " + toFile);
if (!tmpFile.renameTo(toFile)) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 195a886..fa76a4d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -41,6 +41,7 @@
import static android.content.pm.PackageManager.INSTALL_FAILED_UID_CHANGED;
import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
import static android.content.pm.PackageManager.INSTALL_FAILED_USER_RESTRICTED;
+import static android.content.pm.PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
import static android.content.pm.PackageManager.INSTALL_FORWARD_LOCK;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
import static android.content.pm.PackageParser.isApkFile;
@@ -4275,7 +4276,7 @@
// version of the new path against what we have stored to determine
// what to do.
if (DEBUG_INSTALL) Slog.d(TAG, "Path changing from " + ps.codePath);
- if (pkg.mVersionCode < ps.versionCode) {
+ if (pkg.mVersionCode <= ps.versionCode) {
// The system package has been updated and the code path does not match
// Ignore entry. Skip it.
Slog.i(TAG, "Package " + ps.name + " at " + scanFile
@@ -4366,7 +4367,7 @@
* already installed version, hide it. It will be scanned later
* and re-added like an update.
*/
- if (pkg.mVersionCode < ps.versionCode) {
+ if (pkg.mVersionCode <= ps.versionCode) {
shouldHideSystemApp = true;
logCriticalInfo(Log.INFO, "Package " + ps.name + " appeared at " + scanFile
+ " but new version " + pkg.mVersionCode + " better than installed "
@@ -7379,6 +7380,23 @@
out.println(Integer.toHexString(System.identityHashCode(filter)));
}
+ @Override
+ protected Object filterToLabel(PackageParser.ActivityIntentInfo filter) {
+ return filter.activity;
+ }
+
+ protected void dumpFilterLabel(PrintWriter out, String prefix, Object label, int count) {
+ PackageParser.Activity activity = (PackageParser.Activity)label;
+ out.print(prefix); out.print(
+ Integer.toHexString(System.identityHashCode(activity)));
+ out.print(' ');
+ activity.printComponentShortName(out);
+ if (count > 1) {
+ out.print(" ("); out.print(count); out.print(" filters)");
+ }
+ out.println();
+ }
+
// List<ResolveInfo> filterEnabled(List<ResolveInfo> resolveInfoList) {
// final Iterator<ResolveInfo> i = resolveInfoList.iterator();
// final List<ResolveInfo> retList = Lists.newArrayList();
@@ -7578,6 +7596,23 @@
out.println(Integer.toHexString(System.identityHashCode(filter)));
}
+ @Override
+ protected Object filterToLabel(PackageParser.ServiceIntentInfo filter) {
+ return filter.service;
+ }
+
+ protected void dumpFilterLabel(PrintWriter out, String prefix, Object label, int count) {
+ PackageParser.Service service = (PackageParser.Service)label;
+ out.print(prefix); out.print(
+ Integer.toHexString(System.identityHashCode(service)));
+ out.print(' ');
+ service.printComponentShortName(out);
+ if (count > 1) {
+ out.print(" ("); out.print(count); out.print(" filters)");
+ }
+ out.println();
+ }
+
// List<ResolveInfo> filterEnabled(List<ResolveInfo> resolveInfoList) {
// final Iterator<ResolveInfo> i = resolveInfoList.iterator();
// final List<ResolveInfo> retList = Lists.newArrayList();
@@ -7785,6 +7820,23 @@
out.println(Integer.toHexString(System.identityHashCode(filter)));
}
+ @Override
+ protected Object filterToLabel(PackageParser.ProviderIntentInfo filter) {
+ return filter.provider;
+ }
+
+ protected void dumpFilterLabel(PrintWriter out, String prefix, Object label, int count) {
+ PackageParser.Provider provider = (PackageParser.Provider)label;
+ out.print(prefix); out.print(
+ Integer.toHexString(System.identityHashCode(provider)));
+ out.print(' ');
+ provider.printComponentShortName(out);
+ if (count > 1) {
+ out.print(" ("); out.print(count); out.print(" filters)");
+ }
+ out.println();
+ }
+
private final ArrayMap<ComponentName, PackageParser.Provider> mProviders
= new ArrayMap<ComponentName, PackageParser.Provider>();
private int mFlags;
@@ -8806,11 +8858,10 @@
if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
// Check for downgrading.
if ((installFlags & PackageManager.INSTALL_ALLOW_DOWNGRADE) == 0) {
- if (pkgLite.versionCode < pkg.mVersionCode) {
- Slog.w(TAG, "Can't install update of " + packageName
- + " update version " + pkgLite.versionCode
- + " is older than installed version "
- + pkg.mVersionCode);
+ try {
+ checkDowngrade(pkg, pkgLite);
+ } catch (PackageManagerException e) {
+ Slog.w(TAG, "Downgrade detected: " + e.getMessage());
return PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE;
}
}
@@ -12555,22 +12606,22 @@
if (!checkin && dumpState.isDumping(DumpState.DUMP_RESOLVERS)) {
if (mActivities.dump(pw, dumpState.getTitlePrinted() ? "\nActivity Resolver Table:"
: "Activity Resolver Table:", " ", packageName,
- dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS))) {
+ dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS), true)) {
dumpState.setTitlePrinted(true);
}
if (mReceivers.dump(pw, dumpState.getTitlePrinted() ? "\nReceiver Resolver Table:"
: "Receiver Resolver Table:", " ", packageName,
- dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS))) {
+ dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS), true)) {
dumpState.setTitlePrinted(true);
}
if (mServices.dump(pw, dumpState.getTitlePrinted() ? "\nService Resolver Table:"
: "Service Resolver Table:", " ", packageName,
- dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS))) {
+ dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS), true)) {
dumpState.setTitlePrinted(true);
}
if (mProviders.dump(pw, dumpState.getTitlePrinted() ? "\nProvider Resolver Table:"
: "Provider Resolver Table:", " ", packageName,
- dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS))) {
+ dumpState.isOptionEnabled(DumpState.OPTION_SHOW_FILTERS), true)) {
dumpState.setTitlePrinted(true);
}
}
@@ -12583,7 +12634,7 @@
dumpState.getTitlePrinted()
? "\nPreferred Activities User " + user + ":"
: "Preferred Activities User " + user + ":", " ",
- packageName, true)) {
+ packageName, true, false)) {
dumpState.setTitlePrinted(true);
}
}
@@ -12676,8 +12727,8 @@
mSettings.dumpPackagesLPr(pw, packageName, dumpState, checkin);
}
- if (!checkin && dumpState.isDumping(DumpState.DUMP_SHARED_USERS)) {
- mSettings.dumpSharedUsersLPr(pw, packageName, dumpState);
+ if (dumpState.isDumping(DumpState.DUMP_SHARED_USERS)) {
+ mSettings.dumpSharedUsersLPr(pw, packageName, dumpState, checkin);
}
if (!checkin && dumpState.isDumping(DumpState.DUMP_INSTALLS) && packageName == null) {
@@ -12693,23 +12744,17 @@
pw.println();
pw.println("Package warning messages:");
- final File fname = getSettingsProblemFile();
- FileInputStream in = null;
+ BufferedReader in = null;
+ String line = null;
try {
- in = new FileInputStream(fname);
- final int avail = in.available();
- final byte[] data = new byte[avail];
- in.read(data);
- pw.print(new String(data));
- } catch (FileNotFoundException e) {
- } catch (IOException e) {
- } finally {
- if (in != null) {
- try {
- in.close();
- } catch (IOException e) {
- }
+ in = new BufferedReader(new FileReader(getSettingsProblemFile()));
+ while ((line = in.readLine()) != null) {
+ if (line.contains("ignored: updated version")) continue;
+ pw.println(line);
}
+ } catch (IOException ignored) {
+ } finally {
+ IoUtils.closeQuietly(in);
}
}
@@ -12719,6 +12764,7 @@
try {
in = new BufferedReader(new FileReader(getSettingsProblemFile()));
while ((line = in.readLine()) != null) {
+ if (line.contains("ignored: updated version")) continue;
pw.print("msg,");
pw.println(line);
}
@@ -13488,4 +13534,38 @@
}
}
}
+
+ /**
+ * Check and throw if the given before/after packages would be considered a
+ * downgrade.
+ */
+ private static void checkDowngrade(PackageParser.Package before, PackageInfoLite after)
+ throws PackageManagerException {
+ if (after.versionCode < before.mVersionCode) {
+ throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
+ "Update version code " + after.versionCode + " is older than current "
+ + before.mVersionCode);
+ } else if (after.versionCode == before.mVersionCode) {
+ if (after.baseRevisionCode < before.baseRevisionCode) {
+ throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
+ "Update base revision code " + after.baseRevisionCode
+ + " is older than current " + before.baseRevisionCode);
+ }
+
+ if (!ArrayUtils.isEmpty(after.splitNames)) {
+ for (int i = 0; i < after.splitNames.length; i++) {
+ final String splitName = after.splitNames[i];
+ final int j = ArrayUtils.indexOf(before.splitNames, splitName);
+ if (j != -1) {
+ if (after.splitRevisionCodes[i] < before.splitRevisionCodes[j]) {
+ throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
+ "Update split " + splitName + " revision code "
+ + after.splitRevisionCodes[i] + " is older than current "
+ + before.splitRevisionCodes[j]);
+ }
+ }
+ }
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index f0506a6..393ebd6 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -3608,31 +3608,36 @@
}
}
- void dumpSharedUsersLPr(PrintWriter pw, String packageName, DumpState dumpState) {
+ void dumpSharedUsersLPr(PrintWriter pw, String packageName, DumpState dumpState,
+ boolean checkin) {
boolean printedSomething = false;
for (SharedUserSetting su : mSharedUsers.values()) {
if (packageName != null && su != dumpState.getSharedUser()) {
continue;
}
- if (!printedSomething) {
- if (dumpState.onTitlePrinted())
- pw.println();
- pw.println("Shared users:");
- printedSomething = true;
- }
- pw.print(" SharedUser [");
- pw.print(su.name);
- pw.print("] (");
- pw.print(Integer.toHexString(System.identityHashCode(su)));
- pw.println("):");
- pw.print(" userId=");
- pw.print(su.userId);
- pw.print(" gids=");
- pw.println(PackageManagerService.arrayToString(su.gids));
- pw.println(" grantedPermissions:");
- for (String s : su.grantedPermissions) {
- pw.print(" ");
- pw.println(s);
+ if (!checkin) {
+ if (!printedSomething) {
+ if (dumpState.onTitlePrinted())
+ pw.println();
+ pw.println("Shared users:");
+ printedSomething = true;
+ }
+ pw.print(" SharedUser [");
+ pw.print(su.name);
+ pw.print("] (");
+ pw.print(Integer.toHexString(System.identityHashCode(su)));
+ pw.println("):");
+ pw.print(" userId=");
+ pw.print(su.userId);
+ pw.print(" gids=");
+ pw.println(PackageManagerService.arrayToString(su.gids));
+ pw.println(" grantedPermissions:");
+ for (String s : su.grantedPermissions) {
+ pw.print(" ");
+ pw.println(s);
+ }
+ } else {
+ pw.print("suid,"); pw.print(su.userId); pw.print(","); pw.println(su.name);
}
}
}
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 05108c7..2a3d7ab8 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -462,6 +462,16 @@
}
/**
+ * Returns the list of registered SIM call managers.
+ * @return List of registered SIM call managers.
+ * @hide
+ */
+ @SystemApi
+ public List<PhoneAccountHandle> getRegisteredConnectionManagers() {
+ return getSimCallManagers();
+ }
+
+ /**
* Returns a list of the {@link PhoneAccountHandle}s which can be used to make and receive phone
* calls which support the specified URI scheme.
* <P>