From 9bc89af3f1bce8003ee4f93b89a1770d8f5b9cc9 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Wed, 11 Jan 2017 11:25:50 -0700 Subject: [PATCH] Add API for apps to declare their "category". Upcoming platform features need to cluster apps together into broad categories to help summarize information to users. (For example, when presenting battery, network, and disk usage.) We are tightly limiting the set of categories to keep them easily presentable to users when summarizing information. This feature is not designed to be a general-purpose taxonomy, nor should it be allowed to become one. Older apps may not have defined a category in their manifests, so allow the installing app to define a category on their behalf. Test: builds, boots Bug: 33815939 Change-Id: I785b882ee7c18072ef47d56e0fc19ad72888e1b7 --- api/current.txt | 16 ++- api/system-current.txt | 16 ++- api/test-current.txt | 16 ++- .../android/app/ApplicationPackageManager.java | 11 ++ core/java/android/content/pm/ApplicationInfo.java | 148 ++++++++++++++++++++- core/java/android/content/pm/IPackageManager.aidl | 2 + core/java/android/content/pm/PackageManager.java | 10 ++ core/java/android/content/pm/PackageParser.java | 79 +++++------ core/java/android/content/pm/PackageUserState.java | 5 + core/res/res/values/attrs_manifest.xml | 24 ++++ core/res/res/values/public.xml | 1 + core/res/res/values/strings.xml | 18 +++ core/res/res/values/symbols.xml | 10 ++ .../android/server/pm/PackageManagerService.java | 21 +++ .../com/android/server/pm/PackageSettingBase.java | 11 +- .../core/java/com/android/server/pm/Settings.java | 14 ++ .../src/android/test/mock/MockPackageManager.java | 6 + .../bridge/android/BridgePackageManager.java | 4 + 18 files changed, 365 insertions(+), 47 deletions(-) diff --git a/api/current.txt b/api/current.txt index c7577e4c1e31..d759f3af3db7 100644 --- a/api/current.txt +++ b/api/current.txt @@ -284,6 +284,7 @@ package android { field public static final int anyDensity = 16843372; // 0x101026c field public static final int apduServiceBanner = 16843757; // 0x10103ed field public static final int apiKey = 16843281; // 0x1010211 + field public static final int appCategory = 16844101; // 0x1010545 field public static final int author = 16843444; // 0x10102b4 field public static final int authorities = 16842776; // 0x1010018 field public static final int autoAdvanceViewId = 16843535; // 0x101030f @@ -9592,7 +9593,17 @@ package android.content.pm { ctor public ApplicationInfo(android.content.pm.ApplicationInfo); method public int describeContents(); method public void dump(android.util.Printer, java.lang.String); + method public static java.lang.CharSequence getCategoryTitle(android.content.Context, int); method public java.lang.CharSequence loadDescription(android.content.pm.PackageManager); + field public static final int CATEGORY_AUDIO = 1; // 0x1 + field public static final int CATEGORY_GAME = 0; // 0x0 + field public static final int CATEGORY_IMAGE = 3; // 0x3 + field public static final int CATEGORY_MAPS = 6; // 0x6 + field public static final int CATEGORY_NEWS = 5; // 0x5 + field public static final int CATEGORY_PRODUCTIVITY = 7; // 0x7 + field public static final int CATEGORY_SOCIAL = 4; // 0x4 + field public static final int CATEGORY_UNDEFINED = -1; // 0xffffffff + field public static final int CATEGORY_VIDEO = 2; // 0x2 field public static final android.os.Parcelable.Creator CREATOR; field public static final int FLAG_ALLOW_BACKUP = 32768; // 0x8000 field public static final int FLAG_ALLOW_CLEAR_USER_DATA = 64; // 0x40 @@ -9606,7 +9617,7 @@ package android.content.pm { 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 - field public static final int FLAG_IS_GAME = 33554432; // 0x2000000 + field public static final deprecated int FLAG_IS_GAME = 33554432; // 0x2000000 field public static final int FLAG_KILL_AFTER_RESTORE = 65536; // 0x10000 field public static final int FLAG_LARGE_HEAP = 1048576; // 0x100000 field public static final int FLAG_MULTIARCH = -2147483648; // 0x80000000 @@ -9627,6 +9638,7 @@ package android.content.pm { field public static final int FLAG_USES_CLEARTEXT_TRAFFIC = 134217728; // 0x8000000 field public static final int FLAG_VM_SAFE_MODE = 16384; // 0x4000 field public java.lang.String backupAgentName; + field public int category; field public java.lang.String className; field public int compatibleWidthLimitDp; field public java.lang.String dataDir; @@ -10046,6 +10058,7 @@ package android.content.pm { method public abstract android.content.pm.ResolveInfo resolveActivity(android.content.Intent, int); method public abstract android.content.pm.ProviderInfo resolveContentProvider(java.lang.String, int); method public abstract android.content.pm.ResolveInfo resolveService(android.content.Intent, int); + method public abstract void setApplicationCategoryHint(java.lang.String, int); method public abstract void setApplicationEnabledSetting(java.lang.String, int, int); method public abstract void setComponentEnabledSetting(android.content.ComponentName, int, int); method public abstract void setInstallerPackageName(java.lang.String, java.lang.String); @@ -39019,6 +39032,7 @@ package android.test.mock { method public android.content.pm.ResolveInfo resolveActivity(android.content.Intent, int); method public android.content.pm.ProviderInfo resolveContentProvider(java.lang.String, int); method public android.content.pm.ResolveInfo resolveService(android.content.Intent, int); + method public void setApplicationCategoryHint(java.lang.String, int); method public void setApplicationEnabledSetting(java.lang.String, int, int); method public void setComponentEnabledSetting(android.content.ComponentName, int, int); method public void setInstallerPackageName(java.lang.String, java.lang.String); diff --git a/api/system-current.txt b/api/system-current.txt index 28674ca01c7b..95635212cf47 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -393,6 +393,7 @@ package android { field public static final int anyDensity = 16843372; // 0x101026c field public static final int apduServiceBanner = 16843757; // 0x10103ed field public static final int apiKey = 16843281; // 0x1010211 + field public static final int appCategory = 16844101; // 0x1010545 field public static final int author = 16843444; // 0x10102b4 field public static final int authorities = 16842776; // 0x1010018 field public static final int autoAdvanceViewId = 16843535; // 0x101030f @@ -9975,7 +9976,17 @@ package android.content.pm { ctor public ApplicationInfo(android.content.pm.ApplicationInfo); method public int describeContents(); method public void dump(android.util.Printer, java.lang.String); + method public static java.lang.CharSequence getCategoryTitle(android.content.Context, int); method public java.lang.CharSequence loadDescription(android.content.pm.PackageManager); + field public static final int CATEGORY_AUDIO = 1; // 0x1 + field public static final int CATEGORY_GAME = 0; // 0x0 + field public static final int CATEGORY_IMAGE = 3; // 0x3 + field public static final int CATEGORY_MAPS = 6; // 0x6 + field public static final int CATEGORY_NEWS = 5; // 0x5 + field public static final int CATEGORY_PRODUCTIVITY = 7; // 0x7 + field public static final int CATEGORY_SOCIAL = 4; // 0x4 + field public static final int CATEGORY_UNDEFINED = -1; // 0xffffffff + field public static final int CATEGORY_VIDEO = 2; // 0x2 field public static final android.os.Parcelable.Creator CREATOR; field public static final int FLAG_ALLOW_BACKUP = 32768; // 0x8000 field public static final int FLAG_ALLOW_CLEAR_USER_DATA = 64; // 0x40 @@ -9989,7 +10000,7 @@ package android.content.pm { 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 - field public static final int FLAG_IS_GAME = 33554432; // 0x2000000 + field public static final deprecated int FLAG_IS_GAME = 33554432; // 0x2000000 field public static final int FLAG_KILL_AFTER_RESTORE = 65536; // 0x10000 field public static final int FLAG_LARGE_HEAP = 1048576; // 0x100000 field public static final int FLAG_MULTIARCH = -2147483648; // 0x80000000 @@ -10010,6 +10021,7 @@ package android.content.pm { field public static final int FLAG_USES_CLEARTEXT_TRAFFIC = 134217728; // 0x8000000 field public static final int FLAG_VM_SAFE_MODE = 16384; // 0x4000 field public java.lang.String backupAgentName; + field public int category; field public java.lang.String className; field public int compatibleWidthLimitDp; field public java.lang.String credentialProtectedDataDir; @@ -10475,6 +10487,7 @@ package android.content.pm { method public abstract android.content.pm.ProviderInfo resolveContentProvider(java.lang.String, int); method public abstract android.content.pm.ResolveInfo resolveService(android.content.Intent, int); method public abstract void revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle); + method public abstract void setApplicationCategoryHint(java.lang.String, int); method public abstract void setApplicationEnabledSetting(java.lang.String, int, int); method public abstract void setComponentEnabledSetting(android.content.ComponentName, int, int); method public abstract void setInstallerPackageName(java.lang.String, java.lang.String); @@ -42241,6 +42254,7 @@ package android.test.mock { method public android.content.pm.ProviderInfo resolveContentProvider(java.lang.String, int); method public android.content.pm.ResolveInfo resolveService(android.content.Intent, int); method public void revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle); + method public void setApplicationCategoryHint(java.lang.String, int); method public void setApplicationEnabledSetting(java.lang.String, int, int); method public void setComponentEnabledSetting(android.content.ComponentName, int, int); method public void setInstallerPackageName(java.lang.String, java.lang.String); diff --git a/api/test-current.txt b/api/test-current.txt index 71b8c1d77ef7..3a7c5fd8421e 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -284,6 +284,7 @@ package android { field public static final int anyDensity = 16843372; // 0x101026c field public static final int apduServiceBanner = 16843757; // 0x10103ed field public static final int apiKey = 16843281; // 0x1010211 + field public static final int appCategory = 16844101; // 0x1010545 field public static final int author = 16843444; // 0x10102b4 field public static final int authorities = 16842776; // 0x1010018 field public static final int autoAdvanceViewId = 16843535; // 0x101030f @@ -9617,9 +9618,19 @@ package android.content.pm { ctor public ApplicationInfo(android.content.pm.ApplicationInfo); method public int describeContents(); method public void dump(android.util.Printer, java.lang.String); + method public static java.lang.CharSequence getCategoryTitle(android.content.Context, int); method public boolean isPrivilegedApp(); method public boolean isSystemApp(); method public java.lang.CharSequence loadDescription(android.content.pm.PackageManager); + field public static final int CATEGORY_AUDIO = 1; // 0x1 + field public static final int CATEGORY_GAME = 0; // 0x0 + field public static final int CATEGORY_IMAGE = 3; // 0x3 + field public static final int CATEGORY_MAPS = 6; // 0x6 + field public static final int CATEGORY_NEWS = 5; // 0x5 + field public static final int CATEGORY_PRODUCTIVITY = 7; // 0x7 + field public static final int CATEGORY_SOCIAL = 4; // 0x4 + field public static final int CATEGORY_UNDEFINED = -1; // 0xffffffff + field public static final int CATEGORY_VIDEO = 2; // 0x2 field public static final android.os.Parcelable.Creator CREATOR; field public static final int FLAG_ALLOW_BACKUP = 32768; // 0x8000 field public static final int FLAG_ALLOW_CLEAR_USER_DATA = 64; // 0x40 @@ -9633,7 +9644,7 @@ package android.content.pm { 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 - field public static final int FLAG_IS_GAME = 33554432; // 0x2000000 + field public static final deprecated int FLAG_IS_GAME = 33554432; // 0x2000000 field public static final int FLAG_KILL_AFTER_RESTORE = 65536; // 0x10000 field public static final int FLAG_LARGE_HEAP = 1048576; // 0x100000 field public static final int FLAG_MULTIARCH = -2147483648; // 0x80000000 @@ -9654,6 +9665,7 @@ package android.content.pm { field public static final int FLAG_USES_CLEARTEXT_TRAFFIC = 134217728; // 0x8000000 field public static final int FLAG_VM_SAFE_MODE = 16384; // 0x4000 field public java.lang.String backupAgentName; + field public int category; field public java.lang.String className; field public int compatibleWidthLimitDp; field public java.lang.String dataDir; @@ -10075,6 +10087,7 @@ package android.content.pm { method public abstract android.content.pm.ResolveInfo resolveActivity(android.content.Intent, int); method public abstract android.content.pm.ProviderInfo resolveContentProvider(java.lang.String, int); method public abstract android.content.pm.ResolveInfo resolveService(android.content.Intent, int); + method public abstract void setApplicationCategoryHint(java.lang.String, int); method public abstract void setApplicationEnabledSetting(java.lang.String, int, int); method public abstract void setComponentEnabledSetting(android.content.ComponentName, int, int); method public abstract void setInstallerPackageName(java.lang.String, java.lang.String); @@ -39139,6 +39152,7 @@ package android.test.mock { method public android.content.pm.ResolveInfo resolveActivity(android.content.Intent, int); method public android.content.pm.ProviderInfo resolveContentProvider(java.lang.String, int); method public android.content.pm.ResolveInfo resolveService(android.content.Intent, int); + method public void setApplicationCategoryHint(java.lang.String, int); method public void setApplicationEnabledSetting(java.lang.String, int, int); method public void setComponentEnabledSetting(android.content.ComponentName, int, int); method public void setInstallerPackageName(java.lang.String, java.lang.String); diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 627e6611c930..aedcdce980a2 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -2027,6 +2027,17 @@ public class ApplicationPackageManager extends PackageManager { } } + /** @hide */ + @Override + public void setApplicationCategoryHint(String packageName, int categoryHint) { + try { + mPM.setApplicationCategoryHint(packageName, categoryHint, + mContext.getOpPackageName()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + @Override public void getPackageSizeInfoAsUser(String packageName, int userHandle, IPackageStatsObserver observer) { diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index adc99d39d876..71071e1d45ad 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -16,6 +16,9 @@ package android.content.pm; +import static android.os.Build.VERSION_CODES.DONUT; + +import android.annotation.IntDef; import android.annotation.SystemApi; import android.annotation.TestApi; import android.content.Context; @@ -31,13 +34,13 @@ import android.util.Printer; import com.android.internal.util.ArrayUtils; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.text.Collator; import java.util.Arrays; import java.util.Comparator; import java.util.Objects; -import static android.os.Build.VERSION_CODES.DONUT; - /** * Information you can retrieve about a particular application. This * corresponds to information collected from the AndroidManifest.xml's @@ -344,9 +347,12 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { public static final int FLAG_IS_DATA_ONLY = 1<<24; /** - * Value for {@link #flags}: true if the application was declared to be a game, or - * false if it is a non-game application. + * Value for {@link #flags}: true if the application was declared to be a + * game, or false if it is a non-game application. + * + * @deprecated use {@link #CATEGORY_GAME} instead. */ + @Deprecated public static final int FLAG_IS_GAME = 1<<25; /** @@ -779,6 +785,134 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { */ public int networkSecurityConfigRes; + /** + * The category of this app. Categories are used to cluster multiple apps + * together into meaningful groups, such as when summarizing battery, + * network, or disk usage. Apps should only define this value when they fit + * well into one of the specific categories. + *

+ * Set from the {@link android.R.attr#appCategory} attribute in the + * manifest. If the manifest doesn't define a category, this value may have + * been provided by the installer via + * {@link PackageManager#setApplicationCategoryHint(String, int)}. + */ + public @Category int category = CATEGORY_UNDEFINED; + + /** {@hide} */ + @IntDef({ + CATEGORY_UNDEFINED, + CATEGORY_GAME, + CATEGORY_AUDIO, + CATEGORY_VIDEO, + CATEGORY_IMAGE, + CATEGORY_SOCIAL, + CATEGORY_NEWS, + CATEGORY_MAPS, + CATEGORY_PRODUCTIVITY + }) + @Retention(RetentionPolicy.SOURCE) + public @interface Category { + } + + /** + * Value when category is undefined. + * + * @see #category + */ + public static final int CATEGORY_UNDEFINED = -1; + + /** + * Category for apps which are primarily games. + * + * @see #category + */ + public static final int CATEGORY_GAME = 0; + + /** + * Category for apps which primarily work with audio or music, such as music + * players. + * + * @see #category + */ + public static final int CATEGORY_AUDIO = 1; + + /** + * Category for apps which primarily work with video or movies, such as + * streaming video apps. + * + * @see #category + */ + public static final int CATEGORY_VIDEO = 2; + + /** + * Category for apps which primarily work with images or photos, such as + * camera or gallery apps. + * + * @see #category + */ + public static final int CATEGORY_IMAGE = 3; + + /** + * Category for apps which are primarily social apps, such as messaging, + * communication, or social network apps. + * + * @see #category + */ + public static final int CATEGORY_SOCIAL = 4; + + /** + * Category for apps which are primarily news apps, such as newspapers, + * magazines, or sports apps. + * + * @see #category + */ + public static final int CATEGORY_NEWS = 5; + + /** + * Category for apps which are primarily maps apps, such as navigation apps. + * + * @see #category + */ + public static final int CATEGORY_MAPS = 6; + + /** + * Category for apps which are primarily productivity apps, such as cloud + * storage or workplace apps. + * + * @see #category + */ + public static final int CATEGORY_PRODUCTIVITY = 7; + + /** + * Return a concise, localized title for the given + * {@link ApplicationInfo#category} value, or {@code null} for unknown + * values such as {@link #CATEGORY_UNDEFINED}. + * + * @see #category + */ + public static CharSequence getCategoryTitle(Context context, @Category int category) { + switch (category) { + case ApplicationInfo.CATEGORY_GAME: + return context.getText(com.android.internal.R.string.app_category_game); + case ApplicationInfo.CATEGORY_AUDIO: + return context.getText(com.android.internal.R.string.app_category_audio); + case ApplicationInfo.CATEGORY_VIDEO: + return context.getText(com.android.internal.R.string.app_category_video); + case ApplicationInfo.CATEGORY_IMAGE: + return context.getText(com.android.internal.R.string.app_category_image); + case ApplicationInfo.CATEGORY_SOCIAL: + return context.getText(com.android.internal.R.string.app_category_social); + case ApplicationInfo.CATEGORY_NEWS: + return context.getText(com.android.internal.R.string.app_category_news); + case ApplicationInfo.CATEGORY_MAPS: + return context.getText(com.android.internal.R.string.app_category_maps); + case ApplicationInfo.CATEGORY_PRODUCTIVITY: + return context.getText(com.android.internal.R.string.app_category_productivity); + default: + return null; + } + } + public void dump(Printer pw, String prefix) { dump(pw, prefix, DUMP_FLAG_ALL); } @@ -854,6 +988,9 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { pw.println(prefix + "networkSecurityConfigRes=0x" + Integer.toHexString(networkSecurityConfigRes)); } + if (category != CATEGORY_UNDEFINED) { + pw.println(prefix + "category=" + category); + } } super.dumpBack(pw, prefix); } @@ -941,6 +1078,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { backupAgentName = orig.backupAgentName; fullBackupContent = orig.fullBackupContent; networkSecurityConfigRes = orig.networkSecurityConfigRes; + category = orig.category; } public String toString() { @@ -997,6 +1135,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { dest.writeInt(uiOptions); dest.writeInt(fullBackupContent); dest.writeInt(networkSecurityConfigRes); + dest.writeInt(category); } public static final Parcelable.Creator CREATOR @@ -1053,6 +1192,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { uiOptions = source.readInt(); fullBackupContent = source.readInt(); networkSecurityConfigRes = source.readInt(); + category = source.readInt(); } /** diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index b9b61dba8623..eb0ca2ed10be 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -226,6 +226,8 @@ interface IPackageManager { void setInstallerPackageName(in String targetPackage, in String installerPackageName); + void setApplicationCategoryHint(String packageName, int categoryHint, String callerPackageName); + /** @deprecated rawr, don't call AIDL methods directly! */ void deletePackageAsUser(in String packageName, IPackageDeleteObserver observer, int userId, int flags); diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 6a2325bf565d..0cd67b793dc1 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -5585,6 +5585,16 @@ public abstract class PackageManager { */ public abstract boolean isPackageSuspendedForUser(String packageName, int userId); + /** + * Provide a hint of what the {@link ApplicationInfo#category} value should + * be for the given package. + *

+ * This hint can only be set by the app which installed this package, as + * determined by {@link #getInstallerPackageName(String)}. + */ + public abstract void setApplicationCategoryHint(String packageName, + @ApplicationInfo.Category int categoryHint); + /** {@hide} */ public static boolean isMoveStatusFinished(int status) { return (status < 0 || status > 100); diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index ad1ed554a7a4..9e1fe79291fa 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -16,15 +16,30 @@ package android.content.pm; -import android.os.Parcel; -import android.os.Parcelable; -import com.android.internal.R; -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.ArrayUtils; -import com.android.internal.util.XmlUtils; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; +import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE; +import static android.content.pm.ActivityInfo.FLAG_ON_TOP_LAUNCHER; +import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY; +import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY; +import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION; +import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE; +import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE; +import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_AND_PIPABLE; +import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION; +import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; +import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED; +import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_RESIZEABLE_ACTIVITIES_EXPLICITLY_SET; +import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_RESIZEABLE_ACTIVITIES_VIA_SDK_VERSION; +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 static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; +import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE; import android.annotation.IntRange; import android.annotation.NonNull; @@ -42,6 +57,8 @@ import android.content.res.XmlResourceParser; import android.os.Build; import android.os.Bundle; import android.os.FileUtils; +import android.os.Parcel; +import android.os.Parcelable; import android.os.PatternMatcher; import android.os.Trace; import android.os.UserHandle; @@ -62,6 +79,16 @@ import android.util.apk.ApkSignatureSchemeV2Verifier; import android.util.jar.StrictJarFile; import android.view.Gravity; +import com.android.internal.R; +import com.android.internal.annotations.VisibleForTesting; +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.FileOutputStream; import java.io.IOException; @@ -83,38 +110,10 @@ import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; -import java.util.Objects; import java.util.Set; import java.util.concurrent.atomic.AtomicReference; import java.util.zip.ZipEntry; -import libcore.io.IoUtils; - -import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE; -import static android.content.pm.ActivityInfo.FLAG_ON_TOP_LAUNCHER; -import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE; -import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY; -import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY; -import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION; -import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE; -import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_AND_PIPABLE; -import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION; -import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; -import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; -import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED; -import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_RESIZEABLE_ACTIVITIES_EXPLICITLY_SET; -import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_RESIZEABLE_ACTIVITIES_VIA_SDK_VERSION; -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 static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; -import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE; - /** * Parser for package files (APKs) on disk. This supports apps packaged either * as a single "monolithic" APK, or apps packaged as a "cluster" of multiple @@ -3274,6 +3273,9 @@ public class PackageParser { ai.networkSecurityConfigRes = sa.getResourceId( com.android.internal.R.styleable.AndroidManifestApplication_networkSecurityConfig, 0); + ai.category = sa.getInt( + com.android.internal.R.styleable.AndroidManifestApplication_appCategory, + ApplicationInfo.CATEGORY_UNDEFINED); String str; str = sa.getNonConfigurationString( @@ -6255,6 +6257,9 @@ public class PackageParser { ai.enabled = false; } ai.enabledSetting = state.enabled; + if (ai.category == ApplicationInfo.CATEGORY_UNDEFINED) { + ai.category = state.categoryHint; + } } public static ApplicationInfo generateApplicationInfo(Package p, int flags, diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java index 1821458c9869..eb3f27570970 100644 --- a/core/java/android/content/pm/PackageUserState.java +++ b/core/java/android/content/pm/PackageUserState.java @@ -47,6 +47,7 @@ public class PackageUserState { public String lastDisableAppCaller; public int domainVerificationStatus; public int appLinkGeneration; + public int categoryHint = ApplicationInfo.CATEGORY_UNDEFINED; public ArraySet disabledComponents; public ArraySet enabledComponents; @@ -72,6 +73,7 @@ public class PackageUserState { lastDisableAppCaller = o.lastDisableAppCaller; domainVerificationStatus = o.domainVerificationStatus; appLinkGeneration = o.appLinkGeneration; + categoryHint = o.categoryHint; disabledComponents = ArrayUtils.cloneOrNull(o.disabledComponents); enabledComponents = ArrayUtils.cloneOrNull(o.enabledComponents); } @@ -197,6 +199,9 @@ public class PackageUserState { if (appLinkGeneration != oldState.appLinkGeneration) { return false; } + if (categoryHint != oldState.categoryHint) { + return false; + } if ((disabledComponents == null && oldState.disabledComponents != null) || (disabledComponents != null && oldState.disabledComponents == null)) { return false; diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index c91f0a51d5bd..b9613949627c 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -1338,6 +1338,7 @@ any accounts. The type should correspond to the account authenticator type, such as "com.google". --> + + + + + + + + + + + + + + + + + + + + Tooltip + + + Games + + Music & Audio + + Movies & Video + + Photos & Images + + Social & Communication + + News & Magazines + + Maps & Navigation + + Productivity + diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index f44b0397322b..cd07d88de50d 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2783,4 +2783,14 @@ + + + + + + + + + + diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 76ae57e0e15a..395ec1f26187 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -12956,6 +12956,27 @@ public class PackageManagerService extends IPackageManager.Stub { } } + @Override + public void setApplicationCategoryHint(String packageName, int categoryHint, + String callerPackageName) { + mContext.getSystemService(AppOpsManager.class).checkPackage(Binder.getCallingUid(), + callerPackageName); + synchronized (mPackages) { + PackageSetting ps = mSettings.mPackages.get(packageName); + if (ps == null) { + throw new IllegalArgumentException("Unknown target package " + packageName); + } + + if (!Objects.equals(callerPackageName, ps.installerPackageName)) { + throw new IllegalArgumentException("Calling package " + callerPackageName + + " is not installer for " + packageName); + } + + ps.categoryHint = categoryHint; + scheduleWriteSettingsLocked(); + } + } + private void processPendingInstall(final InstallArgs args, final int currentStatus) { // Queue up an async operation since the package installation may take a little while. mHandler.post(new Runnable() { diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java index 6089d2eba960..9456a5c4b6a2 100644 --- a/services/core/java/com/android/server/pm/PackageSettingBase.java +++ b/services/core/java/com/android/server/pm/PackageSettingBase.java @@ -20,6 +20,7 @@ import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED; +import android.content.pm.ApplicationInfo; import android.content.pm.IntentFilterVerificationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageUserState; @@ -129,6 +130,8 @@ abstract class PackageSettingBase extends SettingBase { boolean isOrphaned; /** UUID of {@link VolumeInfo} hosting this app */ String volumeUuid; + /** The category of this app, as hinted by the installer */ + int categoryHint = ApplicationInfo.CATEGORY_UNDEFINED; IntentFilterVerificationInfo verificationInfo; @@ -246,6 +249,7 @@ abstract class PackageSettingBase extends SettingBase { verificationInfo = orig.verificationInfo; versionCode = orig.versionCode; volumeUuid = orig.volumeUuid; + categoryHint = orig.categoryHint; } private PackageUserState modifyUserState(int userId) { @@ -259,10 +263,11 @@ abstract class PackageSettingBase extends SettingBase { public PackageUserState readUserState(int userId) { PackageUserState state = userState.get(userId); - if (state != null) { - return state; + if (state == null) { + return DEFAULT_USER_STATE; } - return DEFAULT_USER_STATE; + state.categoryHint = categoryHint; + return state; } void setEnabled(int state, int userId, String callingPackage) { diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 7187fc26833f..395108bfab94 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -2673,6 +2673,10 @@ final class Settings { if (pkg.volumeUuid != null) { serializer.attribute(null, "volumeUuid", pkg.volumeUuid); } + if (pkg.categoryHint != ApplicationInfo.CATEGORY_UNDEFINED) { + serializer.attribute(null, "categoryHint", + Integer.toString(pkg.categoryHint)); + } if (pkg.parentPackageName != null) { serializer.attribute(null, "parentPackageName", pkg.parentPackageName); } @@ -3538,6 +3542,8 @@ final class Settings { String installerPackageName = null; String isOrphaned = null; String volumeUuid = null; + String categoryHintString = null; + int categoryHint = ApplicationInfo.CATEGORY_UNDEFINED; String uidError = null; int pkgFlags = 0; int pkgPrivateFlags = 0; @@ -3580,6 +3586,13 @@ final class Settings { installerPackageName = parser.getAttributeValue(null, "installer"); isOrphaned = parser.getAttributeValue(null, "isOrphaned"); volumeUuid = parser.getAttributeValue(null, "volumeUuid"); + categoryHintString = parser.getAttributeValue(null, "categoryHint"); + if (categoryHintString != null) { + try { + categoryHint = Integer.parseInt(categoryHintString); + } catch (NumberFormatException e) { + } + } systemStr = parser.getAttributeValue(null, "publicFlags"); if (systemStr != null) { @@ -3731,6 +3744,7 @@ final class Settings { packageSetting.installerPackageName = installerPackageName; packageSetting.isOrphaned = "true".equals(isOrphaned); packageSetting.volumeUuid = volumeUuid; + packageSetting.categoryHint = categoryHint; packageSetting.legacyNativeLibraryPathString = legacyNativeLibraryPathStr; packageSetting.primaryCpuAbiString = primaryCpuAbiString; packageSetting.secondaryCpuAbiString = secondaryCpuAbiString; diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java index 1bd5b1dc98c2..715896971137 100644 --- a/test-runner/src/android/test/mock/MockPackageManager.java +++ b/test-runner/src/android/test/mock/MockPackageManager.java @@ -886,6 +886,12 @@ public class MockPackageManager extends PackageManager { throw new UnsupportedOperationException(); } + /** @hide */ + @Override + public void setApplicationCategoryHint(String packageName, int categoryHint) { + throw new UnsupportedOperationException(); + } + /** * @hide */ diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java index f47b1050ca73..8835b5845175 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java @@ -784,6 +784,10 @@ public class BridgePackageManager extends PackageManager { } @Override + public void setApplicationCategoryHint(String packageName, int categoryHint) { + } + + @Override public int getMoveStatus(int moveId) { return 0; } -- 2.11.0