diff options
80 files changed, 1749 insertions, 859 deletions
@@ -344,6 +344,7 @@ LOCAL_SRC_FILES += \ telephony/java/com/android/internal/telephony/ISms.aidl \ telephony/java/com/android/internal/telephony/IWapPushManager.aidl \ telephony/java/com/android/internal/telephony/ISub.aidl \ + telephony/java/com/android/internal/telephony/IMms.aidl \ wifi/java/android/net/wifi/IWifiManager.aidl \ wifi/java/android/net/wifi/passpoint/IWifiPasspointManager.aidl \ wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl \ @@ -361,7 +362,7 @@ LOCAL_INTERMEDIATE_SOURCES := \ $(framework_res_source_path)/com/android/internal/R.java LOCAL_NO_STANDARD_LIBRARIES := true -LOCAL_JAVA_LIBRARIES := bouncycastle conscrypt core core-junit ext okhttp +LOCAL_JAVA_LIBRARIES := core-libart conscrypt okhttp core-junit bouncycastle ext LOCAL_MODULE := framework-base @@ -589,9 +590,9 @@ framework_docs_LOCAL_INTERMEDIATE_SOURCES := \ $(framework_res_source_path)/com/android/internal/R.java framework_docs_LOCAL_API_CHECK_JAVA_LIBRARIES := \ - bouncycastle \ + core-libart \ conscrypt \ - core \ + bouncycastle \ okhttp \ ext \ framework \ @@ -633,7 +634,7 @@ framework_docs_LOCAL_DROIDDOC_OPTIONS := \ -since $(SRC_API_DIR)/17.txt 17 \ -since $(SRC_API_DIR)/18.txt 18 \ -since $(SRC_API_DIR)/19.txt 19 \ - -werror -hide 113 \ + -werror -hide 111 -hide 113 \ -overview $(LOCAL_PATH)/core/java/overview.html framework_docs_LOCAL_API_CHECK_ADDITIONAL_JAVA_DIR:= \ @@ -902,7 +903,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := $(ext_src_files) LOCAL_NO_STANDARD_LIBRARIES := true -LOCAL_JAVA_LIBRARIES := core +LOCAL_JAVA_LIBRARIES := core-libart LOCAL_JAVA_RESOURCE_DIRS := $(ext_res_dirs) LOCAL_MODULE_TAGS := optional LOCAL_MODULE := ext diff --git a/api/current.txt b/api/current.txt index 931ab3b..589225a 100644 --- a/api/current.txt +++ b/api/current.txt @@ -580,6 +580,7 @@ package android { field public static final int fromXScale = 16843202; // 0x10101c2 field public static final int fromYDelta = 16843208; // 0x10101c8 field public static final int fromYScale = 16843204; // 0x10101c4 + field public static final int fullBackupOnly = 16843893; // 0x1010475 field public static final int fullBright = 16842954; // 0x10100ca field public static final int fullDark = 16842950; // 0x10100c6 field public static final int functionalTest = 16842787; // 0x1010023 @@ -8132,6 +8133,7 @@ package android.content.pm { field public static final int FLAG_DEBUGGABLE = 2; // 0x2 field public static final int FLAG_EXTERNAL_STORAGE = 262144; // 0x40000 field public static final int FLAG_FACTORY_TEST = 16; // 0x10 + field public static final int FLAG_FULL_BACKUP_ONLY = 67108864; // 0x4000000 field public static final int FLAG_HAS_CODE = 4; // 0x4 field public static final int FLAG_INSTALLED = 8388608; // 0x800000 field public static final int FLAG_IS_DATA_ONLY = 16777216; // 0x1000000 @@ -15851,6 +15853,7 @@ package android.media.tv { } public static final class TvContract.Channels implements android.media.tv.TvContract.BaseTvColumns { + method public static final java.lang.String getVideoResolution(java.lang.String); field public static final java.lang.String COLUMN_BROWSABLE = "browsable"; field public static final java.lang.String COLUMN_DESCRIPTION = "description"; field public static final java.lang.String COLUMN_DISPLAY_NAME = "display_name"; @@ -15864,6 +15867,7 @@ package android.media.tv { field public static final java.lang.String COLUMN_TRANSPORT_STREAM_ID = "transport_stream_id"; field public static final java.lang.String COLUMN_TYPE = "type"; field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number"; + field public static final java.lang.String COLUMN_VIDEO_FORMAT = "video_format"; field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/channel"; field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/channel"; field public static final android.net.Uri CONTENT_URI; @@ -15895,6 +15899,22 @@ package android.media.tv { field public static final int TYPE_SECAM = 3; // 0x3 field public static final int TYPE_S_DMB = 393472; // 0x60100 field public static final int TYPE_T_DMB = 393216; // 0x60000 + field public static final java.lang.String VIDEO_FORMAT_1080I = "VIDEO_FORMAT_1080I"; + field public static final java.lang.String VIDEO_FORMAT_1080P = "VIDEO_FORMAT_1080P"; + field public static final java.lang.String VIDEO_FORMAT_2160P = "VIDEO_FORMAT_2160P"; + field public static final java.lang.String VIDEO_FORMAT_240P = "VIDEO_FORMAT_240P"; + field public static final java.lang.String VIDEO_FORMAT_360P = "VIDEO_FORMAT_360P"; + field public static final java.lang.String VIDEO_FORMAT_4320P = "VIDEO_FORMAT_4320P"; + field public static final java.lang.String VIDEO_FORMAT_480I = "VIDEO_FORMAT_480I"; + field public static final java.lang.String VIDEO_FORMAT_480P = "VIDEO_FORMAT_480P"; + field public static final java.lang.String VIDEO_FORMAT_576I = "VIDEO_FORMAT_576I"; + field public static final java.lang.String VIDEO_FORMAT_576P = "VIDEO_FORMAT_576P"; + field public static final java.lang.String VIDEO_FORMAT_720P = "VIDEO_FORMAT_720P"; + field public static final java.lang.String VIDEO_RESOLUTION_ED = "VIDEO_RESOLUTION_ED"; + field public static final java.lang.String VIDEO_RESOLUTION_FHD = "VIDEO_RESOLUTION_FHD"; + field public static final java.lang.String VIDEO_RESOLUTION_HD = "VIDEO_RESOLUTION_HD"; + field public static final java.lang.String VIDEO_RESOLUTION_SD = "VIDEO_RESOLUTION_SD"; + field public static final java.lang.String VIDEO_RESOLUTION_UHD = "VIDEO_RESOLUTION_UHD"; } public static final class TvContract.Channels.Logo { @@ -15915,6 +15935,8 @@ package android.media.tv { field public static final java.lang.String COLUMN_THUMBNAIL_URI = "thumbnail_uri"; field public static final java.lang.String COLUMN_TITLE = "title"; field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number"; + field public static final java.lang.String COLUMN_VIDEO_HEIGHT = "video_height"; + field public static final java.lang.String COLUMN_VIDEO_WIDTH = "video_width"; field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/program"; field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/program"; field public static final android.net.Uri CONTENT_URI; @@ -15923,17 +15945,17 @@ package android.media.tv { public static final class TvContract.Programs.Genres { method public static java.lang.String[] decode(java.lang.String); method public static java.lang.String encode(java.lang.String...); - field public static final java.lang.String ANIMAL_WILDLIFE = "Animal/Wildlife"; - field public static final java.lang.String COMEDY = "Comedy"; - field public static final java.lang.String DRAMA = "Drama"; - field public static final java.lang.String EDUCATION = "Education"; - field public static final java.lang.String FAMILY_KIDS = "Family/Kids"; - field public static final java.lang.String GAMING = "Gaming"; - field public static final java.lang.String MOVIES = "Movies"; - field public static final java.lang.String NEWS = "News"; - field public static final java.lang.String SHOPPING = "Shopping"; - field public static final java.lang.String SPORTS = "Sports"; - field public static final java.lang.String TRAVEL = "Travel"; + field public static final java.lang.String ANIMAL_WILDLIFE = "ANIMAL_WILDLIFE"; + field public static final java.lang.String COMEDY = "COMEDY"; + field public static final java.lang.String DRAMA = "DRAMA"; + field public static final java.lang.String EDUCATION = "EDUCATION"; + field public static final java.lang.String FAMILY_KIDS = "FAMILY_KIDS"; + field public static final java.lang.String GAMING = "GAMING"; + field public static final java.lang.String MOVIES = "MOVIES"; + field public static final java.lang.String NEWS = "NEWS"; + field public static final java.lang.String SHOPPING = "SHOPPING"; + field public static final java.lang.String SPORTS = "SPORTS"; + field public static final java.lang.String TRAVEL = "TRAVEL"; } public final class TvInputInfo implements android.os.Parcelable { @@ -21564,6 +21586,7 @@ package android.os { public class UserManager { method public android.os.Bundle getApplicationRestrictions(java.lang.String); method public android.graphics.drawable.Drawable getBadgedDrawableForUser(android.graphics.drawable.Drawable, android.os.UserHandle); + method public java.lang.String getBadgedLabelForUser(java.lang.String, android.os.UserHandle); method public long getSerialNumberForUser(android.os.UserHandle); method public int getUserCount(); method public android.os.UserHandle getUserForSerialNumber(long); @@ -32562,6 +32585,7 @@ package android.view { method public void dispatchWindowSystemUiVisiblityChanged(int); method public void dispatchWindowVisibilityChanged(int); method public void draw(android.graphics.Canvas); + method public void drawableHotspotChanged(float, float); method protected void drawableStateChanged(); method public android.view.View findFocus(); method public final android.view.View findViewById(int); diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index de0396e..5e17e1a 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -1457,10 +1457,10 @@ final class ApplicationPackageManager extends PackageManager { * @hide */ @Override - public void addCrossProfileIntentFilter(IntentFilter filter, boolean removable, - int sourceUserId, int targetUserId) { + public void addCrossProfileIntentFilter(IntentFilter filter, int sourceUserId, int targetUserId, + int flags) { try { - mPM.addCrossProfileIntentFilter(filter, removable, sourceUserId, targetUserId); + mPM.addCrossProfileIntentFilter(filter, sourceUserId, targetUserId, flags); } catch (RemoteException e) { // Should never happen! } @@ -1470,15 +1470,6 @@ final class ApplicationPackageManager extends PackageManager { * @hide */ @Override - public void addForwardingIntentFilter(IntentFilter filter, boolean removable, int sourceUserId, - int targetUserId) { - addCrossProfileIntentFilter(filter, removable, sourceUserId, targetUserId); - } - - /** - * @hide - */ - @Override public void clearCrossProfileIntentFilters(int sourceUserId) { try { mPM.clearCrossProfileIntentFilters(sourceUserId); @@ -1487,14 +1478,6 @@ final class ApplicationPackageManager extends PackageManager { } } - /** - * @hide - */ - @Override - public void clearForwardingIntentFilters(int sourceUserId) { - clearCrossProfileIntentFilters(sourceUserId); - } - private final ContextImpl mContext; private final IPackageManager mPM; diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 06f4019..be4e864 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -325,6 +325,15 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { public static final int FLAG_IS_GAME = 1<<25; /** + * Value for {@link #flags}: {@code true} if the application asks that only + * full-data streaming backups of its data be performed even though it defines + * a {@link android.app.backup.BackupAgent BackupAgent}, which normally + * indicates that the app will manage its backed-up data via incremental + * key/value updates. + */ + public static final int FLAG_FULL_BACKUP_ONLY = 1<<26; + + /** * Value for {@link #flags}: set to {@code true} if the application * is permitted to hold privileged permissions. * diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 70668e1..00e7918 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -248,8 +248,8 @@ interface IPackageManager { void clearPackagePersistentPreferredActivities(String packageName, int userId); - void addCrossProfileIntentFilter(in IntentFilter filter, boolean removable, int sourceUserId, - int targetUserId); + void addCrossProfileIntentFilter(in IntentFilter intentFilter, int sourceUserId, int targetUserId, + int flags); void clearCrossProfileIntentFilters(int sourceUserId); diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 8d9b8d9..b5ceebe 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -196,6 +196,22 @@ public abstract class PackageManager { */ public static final int MATCH_DEFAULT_ONLY = 0x00010000; + /** + * Flag for {@link addCrossProfileIntentFilter}: if the cross-profile intent has been set by the + * profile owner. + * @hide + */ + public static final int SET_BY_PROFILE_OWNER= 0x00000001; + + /** + * Flag for {@link addCrossProfileIntentFilter}: if this flag is set: + * when resolving an intent that matches the {@link CrossProfileIntentFilter}, the current + * profile will be skipped. + * Only activities in the target user can respond to the intent. + * @hide + */ + public static final int SKIP_CURRENT_PROFILE = 0x00000002; + /** @hide */ @IntDef({PERMISSION_GRANTED, PERMISSION_DENIED}) @Retention(RetentionPolicy.SOURCE) @@ -2870,15 +2886,12 @@ public abstract class PackageManager { * */ public PackageInfo getPackageArchiveInfo(String archiveFilePath, int flags) { - PackageParser packageParser = new PackageParser(archiveFilePath); - DisplayMetrics metrics = new DisplayMetrics(); - metrics.setToDefaults(); - final File sourceFile = new File(archiveFilePath); + final PackageParser parser = new PackageParser(); + final File apkFile = new File(archiveFilePath); try { - PackageParser.Package pkg = packageParser.parseMonolithicPackage(sourceFile, metrics, - 0); + PackageParser.Package pkg = parser.parseMonolithicPackage(apkFile, 0); if ((flags & GET_SIGNATURES) != 0) { - packageParser.collectCertificates(pkg, 0); + parser.collectCertificates(pkg, 0); } PackageUserState state = new PackageUserState(); return PackageParser.generatePackageInfo(pkg, null, flags, 0, 0, null, state); @@ -3586,30 +3599,14 @@ public abstract class PackageManager { * {@link CrossProfileIntentFilter} * @hide */ - public abstract void addCrossProfileIntentFilter(IntentFilter filter, boolean removable, - int sourceUserId, int targetUserId); + public abstract void addCrossProfileIntentFilter(IntentFilter filter, int sourceUserId, + int targetUserId, int flags); /** - * @hide - * @deprecated - * TODO: remove it as soon as the code of ManagedProvisionning is updated - */ - public abstract void addForwardingIntentFilter(IntentFilter filter, boolean removable, - int sourceUserId, int targetUserId); - - /** - * Clearing removable {@link CrossProfileIntentFilter}s which have the specified user as their - * source + * Clearing {@link CrossProfileIntentFilter}s which have the specified user as their + * source, and have been set by the profile owner * @param sourceUserId - * be cleared. * @hide */ public abstract void clearCrossProfileIntentFilters(int sourceUserId); - - /** - * @hide - * @deprecated - * TODO: remove it as soon as the code of ManagedProvisionning is updated - */ - public abstract void clearForwardingIntentFilters(int sourceUserId); } diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 618c2bd..546f3a5 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -16,9 +16,14 @@ package android.content.pm; +import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST; import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME; +import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING; +import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES; import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK; +import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES; +import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION; import android.content.ComponentName; import android.content.Intent; @@ -33,6 +38,8 @@ import android.os.Bundle; import android.os.PatternMatcher; import android.os.UserHandle; import android.text.TextUtils; +import android.util.ArrayMap; +import android.util.ArraySet; import android.util.AttributeSet; import android.util.Base64; import android.util.DisplayMetrics; @@ -41,12 +48,18 @@ import android.util.Pair; import android.util.Slog; import android.util.TypedValue; -import java.io.BufferedInputStream; +import com.android.internal.util.ArrayUtils; +import com.android.internal.util.XmlUtils; + +import libcore.io.IoUtils; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; -import java.lang.ref.WeakReference; import java.security.GeneralSecurityException; import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; @@ -58,21 +71,19 @@ import java.security.spec.InvalidKeySpecException; import java.security.spec.X509EncodedKeySpec; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; import java.util.jar.StrictJarFile; import java.util.zip.ZipEntry; -import com.android.internal.util.ArrayUtils; -import com.android.internal.util.XmlUtils; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; - /** * Package archive parsing * @@ -153,17 +164,21 @@ public class PackageParser { android.os.Build.VERSION_CODES.JELLY_BEAN) }; + /** + * @deprecated callers should move to explicitly passing around source path. + */ + @Deprecated private String mArchiveSourcePath; + private String[] mSeparateProcesses; private boolean mOnlyCoreApps; + private DisplayMetrics mMetrics; + private static final int SDK_VERSION = Build.VERSION.SDK_INT; private static final String[] SDK_CODENAMES = Build.VERSION.ACTIVE_CODENAMES; private int mParseError = PackageManager.INSTALL_SUCCEEDED; - private static final Object mSync = new Object(); - private static WeakReference<byte[]> mReadBuffer; - private static boolean sCompatibilityModeEnabled = true; private static final int PARSE_DEFAULT_INSTALL_LOCATION = PackageInfo.INSTALL_LOCATION_UNSPECIFIED; @@ -247,12 +262,9 @@ public class PackageParser { private static final String TAG = "PackageParser"; - public PackageParser(String archiveSourcePath) { - mArchiveSourcePath = archiveSourcePath; - } - - public PackageParser(File archiveSource) { - this(archiveSource.getAbsolutePath()); + public PackageParser() { + mMetrics = new DisplayMetrics(); + mMetrics.setToDefaults(); } public void setSeparateProcesses(String[] procs) { @@ -263,6 +275,10 @@ public class PackageParser { mOnlyCoreApps = onlyCoreApps; } + public void setDisplayMetrics(DisplayMetrics metrics) { + mMetrics = metrics; + } + private static final boolean isPackageFilename(File file) { return isPackageFilename(file.getName()); } @@ -480,23 +496,21 @@ public class PackageParser { return pi; } - private static Certificate[][] loadCertificates(StrictJarFile jarFile, ZipEntry je, - byte[] readBuffer) { + private static Certificate[][] loadCertificates(StrictJarFile jarFile, ZipEntry entry) + throws PackageParserException { + InputStream is = null; try { // We must read the stream for the JarEntry to retrieve // its certificates. - InputStream is = new BufferedInputStream(jarFile.getInputStream(je)); - while (is.read(readBuffer, 0, readBuffer.length) != -1) { - // not using - } - is.close(); - return je != null ? jarFile.getCertificateChains(je) : null; - } catch (IOException e) { - Slog.w(TAG, "Exception reading " + je.getName() + " in " + jarFile, e); - } catch (RuntimeException e) { - Slog.w(TAG, "Exception reading " + je.getName() + " in " + jarFile, e); + is = jarFile.getInputStream(entry); + readFullyIgnoringContents(is); + return jarFile.getCertificateChains(entry); + } catch (IOException | RuntimeException e) { + throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION, + "Failed reading " + entry.getName() + " in " + jarFile, e); + } finally { + IoUtils.closeQuietly(is); } - return null; } public final static int PARSE_IS_SYSTEM = 1<<0; @@ -508,67 +522,116 @@ public class PackageParser { public final static int PARSE_IS_SYSTEM_DIR = 1<<6; public final static int PARSE_IS_PRIVILEGED = 1<<7; public final static int PARSE_GET_SIGNATURES = 1<<8; + public final static int PARSE_TRUSTED_OVERLAY = 1<<9; + + private static final Comparator<String> sSplitNameComparator = new SplitNameComparator(); /** - * Parse all APK files under the given directory as a single package. + * Used to sort a set of APKs based on their split names, always placing the + * base APK (with {@code null} split name) first. */ - public Package parseSplitPackage(File apkDir, DisplayMetrics metrics, int flags, - boolean trustedOverlay) throws PackageParserException { + private static class SplitNameComparator implements Comparator<String> { + @Override + public int compare(String lhs, String rhs) { + if (lhs == null) { + return -1; + } else if (rhs == null) { + return 1; + } else { + return lhs.compareTo(rhs); + } + } + } + + /** + * Parse all APKs contained in the given directory, treating them as a + * single package. This also performs sanity checking, such as requiring + * identical package name and version codes, a single base APK, and unique + * split names. + * <p> + * Note that this <em>does not</em> perform signature verification; that + * must be done separately in {@link #collectCertificates(Package, int)}. + */ + public Package parseSplitPackage(File apkDir, int flags) throws PackageParserException { final File[] files = apkDir.listFiles(); if (ArrayUtils.isEmpty(files)) { throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, "No packages found in split"); } - File baseFile = null; + String packageName = null; + int versionCode = 0; + + final ArrayMap<String, File> apks = new ArrayMap<>(); for (File file : files) { if (file.isFile() && isPackageFilename(file)) { - ApkLite lite = parseApkLite(file.getAbsolutePath(), 0); - if (lite == null) { - throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, - "Invalid package file: " + file); + final ApkLite lite = parseApkLite(file, 0); + + // Assert that all package names and version codes are + // consistent with the first one we encounter. + if (packageName == null) { + packageName = lite.packageName; + versionCode = lite.versionCode; + } else { + if (!packageName.equals(lite.packageName)) { + throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST, + "Inconsistent package " + lite.packageName + " in " + file + + "; expected " + packageName); + } + if (versionCode != lite.versionCode) { + throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST, + "Inconsistent version " + lite.versionCode + " in " + file + + "; expected " + versionCode); + } } - if (TextUtils.isEmpty(lite.splitName)) { - baseFile = file; - break; + // Assert that each split is defined only once + if (apks.put(lite.splitName, file) != null) { + throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST, + "Split name " + lite.splitName + + " defined more than once; most recent was " + file); } } } + final File baseFile = apks.remove(null); if (baseFile == null) { - throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, + throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST, "Missing base APK in " + apkDir); } - final Package pkg = parseBaseApk(baseFile, metrics, flags, trustedOverlay); + // Always apply deterministic ordering based on splitName + final int size = apks.size(); + + final String[] splitNames = apks.keySet().toArray(new String[size]); + Arrays.sort(splitNames, sSplitNameComparator); + + final File[] splitFiles = new File[size]; + for (int i = 0; i < size; i++) { + splitFiles[i] = apks.get(splitNames[i]); + } + + final Package pkg = parseBaseApk(baseFile, flags); if (pkg == null) { throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, "Failed to parse base APK: " + baseFile); } - for (File file : files) { - if (file.isFile() && isPackageFilename(file) && !file.equals(baseFile)) { - parseSplitApk(pkg, file, metrics, flags, trustedOverlay); - } - } - - // Always use a well-defined sort order - if (pkg.splitCodePaths != null) { - Arrays.sort(pkg.splitCodePaths); + for (File splitFile : splitFiles) { + parseSplitApk(pkg, splitFile, flags); } return pkg; } - public Package parseMonolithicPackage(File apkFile, DisplayMetrics metrics, int flags) - throws PackageParserException { - return parseMonolithicPackage(apkFile, metrics, flags, false); - } - - public Package parseMonolithicPackage(File apkFile, DisplayMetrics metrics, int flags, - boolean trustedOverlay) throws PackageParserException { - final Package pkg = parseBaseApk(apkFile, metrics, flags, trustedOverlay); + /** + * Parse the given APK file, treating it as as a single monolithic package. + * <p> + * Note that this <em>does not</em> perform signature verification; that + * must be done separately in {@link #collectCertificates(Package, int)}. + */ + public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException { + final Package pkg = parseBaseApk(apkFile, flags); if (pkg != null) { return pkg; } else { @@ -576,13 +639,15 @@ public class PackageParser { } } - private Package parseBaseApk(File apkFile, DisplayMetrics metrics, int flags, - boolean trustedOverlay) { + private Package parseBaseApk(File apkFile, int flags) { + final boolean trustedOverlay = (flags & PARSE_TRUSTED_OVERLAY) != 0; + mParseError = PackageManager.INSTALL_SUCCEEDED; + final String apkPath = apkFile.getAbsolutePath(); mArchiveSourcePath = apkFile.getAbsolutePath(); if (!apkFile.isFile()) { - Slog.w(TAG, "Skipping dir: " + mArchiveSourcePath); + Slog.w(TAG, "Skipping dir: " + apkPath); mParseError = PackageManager.INSTALL_PARSE_FAILED_NOT_APK; return null; } @@ -591,14 +656,14 @@ public class PackageParser { if ((flags&PARSE_IS_SYSTEM) == 0) { // We expect to have non-.apk files in the system dir, // so don't warn about them. - Slog.w(TAG, "Skipping non-package file: " + mArchiveSourcePath); + Slog.w(TAG, "Skipping non-package file: " + apkPath); } mParseError = PackageManager.INSTALL_PARSE_FAILED_NOT_APK; return null; } if (DEBUG_JAR) - Slog.d(TAG, "Scanning package: " + mArchiveSourcePath); + Slog.d(TAG, "Scanning package: " + apkPath); XmlResourceParser parser = null; AssetManager assmgr = null; @@ -606,19 +671,18 @@ public class PackageParser { boolean assetError = true; try { assmgr = new AssetManager(); - int cookie = assmgr.addAssetPath(mArchiveSourcePath); + int cookie = assmgr.addAssetPath(apkPath); if (cookie != 0) { - res = new Resources(assmgr, metrics, null); + res = new Resources(assmgr, mMetrics, null); assmgr.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, Build.VERSION.RESOURCES_SDK_INT); parser = assmgr.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME); assetError = false; } else { - Slog.w(TAG, "Failed adding asset path:"+mArchiveSourcePath); + Slog.w(TAG, "Failed adding asset path:" + apkPath); } } catch (Exception e) { - Slog.w(TAG, "Unable to read AndroidManifest.xml of " - + mArchiveSourcePath, e); + Slog.w(TAG, "Unable to read AndroidManifest.xml of " + apkPath, e); } if (assetError) { if (assmgr != null) assmgr.close(); @@ -641,9 +705,9 @@ public class PackageParser { // just means to skip this app so don't make a fuss about it. if (!mOnlyCoreApps || mParseError != PackageManager.INSTALL_SUCCEEDED) { if (errorException != null) { - Slog.w(TAG, mArchiveSourcePath, errorException); + Slog.w(TAG, apkPath, errorException); } else { - Slog.w(TAG, mArchiveSourcePath + " (at " + Slog.w(TAG, apkPath + " (at " + parser.getPositionDescription() + "): " + errorText[0]); } @@ -659,14 +723,16 @@ public class PackageParser { parser.close(); assmgr.close(); - pkg.codePath = mArchiveSourcePath; + pkg.codePath = apkPath; pkg.mSignatures = null; return pkg; } - private void parseSplitApk(Package pkg, File apkFile, DisplayMetrics metrics, int flags, - boolean trustedOverlay) throws PackageParserException { + private void parseSplitApk(Package pkg, File apkFile, int flags) throws PackageParserException { + final String apkPath = apkFile.getAbsolutePath(); + mArchiveSourcePath = apkFile.getAbsolutePath(); + // TODO: expand split APK parsing pkg.splitCodePaths = ArrayUtils.appendElement(String.class, pkg.splitCodePaths, apkFile.getAbsolutePath()); @@ -678,8 +744,9 @@ public class PackageParser { * {@code AndroidManifest.xml}, {@code true} is returned. */ public void collectManifestDigest(Package pkg) throws PackageParserException { + // TODO: extend to gather digest for split APKs try { - final StrictJarFile jarFile = new StrictJarFile(mArchiveSourcePath); + final StrictJarFile jarFile = new StrictJarFile(pkg.codePath); try { final ZipEntry je = jarFile.findEntry(ANDROID_MANIFEST_FILENAME); if (je != null) { @@ -688,186 +755,127 @@ public class PackageParser { } finally { jarFile.close(); } - } catch (IOException e) { + } catch (IOException | RuntimeException e) { throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, "Failed to collect manifest digest"); } } + /** + * Collect certificates from all the APKs described in the given package, + * populating {@link Package#mSignatures}. This also asserts that all APK + * contents are signed correctly and consistently. + */ public void collectCertificates(Package pkg, int flags) throws PackageParserException { - if (!collectCertificatesInternal(pkg, flags)) { - throw new PackageParserException(mParseError, "Failed to collect certificates"); - } - } - - private boolean collectCertificatesInternal(Package pkg, int flags) { + pkg.mCertificates = null; pkg.mSignatures = null; + pkg.mSigningKeys = null; - WeakReference<byte[]> readBufferRef; - byte[] readBuffer = null; - synchronized (mSync) { - readBufferRef = mReadBuffer; - if (readBufferRef != null) { - mReadBuffer = null; - readBuffer = readBufferRef.get(); - } - if (readBuffer == null) { - readBuffer = new byte[8192]; - readBufferRef = new WeakReference<byte[]>(readBuffer); + collectCertificates(pkg, new File(pkg.codePath), flags); + + if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) { + for (String splitCodePath : pkg.splitCodePaths) { + collectCertificates(pkg, new File(splitCodePath), flags); } } + } + + private static void collectCertificates(Package pkg, File apkFile, int flags) + throws PackageParserException { + final String apkPath = apkFile.getAbsolutePath(); + StrictJarFile jarFile = null; try { - StrictJarFile jarFile = new StrictJarFile(mArchiveSourcePath); - - Certificate[][] certs = null; - - if ((flags&PARSE_IS_SYSTEM) != 0) { - // If this package comes from the system image, then we - // can trust it... we'll just use the AndroidManifest.xml - // to retrieve its signatures, not validating all of the - // files. - ZipEntry jarEntry = jarFile.findEntry(ANDROID_MANIFEST_FILENAME); - certs = loadCertificates(jarFile, jarEntry, readBuffer); - if (certs == null) { - Slog.e(TAG, "Package " + pkg.packageName - + " has no certificates at entry " - + jarEntry.getName() + "; ignoring!"); - jarFile.close(); - mParseError = PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES; - return false; - } - if (DEBUG_JAR) { - Slog.i(TAG, "File " + mArchiveSourcePath + ": entry=" + jarEntry - + " certs=" + (certs != null ? certs.length : 0)); - if (certs != null) { - final int N = certs.length; - for (int i=0; i<N; i++) { - Slog.i(TAG, " Public key: " - + certs[i][0].getPublicKey().getEncoded() - + " " + certs[i][0].getPublicKey()); - } - } - } - } else { - Iterator<ZipEntry> entries = jarFile.iterator(); - while (entries.hasNext()) { - final ZipEntry je = entries.next(); - if (je.isDirectory()) continue; + jarFile = new StrictJarFile(apkPath); - final String name = je.getName(); + // Always verify manifest, regardless of source + final ZipEntry manifestEntry = jarFile.findEntry(ANDROID_MANIFEST_FILENAME); + if (manifestEntry == null) { + throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST, + "Package " + apkPath + " has no manifest"); + } - if (name.startsWith("META-INF/")) - continue; + final List<ZipEntry> toVerify = new ArrayList<>(); + toVerify.add(manifestEntry); - if (ANDROID_MANIFEST_FILENAME.equals(name)) { - pkg.manifestDigest = - ManifestDigest.fromInputStream(jarFile.getInputStream(je)); - } + // If we're parsing an untrusted package, verify all contents + if ((flags & PARSE_IS_SYSTEM) == 0) { + final Iterator<ZipEntry> i = jarFile.iterator(); + while (i.hasNext()) { + final ZipEntry entry = i.next(); - final Certificate[][] localCerts = loadCertificates(jarFile, je, readBuffer); - if (DEBUG_JAR) { - Slog.i(TAG, "File " + mArchiveSourcePath + " entry " + je.getName() - + ": certs=" + certs + " (" - + (certs != null ? certs.length : 0) + ")"); - } + if (entry.isDirectory()) continue; + if (entry.getName().startsWith("META-INF/")) continue; + if (entry.getName().equals(ANDROID_MANIFEST_FILENAME)) continue; - if (localCerts == null) { - Slog.e(TAG, "Package " + pkg.packageName - + " has no certificates at entry " - + je.getName() + "; ignoring!"); - jarFile.close(); - mParseError = PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES; - return false; - } else if (certs == null) { - certs = localCerts; - } else { - // Ensure all certificates match. - for (int i=0; i<certs.length; i++) { - boolean found = false; - for (int j=0; j<localCerts.length; j++) { - if (certs[i] != null && - certs[i].equals(localCerts[j])) { - found = true; - break; - } - } - if (!found || certs.length != localCerts.length) { - Slog.e(TAG, "Package " + pkg.packageName - + " has mismatched certificates at entry " - + je.getName() + "; ignoring!"); - jarFile.close(); - mParseError = PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES; - return false; - } - } - } + toVerify.add(entry); } } - jarFile.close(); - - synchronized (mSync) { - mReadBuffer = readBufferRef; - } - if (!ArrayUtils.isEmpty(certs)) { - pkg.mSignatures = convertToSignatures(certs); - } else { - Slog.e(TAG, "Package " + pkg.packageName - + " has no certificates; ignoring!"); - mParseError = PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES; - return false; - } + // Verify that entries are signed consistently with the first entry + // we encountered. Note that for splits, certificates may have + // already been populated during an earlier parse of a base APK. + for (ZipEntry entry : toVerify) { + final Certificate[][] entryCerts = loadCertificates(jarFile, entry); + if (ArrayUtils.isEmpty(entryCerts)) { + throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, + "Package " + apkPath + " has no certificates at entry " + + entry.getName()); + } - // Add the signing KeySet to the system - pkg.mSigningKeys = new HashSet<PublicKey>(); - for (int i=0; i < certs.length; i++) { - pkg.mSigningKeys.add(certs[i][0].getPublicKey()); + if (pkg.mCertificates == null) { + pkg.mCertificates = entryCerts; + pkg.mSignatures = convertToSignatures(entryCerts); + pkg.mSigningKeys = new ArraySet<>(); + for (int i = 0; i < entryCerts.length; i++) { + pkg.mSigningKeys.add(entryCerts[i][0].getPublicKey()); + } + } else { + final boolean certsMatch = (pkg.mCertificates.length == entryCerts.length) + && ArrayUtils.containsAll(pkg.mCertificates, entryCerts) + && ArrayUtils.containsAll(entryCerts, pkg.mCertificates); + if (!certsMatch) { + throw new PackageParserException( + INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES, "Package " + apkPath + + " has mismatched certificates at entry " + + entry.getName()); + } + } } - - } catch (CertificateEncodingException e) { - Slog.w(TAG, "Exception reading " + mArchiveSourcePath, e); - mParseError = PackageManager.INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING; - return false; - } catch (IOException e) { - Slog.w(TAG, "Exception reading " + mArchiveSourcePath, e); - mParseError = PackageManager.INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING; - return false; - } catch (SecurityException e) { - Slog.w(TAG, "Exception reading " + mArchiveSourcePath, e); - mParseError = PackageManager.INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING; - return false; - } catch (RuntimeException e) { - Slog.w(TAG, "Exception reading " + mArchiveSourcePath, e); - mParseError = PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION; - return false; + } catch (GeneralSecurityException | IOException | RuntimeException e) { + throw new PackageParserException(INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING, + "Failed to collect certificates from " + apkPath, e); + } finally { + closeQuietly(jarFile); } - - return true; } /** * Only collect certificates on the manifest; does not validate signatures * across remainder of package. */ - private static Signature[] collectCertificates(String packageFilePath) { + private static Signature[] collectManifestCertificates(File apkFile) + throws PackageParserException { + final String apkPath = apkFile.getAbsolutePath(); try { - final StrictJarFile jarFile = new StrictJarFile(packageFilePath); + final StrictJarFile jarFile = new StrictJarFile(apkPath); try { final ZipEntry jarEntry = jarFile.findEntry(ANDROID_MANIFEST_FILENAME); - if (jarEntry != null) { - final Certificate[][] certs = loadCertificates(jarFile, jarEntry, null); - return convertToSignatures(certs); + if (jarEntry == null) { + throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, + "Package " + apkPath + " has no manifest"); } + + final Certificate[][] certs = loadCertificates(jarFile, jarEntry); + return convertToSignatures(certs); + } finally { jarFile.close(); } - } catch (GeneralSecurityException e) { - Slog.w(TAG, "Failed to collect certs from " + packageFilePath + ": " + e); - } catch (IOException e) { - Slog.w(TAG, "Failed to collect certs from " + packageFilePath + ": " + e); + } catch (GeneralSecurityException | IOException | RuntimeException e) { + throw new PackageParserException(INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING, + "Failed to collect certificates from " + apkPath, e); } - return null; } private static Signature[] convertToSignatures(Certificate[][] certs) @@ -879,67 +887,55 @@ public class PackageParser { return res; } - /* - * Utility method that retrieves just the package name and install - * location from the apk location at the given file path. - * @param packageFilePath file location of the apk - * @param flags Special parse flags - * @return PackageLite object with package information or null on failure. + /** + * Utility method that retrieves lightweight details about a single APK + * file, including package name, split name, and install location. + * + * @param apkFile path to a single APK + * @param flags optional parse flags, such as {@link #PARSE_GET_SIGNATURES} */ - public static ApkLite parseApkLite(String packageFilePath, int flags) { + public static ApkLite parseApkLite(File apkFile, int flags) + throws PackageParserException { + final String apkPath = apkFile.getAbsolutePath(); + AssetManager assmgr = null; - final XmlResourceParser parser; - final Resources res; + XmlResourceParser parser = null; try { assmgr = new AssetManager(); assmgr.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, Build.VERSION.RESOURCES_SDK_INT); - int cookie = assmgr.addAssetPath(packageFilePath); + int cookie = assmgr.addAssetPath(apkPath); if (cookie == 0) { - return null; + throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK, + "Failed to parse " + apkPath); } final DisplayMetrics metrics = new DisplayMetrics(); metrics.setToDefaults(); - res = new Resources(assmgr, metrics, null); + + final Resources res = new Resources(assmgr, metrics, null); parser = assmgr.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME); - } catch (Exception e) { - if (assmgr != null) assmgr.close(); - Slog.w(TAG, "Unable to read AndroidManifest.xml of " - + packageFilePath, e); - return null; - } - // Only collect certificates on the manifest; does not validate - // signatures across remainder of package. - final Signature[] signatures; - if ((flags & PARSE_GET_SIGNATURES) != 0) { - signatures = collectCertificates(packageFilePath); - } else { - signatures = null; - } + // Only collect certificates on the manifest; does not validate + // signatures across remainder of package. + final Signature[] signatures; + if ((flags & PARSE_GET_SIGNATURES) != 0) { + signatures = collectManifestCertificates(apkFile); + } else { + signatures = null; + } - final AttributeSet attrs = parser; - final String errors[] = new String[1]; - ApkLite packageLite = null; - try { - packageLite = parseApkLite(res, parser, attrs, flags, signatures, errors); - } catch (PackageParserException e) { - Slog.w(TAG, packageFilePath, e); - } catch (IOException e) { - Slog.w(TAG, packageFilePath, e); - } catch (XmlPullParserException e) { - Slog.w(TAG, packageFilePath, e); + final AttributeSet attrs = parser; + return parseApkLite(res, parser, attrs, flags, signatures); + + } catch (XmlPullParserException | IOException | RuntimeException e) { + throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION, + "Failed to parse " + apkPath, e); } finally { if (parser != null) parser.close(); if (assmgr != null) assmgr.close(); } - if (packageLite == null) { - Slog.e(TAG, "parsePackageLite error: " + errors[0]); - return null; - } - return packageLite; } private static String validateName(String name, boolean requiresSeparator) { @@ -995,12 +991,16 @@ public class PackageParser { } } - final String splitName = attrs.getAttributeValue(null, "split"); + String splitName = attrs.getAttributeValue(null, "split"); if (splitName != null) { - final String error = validateName(splitName, true); - if (error != null) { - throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME, - "Invalid manifest split: " + error); + if (splitName.length() == 0) { + splitName = null; + } else { + final String error = validateName(splitName, true); + if (error != null) { + throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME, + "Invalid manifest split: " + error); + } } } @@ -1009,8 +1009,8 @@ public class PackageParser { } private static ApkLite parseApkLite(Resources res, XmlPullParser parser, - AttributeSet attrs, int flags, Signature[] signatures, String[] outError) - throws IOException, XmlPullParserException, PackageParserException { + AttributeSet attrs, int flags, Signature[] signatures) throws IOException, + XmlPullParserException, PackageParserException { final Pair<String, String> packageSplit = parsePackageSplitNames(parser, attrs, flags); int installLocation = PARSE_DEFAULT_INSTALL_LOCATION; @@ -1043,7 +1043,7 @@ public class PackageParser { } if (parser.getDepth() == searchDepth && "package-verifier".equals(parser.getName())) { - final VerifierInfo verifier = parseVerifier(res, parser, attrs, flags, outError); + final VerifierInfo verifier = parseVerifier(res, parser, attrs, flags); if (verifier != null) { verifiers.add(verifier); } @@ -1793,7 +1793,7 @@ public class PackageParser { } } - owner.mKeySetMapping = new HashMap<String, Set<PublicKey>>(); + owner.mKeySetMapping = new ArrayMap<String, ArraySet<PublicKey>>(); for (Map.Entry<PublicKey, Set<String>> e : definedKeySets.entrySet()) { PublicKey key = e.getKey(); Set<String> keySetNames = e.getValue(); @@ -1801,7 +1801,7 @@ public class PackageParser { if (owner.mKeySetMapping.containsKey(alias)) { owner.mKeySetMapping.get(alias).add(key); } else { - Set<PublicKey> keys = new HashSet<PublicKey>(); + ArraySet<PublicKey> keys = new ArraySet<PublicKey>(); keys.add(key); owner.mKeySetMapping.put(alias, keys); } @@ -2088,6 +2088,11 @@ public class PackageParser { false)) { ai.flags |= ApplicationInfo.FLAG_RESTORE_ANY_VERSION; } + if (sa.getBoolean( + com.android.internal.R.styleable.AndroidManifestApplication_fullBackupOnly, + false)) { + ai.flags |= ApplicationInfo.FLAG_FULL_BACKUP_ONLY; + } } } @@ -3427,8 +3432,7 @@ public class PackageParser { } private static VerifierInfo parseVerifier(Resources res, XmlPullParser parser, - AttributeSet attrs, int flags, String[] outError) throws XmlPullParserException, - IOException { + AttributeSet attrs, int flags) { final TypedArray sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.AndroidManifestPackageVerifier); @@ -3671,7 +3675,10 @@ public class PackageParser { public String packageName; // TODO: work towards making these paths invariant + + /** Base APK */ public String codePath; + /** Split APKs, ordered by parsed splitName */ public String[] splitCodePaths; // For now we only support one application per package. @@ -3717,7 +3724,8 @@ public class PackageParser { public int mSharedUserLabel; // Signatures that were read from the package. - public Signature mSignatures[]; + public Signature[] mSignatures; + public Certificate[][] mCertificates; // For use by package manager service for quick lookup of // preferred up order. @@ -3779,8 +3787,8 @@ public class PackageParser { /** * Data used to feed the KeySetManager */ - public Set<PublicKey> mSigningKeys; - public Map<String, Set<PublicKey>> mKeySetMapping; + public ArraySet<PublicKey> mSigningKeys; + public ArrayMap<String, ArraySet<PublicKey>> mKeySetMapping; public Package(String packageName) { this.packageName = packageName; @@ -3788,6 +3796,15 @@ public class PackageParser { applicationInfo.uid = -1; } + public Collection<String> getAllCodePaths() { + ArrayList<String> paths = new ArrayList<>(); + paths.add(codePath); + if (!ArrayUtils.isEmpty(splitCodePaths)) { + Collections.addAll(paths, splitCodePaths); + } + return paths; + } + public void setPackageName(String newName) { packageName = newName; applicationInfo.packageName = newName; @@ -4390,6 +4407,33 @@ public class PackageParser { sCompatibilityModeEnabled = compatibilityModeEnabled; } + private static AtomicReference<byte[]> sBuffer = new AtomicReference<byte[]>(); + + public static long readFullyIgnoringContents(InputStream in) throws IOException { + byte[] buffer = sBuffer.getAndSet(null); + if (buffer == null) { + buffer = new byte[4096]; + } + + int n = 0; + int count = 0; + while ((n = in.read(buffer, 0, buffer.length)) != -1) { + count += n; + } + + sBuffer.set(buffer); + return count; + } + + public static void closeQuietly(StrictJarFile jarFile) { + if (jarFile != null) { + try { + jarFile.close(); + } catch (Exception ignored) { + } + } + } + public static class PackageParserException extends Exception { public final int error; @@ -4397,5 +4441,10 @@ public class PackageParser { super(detailMessage); this.error = error; } + + public PackageParserException(int error, String detailMessage, Throwable throwable) { + super(detailMessage, throwable); + this.error = error; + } } } diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 757f38e..96db772 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -719,6 +719,26 @@ public class UserManager { /** * If the target user is a managed profile of the calling user or the caller + * is itself a managed profile, then this returns a copy of the label with + * badging for accessibility services like talkback. E.g. passing in "Email" + * and it might return "Work Email" for Email in the work profile. + * + * @param label The label to change. + * @param user The target user. + * @return A label that combines the original label and a badge as + * determined by the system. + */ + public String getBadgedLabelForUser(String label, UserHandle user) { + UserInfo userInfo = getUserIfProfile(user.getIdentifier()); + if (userInfo != null && userInfo.isManagedProfile()) { + return Resources.getSystem().getString( + R.string.managed_profile_label_badge, label); + } + return label; + } + + /** + * If the target user is a managed profile of the calling user or the caller * is itself a managed profile, then this returns a drawable to use as a small * icon to include in a view to distinguish it from the original icon. * diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 9156216..89c2f37 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -45,7 +45,6 @@ import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Region; import android.graphics.Shader; -import android.graphics.PorterDuff.Mode; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.hardware.display.DisplayManagerGlobal; @@ -4822,20 +4821,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, final float x = r.exactCenterX(); final float y = r.exactCenterY(); - setDrawableHotspot(x, y); - } - - /** - * Sets the hotspot position for this View's drawables. - * - * @param x hotspot x coordinate - * @param y hotspot y coordinate - * @hide - */ - protected void setDrawableHotspot(float x, float y) { - if (mBackground != null) { - mBackground.setHotspot(x, y); - } + drawableHotspotChanged(x, y); } /** @@ -6798,7 +6784,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ private void setPressed(boolean pressed, float x, float y) { if (pressed) { - setDrawableHotspot(x, y); + drawableHotspotChanged(x, y); } setPressed(pressed); @@ -9149,7 +9135,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, break; case MotionEvent.ACTION_MOVE: - setDrawableHotspot(x, y); + drawableHotspotChanged(x, y); // Be lenient about moving outside of buttons if (!pointInView(x, y, mTouchSlop)) { @@ -15531,6 +15517,20 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * This function is called whenever the drawable hotspot changes. + * <p> + * Be sure to call through to the superclass when overriding this function. + * + * @param x hotspot x coordinate + * @param y hotspot y coordinate + */ + public void drawableHotspotChanged(float x, float y) { + if (mBackground != null) { + mBackground.setHotspot(x, y); + } + } + + /** * Call this to force a view to update its drawable state. This will cause * drawableStateChanged to be called on this view. Views that are interested * in the new state should call getDrawableState. diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java index eb3f882..7e2d809 100644 --- a/core/java/android/widget/AbsSeekBar.java +++ b/core/java/android/widget/AbsSeekBar.java @@ -24,7 +24,6 @@ import android.graphics.Canvas; import android.graphics.Insets; import android.graphics.PorterDuff; import android.graphics.Rect; -import android.graphics.PorterDuff.Mode; import android.graphics.Region.Op; import android.graphics.drawable.Drawable; import android.os.Bundle; @@ -343,7 +342,10 @@ public abstract class AbsSeekBar extends ProgressBar { @Override public void jumpDrawablesToCurrentState() { super.jumpDrawablesToCurrentState(); - if (mThumb != null) mThumb.jumpToCurrentState(); + + if (mThumb != null) { + mThumb.jumpToCurrentState(); + } } @Override @@ -361,29 +363,12 @@ public abstract class AbsSeekBar extends ProgressBar { } } - /** @hide */ @Override - protected void setDrawableHotspot(float x, float y) { - super.setDrawableHotspot(x, y); + public void drawableHotspotChanged(float x, float y) { + super.drawableHotspotChanged(x, y); - final Drawable progressDrawable = getProgressDrawable(); - if (progressDrawable != null) { - progressDrawable.setHotspot(x, y); - } - - final Drawable thumb = mThumb; - if (thumb != null) { - thumb.setHotspot(x, y); - } - } - - @Override - public void invalidateDrawable(Drawable dr) { - super.invalidateDrawable(dr); - - if (dr == mThumb) { - // Handle changes to thumb width and height. - requestLayout(); + if (mThumb != null) { + mThumb.setHotspot(x, y); } } @@ -479,11 +464,11 @@ public abstract class AbsSeekBar extends ProgressBar { final Drawable background = getBackground(); if (background != null) { - final Rect bounds = mThumb.getBounds(); + final Rect bounds = thumb.getBounds(); final int offsetX = mPaddingLeft - mThumbOffset; final int offsetY = mPaddingTop; - background.setHotspotBounds(left + offsetX, bounds.top + offsetY, - right + offsetX, bounds.bottom + offsetY); + background.setHotspotBounds(left + offsetX, top + offsetY, + right + offsetX, bottom + offsetY); } // Canvas will be translated, so 0,0 is where we start drawing @@ -505,8 +490,8 @@ public abstract class AbsSeekBar extends ProgressBar { @Override protected synchronized void onDraw(Canvas canvas) { super.onDraw(canvas); - drawThumb(canvas); + } @Override diff --git a/core/java/android/widget/CheckedTextView.java b/core/java/android/widget/CheckedTextView.java index 7113793..4aa2300 100644 --- a/core/java/android/widget/CheckedTextView.java +++ b/core/java/android/widget/CheckedTextView.java @@ -307,10 +307,9 @@ public class CheckedTextView extends TextView implements Checkable { } } - /** @hide */ @Override - protected void setDrawableHotspot(float x, float y) { - super.setDrawableHotspot(x, y); + public void drawableHotspotChanged(float x, float y) { + super.drawableHotspotChanged(x, y); if (mCheckMarkDrawable != null) { mCheckMarkDrawable.setHotspot(x, y); diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java index 747d2b1..9ba0fe1 100644 --- a/core/java/android/widget/CompoundButton.java +++ b/core/java/android/widget/CompoundButton.java @@ -24,7 +24,6 @@ import android.content.Context; import android.content.res.ColorStateList; import android.content.res.TypedArray; import android.graphics.Canvas; -import android.graphics.PorterDuff.Mode; import android.graphics.drawable.Drawable; import android.os.Parcel; import android.os.Parcelable; @@ -431,10 +430,9 @@ public abstract class CompoundButton extends Button implements Checkable { } } - /** @hide */ @Override - protected void setDrawableHotspot(float x, float y) { - super.setDrawableHotspot(x, y); + public void drawableHotspotChanged(float x, float y) { + super.drawableHotspotChanged(x, y); if (mButtonDrawable != null) { mButtonDrawable.setHotspot(x, y); diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java index 5a14929..34f333e 100644 --- a/core/java/android/widget/FrameLayout.java +++ b/core/java/android/widget/FrameLayout.java @@ -26,7 +26,6 @@ import android.graphics.Canvas; import android.graphics.PorterDuff; import android.graphics.Rect; import android.graphics.Region; -import android.graphics.PorterDuff.Mode; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.Gravity; @@ -223,10 +222,9 @@ public class FrameLayout extends ViewGroup { } } - /** @hide */ @Override - protected void setDrawableHotspot(float x, float y) { - super.setDrawableHotspot(x, y); + public void drawableHotspotChanged(float x, float y) { + super.drawableHotspotChanged(x, y); if (mForeground != null) { mForeground.setHotspot(x, y); diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java index 399e087..5d578ca 100644 --- a/core/java/android/widget/ImageView.java +++ b/core/java/android/widget/ImageView.java @@ -32,7 +32,6 @@ import android.graphics.PorterDuffColorFilter; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Xfermode; -import android.graphics.PorterDuff.Mode; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; @@ -1109,10 +1108,9 @@ public class ImageView extends View { } } - /** @hide */ @Override - protected void setDrawableHotspot(float x, float y) { - super.setDrawableHotspot(x, y); + public void drawableHotspotChanged(float x, float y) { + super.drawableHotspotChanged(x, y); if (mDrawable != null) { mDrawable.setHotspot(x, y); diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java index 62a8bec..394b255 100644 --- a/core/java/android/widget/ProgressBar.java +++ b/core/java/android/widget/ProgressBar.java @@ -1623,10 +1623,9 @@ public class ProgressBar extends View { } } - /** @hide */ @Override - protected void setDrawableHotspot(float x, float y) { - super.setDrawableHotspot(x, y); + public void drawableHotspotChanged(float x, float y) { + super.drawableHotspotChanged(x, y); if (mProgressDrawable != null) { mProgressDrawable.setHotspot(x, y); diff --git a/core/java/android/widget/QuickContactBadge.java b/core/java/android/widget/QuickContactBadge.java index 14d782d..23fa402 100644 --- a/core/java/android/widget/QuickContactBadge.java +++ b/core/java/android/widget/QuickContactBadge.java @@ -112,10 +112,9 @@ public class QuickContactBadge extends ImageView implements OnClickListener { } } - /** @hide */ @Override - protected void setDrawableHotspot(float x, float y) { - super.setDrawableHotspot(x, y); + public void drawableHotspotChanged(float x, float y) { + super.drawableHotspotChanged(x, y); if (mOverlay != null) { mOverlay.setHotspot(x, y); diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java index 03193a2..cca29cf 100644 --- a/core/java/android/widget/Switch.java +++ b/core/java/android/widget/Switch.java @@ -962,10 +962,9 @@ public class Switch extends CompoundButton { invalidate(); } - /** @hide */ @Override - protected void setDrawableHotspot(float x, float y) { - super.setDrawableHotspot(x, y); + public void drawableHotspotChanged(float x, float y) { + super.drawableHotspotChanged(x, y); if (mThumbDrawable != null) { mThumbDrawable.setHotspot(x, y); diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 0f51e8b..d470586 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -3503,10 +3503,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } - /** @hide */ @Override - protected void setDrawableHotspot(float x, float y) { - super.setDrawableHotspot(x, y); + public void drawableHotspotChanged(float x, float y) { + super.drawableHotspotChanged(x, y); final Drawables dr = mDrawables; if (dr != null) { diff --git a/core/res/res/drawable-hdpi/ic_corp_badge.png b/core/res/res/drawable-hdpi/ic_corp_badge.png Binary files differdeleted file mode 100644 index c79ce92..0000000 --- a/core/res/res/drawable-hdpi/ic_corp_badge.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/ic_corp_icon_badge.png b/core/res/res/drawable-hdpi/ic_corp_icon_badge.png Binary files differdeleted file mode 100644 index 0059e09..0000000 --- a/core/res/res/drawable-hdpi/ic_corp_icon_badge.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/ic_corp_badge.png b/core/res/res/drawable-mdpi/ic_corp_badge.png Binary files differdeleted file mode 100644 index c1447fe..0000000 --- a/core/res/res/drawable-mdpi/ic_corp_badge.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/ic_corp_icon_badge.png b/core/res/res/drawable-mdpi/ic_corp_icon_badge.png Binary files differdeleted file mode 100644 index 5ff8c5d..0000000 --- a/core/res/res/drawable-mdpi/ic_corp_icon_badge.png +++ /dev/null diff --git a/core/res/res/drawable-xhdpi/ic_corp_badge.png b/core/res/res/drawable-xhdpi/ic_corp_badge.png Binary files differdeleted file mode 100644 index 2d3d748..0000000 --- a/core/res/res/drawable-xhdpi/ic_corp_badge.png +++ /dev/null diff --git a/core/res/res/drawable-xhdpi/ic_corp_icon_badge.png b/core/res/res/drawable-xhdpi/ic_corp_icon_badge.png Binary files differdeleted file mode 100644 index dc5716d..0000000 --- a/core/res/res/drawable-xhdpi/ic_corp_icon_badge.png +++ /dev/null diff --git a/core/res/res/drawable-xxhdpi/ic_corp_badge.png b/core/res/res/drawable-xxhdpi/ic_corp_badge.png Binary files differdeleted file mode 100644 index 430e63b..0000000 --- a/core/res/res/drawable-xxhdpi/ic_corp_badge.png +++ /dev/null diff --git a/core/res/res/drawable-xxhdpi/ic_corp_icon_badge.png b/core/res/res/drawable-xxhdpi/ic_corp_icon_badge.png Binary files differdeleted file mode 100644 index cc00dd8..0000000 --- a/core/res/res/drawable-xxhdpi/ic_corp_icon_badge.png +++ /dev/null diff --git a/core/res/res/drawable/ic_corp_badge.xml b/core/res/res/drawable/ic_corp_badge.xml new file mode 100644 index 0000000..16c101b --- /dev/null +++ b/core/res/res/drawable/ic_corp_badge.xml @@ -0,0 +1,43 @@ +<!-- +Copyright (C) 2014 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" > + <size + android:width="20.0dp" + android:height="20.0dp"/> + + <viewport + android:viewportWidth="20.0" + android:viewportHeight="20.0"/> + + <path + android:pathData="M10.0,10.0m-10.0,0.0a10.0,10.0 0.0,1.0 1.0,20.0 0.0a10.0,10.0 0.0,1.0 1.0,-20.0 0.0" + android:fill="#FF5722"/> + <path + android:pathData="M11.139,12.149l-0.001,0.0L8.996,12.149l0.0,-0.571L4.738,11.578l-0.002,2.198c0.0,0.589 0.477,1.066 1.066,1.066l8.535,0.0c0.589,0.0 1.066,-0.477 1.066,-1.066l0.0,-2.198l-4.264,0.0L11.139,12.149z" + android:fill="#FFFFFF"/> + <path + android:pathData="M8.996,10.006l2.143,0.0l0.0,0.52l4.442,0.0L15.580999,7.909c0.0,-0.589 -0.477,-1.066 -1.066,-1.066l-1.877,0.0L7.544,6.843L5.606,6.843c-0.589,0.0 -1.061,0.477 -1.061,1.066l-0.003,2.617l4.453,0.0L8.996,10.006L8.996,10.006z" + android:fill="#FFFFFF"/> + <path + android:pathData="M3.367,3.456 h13.016 v13.016 h-13.016z" + android:fill="#00000000"/> + <path + android:pathData="M7.368,5.263l5.263,0.0l0.0,1.053l-5.263,0.0z" + android:fill="#FFFFFF"/> + <path + android:pathData="M8.996,12.149l2.1419992,0.0 0.0010004044,0.0 0.0,-0.5699997 -2.1429996,0.0z" + android:fill="#00000000"/> +</vector> diff --git a/core/res/res/drawable/ic_corp_icon_badge.xml b/core/res/res/drawable/ic_corp_icon_badge.xml new file mode 100644 index 0000000..c8e49e1 --- /dev/null +++ b/core/res/res/drawable/ic_corp_icon_badge.xml @@ -0,0 +1,54 @@ +<!-- +Copyright (C) 2014 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" > + <size + android:width="64.0dp" + android:height="64.0dp"/> + + <viewport + android:viewportWidth="64.0" + android:viewportHeight="64.0"/> + + <path + android:fill="#FF000000" + android:pathData="M49.062,50.0m-14.0,0.0a14.0,14.0 0.0,1.0 1.0,28.0 0.0a14.0,14.0 0.0,1.0 1.0,-28.0 0.0" + android:fillOpacity="0.2"/> + <path + android:fill="#FF000000" + android:pathData="M49.0,49.5m-14.0,0.0a14.0,14.0 0.0,1.0 1.0,28.0 0.0a14.0,14.0 0.0,1.0 1.0,-28.0 0.0" + android:fillOpacity="0.2"/> + <path + android:pathData="M49.0,49.0m-14.0,0.0a14.0,14.0 0.0,1.0 1.0,28.0 0.0a14.0,14.0 0.0,1.0 1.0,-28.0 0.0" + android:fill="#FF5722"/> + <path + android:pathData="M50.594,52.009l-3.0,0.0L47.594,51.0l-5.961,0.0l-0.003,3.289c0.0,0.826 0.668,1.494 1.494,1.494l11.948,0.0c0.826,0.0 1.494,-0.668 1.494,-1.494L56.566006,51.0l-5.972,0.0C50.594,51.0 50.594,52.009 50.594,52.009z" + android:fill="#FFFFFF"/> + <path + android:pathData="M47.594,49.009l3.0,0.0L50.594,50.0l6.22,0.0l0.0,-3.925c0.0,-0.826 -0.668,-1.494 -1.494,-1.494l-2.627,0.0l-7.131,-0.001l-2.713,0.0c-0.826,0.0 -1.486,0.668 -1.486,1.494L41.359,50.0l6.235,0.0L47.594,49.009z" + android:fill="#FFFFFF"/> + <path + android:pathData="M39.714,39.838 h18.221 v18.221 h-18.221z" + android:fill="#00000000"/> + <path + android:pathData="M47.594,49.009 h3.0 v0.991 h-3.0z" + android:fill="#00000000"/> + <path + android:pathData="M47.594,51.0 h3.0 v1.009 h-3.0z" + android:fill="#00000000"/> + <path + android:pathData="M46.0,43.0l6.0,0.0l0.0,1.0l-6.0,0.0z" + android:fill="#FFFFFF"/> +</vector> diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index a8735cb..4e06d9a 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -814,6 +814,14 @@ via adb. The default value of this attribute is <code>true</code>. --> <attr name="allowBackup" format="boolean" /> + <!-- Indicates that even though the application provides a <code>BackupAgent</code>, + only full-data streaming backup operations are to be performed to save the app's + data. This lets the app rely on full-data backups while still participating in + the backup and restore process via the BackupAgent's full-data backup APIs. + When this attribute is <code>true</code> the app's BackupAgent overrides of + the onBackup() and onRestore() callbacks can be empty stubs. --> + <attr name="fullBackupOnly" format="boolean" /> + <!-- Whether the application in question should be terminated after its settings have been restored during a full-system restore operation. Single-package restore operations will never cause the application to @@ -1060,6 +1068,7 @@ <attr name="testOnly" /> <attr name="backupAgent" /> <attr name="allowBackup" /> + <attr name="fullBackupOnly" /> <attr name="killAfterRestore" /> <attr name="restoreNeedsApplication" /> <attr name="restoreAnyVersion" /> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index a4f78bd..471eece 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1464,7 +1464,7 @@ <!-- Name of the CustomDialog that is used for VPN --> <string name="config_customVpnConfirmDialogComponent" - >com.android.vpndialogs/com.android.vpndialogs.CustomDialog</string> + >com.android.vpndialogs/com.android.vpndialogs.ConfirmDialog</string> <!-- Apps that are authorized to access shared accounts, overridden by product overlays --> <string name="config_appsAuthorizedForSharedAccounts">;com.android.settings;</string> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index c5ce229..f6ad78b 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2206,6 +2206,7 @@ <public type="attr" name="buttonTintMode" /> <public type="attr" name="thumbTint" /> <public type="attr" name="thumbTintMode" /> + <public type="attr" name="fullBackupOnly" /> <public-padding type="dimen" name="l_resource_pad" end="0x01050010" /> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index e31dbaf..c8c0d23 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -4728,6 +4728,13 @@ <!-- Accessibility announcement when a number that had been typed in is deleted [CHAR_LIMIT=NONE] --> <string name="deleted_key"><xliff:g id="key" example="4">%1$s</xliff:g> deleted</string> + <!-- + Used to wrap a label for content description for a managed profile, e.g. "Work Email" instead + of email when there are two email apps. + [CHAR LIMIT=20] + --> + <string name="managed_profile_label_badge">Work <xliff:g id="label" example="Email">%1$s</xliff:g></string> + <!-- DO NOT TRANSLATE --> <string name="time_placeholder">--</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 6a6a045..1547bbd 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -864,6 +864,7 @@ <java-symbol type="string" name="action_bar_home_description_format" /> <java-symbol type="string" name="action_bar_home_subtitle_description_format" /> <java-symbol type="string" name="wireless_display_route_description" /> + <java-symbol type="string" name="managed_profile_label_badge" /> <java-symbol type="string" name="mediasize_unknown_portrait" /> <java-symbol type="string" name="mediasize_unknown_landscape" /> <java-symbol type="string" name="mediasize_iso_a0" /> diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java index 7251e7c..7f41ac1c 100644 --- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java +++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java @@ -305,11 +305,9 @@ public class PackageManagerTests extends AndroidTestCase { private PackageParser.Package parsePackage(Uri packageURI) throws PackageParserException { final String archiveFilePath = packageURI.getPath(); - PackageParser packageParser = new PackageParser(archiveFilePath); + PackageParser packageParser = new PackageParser(); File sourceFile = new File(archiveFilePath); - DisplayMetrics metrics = new DisplayMetrics(); - metrics.setToDefaults(); - PackageParser.Package pkg = packageParser.parseMonolithicPackage(sourceFile, metrics, 0); + PackageParser.Package pkg = packageParser.parseMonolithicPackage(sourceFile, 0); packageParser = null; return pkg; } diff --git a/data/fonts/fallback_fonts.xml b/data/fonts/fallback_fonts.xml index 1eaae65..37e9b15 100644 --- a/data/fonts/fallback_fonts.xml +++ b/data/fonts/fallback_fonts.xml @@ -219,6 +219,21 @@ </family> <family> <fileset> + <file>NotoSansCherokee-Regular.ttf</file> + </fileset> + </family> + <family> + <fileset> + <file>NotoSansCanadianAboriginal-Regular.ttf</file> + </fileset> + </family> + <family> + <fileset> + <file>NotoSansYi-Regular.ttf</file> + </fileset> + </family> + <family> + <fileset> <file lang="zh-CN">NotoSansHans-Regular.otf</file> </fileset> </family> diff --git a/docs/html/images/tools/android-studio.png b/docs/html/images/tools/android-studio.png Binary files differdeleted file mode 100644 index 4d93a86..0000000 --- a/docs/html/images/tools/android-studio.png +++ /dev/null diff --git a/docs/html/images/tools/laptop-studio.png b/docs/html/images/tools/laptop-studio.png Binary files differnew file mode 100644 index 0000000..3684ff0 --- /dev/null +++ b/docs/html/images/tools/laptop-studio.png diff --git a/docs/html/jd_extras.js b/docs/html/jd_extras.js index d8db5bf..2f63700 100644 --- a/docs/html/jd_extras.js +++ b/docs/html/jd_extras.js @@ -15,6 +15,17 @@ DISTRIBUTE_RESOURCES = DISTRIBUTE_RESOURCES.concat([ { + "title":"Android L Developer Preview", + "titleFriendly":"", + "summary":"<p style='font-size:18px;'>Get an early look at the next release and get your apps ready when the platform officially launches.</p>", + "url":"preview/index.html", + "group":"", + "keywords": [], + "tags": [], + "image":"preview/images/l-dev-prev.png", + "type":"" + }, + { "title":"Developer Registration", "titleFriendly":"", "summary":"Additional information about the registration process.", diff --git a/docs/html/preview/api-overview.jd b/docs/html/preview/api-overview.jd index 2fa029f..8ec2470 100644 --- a/docs/html/preview/api-overview.jd +++ b/docs/html/preview/api-overview.jd @@ -18,7 +18,6 @@ sdk.platform.apiLevel=20 <li><a href="#ART">New Android Runtime (ART)</a></li> <li><a href="#BehaviorNotifications">If your app implements notifications...</a></li> <li><a href="#BehaviorMediaControl">If your app uses RemoteControlClient...</a></li> - <li><a href="#BehaviorFullscreen">If your app uses fullScreenIntent...</a></li> <li><a href="#BehaviorGetRecentTasks">If your app uses ActivityManager.getRecentTasks()...</a></li> </ol> </li> @@ -64,12 +63,13 @@ sdk.platform.apiLevel=20 <li><a href="#Power">Power Efficiency</a> <ol> <li><a href="#JobScheduler">Scheduling Jobs</a></li> - <li><a href="#PowerMeasurementTools">Developer tools and APIs for power measurement</a> + <li><a href="#PowerMeasurementTools">Developer tools for power measurement</a> </ol> </li> <li><a href="#Enterprise">Enterprise</a> <ol> <li><a href="#ManagedProvisioning">Managed provisioning</a></li> + <li><a href="#LockToAppMode">Lock-to-App mode</a></li> </ol> </li> <li><a href="#Printing">Printing Framework</a> @@ -99,15 +99,15 @@ Differences Report »</a> </li> </div> </div> -<p>The L Developer Preview gives you an advance look at the upcoming release for -the Android platform, -which offers new features for users and app developers. This document provides -an introduction to the most notable APIs.</p> +<p>The L Developer Preview gives you an advance look at the upcoming release +for the Android platform, which offers new features for users and app +developers. This document provides an introduction to the most notable APIs.</p> -<p>The L Developer Preview is intended for <strong>developer early adopters</strong> and -<strong>testers</strong>. If you are interested in influencing the direction of the -Android framework, <a href="{@docRoot}preview/setup-sdk.html">give the L -Developer Preview a try</a> and send us your feedback!</p> +<p>The L Developer Preview is intended for <strong>developer early +adopters</strong> and <strong>testers</strong>. If you are interested in +influencing the direction of the Android framework, +<a href="{@docRoot}preview/setup-sdk.html">give the L Developer Preview a +try</a> and send us your feedback!</p> <p class="caution"><strong>Caution:</strong> Do not not publish apps that use the L Developer Preview to the Google Play store.</p> @@ -128,8 +128,8 @@ reference</a>.</p> <h3 id="ART">New Android Runtime (ART)</h3> <p>The 4.4 release introduced a new, experimental Android runtime, ART. Under -4.4, ART was optional, and the default runtime remained Dalvik. With the L Developer Preview, ART is -now the default runtime.</p> +4.4, ART was optional, and the default runtime remained Dalvik. With the L +Developer Preview, ART is now the default runtime.</p> <p>For an overview of ART's new features, see <a href="https://source.android.com/devices/tech/dalvik/art.html">Introducing @@ -163,6 +163,15 @@ Behavior on the Android Runtime (ART)</a>. Pay particular attention if:</p> backgrounds to match the new material design widgets. Make sure that all your notifications look right with the new color scheme:</p> +<div class="figure" style="width:220px"> + <img src="images/hun-example.png" + srcset="images/hun-example@2x.png 2x" + alt="" width="220" height="372" id="figure1" /> + <p class="img-caption"> + <strong>Figure 1.</strong> Fullscreen activity showing a heads-up notification + </p> +</div> + <ul> <li>Update or remove assets that involve color.</li> @@ -179,39 +188,48 @@ notifications look right with the new color scheme:</p> <p>If you are currently adding sounds and vibrations to your notifications by using the {@link android.media.Ringtone}, {@link android.media.MediaPlayer}, or {@link android.os.Vibrator} classes, remove this code so that -the system can present notifications correctly in <a href="#DoNotDisturb">Do Not Disturb</a> mode. -Instead, use the {@link android.app.Notification.Builder} methods instead to add -sounds and vibration.</p> +the system can present notifications correctly in <a href="#DoNotDisturb">Do Not Disturb</a> mode. Instead, use the {@link android.app.Notification.Builder} methods instead to add sounds and vibration.</p> + +<p>Notifications now appear in a small floating window +(also called a <em>heads-up notification</em>) when the device is active +(that is, the device is unlocked and its screen is on). These notifications +appear similar to the compact form of your notification, except that the +heads-up notification also shows action buttons. Users can act on, or dismiss, +a heads-up notification without leaving the current app.</p> + +<p>Examples of conditions that may trigger heads-up notifications include:</p> + +<ul> + <li>The user's activity is in fullscreen mode (the app uses +{@link android.app.Notification#fullScreenIntent}), or</li> + <li>The notification has high priority and uses ringtones or + vibrations</li> +</ul> + +<p>If your app implements notifications under those scenarios, make sure that +heads-up notifications are presented correctly.</p> <h3 id="BehaviorMediaControl">If your app uses RemoteControlClient...</h3> -<p>Lockscreens in the L Developer Preview do not show transport controls for your -{@link android.media.RemoteControlClient}. Instead, your app can provide -media playback control from the lockscreen through a media notification. This +<p>Lockscreens in the L Developer Preview do not show transport controls for +your {@link android.media.RemoteControlClient}. Instead, your app can provide +media playback control from the lockscreen through a notification. This gives your app more control over the presentation of media buttons, while providing a consistent experience for users across the lockscreen and unlocked device.</p> +<p>The L Developer Preview introduces a new {@code android.app.Notification.MediaStyle} template which is recommended for this purpose. {@code MediaStyle} converts notification actions that you added with {@link android.app.Notification.Builder#addAction(int, java.lang.CharSequence, android.app.PendingIntent) Notification.Builder.addAction()} into compact buttons embedded in your app's media playback notifications.</p> + +<p>If you are using the new +{@code android.media.session.MediaSession} class (see <a href="#MediaPlaybackControl">Media Playback Control</a> below), attach your session +token with {@code Notification.MediaStyle.setMediaToken()} to inform the +system that this notification controls an ongoing media session.</p> + <p>Call {@code Notification.Builder.setVisibility(Notification.VISIBILITY_PUBLIC)} to mark a -notification as safe to display on the lockscreen (even when the lockscreen is -secured with a PIN, pattern, or password). For more information, see +notification as safe to show atop any lockscreen (secure or otherwise). For more information, see <a href="#LockscreenNotifications">Lockscreen Notifications</a>.</p> -<h3 id="BehaviorFullscreen">If your app uses fullScreenIntent...</h3> - -<p>Notifications now appear in a small floating window if all these conditions -are met:</p> - -<ul> - <li>The user’s activity is in fullscreen mode,</li> - <li>The screen is on, and</li> - <li>The device is unlocked</li> -</ul> - -<p>If your app implements fullscreen activities, make sure that -these heads-up notifications are presented correctly.</p> - <h3 id="BehaviorGetRecentTasks">If your app uses ActivityManager.getRecentTasks()...</h3> <p>With the introduction of the new <em>concurrent documents and activities tasks</em> feature in the upcoming @@ -230,7 +248,6 @@ information.</p> <h3 id="MaterialDesign">Material design support</h3> - <p>The upcoming release adds support for Android's new <em>material</em> design style. You can create apps with material design that are visually dynamic and have UI element transitions @@ -262,16 +279,17 @@ values:</p> <ul> <li>{@code VISIBILITY_PRIVATE}. Shows basic information, such as the -notification’s icon, but hides the notification’s full content. If you want to -provide a redacted public version of your notification for the system to display -on a secure lockscreen, create a public notification object and put a reference -to it in the private notification's {@code publicVersion} field.</li> -<li>{@code VISIBILITY_PUBLIC}. Shows the notification’s full content. This is - the system default if visibility is left unspecified.</li> -<li>{@code VISIBILITY_SECRET}. Shows only the most minimal information, -excluding even the notification’s icon.</li> +notification’s icon, but hides the notification’s full content.</li> +<li>{@code VISIBILITY_PUBLIC}. Shows the notification’s full content.</li> +<li>{@code VISIBILITY_SECRET}. Shows nothing, excluding even the +notification’s icon.</li> </ul> +<p>When {@code VISIBILITY_PRIVATE} is set, you can also provide a redacted +version of the notification content that hides personal details. For example, +an SMS app might display a notification that shows "You have 3 new text messages." but hides the message content and senders. To provide this alternative notification, first create the replacement notification using {@link android.app.Notification.Builder}. When you create the private notification object, attach +the replacement notification to it through the {@code Notification.Builder.setPublicVersion()} method.</p> + <h3 id="DoNotDisturb">Do Not Disturb mode</h3> <p>The L Developer Preview introduces a new <em>Do Not Disturb</em> mode. When @@ -295,7 +313,7 @@ make sure <em>Do Not Disturb</em> mode handles them properly. For example, if your app is an alarm clock, you can tag the notification as an alarm so it will wake the user up even if the device is in <em>Do Not Disturb</em> mode. For more information, see <a -href="NotificationsMetadata">Notifications metadata</a>.</p> +href="#NotificationsMetadata">Notifications metadata</a>.</p> <h3 id="NotificationsMetadata">Notifications metadata</h3> <p>The L Developer Preview uses metadata associated with your app notifications @@ -483,10 +501,9 @@ knows about your playback and can extract and show album art.</p> <h3 id="DirectorySelection">Directory selection</h3> -<p>The L Developer Preview extends the <a href="{@docRoot}guide/topics/providers/document-provider.html">Storage Access Framework</a> to let users -select an entire directory, rather than individual files, to give your app -read/write access to media files. When a directory is selected, your app also -has access to all its child directories and content.</p> +<p>The L Developer Preview extends the <a href="{@docRoot}guide/topics/providers/document-provider.html">Storage Access Framework</a> to let users select an entire directory, rather than individual files, to +give your app read/write access to media files. When a directory is selected, +your app also has access to all its child directories and content.</p> <p>To get the absolute paths to directories on external storage devices where applications can store media files, call the new @@ -498,6 +515,13 @@ considers to be a permanent part of the device, and includes emulated external storage and physical media slots such as SD cards in battery compartments.</p> +<p>You can bring up a system UI to allow the user to pick a directory subtree. +To do so, send {@code android.intent.action.OPEN_DOCUMENT_TREE} in an +{@link android.content.Intent}. If the call is successful, the system displays +the {@link android.provider.DocumentsProvider} instances installed on the +device for the user to select. When the user selects a directory from this UI, +the system returns a URI representing the selected directory tree.</p> + <p>If you want to access a document in an existing directory, call the {@code android.provider.DocumentsContract.buildDocumentViaUri()} method. Pass the method a URI representing the path to the parent directory, and the @@ -591,9 +615,9 @@ API that lets you optimize battery life by defining jobs for the system to run asynchronously at a later time or under specified conditions (such as when the device is charging). This is useful in such situations as:</p> <ul> - <li>The app has non-user-facing work that you want to defer until the unit is - plugged in.</li> - <li>The app has a task that requires network access (or requires a wifi + <li>The app has non-user-facing work that you can defer.</li> + <li>The app has work you'd prefer to do when the unit is plugged in.</li> + <li>The app has a task that requires network access (or requires a Wi-Fi connection).</li> <li>The app has a number of tasks that you want to run as a batch on a regular schedule.</li> @@ -612,6 +636,7 @@ conditions, such as:</p> <li>The device is charging</li> <li>The device is connected to an unmetered network</li> <li>The system deems the device to be idle</li> + <li>Completion with a minimum delay or within a specific deadline.</li> </ul> <p>For example, you can add code like this to run your task on an @@ -627,7 +652,7 @@ JobScheduler jobScheduler = jobScheduler.schedule(uploadTask); </pre> -<h3 id="PowerMeasurementTools">Developer tools and APIs for power measurement</h3> +<h3 id="PowerMeasurementTools">Developer tools for power measurement</h3> <p>The L Developer Preview provides several new developer tools and APIs to help you better measure and understand your app's power usage.</p> @@ -668,9 +693,9 @@ in {@code <sdk>/tools}.</p> <img src="images/battery_historian.png" srcset="images/battery_historian@2x.png 2x" alt="" width="440" height="240" - id="figure1" /> + id="figure2" /> <p class="img-caption"> - <strong>Figure 1.</strong>HTML visualization generated by the Battery + <strong>Figure 2.</strong>HTML visualization generated by the Battery Historian tool. </p> @@ -701,9 +726,9 @@ $ historian.par [-p powerfile] bugreport.txt > out.html <div class="figure" style="width:360px"> <img src="images/managed_apps_launcher.png" srcset="images/managed_apps_launcher@2x.png 2x" - alt="" width="360" height="572" id="figure2" /> + alt="" width="360" height="572" id="figure3" /> <p class="img-caption"> - <strong>Figure 2.</strong> Launcher screen showing managed apps (marked with + <strong>Figure 3.</strong> Launcher screen showing managed apps (marked with a lock badge) </p> </div> @@ -742,6 +767,36 @@ for the current user and any associated managed profiles. Your Launcher can make the managed apps visually prominent by appending a “work” badge to the icon drawable with {@code android.os.UserManager.getBadgeDrawableForUser()}.</p> +<h3 id="LockToAppMode">Lock-to-App mode</h3> +<p>The L Developer Preview introduces a new <em>Lock-to-App</em> mode that +lets you temporarily restrict users from leaving your app or being interrupted +by notifications. Once your app activates this mode, users will not be able to +see notifications, access other apps, or return to the Home screen, until your +app exits the mode.</p> + +<p>To prevent unauthorized usage, the device on which you want to activate +this mode must have managed profiles or must be fully controlled by a device administrator (see <a href="#ManagedProvisioning">Managed Provisioning</a> for more information). Furthermore, the device or managed profile owner must +authorize apps to use this mode by calling {@code android.app.admin.DevicePolicyManager.setLockTaskComponents()}.</p> + +<p>Before activating this mode in your app, verify that your activity is authorized by calling {@code DevicePolicyManager.isLockTaskPermitted()}.</p> + +<p>To activate <em>Lock-to-App</em> mode, call +{@code android.app.Activity.startLockTask()} from your authorized activity.</p> + +<p>When <em>Lock-to-App</em> mode is active, the following behavior takes +effect:</p> + +<ul> +<li>The status bar is blank, and user notifications and status information is hidden.</li> +<li>The Home and Recent Apps button is hidden.</li> +<li>Other apps may not launch new activities.</li> +<li>The current app may start new activities, as long as doing so does not +create new tasks.</li> +</ul> + +<p>The device will remain in this mode until an authorized activity calls +{@code Activity.stopLockTask()}. + <h2 id="Printing">Printing Framework</h2> <h3 id="PDFRender">Render PDF as bitmap</h3> @@ -752,7 +807,7 @@ accessed) on which the system writes the the printable content. Your app can obtain a page for rendering with {@code openPage()}, then call {@code render()} to turn the opened {@code PdfRenderer.Page} into a bitmap. You can also set additional parameters if you only want to convert a portion of the document into -a bitmap image (for example, to implement <a href="http://en.wikipedia.org/wiki/Tiled_rendering">tile rendering</a> in order to zoom in on the document).</p> +a bitmap image (for example, to implement <a href="http://en.wikipedia.org/wiki/Tiled_rendering">tiled rendering</a> in order to zoom in on the document).</p> <h2 id="TestingA11y">Testing & Accessibility </h2> @@ -796,8 +851,7 @@ your app needs.</p> <ul> <li>{@code FEATURE_LEANBACK}. Declares that your app must be installed only on -devices that support the <a href="{@docRoot}training/tv}">Android TV</a> user -interface. Example: +devices that support the <a href="{@docRoot}training/tv/index.html}">Android TV</a>user interface. Example: <pre> <uses-feature android:name="android.software.leanback" android:required="true" /> diff --git a/docs/html/preview/images/hun-example.png b/docs/html/preview/images/hun-example.png Binary files differnew file mode 100644 index 0000000..9613a92 --- /dev/null +++ b/docs/html/preview/images/hun-example.png diff --git a/docs/html/preview/images/hun-example@2x.png b/docs/html/preview/images/hun-example@2x.png Binary files differnew file mode 100644 index 0000000..3cb8f5b --- /dev/null +++ b/docs/html/preview/images/hun-example@2x.png diff --git a/docs/html/preview/images/l-dev-prev.png b/docs/html/preview/images/l-dev-prev.png Binary files differnew file mode 100644 index 0000000..95bad8c --- /dev/null +++ b/docs/html/preview/images/l-dev-prev.png diff --git a/docs/html/preview/images/managed_apps_launcher@2.png b/docs/html/preview/images/managed_apps_launcher@2x.png Binary files differindex d298fd2..d298fd2 100644 --- a/docs/html/preview/images/managed_apps_launcher@2.png +++ b/docs/html/preview/images/managed_apps_launcher@2x.png diff --git a/docs/html/preview/index.jd b/docs/html/preview/index.html index e44e9f3..368db84 100644 --- a/docs/html/preview/index.jd +++ b/docs/html/preview/index.html @@ -1,13 +1,143 @@ -page.title=Android L Developer Preview -page.viewport_width=970 -fullpage=true -no_footer_links=true -page.type=about -page.metaDescription=Test and build your apps against the next version of Android to ensure they're ready when the platform officially launches. -page.image={@docRoot}preview/images/hero.jpg -@jd:body +<!DOCTYPE html> -<style> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +<html> +<head> + + +<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> +<meta name="viewport" content="width=970" /> + +<meta name="Description" content="Test and build your apps against the next version of Android to ensure they're ready when the platform officially launches."> +<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" /> +<title>Android L Developer Preview | Android Developers</title> + +<!-- STYLESHEETS --> +<link rel="stylesheet" +href="//fonts.googleapis.com/css?family=Roboto+Condensed"> +<link rel="stylesheet" href="//fonts.googleapis.com/css?family=Roboto:light,regular,medium,thin,italic,mediumitalic,bold" + title="roboto"> +<link href="/assets/css/default.css" rel="stylesheet" type="text/css"> + + + +<!-- JAVASCRIPT --> +<script src="//www.google.com/jsapi" type="text/javascript"></script> +<script src="/assets/js/android_3p-bundle.js" type="text/javascript"></script> +<script type="text/javascript"> + var toRoot = "/"; + var metaTags = []; + var devsite = false; +</script> +<script src="/assets/js/docs.js" type="text/javascript"></script> + +<script> + (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ + (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), + m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) + })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); + + ga('create', 'UA-5831155-1', 'android.com'); + ga('create', 'UA-49880327-2', 'android.com', {'name': 'universal'}); // New tracker); + ga('send', 'pageview'); + ga('universal.send', 'pageview'); // Send page view for new tracker. +</script> + +</head> + +<body class="gc-documentation + +" itemscope itemtype="http://schema.org/Article"> + + +<a name="top"></a> +<div id="body-content"> +<div class="fullpage" > +<div id="jd-content"> + <div class="jd-descr" itemprop="articleBody"> + <style> .fullpage>#footer, #jd-content>.content-footer.wrap { display:none; @@ -23,61 +153,36 @@ page.image={@docRoot}preview/images/hero.jpg } </style> -<div class="landing-body-content"> - <div class="landing-hero-container"> - <div class="landing-section preview-hero"> - <div class="landing-hero-scrim"></div> - <div class="landing-hero-wrap"> - <div class="vertical-center-outer"> - <div class="vertical-center-inner"> - - <div class="col-12"> - <div class="landing-section-header"> - - <div class="landing-h1 hero">L Developer Preview</div> - <div class="landing-subhead hero"> - <p>An early look at the next release</p> - </div> - <div class="landing-hero-description"> - <p>Test and build your apps against the next<br /> - version of Android to ensure they're ready<br/> - when the platform officially launches.</p> - </div> - - <div class="landing-body"> - <a href="/preview/setup-sdk.html" class="landing-button landing-primary" style="margin-top: 40px;"> - Get Started - </a> - </div> - </div> - - </div> - </div> - </div> <!-- end .wrap --> - <div class="landing-scroll-down-affordance"> - <a class="landing-down-arrow" href="#extending-android-to-landingables"> - <img src="/wear/images/carrot.png" alt="Scroll down to read more"> - </a> - </div> - </div> <!-- end .landing-section .landing-hero --> - </div> <!-- end .landing-hero-container --> - - <div class="landing-rest-of-page"> - <div class="landing-section" id="extending-android-to-landingables"> + <div class="landing-section" style="padding-top:30px"> <div class="wrap"> <div class="landing-section-header"> - <div class="landing-h1">See What's New</div> + <div class="landing-h1">Android L Developer Preview</div> <div class="landing-subhead"> - Take advantage of all the new capabilities, which are focused on design and performance. + Get an early look at the next release and get your apps ready when the + platform officially launches. + </div> + + <img src="/preview/images/l-dev-prev.png" style=" margin:10px 0 0 100px" width="700px"/> + <div class="col-6" style="margin-left:630px; margin-top:-40px"> + <a href="/preview/setup-sdk.html" class="landing-button landing-secondary" style="position:absolute;z-index:100;float:right;margin-top: 0px; background-color:#09c">Get Started</a><!-- + <p>Set up your environment and check out all the docs to get up and running.</p>--> + + </div> </div> + </div> <!-- end .wrap --> + </div> <!-- end .landing-section --> - <div class="landing-body"> + + +<div class="landing-section landing-gray-background" style="margin-top:-80px; padding-bottom:20px"> + <div class="wrap"> + <div class="cols"> +<div class="landing-body" style="margin-top:-80px" > <div class="landing-breakout cols"> <div class="col-4"> - <img src="/preview/images/material.png" style="opacity:.6" alt=""> <p>A New UI Design</p> <p class="landing-small"> Create a consistent experience across mobile and the web with @@ -88,19 +193,16 @@ page.image={@docRoot}preview/images/hero.jpg </p> </div> <div class="col-4"> - <img src="/preview/images/art.png" alt=""> <p>A Rehauled Runtime</p> <p class="landing-small"> Test your apps and get them ready for <b>ART</b> (<b>A</b>ndroid <b>R</b>un<b>t</b>ime), the default runtime in the next release. - </p> <p class="landing-small"> <a href="/preview/api-overview.html#ART">Learn about ART</a> </p> </div> <div class="col-4"> - <img src="/preview/images/notifications.png" alt=""> <p style="width:230px">Enhanced Notifications</p> <p class="landing-small"> Get more control over where notifications appear, @@ -111,7 +213,6 @@ page.image={@docRoot}preview/images/hero.jpg </p> </div> <div class="col-4"> - <img src="/preview/images/volta.png" alt=""> <p>Project Volta</p> <p class="landing-small"> We've tuned the platform to be more energy efficient and @@ -122,44 +223,17 @@ page.image={@docRoot}preview/images/hero.jpg </p> </div> </div> - <p>See the <a href="{@docRoot}preview/api-overview.html">API overview</a> for more information + <p style="margin-left:20px">See the <a href="/preview/api-overview.html">API overview</a> for more information on the rest of the new and updated features.</p> </div> - </div> <!-- end .wrap --> - </div> <!-- end .landing-section --> - - - - <div class="landing-section landing-gray-background"> - <div class="wrap"> - <div class="landing-section-header"> - <div class="landing-h1">Get Your Apps Ready</div> - <div class="landing-subhead"> - <p>We're giving you an early look at the SDK, so you can test your apps and build in new features.</p> - </div> - </div> - <div class="landing-body"> - <p>You'll get the system images for the Nexus 5, Nexus 7 (v2), - and the emulator to take the new platform for a spin. In addition, you'll have - access to all the APIs with a preview build of the SDK. - </p> - - <p>Check out the getting started, developer guides, and reference documentation - for all the information you need to get up and running.</p> - - <a href="/preview/setup-sdk.html" class="landing-button landing-secondary" style="margin-top: 20px;"> - Get Started - </a> - </div> - </div> - </div> + </div></div></div> <div class="landing-section"> <div class="wrap"> <div class="cols"> <div class="landing-body"> <div class="col-3-wide"> - <a target="_blank" href="http://submit-bugs!"> - <img class="landing-social-image" src="{@docRoot}preview/images/bugs.png" alt=""> + <a target="_blank" href="https://code.google.com/p/android-developer-preview/"> + <img class="landing-social-image" src="/preview/images/bugs.png" alt=""> </a> <div class="landing-social-copy"> <p>Issue Tracker</p> @@ -167,7 +241,7 @@ page.image={@docRoot}preview/images/hero.jpg Let us know when you encounter problems, so we can fix them and make the platform better for you and your users. </p><p class="landing-small"> - <a target="_blank" href="http://submit-bugs!"> + <a target="_blank" href="https://code.google.com/p/android-developer-preview/"> Report Issues</a> </p> <p></p> @@ -189,8 +263,8 @@ page.image={@docRoot}preview/images/hero.jpg </div> </div> <div class="col-3-wide"> - <a target="_blank" href="{@docRoot}preview/release-notes.html"> - <img class="landing-social-image" src="{@docRoot}preview/images/updates.png" alt=""> + <a target="_blank" href="/preview/support.html"> + <img class="landing-social-image" src="/preview/images/updates.png" alt=""> </a> <div class="landing-social-copy"> <p>Support and Updates</p> @@ -200,13 +274,13 @@ page.image={@docRoot}preview/images/hero.jpg for news about the changes. </p> <p class="landing-small"> - <a target="_blank" href="{@docRoot}preview/support.html">Get Support</a> + <a target="_blank" href="/preview/support.html">Get Support</a> </p> </div> </div> </div> </div> - </div> <!-- end .wrap --> + </div> </div> <div class="content-footer wrap" itemscope="" itemtype="http://schema.org/SiteNavigationElement"> @@ -233,4 +307,55 @@ page.image={@docRoot}preview/images/hero.jpg }, 1000, "easeOutQuint"); e.preventDefault(); }); - </script>
\ No newline at end of file + </script> + </div> + + <div class="content-footer wrap" + itemscope itemtype="http://schema.org/SiteNavigationElement"> + + <div class="paging-links layout-content-col col-10"> + + </div> + <div class="layout-content-col plus-container col-2" > + <style>#___plusone_0 {float:right !important;}</style> + <div class="g-plusone" data-size="medium"></div> + + </div> + + </div> + + + + + </div> <!-- end jd-content --> + +<div id="footer" class="wrap" style="width:940px"> + + + <div id="copyright"> + + Except as noted, this content is + licensed under <a href="http://creativecommons.org/licenses/by/2.5/"> + Creative Commons Attribution 2.5</a>. For details and + restrictions, see the <a href="/license.html">Content + License</a>. + </div> + + +</div> <!-- end footer --> +</div><!-- end doc-content --> + +</div> <!-- end body-content --> + + + + + + <script src="https://developer.android.com/ytblogger_lists_unified.js" type="text/javascript"></script> + <script src="/jd_lists_unified.js" type="text/javascript"></script> + <script src="/jd_extras.js" type="text/javascript"></script> + <script src="/jd_collections.js" type="text/javascript"></script> + <script src="/jd_tag_helpers.js" type="text/javascript"></script> + +</body> +</html>
\ No newline at end of file diff --git a/docs/html/sdk/installing/studio.jd b/docs/html/sdk/installing/studio.jd index 8ac6163..894514a 100644 --- a/docs/html/sdk/installing/studio.jd +++ b/docs/html/sdk/installing/studio.jd @@ -186,7 +186,7 @@ This is the Android Software Development Kit License Agreement <div id="main"> <div class="figure" style="width:400px;margin-top:-75px"> -<img src="{@docRoot}images/tools/android-studio.png" height="330" width="400" style="margin-bottom:20px" /> +<img src="{@docRoot}images/tools/laptop-studio.png" height="366" width="400" style="margin-bottom:20px" /> <a class="big button subtitle" id="download-ide-button" href="" style="display:none;width:368px;margin:0 auto;display:block;font-size:18px" ></a> @@ -218,7 +218,7 @@ capabilities you expect from IntelliJ, Android Studio offers:</p> <li>Lint tools to catch performance, usability, version compatibility, and other problems.</li> <li>ProGuard and app-signing capabilities.</li> <li>Built-in support for <a - href="http://android-developers.blogspot.com/2013/06/adding-backend-to-your-app-in-android.html" + href="https://developers.google.com/cloud/devtools/android_studio_templates/" class="external-link">Google Cloud Platform</a>, making it easy to integrate Google Cloud Messaging and App Engine. </ul> diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java index 8be6eb7..38b8aaf 100644 --- a/graphics/java/android/graphics/drawable/DrawableContainer.java +++ b/graphics/java/android/graphics/drawable/DrawableContainer.java @@ -65,8 +65,6 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { private long mExitAnimationEnd; private Drawable mLastDrawable; - private Insets mInsets = Insets.NONE; - // overrides from Drawable @Override @@ -118,7 +116,10 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { */ @Override public Insets getOpticalInsets() { - return mInsets; + if (mCurrDrawable != null) { + return mCurrDrawable.getOpticalInsets(); + } + return Insets.NONE; } @Override @@ -203,9 +204,6 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { } if (mCurrDrawable != null) { mCurrDrawable.setBounds(bounds); - - // Must obtain optical insets after setting bounds. - mInsets = mCurrDrawable.getOpticalInsets(); } } @@ -422,15 +420,9 @@ public class DrawableContainer extends Drawable implements Drawable.Callback { d.setBounds(getBounds()); d.setLayoutDirection(getLayoutDirection()); d.setAutoMirrored(mDrawableContainerState.mAutoMirrored); - - // Must obtain optical insets after setting bounds. - mInsets = d.getOpticalInsets(); - } else { - mInsets = Insets.NONE; } } else { mCurrDrawable = null; - mInsets = Insets.NONE; mCurIndex = -1; } diff --git a/graphics/java/android/graphics/drawable/Ripple.java b/graphics/java/android/graphics/drawable/Ripple.java index 345400e..2d49365 100644 --- a/graphics/java/android/graphics/drawable/Ripple.java +++ b/graphics/java/android/graphics/drawable/Ripple.java @@ -142,14 +142,16 @@ class Ripple { } private void clampStartingPosition() { - final float dX = mStartingX - mBounds.exactCenterX(); - final float dY = mStartingY - mBounds.exactCenterY(); + final float cX = mBounds.exactCenterX(); + final float cY = mBounds.exactCenterY(); + final float dX = mStartingX - cX; + final float dY = mStartingY - cY; final float r = mOuterRadius; if (dX * dX + dY * dY > r * r) { // Point is outside the circle, clamp to the circumference. final double angle = Math.atan2(dY, dX); - mClampedStartingX = (float) (Math.cos(angle) * r); - mClampedStartingY = (float) (Math.sin(angle) * r); + mClampedStartingX = cX + (float) (Math.cos(angle) * r); + mClampedStartingY = cY + (float) (Math.sin(angle) * r); } else { mClampedStartingX = mStartingX; mClampedStartingY = mStartingY; diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 2f1e11e..3238498 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -2534,6 +2534,9 @@ public class AudioManager { // from AudioManager. AudioSystem is an internal class used by AudioManager and AudioService. /** @hide + * The audio device code for representing "no device." */ + public static final int DEVICE_NONE = AudioSystem.DEVICE_NONE; + /** @hide * The audio output device code for the small speaker at the front of the device used * when placing calls. Does not refer to an in-ear headphone without attached microphone, * such as earbuds, earphones, or in-ear monitors (IEM). Those would be handled as a diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index 9fbcd18..63ed10c 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -225,6 +225,7 @@ public class AudioSystem // audio device definitions: must be kept in sync with values in system/core/audio.h // + public static final int DEVICE_NONE = 0x0; // reserved bits public static final int DEVICE_BIT_IN = 0x80000000; public static final int DEVICE_BIT_DEFAULT = 0x40000000; diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java index bbe650d..7e9d279 100644 --- a/media/java/android/media/tv/TvContract.java +++ b/media/java/android/media/tv/TvContract.java @@ -22,7 +22,9 @@ import android.content.ContentUris; import android.net.Uri; import android.provider.BaseColumns; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** * <p> @@ -380,6 +382,81 @@ public final class TvContract { /** The service type for radio channels that have audio only. */ public static final int SERVICE_TYPE_AUDIO = 0x2; + /** The video format for 240p. */ + public static final String VIDEO_FORMAT_240P = "VIDEO_FORMAT_240P"; + + /** The video format for 360p. */ + public static final String VIDEO_FORMAT_360P = "VIDEO_FORMAT_360P"; + + /** The video format for 480i. */ + public static final String VIDEO_FORMAT_480I = "VIDEO_FORMAT_480I"; + + /** The video format for 480p. */ + public static final String VIDEO_FORMAT_480P = "VIDEO_FORMAT_480P"; + + /** The video format for 576i. */ + public static final String VIDEO_FORMAT_576I = "VIDEO_FORMAT_576I"; + + /** The video format for 576p. */ + public static final String VIDEO_FORMAT_576P = "VIDEO_FORMAT_576P"; + + /** The video format for 720p. */ + public static final String VIDEO_FORMAT_720P = "VIDEO_FORMAT_720P"; + + /** The video format for 1080i. */ + public static final String VIDEO_FORMAT_1080I = "VIDEO_FORMAT_1080I"; + + /** The video format for 1080p. */ + public static final String VIDEO_FORMAT_1080P = "VIDEO_FORMAT_1080P"; + + /** The video format for 2160p. */ + public static final String VIDEO_FORMAT_2160P = "VIDEO_FORMAT_2160P"; + + /** The video format for 4320p. */ + public static final String VIDEO_FORMAT_4320P = "VIDEO_FORMAT_4320P"; + + /** The video resolution for standard-definition. */ + public static final String VIDEO_RESOLUTION_SD = "VIDEO_RESOLUTION_SD"; + + /** The video resolution for enhanced-definition. */ + public static final String VIDEO_RESOLUTION_ED = "VIDEO_RESOLUTION_ED"; + + /** The video resolution for high-definition. */ + public static final String VIDEO_RESOLUTION_HD = "VIDEO_RESOLUTION_HD"; + + /** The video resolution for full high-definition. */ + public static final String VIDEO_RESOLUTION_FHD = "VIDEO_RESOLUTION_FHD"; + + /** The video resolution for ultra high-definition. */ + public static final String VIDEO_RESOLUTION_UHD = "VIDEO_RESOLUTION_UHD"; + + private static final Map<String, String> VIDEO_FORMAT_TO_RESOLUTION_MAP = + new HashMap<String, String>(); + + static { + VIDEO_FORMAT_TO_RESOLUTION_MAP.put(VIDEO_FORMAT_480I, VIDEO_RESOLUTION_SD); + VIDEO_FORMAT_TO_RESOLUTION_MAP.put(VIDEO_FORMAT_480P, VIDEO_RESOLUTION_ED); + VIDEO_FORMAT_TO_RESOLUTION_MAP.put(VIDEO_FORMAT_576I, VIDEO_RESOLUTION_SD); + VIDEO_FORMAT_TO_RESOLUTION_MAP.put(VIDEO_FORMAT_576P, VIDEO_RESOLUTION_ED); + VIDEO_FORMAT_TO_RESOLUTION_MAP.put(VIDEO_FORMAT_720P, VIDEO_RESOLUTION_HD); + VIDEO_FORMAT_TO_RESOLUTION_MAP.put(VIDEO_FORMAT_1080I, VIDEO_RESOLUTION_HD); + VIDEO_FORMAT_TO_RESOLUTION_MAP.put(VIDEO_FORMAT_1080P, VIDEO_RESOLUTION_FHD); + VIDEO_FORMAT_TO_RESOLUTION_MAP.put(VIDEO_FORMAT_2160P, VIDEO_RESOLUTION_UHD); + VIDEO_FORMAT_TO_RESOLUTION_MAP.put(VIDEO_FORMAT_4320P, VIDEO_RESOLUTION_UHD); + } + + /** + * Returns the video resolution (definition) for a given video format. + * + * @param videoFormat The video format defined in {@link Channels}. + * @return the corresponding video resolution string. {@code null} if the resolution string + * is not defined for the given video format. + * @see #COLUMN_VIDEO_FORMAT + */ + public static final String getVideoResolution(String videoFormat) { + return VIDEO_FORMAT_TO_RESOLUTION_MAP.get(videoFormat); + } + /** * The name of the {@link TvInputService} subclass that provides this TV channel. This * should be a fully qualified class name (such as, "com.example.project.TvInputService"). @@ -513,6 +590,24 @@ public final class TvContract { public static final String COLUMN_DESCRIPTION = "description"; /** + * The typical video format for programs from this TV channel. + * <p> + * This is primarily used to filter out channels based on video format by applications. The + * value should match one of the followings: {@link #VIDEO_FORMAT_240P}, + * {@link #VIDEO_FORMAT_360P}, {@link #VIDEO_FORMAT_480I}, {@link #VIDEO_FORMAT_480P}, + * {@link #VIDEO_FORMAT_576I}, {@link #VIDEO_FORMAT_576P}, {@link #VIDEO_FORMAT_720P}, + * {@link #VIDEO_FORMAT_1080I}, {@link #VIDEO_FORMAT_1080P}, {@link #VIDEO_FORMAT_2160P}, + * {@link #VIDEO_FORMAT_4320P}. Note that the actual video resolution of each program from a + * given channel can vary thus one should use {@link Programs#COLUMN_VIDEO_WIDTH} and + * {@link Programs#COLUMN_VIDEO_HEIGHT} to get more accurate video resolution. + * </p><p> + * Type: TEXT + * </p><p> + * @see #getVideoResolution + */ + public static final String COLUMN_VIDEO_FORMAT = "video_format"; + + /** * The flag indicating whether this TV channel is browsable or not. * <p> * A value of 1 indicates the channel is included in the channel list that applications use @@ -719,6 +814,32 @@ public final class TvContract { public static final String COLUMN_LONG_DESCRIPTION = "long_description"; /** + * The width of the video for this TV program, in the unit of pixels. + * <p> + * Together with {@link #COLUMN_VIDEO_HEIGHT} this is used to determine the video resolution + * of the current TV program. Can be empty if it is not known initially or the program does + * not convey any video such as the programs from type {@link Channels#SERVICE_TYPE_AUDIO} + * channels. + * </p><p> + * Type: INTEGER + * </p> + */ + public static final String COLUMN_VIDEO_WIDTH = "video_width"; + + /** + * The height of the video for this TV program, in the unit of pixels. + * <p> + * Together with {@link #COLUMN_VIDEO_WIDTH} this is used to determine the video resolution + * of the current TV program. Can be empty if it is not known initially or the program does + * not convey any video such as the programs from type {@link Channels#SERVICE_TYPE_AUDIO} + * channels. + * </p><p> + * Type: INTEGER + * </p> + */ + public static final String COLUMN_VIDEO_HEIGHT = "video_height"; + + /** * The comma-separated audio languages of this TV program. * <p> * This is used to describe available audio languages included in the program. Use @@ -778,37 +899,37 @@ public final class TvContract { /** Canonical genres for TV programs. */ public static final class Genres { /** The genre for Family/Kids. */ - public static final String FAMILY_KIDS = "Family/Kids"; + public static final String FAMILY_KIDS = "FAMILY_KIDS"; /** The genre for Sports. */ - public static final String SPORTS = "Sports"; + public static final String SPORTS = "SPORTS"; /** The genre for Shopping. */ - public static final String SHOPPING = "Shopping"; + public static final String SHOPPING = "SHOPPING"; /** The genre for Movies. */ - public static final String MOVIES = "Movies"; + public static final String MOVIES = "MOVIES"; /** The genre for Comedy. */ - public static final String COMEDY = "Comedy"; + public static final String COMEDY = "COMEDY"; /** The genre for Travel. */ - public static final String TRAVEL = "Travel"; + public static final String TRAVEL = "TRAVEL"; /** The genre for Drama. */ - public static final String DRAMA = "Drama"; + public static final String DRAMA = "DRAMA"; /** The genre for Education. */ - public static final String EDUCATION = "Education"; + public static final String EDUCATION = "EDUCATION"; /** The genre for Animal/Wildlife. */ - public static final String ANIMAL_WILDLIFE = "Animal/Wildlife"; + public static final String ANIMAL_WILDLIFE = "ANIMAL_WILDLIFE"; /** The genre for News. */ - public static final String NEWS = "News"; + public static final String NEWS = "NEWS"; /** The genre for Gaming. */ - public static final String GAMING = "Gaming"; + public static final String GAMING = "GAMING"; private Genres() {} diff --git a/media/java/android/media/tv/TvInputHardwareInfo.java b/media/java/android/media/tv/TvInputHardwareInfo.java index 4beb960..e5f9889 100644 --- a/media/java/android/media/tv/TvInputHardwareInfo.java +++ b/media/java/android/media/tv/TvInputHardwareInfo.java @@ -16,6 +16,7 @@ package android.media.tv; +import android.media.AudioManager; import android.os.Parcel; import android.os.Parcelable; import android.util.Log; @@ -56,14 +57,11 @@ public final class TvInputHardwareInfo implements Parcelable { private int mDeviceId; private int mType; - // TODO: Add audio port & audio address for audio service. - // TODO: Add HDMI handle for HDMI service. + private int mAudioType; + private String mAudioAddress; + private int mHdmiPortId; - public TvInputHardwareInfo() { } - - public TvInputHardwareInfo(int deviceId, int type) { - mDeviceId = deviceId; - mType = type; + private TvInputHardwareInfo() { } public int getDeviceId() { @@ -74,6 +72,21 @@ public final class TvInputHardwareInfo implements Parcelable { return mType; } + public int getAudioType() { + return mAudioType; + } + + public String getAudioAddress() { + return mAudioAddress; + } + + public int getHdmiPortId() { + if (mType != TV_INPUT_TYPE_HDMI) { + throw new IllegalStateException(); + } + return mHdmiPortId; + } + // Parcelable @Override public int describeContents() { @@ -84,10 +97,78 @@ public final class TvInputHardwareInfo implements Parcelable { public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mDeviceId); dest.writeInt(mType); + dest.writeInt(mAudioType); + dest.writeString(mAudioAddress); + if (mType == TV_INPUT_TYPE_HDMI) { + dest.writeInt(mHdmiPortId); + } } public void readFromParcel(Parcel source) { mDeviceId = source.readInt(); mType = source.readInt(); + mAudioType = source.readInt(); + mAudioAddress = source.readString(); + if (mType == TV_INPUT_TYPE_HDMI) { + mHdmiPortId = source.readInt(); + } + } + + public static final class Builder { + private Integer mDeviceId = null; + private Integer mType = null; + private int mAudioType = AudioManager.DEVICE_NONE; + private String mAudioAddress = ""; + private Integer mHdmiPortId = null; + + public Builder() { + } + + public Builder deviceId(int deviceId) { + mDeviceId = deviceId; + return this; + } + + public Builder type(int type) { + mType = type; + return this; + } + + public Builder audioType(int audioType) { + mAudioType = audioType; + return this; + } + + public Builder audioAddress(String audioAddress) { + mAudioAddress = audioAddress; + return this; + } + + public Builder hdmiPortId(int hdmiPortId) { + mHdmiPortId = hdmiPortId; + return this; + } + + public TvInputHardwareInfo build() { + if (mDeviceId == null || mType == null) { + throw new UnsupportedOperationException(); + } + if ((mType == TV_INPUT_TYPE_HDMI && mHdmiPortId == null) || + (mType != TV_INPUT_TYPE_HDMI && mHdmiPortId != null)) { + throw new UnsupportedOperationException(); + } + + TvInputHardwareInfo info = new TvInputHardwareInfo(); + info.mDeviceId = mDeviceId; + info.mType = mType; + info.mAudioType = mAudioType; + if (info.mAudioType != AudioManager.DEVICE_NONE) { + info.mAudioAddress = mAudioAddress; + } + if (mHdmiPortId != null) { + info.mHdmiPortId = mHdmiPortId; + } + return info; + } } } diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java index 2ed3d73..52db30a 100644 --- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java +++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java @@ -27,6 +27,7 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageInfoLite; import android.content.pm.PackageManager; import android.content.pm.PackageParser; +import android.content.pm.PackageParser.PackageParserException; import android.content.res.ObbInfo; import android.content.res.ObbScanner; import android.net.Uri; @@ -157,6 +158,7 @@ public class DefaultContainerService extends IntentService { * @return Returns PackageInfoLite object containing * the package info and recommended app location. */ + @Override public PackageInfoLite getMinimalPackageInfo(final String packagePath, int flags, long threshold, String abiOverride) { PackageInfoLite ret = new PackageInfoLite(); @@ -167,14 +169,13 @@ public class DefaultContainerService extends IntentService { return ret; } - DisplayMetrics metrics = new DisplayMetrics(); - metrics.setToDefaults(); - - PackageParser.ApkLite pkg = PackageParser.parseApkLite(packagePath, 0); - if (pkg == null) { + final File apkFile = new File(packagePath); + final PackageParser.ApkLite pkg; + try { + pkg = PackageParser.parseApkLite(apkFile, 0); + } catch (PackageParserException e) { Slog.w(TAG, "Failed to parse package"); - final File apkFile = new File(packagePath); if (!apkFile.exists()) { ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_URI; } else { diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index 8c1a9c7..3bd8689 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -46,9 +46,7 @@ <color name="keyguard_overflow_content_color">#ff686868</color> <!-- The default recents task bar background color. --> - <color name="recents_task_bar_default_background_color">#e6444444</color> - <!-- The default recents task bar text color. --> - <color name="recents_task_bar_default_text_color">#ffeeeeee</color> + <color name="recents_task_bar_default_background_color">#ffe6e6e6</color> <!-- The recents task bar light text color to be drawn on top of dark backgrounds. --> <color name="recents_task_bar_light_text_color">#ffeeeeee</color> <!-- The recents task bar dark text color to be drawn on top of light backgrounds. --> diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java index 88ff726..9ea346b 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java @@ -277,13 +277,21 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView !mFullScreenshotView.cancelAnimateOnEnterRecents(mFinishRunnable)) { // If we have a focused task, then launch that task if (!mRecentsView.launchFocusedTask()) { - // If there are any tasks, then launch the first task - if (!mRecentsView.launchFirstTask()) { - // We really shouldn't hit this, but if we do, just animate out (aka. finish) + if (mConfig.launchedFromHome) { + // Just start the animation out of recents ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(this, null, mFinishRunnable, null); mRecentsView.startExitToHomeAnimation( new ViewAnimation.TaskViewExitContext(exitTrigger)); + } else { + // Otherwise, try and launch the first task + if (!mRecentsView.launchFirstTask()) { + // If there are no tasks, then just finish recents + ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(this, + null, mFinishRunnable, null); + mRecentsView.startExitToHomeAnimation( + new ViewAnimation.TaskViewExitContext(exitTrigger)); + } } } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java index 63ef773..10978ca 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java @@ -81,7 +81,6 @@ public class RecentsConfiguration { /** Task bar colors */ public int taskBarViewDefaultBackgroundColor; - public int taskBarViewDefaultTextColor; public int taskBarViewLightTextColor; public int taskBarViewDarkTextColor; public int taskBarViewHighlightColor; @@ -202,8 +201,6 @@ public class RecentsConfiguration { // Task bar colors taskBarViewDefaultBackgroundColor = res.getColor(R.color.recents_task_bar_default_background_color); - taskBarViewDefaultTextColor = - res.getColor(R.color.recents_task_bar_default_text_color); taskBarViewLightTextColor = res.getColor(R.color.recents_task_bar_light_text_color); taskBarViewDarkTextColor = diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java index bf25760..1ef58ad 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java @@ -151,16 +151,14 @@ class TaskBarView extends FrameLayout { mActivityDescription.setText(t.activityLabel); // Try and apply the system ui tint int tint = t.colorPrimary; - if (Constants.DebugFlags.App.EnableTaskBarThemeColors && tint != 0) { - setBackgroundColor(tint); - mActivityDescription.setTextColor(Utilities.getIdealColorForBackgroundColor(tint, - mConfig.taskBarViewLightTextColor, mConfig.taskBarViewDarkTextColor)); - mDismissButton.setImageDrawable(Utilities.getIdealResourceForBackgroundColor(tint, - mLightDismissDrawable, mDarkDismissDrawable)); - } else { - setBackgroundColor(mConfig.taskBarViewDefaultBackgroundColor); - mActivityDescription.setTextColor(mConfig.taskBarViewDefaultTextColor); + if (!Constants.DebugFlags.App.EnableTaskBarThemeColors || tint == 0) { + tint = mConfig.taskBarViewDefaultBackgroundColor; } + setBackgroundColor(tint); + mActivityDescription.setTextColor(Utilities.getIdealColorForBackgroundColor(tint, + mConfig.taskBarViewLightTextColor, mConfig.taskBarViewDarkTextColor)); + mDismissButton.setImageDrawable(Utilities.getIdealResourceForBackgroundColor(tint, + mLightDismissDrawable, mDarkDismissDrawable)); } /** Unbinds the bar view from the task */ diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java index 6c12218..55f9335 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java @@ -973,7 +973,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal int getExitTransformsForFilterAnimation(ArrayList<Task> curTasks, ArrayList<TaskViewTransform> curTaskTransforms, ArrayList<Task> tasks, ArrayList<TaskViewTransform> taskTransforms, - HashMap<TaskView, Pair<Integer, TaskViewTransform>> childViewTransformsOut, + HashMap<TaskView, TaskViewTransform> childViewTransformsOut, ArrayList<TaskView> childrenToRemoveOut) { // Animate all of the existing views out of view (if they are not in the visible range in // the new stack) or to their final positions in the new stack @@ -1003,9 +1003,8 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal (int) tv.getTranslationY())); } - int startDelay = offset * - Constants.Values.TaskStackView.FilterStartDelay; - childViewTransformsOut.put(tv, new Pair(startDelay, toTransform)); + toTransform.startDelay = offset * Constants.Values.TaskStackView.FilterStartDelay; + childViewTransformsOut.put(tv, toTransform); offset++; } return mConfig.filteringCurrentViewsAnimDuration; @@ -1017,7 +1016,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal */ int getEnterTransformsForFilterAnimation(ArrayList<Task> tasks, ArrayList<TaskViewTransform> taskTransforms, - HashMap<TaskView, Pair<Integer, TaskViewTransform>> childViewTransformsOut) { + HashMap<TaskView, TaskViewTransform> childViewTransformsOut) { int offset = 0; int movement = 0; int taskCount = tasks.size(); @@ -1035,9 +1034,8 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal tv.prepareTaskTransformForFilterTaskHidden(fromTransform); tv.updateViewPropertiesToTaskTransform(fromTransform, 0); - int startDelay = offset * - Constants.Values.TaskStackView.FilterStartDelay; - childViewTransformsOut.put(tv, new Pair(startDelay, toTransform)); + toTransform.startDelay = offset * Constants.Values.TaskStackView.FilterStartDelay; + childViewTransformsOut.put(tv, toTransform); // Use the movement of the new views to calculate the duration of the animation movement = Math.max(movement, @@ -1057,8 +1055,8 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal // Calculate the transforms to animate out all the existing views if they are not in the // new visible range (or to their final positions in the stack if they are) final ArrayList<TaskView> childrenToRemove = new ArrayList<TaskView>(); - final HashMap<TaskView, Pair<Integer, TaskViewTransform>> childViewTransforms = - new HashMap<TaskView, Pair<Integer, TaskViewTransform>>(); + final HashMap<TaskView, TaskViewTransform> childViewTransforms = + new HashMap<TaskView, TaskViewTransform>(); int duration = getExitTransformsForFilterAnimation(curTasks, curTaskTransforms, tasks, taskTransforms, childViewTransforms, childrenToRemove); @@ -1073,10 +1071,9 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal // Animate all the views to their final transforms for (final TaskView tv : childViewTransforms.keySet()) { - Pair<Integer, TaskViewTransform> t = childViewTransforms.get(tv); + TaskViewTransform t = childViewTransforms.get(tv); tv.animate().cancel(); tv.animate() - .setStartDelay(t.first) .withEndAction(new Runnable() { @Override public void run() { @@ -1093,15 +1090,14 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal int duration = getEnterTransformsForFilterAnimation(tasks, taskTransforms, childViewTransforms); for (final TaskView tv : childViewTransforms.keySet()) { - Pair<Integer, TaskViewTransform> t = childViewTransforms.get(tv); - tv.animate().setStartDelay(t.first); - tv.updateViewPropertiesToTaskTransform(t.second, duration); + TaskViewTransform t = childViewTransforms.get(tv); + tv.updateViewPropertiesToTaskTransform(t, duration); } } } } }); - tv.updateViewPropertiesToTaskTransform(t.second, duration); + tv.updateViewPropertiesToTaskTransform(t, duration); } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java index 0b19162..cfba74c 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java @@ -202,7 +202,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On if (useLayers) { anim.withLayer(); } - anim.setStartDelay(0) + anim.setStartDelay(toTransform.startDelay) .setDuration(duration) .setInterpolator(mConfig.fastOutSlowInInterpolator) .start(); @@ -248,6 +248,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On // Fade the view out and slide it away toTransform.alpha = 0f; toTransform.translationY += 200; + toTransform.translationZ = 0; } /** @@ -585,19 +586,25 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View.On } @Override - public void onClick(View v) { - if (v == mBarView.mApplicationIcon) { - mCb.onTaskIconClicked(this); - } else if (v == mBarView.mDismissButton) { - // Animate out the view and call the callback - final TaskView tv = this; - startDeleteTaskAnimation(new Runnable() { - @Override - public void run() { - mCb.onTaskDismissed(tv); + public void onClick(final View v) { + // We purposely post the handler delayed to allow for the touch feedback to draw + final TaskView tv = this; + postDelayed(new Runnable() { + @Override + public void run() { + if (v == mBarView.mApplicationIcon) { + mCb.onTaskIconClicked(tv); + } else if (v == mBarView.mDismissButton) { + // Animate out the view and call the callback + startDeleteTaskAnimation(new Runnable() { + @Override + public void run() { + mCb.onTaskDismissed(tv); + } + }); } - }); - } + } + }, 125); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java index 1947e30..b351b03 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java @@ -21,6 +21,7 @@ import android.graphics.Rect; /* The transform state for a task view */ public class TaskViewTransform { + public int startDelay = 0; public int translationY = 0; public int translationZ = 0; public float scale = 1f; @@ -35,6 +36,7 @@ public class TaskViewTransform { } public TaskViewTransform(TaskViewTransform o) { + startDelay = o.startDelay; translationY = o.translationY; translationZ = o.translationZ; scale = o.scale; @@ -47,6 +49,7 @@ public class TaskViewTransform { /** Resets the current transform */ public void reset() { + startDelay = 0; translationY = 0; translationZ = 0; scale = 1f; @@ -76,8 +79,8 @@ public class TaskViewTransform { @Override public String toString() { - return "TaskViewTransform y: " + translationY + " z: " + translationZ + " scale: " + scale + - " alpha: " + alpha + " visible: " + visible + " rect: " + rect + + return "TaskViewTransform delay: " + startDelay + " y: " + translationY + " z: " + translationZ + + " scale: " + scale + " alpha: " + alpha + " visible: " + visible + " rect: " + rect + " dismissAlpha: " + dismissAlpha; } } diff --git a/rs/java/android/renderscript/Allocation.java b/rs/java/android/renderscript/Allocation.java index 2191b54..d9588e8 100644 --- a/rs/java/android/renderscript/Allocation.java +++ b/rs/java/android/renderscript/Allocation.java @@ -770,10 +770,11 @@ public class Allocation extends BaseObj { mRS.validate(); int eSize = mType.mElement.getBytesSize(); final byte[] data = fp.getData(); + int data_length = fp.getPos(); - int count = data.length / eSize; - if ((eSize * count) != data.length) { - throw new RSIllegalArgumentException("Field packer length " + data.length + + int count = data_length / eSize; + if ((eSize * count) != data_length) { + throw new RSIllegalArgumentException("Field packer length " + data_length + " not divisible by element size " + eSize + "."); } copy1DRangeFromUnchecked(xoff, count, data); @@ -797,16 +798,17 @@ public class Allocation extends BaseObj { } final byte[] data = fp.getData(); + int data_length = fp.getPos(); int eSize = mType.mElement.mElements[component_number].getBytesSize(); eSize *= mType.mElement.mArraySizes[component_number]; - if (data.length != eSize) { - throw new RSIllegalArgumentException("Field packer sizelength " + data.length + + if (data_length != eSize) { + throw new RSIllegalArgumentException("Field packer sizelength " + data_length + " does not match component size " + eSize + "."); } mRS.nAllocationElementData1D(getIDSafe(), xoff, mSelectedLOD, - component_number, data, data.length); + component_number, data, data_length); } private void data1DChecks(int off, int count, int len, int dataSize) { diff --git a/rs/java/android/renderscript/Element.java b/rs/java/android/renderscript/Element.java index 55b671d..c6b5b0d 100644 --- a/rs/java/android/renderscript/Element.java +++ b/rs/java/android/renderscript/Element.java @@ -140,17 +140,17 @@ public class Element extends BaseObj { MATRIX_3X3 (17, 36), MATRIX_2X2 (18, 16), - RS_ELEMENT (1000, 4), - RS_TYPE (1001, 4), - RS_ALLOCATION (1002, 4), - RS_SAMPLER (1003, 4), - RS_SCRIPT (1004, 4), - RS_MESH (1005, 4), - RS_PROGRAM_FRAGMENT (1006, 4), - RS_PROGRAM_VERTEX (1007, 4), - RS_PROGRAM_RASTER (1008, 4), - RS_PROGRAM_STORE (1009, 4), - RS_FONT (1010, 4); + RS_ELEMENT (1000), + RS_TYPE (1001), + RS_ALLOCATION (1002), + RS_SAMPLER (1003), + RS_SCRIPT (1004), + RS_MESH (1005), + RS_PROGRAM_FRAGMENT (1006), + RS_PROGRAM_VERTEX (1007), + RS_PROGRAM_RASTER (1008), + RS_PROGRAM_STORE (1009), + RS_FONT (1010); int mID; int mSize; @@ -158,6 +158,14 @@ public class Element extends BaseObj { mID = id; mSize = size; } + + DataType(int id) { + mID = id; + mSize = 4; + if (RenderScript.sPointerSize == 8) { + mSize = 32; + } + } } /** diff --git a/rs/java/android/renderscript/FieldPacker.java b/rs/java/android/renderscript/FieldPacker.java index c9bba69..f39aa5f 100644 --- a/rs/java/android/renderscript/FieldPacker.java +++ b/rs/java/android/renderscript/FieldPacker.java @@ -75,7 +75,7 @@ public class FieldPacker { mPos = 0; } public void reset(int i) { - if ((i < 0) || (i >= mLen)) { + if ((i < 0) || (i > mLen)) { throw new RSIllegalArgumentException("out of range argument: " + i); } mPos = i; @@ -605,6 +605,15 @@ public class FieldPacker { return mData; } + /** + * Get the actual length used for the FieldPacker. + * + * @hide + */ + public int getPos() { + return mPos; + } + private final byte mData[]; private int mPos; private int mLen; diff --git a/rs/java/android/renderscript/ProgramVertexFixedFunction.java b/rs/java/android/renderscript/ProgramVertexFixedFunction.java index 5173af2..45840ae 100644 --- a/rs/java/android/renderscript/ProgramVertexFixedFunction.java +++ b/rs/java/android/renderscript/ProgramVertexFixedFunction.java @@ -245,6 +245,9 @@ public class ProgramVertexFixedFunction extends ProgramVertex { for(int i = 0; i < 16; i ++) { mIOBuffer.addF32(m.mMat[i]); } + // Reset the buffer back to the end, since we want to flush all of + // the contents back (and not just what we wrote now). + mIOBuffer.reset(mIOBuffer.getData().length); mAlloc.setFromFieldPacker(0, mIOBuffer); } diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index e07463d..9264186 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -3005,13 +3005,15 @@ public final class ActivityStackSupervisor implements DisplayListener { } void setLockTaskModeLocked(TaskRecord task) { - final Message lockTaskMsg = Message.obtain(); if (task == null) { - // Take out of lock task mode. - lockTaskMsg.arg1 = mLockTaskModeTask.userId; - lockTaskMsg.what = LOCK_TASK_END_MSG; - mLockTaskModeTask = null; - mHandler.sendMessage(lockTaskMsg); + // Take out of lock task mode if necessary + if (mLockTaskModeTask != null) { + final Message lockTaskMsg = Message.obtain(); + lockTaskMsg.arg1 = mLockTaskModeTask.userId; + lockTaskMsg.what = LOCK_TASK_END_MSG; + mLockTaskModeTask = null; + mHandler.sendMessage(lockTaskMsg); + } return; } if (isLockTaskModeViolation(task)) { @@ -3021,6 +3023,8 @@ public final class ActivityStackSupervisor implements DisplayListener { mLockTaskModeTask = task; findTaskToMoveToFrontLocked(task, 0, null); resumeTopActivitiesLocked(); + + final Message lockTaskMsg = Message.obtain(); lockTaskMsg.obj = mLockTaskModeTask.intent.getComponent().getPackageName(); lockTaskMsg.arg1 = mLockTaskModeTask.userId; lockTaskMsg.what = LOCK_TASK_START_MSG; diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java index 0e9a9cc..cab2728 100644 --- a/services/core/java/com/android/server/job/JobSchedulerService.java +++ b/services/core/java/com/android/server/job/JobSchedulerService.java @@ -22,6 +22,7 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import android.app.AppGlobals; import android.app.job.JobInfo; import android.app.job.JobScheduler; import android.app.job.JobService; @@ -31,8 +32,8 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.IPackageManager; import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ServiceInfo; import android.os.Binder; import android.os.Handler; @@ -616,10 +617,13 @@ public class JobSchedulerService extends com.android.server.SystemService // job that runs one of the app's services, as well as verifying that the // named service properly requires the BIND_JOB_SERVICE permission private void enforceValidJobRequest(int uid, JobInfo job) { - final PackageManager pm = getContext().getPackageManager(); + final IPackageManager pm = AppGlobals.getPackageManager(); final ComponentName service = job.getService(); try { - ServiceInfo si = pm.getServiceInfo(service, 0); + ServiceInfo si = pm.getServiceInfo(service, 0, UserHandle.getUserId(uid)); + if (si == null) { + throw new IllegalArgumentException("No such service " + service); + } if (si.applicationInfo.uid != uid) { throw new IllegalArgumentException("uid " + uid + " cannot schedule job in " + service.getPackageName()); @@ -628,8 +632,8 @@ public class JobSchedulerService extends com.android.server.SystemService throw new IllegalArgumentException("Scheduled service " + service + " does not require android.permission.BIND_JOB_SERVICE permission"); } - } catch (NameNotFoundException e) { - throw new IllegalArgumentException("No such service: " + service); + } catch (RemoteException e) { + // Can't happen; the Package Manager is in this same process } } diff --git a/services/core/java/com/android/server/pm/CrossProfileIntentFilter.java b/services/core/java/com/android/server/pm/CrossProfileIntentFilter.java index 3d432dc..3ce19c1 100644 --- a/services/core/java/com/android/server/pm/CrossProfileIntentFilter.java +++ b/services/core/java/com/android/server/pm/CrossProfileIntentFilter.java @@ -32,36 +32,32 @@ import android.os.UserHandle; */ class CrossProfileIntentFilter extends IntentFilter { private static final String ATTR_TARGET_USER_ID = "targetUserId"; - private static final String ATTR_USER_ID_DEST = "userIdDest";//Old name. Kept for compatibility. - private static final String ATTR_REMOVABLE = "removable"; + private static final String ATTR_FLAGS = "flags"; private static final String ATTR_FILTER = "filter"; private static final String TAG = "CrossProfileIntentFilter"; // If the intent matches the IntentFilter, then it can be forwarded to this userId. final int mTargetUserId; - boolean mRemovable; + final int mFlags; - CrossProfileIntentFilter(IntentFilter filter, boolean removable, int targetUserId) { + CrossProfileIntentFilter(IntentFilter filter, int targetUserId, int flags) { super(filter); mTargetUserId = targetUserId; - mRemovable = removable; + mFlags = flags; } public int getTargetUserId() { return mTargetUserId; } - public boolean isRemovable() { - return mRemovable; + public int getFlags() { + return mFlags; } CrossProfileIntentFilter(XmlPullParser parser) throws XmlPullParserException, IOException { String targetUserIdString = parser.getAttributeValue(null, ATTR_TARGET_USER_ID); if (targetUserIdString == null) { - targetUserIdString = parser.getAttributeValue(null, ATTR_USER_ID_DEST); - } - if (targetUserIdString == null) { String msg = "Missing element under " + TAG +": " + ATTR_TARGET_USER_ID + " at " + parser.getPositionDescription(); PackageManagerService.reportSettingsProblem(Log.WARN, msg); @@ -69,9 +65,14 @@ class CrossProfileIntentFilter extends IntentFilter { } else { mTargetUserId = Integer.parseInt(targetUserIdString); } - String removableString = parser.getAttributeValue(null, ATTR_REMOVABLE); - if (removableString != null) { - mRemovable = Boolean.parseBoolean(removableString); + String flagsString = parser.getAttributeValue(null, ATTR_FLAGS); + if (flagsString == null) { + String msg = "Missing element under " + TAG +": " + ATTR_FLAGS + " at " + + parser.getPositionDescription(); + PackageManagerService.reportSettingsProblem(Log.WARN, msg); + mFlags = 0; + } else { + mFlags = Integer.parseInt(flagsString); } int outerDepth = parser.getDepth(); String tagName = parser.getName(); @@ -104,7 +105,7 @@ class CrossProfileIntentFilter extends IntentFilter { public void writeToXml(XmlSerializer serializer) throws IOException { serializer.attribute(null, ATTR_TARGET_USER_ID, Integer.toString(mTargetUserId)); - serializer.attribute(null, ATTR_REMOVABLE, Boolean.toString(mRemovable)); + serializer.attribute(null, ATTR_FLAGS, Integer.toString(mFlags)); serializer.startTag(null, ATTR_FILTER); super.writeToXml(serializer); serializer.endTag(null, ATTR_FILTER); diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index dd33771..89ab2ae 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -29,6 +29,7 @@ import android.content.pm.PackageInstallerParams; import android.content.pm.PackageManager; import android.content.pm.PackageParser; import android.content.pm.PackageParser.ApkLite; +import android.content.pm.PackageParser.PackageParserException; import android.content.pm.Signature; import android.os.Build; import android.os.Bundle; @@ -50,14 +51,10 @@ import com.android.internal.content.NativeLibraryHelper; import com.android.internal.util.ArrayUtils; import com.android.internal.util.Preconditions; -import libcore.io.IoUtils; import libcore.io.Libcore; -import libcore.io.Streams; import java.io.File; import java.io.FileDescriptor; -import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; @@ -297,11 +294,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // Verify that all staged packages are internally consistent for (File file : files) { - final ApkLite info = PackageParser.parseApkLite(file.getAbsolutePath(), - PackageParser.PARSE_GET_SIGNATURES); - if (info == null) { + final ApkLite info; + try { + info = PackageParser.parseApkLite(file, PackageParser.PARSE_GET_SIGNATURES); + } catch (PackageParserException e) { throw new InstallFailedException(INSTALL_FAILED_INVALID_APK, - "Failed to parse " + file); + "Failed to parse " + file + ": " + e); } if (!seenSplits.add(info.splitName)) { @@ -356,11 +354,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { "Missing existing base package for " + mPackageName); } - final ApkLite info = PackageParser.parseApkLite(app.sourceDir, - PackageParser.PARSE_GET_SIGNATURES); - if (info == null) { + final ApkLite info; + try { + info = PackageParser.parseApkLite(new File(app.sourceDir), + PackageParser.PARSE_GET_SIGNATURES); + } catch (PackageParserException e) { throw new InstallFailedException(INSTALL_FAILED_INVALID_APK, - "Failed to parse existing base " + app.sourceDir); + "Failed to parse existing base " + app.sourceDir + ": " + e); } assertPackageConsistent("Existing base", info.packageName, info.versionCode, diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index dc76455..a7fc7eb 100755 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -147,6 +147,7 @@ import android.util.LogPrinter; import android.util.PrintStreamPrinter; import android.util.Slog; import android.util.SparseArray; +import android.util.SparseBooleanArray; import android.util.Xml; import android.view.Display; @@ -3347,7 +3348,7 @@ public class PackageManagerService extends IPackageManager.Stub { } /* - * Returns if intent can be forwarded from the userId from to dest + * Returns if intent can be forwarded from the sourceUserId to the targetUserId */ @Override public boolean canForwardTo(Intent intent, String resolvedType, int sourceUserId, @@ -3367,9 +3368,9 @@ public class PackageManagerService extends IPackageManager.Stub { private List<CrossProfileIntentFilter> getMatchingCrossProfileIntentFilters(Intent intent, String resolvedType, int userId) { - CrossProfileIntentResolver cpir = mSettings.mCrossProfileIntentResolvers.get(userId); - if (cpir != null) { - return cpir.queryIntent(intent, resolvedType, false, userId); + CrossProfileIntentResolver resolver = mSettings.mCrossProfileIntentResolvers.get(userId); + if (resolver != null) { + return resolver.queryIntent(intent, resolvedType, false, userId); } return null; } @@ -3402,36 +3403,24 @@ public class PackageManagerService extends IPackageManager.Stub { synchronized (mPackages) { final String pkgName = intent.getPackage(); if (pkgName == null) { - List<ResolveInfo> result = - mActivities.queryIntent(intent, resolvedType, flags, userId); - // Checking if we can forward the intent to another user - List<CrossProfileIntentFilter> cpifs = + List<ResolveInfo> result; + List<CrossProfileIntentFilter> matchingFilters = getMatchingCrossProfileIntentFilters(intent, resolvedType, userId); - if (cpifs != null) { - CrossProfileIntentFilter crossProfileIntentFilterWithResult = null; - HashSet<Integer> alreadyTriedUserIds = new HashSet<Integer>(); - for (CrossProfileIntentFilter cpif : cpifs) { - int targetUserId = cpif.getTargetUserId(); - // Two {@link CrossProfileIntentFilter}s can have the same targetUserId and - // match the same an intent. For performance reasons, it is better not to - // run queryIntent twice for the same userId - if (!alreadyTriedUserIds.contains(targetUserId)) { - List<ResolveInfo> resultUser = mActivities.queryIntent(intent, - resolvedType, flags, targetUserId); - if (resultUser != null) { - crossProfileIntentFilterWithResult = cpif; - // As soon as there is a match in another user, we add the - // intentForwarderActivity to the list of ResolveInfo. - break; - } - alreadyTriedUserIds.add(targetUserId); - } - } - if (crossProfileIntentFilterWithResult != null) { - ResolveInfo forwardingResolveInfo = createForwardingResolveInfo( - crossProfileIntentFilterWithResult, userId); - result.add(forwardingResolveInfo); - } + // Check for results that need to skip the current profile. + ResolveInfo resolveInfo = querySkipCurrentProfileIntents(matchingFilters, intent, + resolvedType, flags, userId); + if (resolveInfo != null) { + result = new ArrayList<ResolveInfo>(1); + result.add(resolveInfo); + return result; + } + // Check for results in the current profile. + result = mActivities.queryIntent(intent, resolvedType, flags, userId); + // Check for cross profile results. + resolveInfo = queryCrossProfileIntents( + matchingFilters, intent, resolvedType, flags, userId); + if (resolveInfo != null) { + result.add(resolveInfo); } return result; } @@ -3444,10 +3433,68 @@ public class PackageManagerService extends IPackageManager.Stub { } } - private ResolveInfo createForwardingResolveInfo(CrossProfileIntentFilter cpif, + private ResolveInfo querySkipCurrentProfileIntents( + List<CrossProfileIntentFilter> matchingFilters, Intent intent, String resolvedType, + int flags, int sourceUserId) { + if (matchingFilters != null) { + int size = matchingFilters.size(); + for (int i = 0; i < size; i ++) { + CrossProfileIntentFilter filter = matchingFilters.get(i); + if ((filter.getFlags() & PackageManager.SKIP_CURRENT_PROFILE) != 0) { + // Checking if there are activities in the target user that can handle the + // intent. + ResolveInfo resolveInfo = checkTargetCanHandle(filter, intent, resolvedType, + flags, sourceUserId); + if (resolveInfo != null) { + return createForwardingResolveInfo(filter, sourceUserId); + } + } + } + } + return null; + } + + // Return matching ResolveInfo if any for skip current profile intent filters. + private ResolveInfo queryCrossProfileIntents( + List<CrossProfileIntentFilter> matchingFilters, Intent intent, String resolvedType, + int flags, int sourceUserId) { + if (matchingFilters != null) { + // Two {@link CrossProfileIntentFilter}s can have the same targetUserId and + // match the same intent. For performance reasons, it is better not to + // run queryIntent twice for the same userId + SparseBooleanArray alreadyTriedUserIds = new SparseBooleanArray(); + int size = matchingFilters.size(); + for (int i = 0; i < size; i++) { + CrossProfileIntentFilter filter = matchingFilters.get(i); + int targetUserId = filter.getTargetUserId(); + if ((filter.getFlags() & PackageManager.SKIP_CURRENT_PROFILE) == 0 + && !alreadyTriedUserIds.get(targetUserId)) { + // Checking if there are activities in the target user that can handle the + // intent. + ResolveInfo resolveInfo = checkTargetCanHandle(filter, intent, resolvedType, + flags, sourceUserId); + if (resolveInfo != null) return resolveInfo; + alreadyTriedUserIds.put(targetUserId, true); + } + } + } + return null; + } + + private ResolveInfo checkTargetCanHandle(CrossProfileIntentFilter filter, Intent intent, + String resolvedType, int flags, int sourceUserId) { + List<ResolveInfo> resultTargetUser = mActivities.queryIntent(intent, + resolvedType, flags, filter.getTargetUserId()); + if (resultTargetUser != null) { + return createForwardingResolveInfo(filter, sourceUserId); + } + return null; + } + + private ResolveInfo createForwardingResolveInfo(CrossProfileIntentFilter filter, int sourceUserId) { String className; - int targetUserId = cpif.getTargetUserId(); + int targetUserId = filter.getTargetUserId(); if (targetUserId == UserHandle.USER_OWNER) { className = FORWARD_INTENT_TO_USER_OWNER; } else { @@ -3463,7 +3510,7 @@ public class PackageManagerService extends IPackageManager.Stub { forwardingResolveInfo.preferredOrder = 0; forwardingResolveInfo.match = 0; forwardingResolveInfo.isDefault = true; - forwardingResolveInfo.filter = cpif; + forwardingResolveInfo.filter = filter; return forwardingResolveInfo; } @@ -4199,14 +4246,18 @@ public class PackageManagerService extends IPackageManager.Stub { String scanPath = scanFile.getPath(); if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanPath); parseFlags |= mDefParseFlags; - PackageParser pp = new PackageParser(scanPath); + PackageParser pp = new PackageParser(); pp.setSeparateProcesses(mSeparateProcesses); pp.setOnlyCoreApps(mOnlyCore); + pp.setDisplayMetrics(mMetrics); + + if ((scanMode & SCAN_TRUSTED_OVERLAY) != 0) { + parseFlags |= PackageParser.PARSE_TRUSTED_OVERLAY; + } final PackageParser.Package pkg; try { - pkg = pp.parseMonolithicPackage(scanFile, mMetrics, parseFlags, - (scanMode & SCAN_TRUSTED_OVERLAY) != 0); + pkg = pp.parseMonolithicPackage(scanFile, parseFlags); } catch (PackageParserException e) { mLastScanError = e.error; return null; @@ -4637,12 +4688,7 @@ public class PackageManagerService extends IPackageManager.Stub { } if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0) { - final ArrayList<String> paths = new ArrayList<>(); - paths.add(pkg.codePath); - if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) { - Collections.addAll(paths, pkg.splitCodePaths); - } - + final Collection<String> paths = pkg.getAllCodePaths(); for (String path : paths) { try { boolean isDexOptNeededInternal = DexFile.isDexOptNeededInternal(path, @@ -4832,10 +4878,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } if (p != null) { - usesLibraryFiles.add(p.codePath); - if (!ArrayUtils.isEmpty(p.splitCodePaths)) { - Collections.addAll(usesLibraryFiles, p.splitCodePaths); - } + usesLibraryFiles.addAll(p.getAllCodePaths()); } } @@ -5674,7 +5717,8 @@ public class PackageManagerService extends IPackageManager.Stub { try { ksm.addSigningKeySetToPackage(pkg.packageName, pkg.mSigningKeys); if (pkg.mKeySetMapping != null) { - for (Map.Entry<String, Set<PublicKey>> entry : pkg.mKeySetMapping.entrySet()) { + for (Map.Entry<String, ArraySet<PublicKey>> entry : + pkg.mKeySetMapping.entrySet()) { if (entry.getValue() != null) { ksm.addDefinedKeySetToPackage(pkg.packageName, entry.getValue(), entry.getKey()); @@ -9713,7 +9757,7 @@ public class PackageManagerService extends IPackageManager.Stub { return PackageManager.INSTALL_SUCCEEDED; } - }; + } static String getAsecPackageName(String packageCid) { int idx = packageCid.lastIndexOf("-"); @@ -10206,13 +10250,13 @@ public class PackageManagerService extends IPackageManager.Stub { int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY | (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0) | (onSd ? PackageParser.PARSE_ON_SDCARD : 0); - PackageParser pp = new PackageParser(tmpPackageFile.getPath()); + PackageParser pp = new PackageParser(); pp.setSeparateProcesses(mSeparateProcesses); + pp.setDisplayMetrics(mMetrics); final PackageParser.Package pkg; try { - pkg = pp.parseMonolithicPackage(tmpPackageFile, mMetrics, - parseFlags); + pkg = pp.parseMonolithicPackage(tmpPackageFile, parseFlags); } catch (PackageParserException e) { res.returnCode = e.error; return; @@ -11542,17 +11586,18 @@ public class PackageManagerService extends IPackageManager.Stub { } @Override - public void addCrossProfileIntentFilter(IntentFilter filter, boolean removable, - int sourceUserId, int targetUserId) { + public void addCrossProfileIntentFilter(IntentFilter intentFilter, int sourceUserId, + int targetUserId, int flags) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null); - if (filter.countActions() == 0) { + if (intentFilter.countActions() == 0) { Slog.w(TAG, "Cannot set a crossProfile intent filter with no filter actions"); return; } synchronized (mPackages) { - mSettings.editCrossProfileIntentResolverLPw(sourceUserId).addFilter( - new CrossProfileIntentFilter(filter, removable, targetUserId)); + CrossProfileIntentFilter filter = new CrossProfileIntentFilter(intentFilter, + targetUserId, flags); + mSettings.editCrossProfileIntentResolverLPw(sourceUserId).addFilter(filter); mSettings.writePackageRestrictionsLPr(sourceUserId); } } @@ -11562,12 +11607,14 @@ public class PackageManagerService extends IPackageManager.Stub { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null); synchronized (mPackages) { - CrossProfileIntentResolver cpir = + CrossProfileIntentResolver resolver = mSettings.editCrossProfileIntentResolverLPw(sourceUserId); HashSet<CrossProfileIntentFilter> set = - new HashSet<CrossProfileIntentFilter>(cpir.filterSet()); - for (CrossProfileIntentFilter cpif : set) { - if (cpif.isRemovable()) cpir.removeFilter(cpif); + new HashSet<CrossProfileIntentFilter>(resolver.filterSet()); + for (CrossProfileIntentFilter filter : set) { + if ((filter.getFlags() & PackageManager.SET_BY_PROFILE_OWNER) != 0) { + resolver.removeFilter(filter); + } } mSettings.writePackageRestrictionsLPr(sourceUserId); } diff --git a/services/core/java/com/android/server/tv/TvInputHal.java b/services/core/java/com/android/server/tv/TvInputHal.java index 34168a8..1535e7a 100644 --- a/services/core/java/com/android/server/tv/TvInputHal.java +++ b/services/core/java/com/android/server/tv/TvInputHal.java @@ -94,8 +94,7 @@ final class TvInputHal { } // Called from native - private void deviceAvailableFromNative(int deviceId, int type) { - final TvInputHardwareInfo info = new TvInputHardwareInfo(deviceId, type); + private void deviceAvailableFromNative(final TvInputHardwareInfo info) { mHandler.post(new Runnable() { @Override public void run() { @@ -105,23 +104,21 @@ final class TvInputHal { }); } - private void deviceUnavailableFromNative(int deviceId) { - final int id = deviceId; + private void deviceUnavailableFromNative(final int deviceId) { mHandler.post(new Runnable() { @Override public void run() { - mCallback.onDeviceUnavailable(id); + mCallback.onDeviceUnavailable(deviceId); } }); } - private void streamConfigsChangedFromNative(int deviceId) { - final int id = deviceId; + private void streamConfigsChangedFromNative(final int deviceId) { mHandler.post(new Runnable() { @Override public void run() { - retrieveStreamConfigs(id); - mCallback.onStreamConfigurationChanged(id, mStreamConfigs); + retrieveStreamConfigs(deviceId); + mCallback.onStreamConfigurationChanged(deviceId, mStreamConfigs); } }); } diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java index d72ed9e..1146f0f 100644 --- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java +++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java @@ -17,6 +17,11 @@ package com.android.server.tv; import android.content.Context; +import android.media.AudioDevicePort; +import android.media.AudioManager; +import android.media.AudioPatch; +import android.media.AudioPort; +import android.media.AudioPortConfig; import android.media.tv.ITvInputHardware; import android.media.tv.ITvInputHardwareCallback; import android.media.tv.TvInputHardwareInfo; @@ -48,11 +53,13 @@ class TvInputHardwareManager implements TvInputHal.Callback { private final List<TvInputHardwareInfo> mInfoList = new ArrayList<TvInputHardwareInfo>(); private final Context mContext; private final Set<Integer> mActiveHdmiSources = new HashSet<Integer>(); + private final AudioManager mAudioManager; private final Object mLock = new Object(); public TvInputHardwareManager(Context context) { mContext = context; + mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); // TODO(hdmi): mHdmiManager = mContext.getSystemService(...); // TODO(hdmi): mHdmiClient = mHdmiManager.getTvClient(); mHal.init(); @@ -258,12 +265,48 @@ class TvInputHardwareManager implements TvInputHal.Callback { private boolean mReleased = false; private final Object mImplLock = new Object(); + private final AudioDevicePort mAudioSource; + private final AudioDevicePort mAudioSink; + private AudioPatch mAudioPatch = null; + public TvInputHardwareImpl(TvInputHardwareInfo info) { mInfo = info; + AudioDevicePort audioSource = null; + AudioDevicePort audioSink = null; + if (mInfo.getAudioType() != AudioManager.DEVICE_NONE) { + ArrayList<AudioPort> devicePorts = new ArrayList<AudioPort>(); + if (mAudioManager.listAudioDevicePorts(devicePorts) == AudioManager.SUCCESS) { + // Find source + for (AudioPort port : devicePorts) { + AudioDevicePort devicePort = (AudioDevicePort) port; + if (devicePort.type() == mInfo.getAudioType() && + devicePort.address().equals(mInfo.getAudioAddress())) { + audioSource = devicePort; + break; + } + } + // Find sink + // TODO: App may want to specify sink device? + int sinkDevices = mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC); + for (AudioPort port : devicePorts) { + AudioDevicePort devicePort = (AudioDevicePort) port; + if (devicePort.type() == sinkDevices) { + audioSink = devicePort; + break; + } + } + } + } + mAudioSource = audioSource; + mAudioSink = audioSink; } public void release() { synchronized (mImplLock) { + if (mAudioPatch != null) { + mAudioManager.releaseAudioPatch(mAudioPatch); + mAudioPatch = null; + } mReleased = true; } } @@ -288,6 +331,22 @@ class TvInputHardwareManager implements TvInputHal.Callback { } } } + if (mAudioSource != null && mAudioSink != null) { + if (surface != null) { + AudioPortConfig sourceConfig = mAudioSource.activeConfig(); + AudioPortConfig sinkConfig = mAudioSink.activeConfig(); + AudioPatch[] audioPatchArray = new AudioPatch[] { mAudioPatch }; + // TODO: build config if activeConfig() == null + mAudioManager.createAudioPatch( + audioPatchArray, + new AudioPortConfig[] { sourceConfig }, + new AudioPortConfig[] { sinkConfig }); + mAudioPatch = audioPatchArray[0]; + } else { + mAudioManager.releaseAudioPatch(mAudioPatch); + mAudioPatch = null; + } + } return mHal.setSurface(mInfo.getDeviceId(), surface, config) == TvInputHal.SUCCESS; } } @@ -299,7 +358,7 @@ class TvInputHardwareManager implements TvInputHal.Callback { throw new IllegalStateException("Device already released."); } } - // TODO + // TODO: Use AudioGain? } @Override diff --git a/services/core/jni/com_android_server_tv_TvInputHal.cpp b/services/core/jni/com_android_server_tv_TvInputHal.cpp index afe629d..9cecdf0 100644 --- a/services/core/jni/com_android_server_tv_TvInputHal.cpp +++ b/services/core/jni/com_android_server_tv_TvInputHal.cpp @@ -54,6 +54,18 @@ static struct { jmethodID build; } gTvStreamConfigBuilderClassInfo; +static struct { + jclass clazz; + + jmethodID constructor; + jmethodID deviceId; + jmethodID type; + jmethodID hdmiPortId; + jmethodID audioType; + jmethodID audioAddress; + jmethodID build; +} gTvInputHardwareInfoBuilderClassInfo; + //////////////////////////////////////////////////////////////////////////////// class JTvInputHal { @@ -209,7 +221,6 @@ const tv_stream_config_t* JTvInputHal::getStreamConfigs(int deviceId, int* numCo return configs; } - // static void JTvInputHal::notify( tv_input_device_t* dev, tv_input_event_t* event, void* data) { @@ -232,11 +243,36 @@ void JTvInputHal::notify( void JTvInputHal::onDeviceAvailable(const tv_input_device_info_t& info) { JNIEnv* env = AndroidRuntime::getJNIEnv(); mConnections.add(info.device_id, Connection()); + + jobject builder = env->NewObject( + gTvInputHardwareInfoBuilderClassInfo.clazz, + gTvInputHardwareInfoBuilderClassInfo.constructor); + env->CallObjectMethod( + builder, gTvInputHardwareInfoBuilderClassInfo.deviceId, info.device_id); + env->CallObjectMethod( + builder, gTvInputHardwareInfoBuilderClassInfo.type, info.type); + if (info.type == TV_INPUT_TYPE_HDMI) { + env->CallObjectMethod( + builder, gTvInputHardwareInfoBuilderClassInfo.hdmiPortId, info.hdmi.port_id); + } + env->CallObjectMethod( + builder, gTvInputHardwareInfoBuilderClassInfo.audioType, info.audio_type); + if (info.audio_type != AUDIO_DEVICE_NONE) { + jstring audioAddress = env->NewStringUTF(info.audio_address); + env->CallObjectMethod( + builder, gTvInputHardwareInfoBuilderClassInfo.audioAddress, audioAddress); + env->DeleteLocalRef(audioAddress); + } + + jobject infoObject = env->CallObjectMethod(builder, gTvInputHardwareInfoBuilderClassInfo.build); + env->CallVoidMethod( mThiz, gTvInputHalClassInfo.deviceAvailable, - info.device_id, - info.type); + infoObject); + + env->DeleteLocalRef(builder); + env->DeleteLocalRef(infoObject); } void JTvInputHal::onDeviceUnavailable(int deviceId) { @@ -339,7 +375,8 @@ int register_android_server_tv_TvInputHal(JNIEnv* env) { FIND_CLASS(clazz, "com/android/server/tv/TvInputHal"); GET_METHOD_ID( - gTvInputHalClassInfo.deviceAvailable, clazz, "deviceAvailableFromNative", "(II)V"); + gTvInputHalClassInfo.deviceAvailable, clazz, + "deviceAvailableFromNative", "(Landroid/media/tv/TvInputHardwareInfo;)V"); GET_METHOD_ID( gTvInputHalClassInfo.deviceUnavailable, clazz, "deviceUnavailableFromNative", "(I)V"); GET_METHOD_ID( @@ -382,6 +419,40 @@ int register_android_server_tv_TvInputHal(JNIEnv* env) { gTvStreamConfigBuilderClassInfo.clazz, "build", "()Landroid/media/tv/TvStreamConfig;"); + FIND_CLASS(gTvInputHardwareInfoBuilderClassInfo.clazz, + "android/media/tv/TvInputHardwareInfo$Builder"); + gTvInputHardwareInfoBuilderClassInfo.clazz = + jclass(env->NewGlobalRef(gTvInputHardwareInfoBuilderClassInfo.clazz)); + + GET_METHOD_ID( + gTvInputHardwareInfoBuilderClassInfo.constructor, + gTvInputHardwareInfoBuilderClassInfo.clazz, + "<init>", "()V"); + GET_METHOD_ID( + gTvInputHardwareInfoBuilderClassInfo.deviceId, + gTvInputHardwareInfoBuilderClassInfo.clazz, + "deviceId", "(I)Landroid/media/tv/TvInputHardwareInfo$Builder;"); + GET_METHOD_ID( + gTvInputHardwareInfoBuilderClassInfo.type, + gTvInputHardwareInfoBuilderClassInfo.clazz, + "type", "(I)Landroid/media/tv/TvInputHardwareInfo$Builder;"); + GET_METHOD_ID( + gTvInputHardwareInfoBuilderClassInfo.hdmiPortId, + gTvInputHardwareInfoBuilderClassInfo.clazz, + "hdmiPortId", "(I)Landroid/media/tv/TvInputHardwareInfo$Builder;"); + GET_METHOD_ID( + gTvInputHardwareInfoBuilderClassInfo.audioType, + gTvInputHardwareInfoBuilderClassInfo.clazz, + "audioType", "(I)Landroid/media/tv/TvInputHardwareInfo$Builder;"); + GET_METHOD_ID( + gTvInputHardwareInfoBuilderClassInfo.audioAddress, + gTvInputHardwareInfoBuilderClassInfo.clazz, + "audioAddress", "(Ljava/lang/String;)Landroid/media/tv/TvInputHardwareInfo$Builder;"); + GET_METHOD_ID( + gTvInputHardwareInfoBuilderClassInfo.build, + gTvInputHardwareInfoBuilderClassInfo.clazz, + "build", "()Landroid/media/tv/TvInputHardwareInfo;"); + return 0; } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 5cf5713..4897b1d 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -3454,12 +3454,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { long id = Binder.clearCallingIdentity(); try { if ((flags & DevicePolicyManager.FLAG_PARENT_CAN_ACCESS_MANAGED) != 0) { - pm.addCrossProfileIntentFilter(filter, true /*removable*/, callingUserId, - UserHandle.USER_OWNER); + pm.addCrossProfileIntentFilter(filter, callingUserId, UserHandle.USER_OWNER, + PackageManager.SET_BY_PROFILE_OWNER); } if ((flags & DevicePolicyManager.FLAG_MANAGED_CAN_ACCESS_PARENT) != 0) { - pm.addCrossProfileIntentFilter(filter, true /*removable*/, UserHandle.USER_OWNER, - callingUserId); + pm.addCrossProfileIntentFilter(filter, UserHandle.USER_OWNER, callingUserId, + PackageManager.SET_BY_PROFILE_OWNER); } } catch (RemoteException re) { // Shouldn't happen @@ -3480,6 +3480,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { long id = Binder.clearCallingIdentity(); try { pm.clearCrossProfileIntentFilters(callingUserId); + // If we want to support multiple managed profiles, we will have to only remove + // those that have callingUserId as their target. pm.clearCrossProfileIntentFilters(UserHandle.USER_OWNER); } catch (RemoteException re) { // Shouldn't happen diff --git a/telecomm/java/android/telecomm/TelecommManager.java b/telecomm/java/android/telecomm/TelecommManager.java index a97e7e4..989c2cd 100644 --- a/telecomm/java/android/telecomm/TelecommManager.java +++ b/telecomm/java/android/telecomm/TelecommManager.java @@ -16,7 +16,9 @@ package android.telecomm; +import android.content.ComponentName; import android.content.Context; +import android.os.RemoteException; import com.android.internal.telecomm.ITelecommService; @@ -45,4 +47,14 @@ public class TelecommManager { public static TelecommManager from(Context context) { return (TelecommManager) context.getSystemService(Context.TELECOMM_SERVICE); } + + /** {@hide} */ + public ComponentName getSystemPhoneApplication() { + try { + return mService.getSystemPhoneApplication(); + } catch (RemoteException e) { + Log.e(TAG, e, "Error calling ITelecommService#getSystemPhoneApplication"); + return null; + } + } } diff --git a/telephony/java/com/android/internal/telephony/IMms.aidl b/telephony/java/com/android/internal/telephony/IMms.aidl new file mode 100644 index 0000000..a745420 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/IMms.aidl @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony; + +import android.app.PendingIntent; + +/** + * Service interface to handle MMS API requests + */ +interface IMms { + /** + * Send an MMS message + * + * @param callingPkg the package name of the calling app + * @param pdu the MMS message encoded in standard MMS PDU format + * @param locationUrl the optional location url for where this message should be sent to + * @param sentIntent if not NULL this <code>PendingIntent</code> is + * broadcast when the message is successfully sent, or failed + */ + void sendMessage(String callingPkg, in byte[] pdu, String locationUrl, + in PendingIntent sentIntent); + + /** + * Download an MMS message using known location and transaction id + * + * @param callingPkg the package name of the calling app + * @param locationUrl the location URL of the MMS message to be downloaded, usually obtained + * from the MMS WAP push notification + * @param downloadedIntent if not NULL this <code>PendingIntent</code> is + * broadcast when the message is downloaded, or the download is failed + */ + void downloadMessage(String callingPkg, String locationUrl, in PendingIntent downloadedIntent); +} diff --git a/test-runner/Android.mk b/test-runner/Android.mk index 0d9e4f1..b12795c 100644 --- a/test-runner/Android.mk +++ b/test-runner/Android.mk @@ -20,7 +20,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := $(call all-java-files-under, src) -LOCAL_JAVA_LIBRARIES := core core-junit framework +LOCAL_JAVA_LIBRARIES := core-libart core-junit framework LOCAL_STATIC_JAVA_LIBRARIES := junit-runner LOCAL_MODULE:= android.test.runner diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java index 17db1b4..a3b32b3 100644 --- a/test-runner/src/android/test/mock/MockPackageManager.java +++ b/test-runner/src/android/test/mock/MockPackageManager.java @@ -707,17 +707,8 @@ public class MockPackageManager extends PackageManager { * @hide */ @Override - public void addCrossProfileIntentFilter(IntentFilter filter, boolean removable, - int sourceUserId, int targetUserId) { - throw new UnsupportedOperationException(); - } - - /** - * @hide - */ - @Override - public void addForwardingIntentFilter(IntentFilter filter, boolean removable, int sourceUserId, - int targetUserId) { + public void addCrossProfileIntentFilter(IntentFilter filter, int sourceUserId, int targetUserId, + int flags) { throw new UnsupportedOperationException(); } @@ -729,14 +720,6 @@ public class MockPackageManager extends PackageManager { throw new UnsupportedOperationException(); } - /** - * @hide - */ - @Override - public void clearForwardingIntentFilters(int sourceUserId) { - throw new UnsupportedOperationException(); - } - /** {@hide} */ public PackageInstaller getPackageInstaller() { throw new UnsupportedOperationException(); diff --git a/tests/JobSchedulerTestApp/res/layout/activity_main.xml b/tests/JobSchedulerTestApp/res/layout/activity_main.xml index 7f4961b..d3429ff 100644 --- a/tests/JobSchedulerTestApp/res/layout/activity_main.xml +++ b/tests/JobSchedulerTestApp/res/layout/activity_main.xml @@ -1,12 +1,12 @@ <?xml version="1.0" encoding="utf-8"?> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" +<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <LinearLayout android:layout_width="match_parent" - android:layout_height="wrap_content" + android:layout_height="match_parent" android:layout_weight="1" android:orientation="vertical"> @@ -54,6 +54,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/constraints" + android:layout_margin="15dp" android:textSize="18dp"/> <LinearLayout android:layout_width="match_parent" @@ -83,43 +84,81 @@ </RadioGroup> </LinearLayout> - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content"> - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/timing"/> - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginLeft="15dp" - android:textSize="17dp" - android:text="@string/delay"/> - <EditText - android:id="@+id/delay_time" - android:layout_width="60dp" - android:layout_height="wrap_content" - android:inputType="number"/> - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/deadline" - android:textSize="17dp"/> - <EditText - android:id="@+id/deadline_time" - android:layout_width="60dp" - android:layout_height="wrap_content" - android:inputType="number"/> - </LinearLayout> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content"> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/timing"/> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginLeft="15dp" + android:textSize="17dp" + android:text="@string/delay"/> + <EditText + android:id="@+id/delay_time" + android:layout_width="60dp" + android:layout_height="wrap_content" + android:inputType="number"/> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/deadline" + android:textSize="17dp"/> + <EditText + android:id="@+id/deadline_time" + android:layout_width="60dp" + android:layout_height="wrap_content" + android:inputType="number"/> + </LinearLayout> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content"> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/charging_caption" + android:layout_marginRight="15dp"/> + <CheckBox + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/checkbox_charging" + android:text="@string/charging_text"/> + </LinearLayout> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content"> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/idle_caption" + android:layout_marginRight="15dp"/> + <CheckBox + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/checkbox_idle" + android:text="@string/idle_mode_text"/> + </LinearLayout> </LinearLayout> <Button android:id="@+id/schedule_button" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_margin="40dp" + android:layout_marginTop="20dp" + android:layout_marginLeft="40dp" + android:layout_marginRight="40dp" android:onClick="scheduleJob" android:text="@string/schedule_job_button_text"/> + <Button + android:id="@+id/cancel_button" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="40dp" + android:layout_marginRight="40dp" + android:onClick="cancelAllJobs" + android:text="@string/cancel_all_jobs_button_text"/> </LinearLayout> -</LinearLayout> +</ScrollView> diff --git a/tests/JobSchedulerTestApp/res/values/strings.xml b/tests/JobSchedulerTestApp/res/values/strings.xml index 824d4b1..eebfb19 100644 --- a/tests/JobSchedulerTestApp/res/values/strings.xml +++ b/tests/JobSchedulerTestApp/res/values/strings.xml @@ -20,9 +20,13 @@ limitations under the License. <string name="onstarttask">onStartTask</string> <string name="defaultparamtext">task params will show up here.</string> <string name="schedule_job_button_text">Schedule Job</string> + <string name="cancel_all_jobs_button_text">Cancel all</string> <string name="app_name">Job Scheduler Test</string> <string name="finish_job_button_text">taskFinished</string> - <string name="manual_sync_text">Manual Sync</string> + <string name="idle_mode_text">Requires device in idle mode.</string> + <string name="charging_caption">Charging:</string> + <string name="charging_text">Requires device plugged in.</string> + <string name="idle_caption">Idle:</string> <string name="constraints">Constraints</string> <string name="connectivity">Connectivity:</string> <string name="any">Any</string> diff --git a/tests/JobSchedulerTestApp/src/com/android/demo/jobSchedulerApp/MainActivity.java b/tests/JobSchedulerTestApp/src/com/android/demo/jobSchedulerApp/MainActivity.java index 15050ef..e15929d 100644 --- a/tests/JobSchedulerTestApp/src/com/android/demo/jobSchedulerApp/MainActivity.java +++ b/tests/JobSchedulerTestApp/src/com/android/demo/jobSchedulerApp/MainActivity.java @@ -19,7 +19,9 @@ package com.android.demo.jobSchedulerApp; import android.app.Activity; import android.app.job.JobInfo; import android.app.job.JobParameters; +import android.app.job.JobScheduler; import android.content.ComponentName; +import android.content.Context; import android.content.Intent; import android.content.res.Resources; import android.os.Bundle; @@ -28,6 +30,7 @@ import android.os.Message; import android.os.Messenger; import android.text.TextUtils; import android.view.View; +import android.widget.CheckBox; import android.widget.EditText; import android.widget.RadioButton; import android.widget.TextView; @@ -60,7 +63,8 @@ public class MainActivity extends Activity { mDeadlineEditText = (EditText) findViewById(R.id.deadline_time); mWiFiConnectivityRadioButton = (RadioButton) findViewById(R.id.checkbox_unmetered); mAnyConnectivityRadioButton = (RadioButton) findViewById(R.id.checkbox_any); - + mRequiresChargingCheckBox = (CheckBox) findViewById(R.id.checkbox_charging); + mRequiresIdleCheckbox = (CheckBox) findViewById(R.id.checkbox_idle); mServiceComponent = new ComponentName(this, TestJobService.class); // Start service and provide it a way to communicate with us. Intent startServiceIntent = new Intent(this, TestJobService.class); @@ -79,6 +83,9 @@ public class MainActivity extends Activity { EditText mDeadlineEditText; RadioButton mWiFiConnectivityRadioButton; RadioButton mAnyConnectivityRadioButton; + CheckBox mRequiresChargingCheckBox; + CheckBox mRequiresIdleCheckbox; + ComponentName mServiceComponent; /** Service object to interact scheduled jobs. */ TestJobService mTestService; @@ -124,24 +131,32 @@ public class MainActivity extends Activity { String delay = mDelayEditText.getText().toString(); if (delay != null && !TextUtils.isEmpty(delay)) { - builder.setMinimumLatency(Long.valueOf(delay)); + builder.setMinimumLatency(Long.valueOf(delay) * 1000); } String deadline = mDeadlineEditText.getText().toString(); if (deadline != null && !TextUtils.isEmpty(deadline)) { - builder.setOverrideDeadline(Long.valueOf(deadline)); + builder.setOverrideDeadline(Long.valueOf(deadline) * 1000); } - boolean requiresUnmetered = mWiFiConnectivityRadioButton.isSelected(); - boolean requiresAnyConnectivity = mAnyConnectivityRadioButton.isSelected(); + boolean requiresUnmetered = mWiFiConnectivityRadioButton.isChecked(); + boolean requiresAnyConnectivity = mAnyConnectivityRadioButton.isChecked(); if (requiresUnmetered) { builder.setRequiredNetworkCapabilities(JobInfo.NetworkType.UNMETERED); } else if (requiresAnyConnectivity) { builder.setRequiredNetworkCapabilities(JobInfo.NetworkType.ANY); } + builder.setRequiresDeviceIdle(mRequiresIdleCheckbox.isChecked()); + builder.setRequiresCharging(mRequiresChargingCheckBox.isChecked()); mTestService.scheduleJob(builder.build()); } + public void cancelAllJobs(View v) { + JobScheduler tm = + (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE); + tm.cancelAll(); + } + /** * UI onclick listener to call jobFinished() in our service. */ diff --git a/tools/layoutlib/Android.mk b/tools/layoutlib/Android.mk index 08486e6..1942831 100644 --- a/tools/layoutlib/Android.mk +++ b/tools/layoutlib/Android.mk @@ -30,8 +30,8 @@ LOCAL_JAVACFLAGS := -source 6 -target 6 built_framework_dep := $(call java-lib-deps,framework-base) built_framework_classes := $(call java-lib-files,framework-base) -built_core_dep := $(call java-lib-deps,core) -built_core_classes := $(call java-lib-files,core) +built_core_dep := $(call java-lib-deps,core-libart) +built_core_classes := $(call java-lib-files,core-libart) built_ext_dep := $(call java-lib-deps,ext) built_ext_classes := $(call java-lib-files,ext) |
