diff options
64 files changed, 1729 insertions, 382 deletions
diff --git a/api/current.txt b/api/current.txt index a71c63ae598c..a04f95bde91d 100644 --- a/api/current.txt +++ b/api/current.txt @@ -41102,6 +41102,7 @@ package android.text { public static class InputFilter.AllCaps implements android.text.InputFilter { ctor public InputFilter.AllCaps(); + ctor public InputFilter.AllCaps(java.util.Locale); method public java.lang.CharSequence filter(java.lang.CharSequence, int, int, android.text.Spanned, int, int); } diff --git a/api/system-current.txt b/api/system-current.txt index fbe210925ab3..99208d5b3730 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -44647,6 +44647,7 @@ package android.text { public static class InputFilter.AllCaps implements android.text.InputFilter { ctor public InputFilter.AllCaps(); + ctor public InputFilter.AllCaps(java.util.Locale); method public java.lang.CharSequence filter(java.lang.CharSequence, int, int, android.text.Spanned, int, int); } diff --git a/api/test-current.txt b/api/test-current.txt index 96bda4f3c808..9326217e21d4 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -41329,6 +41329,7 @@ package android.text { public static class InputFilter.AllCaps implements android.text.InputFilter { ctor public InputFilter.AllCaps(); + ctor public InputFilter.AllCaps(java.util.Locale); method public java.lang.CharSequence filter(java.lang.CharSequence, int, int, android.text.Spanned, int, int); } diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java index 1bcfb22fe2f2..1b77427c5b72 100644 --- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java +++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java @@ -270,7 +270,8 @@ public final class Bmgr { case BackupManager.ERROR_TRANSPORT_ABORTED: return "Transport error"; case BackupManager.ERROR_TRANSPORT_PACKAGE_REJECTED: - return "Transport rejected package"; + return "Transport rejected package because it wasn't able to process it" + + " at the time"; case BackupManager.ERROR_AGENT_FAILURE: return "Agent error"; case BackupManager.ERROR_TRANSPORT_QUOTA_EXCEEDED: diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java index d71573f7ca50..9813cb1631ce 100644 --- a/cmds/pm/src/com/android/commands/pm/Pm.java +++ b/cmds/pm/src/com/android/commands/pm/Pm.java @@ -1033,7 +1033,7 @@ public final class Pm { if (info != null) { System.out.println("Success: created user id " + info.id); - return 1; + return 0; } else { System.err.println("Error: couldn't create User."); return 1; diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java index 9fb9c00ee9e9..3ad30bf2b045 100644 --- a/core/java/android/app/FragmentManager.java +++ b/core/java/android/app/FragmentManager.java @@ -679,7 +679,8 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate ArrayList<Integer> mAvailBackStackIndices; ArrayList<OnBackStackChangedListener> mBackStackChangeListeners; - CopyOnWriteArrayList<Pair<FragmentLifecycleCallbacks, Boolean>> mLifecycleCallbacks; + final CopyOnWriteArrayList<Pair<FragmentLifecycleCallbacks, Boolean>> + mLifecycleCallbacks = new CopyOnWriteArrayList<>(); int mCurState = Fragment.INITIALIZING; FragmentHostCallback<?> mHost; @@ -3189,17 +3190,10 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate public void registerFragmentLifecycleCallbacks(FragmentLifecycleCallbacks cb, boolean recursive) { - if (mLifecycleCallbacks == null) { - mLifecycleCallbacks = new CopyOnWriteArrayList<>(); - } - mLifecycleCallbacks.add(new Pair(cb, recursive)); + mLifecycleCallbacks.add(new Pair<>(cb, recursive)); } public void unregisterFragmentLifecycleCallbacks(FragmentLifecycleCallbacks cb) { - if (mLifecycleCallbacks == null) { - return; - } - synchronized (mLifecycleCallbacks) { for (int i = 0, N = mLifecycleCallbacks.size(); i < N; i++) { if (mLifecycleCallbacks.get(i).first == cb) { @@ -3218,9 +3212,6 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate .dispatchOnFragmentPreAttached(f, context, true); } } - if (mLifecycleCallbacks == null) { - return; - } for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) { if (!onlyRecursive || p.second) { p.first.onFragmentPreAttached(this, f, context); @@ -3236,9 +3227,6 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate .dispatchOnFragmentAttached(f, context, true); } } - if (mLifecycleCallbacks == null) { - return; - } for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) { if (!onlyRecursive || p.second) { p.first.onFragmentAttached(this, f, context); @@ -3255,9 +3243,6 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate .dispatchOnFragmentPreCreated(f, savedInstanceState, true); } } - if (mLifecycleCallbacks == null) { - return; - } for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) { if (!onlyRecursive || p.second) { p.first.onFragmentPreCreated(this, f, savedInstanceState); @@ -3273,9 +3258,6 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate .dispatchOnFragmentCreated(f, savedInstanceState, true); } } - if (mLifecycleCallbacks == null) { - return; - } for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) { if (!onlyRecursive || p.second) { p.first.onFragmentCreated(this, f, savedInstanceState); @@ -3292,9 +3274,6 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate .dispatchOnFragmentActivityCreated(f, savedInstanceState, true); } } - if (mLifecycleCallbacks == null) { - return; - } for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) { if (!onlyRecursive || p.second) { p.first.onFragmentActivityCreated(this, f, savedInstanceState); @@ -3311,9 +3290,6 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate .dispatchOnFragmentViewCreated(f, v, savedInstanceState, true); } } - if (mLifecycleCallbacks == null) { - return; - } for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) { if (!onlyRecursive || p.second) { p.first.onFragmentViewCreated(this, f, v, savedInstanceState); @@ -3329,9 +3305,6 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate .dispatchOnFragmentStarted(f, true); } } - if (mLifecycleCallbacks == null) { - return; - } for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) { if (!onlyRecursive || p.second) { p.first.onFragmentStarted(this, f); @@ -3347,9 +3320,6 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate .dispatchOnFragmentResumed(f, true); } } - if (mLifecycleCallbacks == null) { - return; - } for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) { if (!onlyRecursive || p.second) { p.first.onFragmentResumed(this, f); @@ -3365,9 +3335,6 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate .dispatchOnFragmentPaused(f, true); } } - if (mLifecycleCallbacks == null) { - return; - } for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) { if (!onlyRecursive || p.second) { p.first.onFragmentPaused(this, f); @@ -3383,9 +3350,6 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate .dispatchOnFragmentStopped(f, true); } } - if (mLifecycleCallbacks == null) { - return; - } for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) { if (!onlyRecursive || p.second) { p.first.onFragmentStopped(this, f); @@ -3401,9 +3365,6 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate .dispatchOnFragmentSaveInstanceState(f, outState, true); } } - if (mLifecycleCallbacks == null) { - return; - } for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) { if (!onlyRecursive || p.second) { p.first.onFragmentSaveInstanceState(this, f, outState); @@ -3419,9 +3380,6 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate .dispatchOnFragmentViewDestroyed(f, true); } } - if (mLifecycleCallbacks == null) { - return; - } for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) { if (!onlyRecursive || p.second) { p.first.onFragmentViewDestroyed(this, f); @@ -3437,9 +3395,6 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate .dispatchOnFragmentDestroyed(f, true); } } - if (mLifecycleCallbacks == null) { - return; - } for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) { if (!onlyRecursive || p.second) { p.first.onFragmentDestroyed(this, f); @@ -3455,9 +3410,6 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate .dispatchOnFragmentDetached(f, true); } } - if (mLifecycleCallbacks == null) { - return; - } for (Pair<FragmentLifecycleCallbacks, Boolean> p : mLifecycleCallbacks) { if (!onlyRecursive || p.second) { p.first.onFragmentDetached(this, f); diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 9b0bab427478..6109531474d9 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -27,9 +27,9 @@ import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VER 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_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE; import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE; import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION; +import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE; 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; @@ -50,9 +50,9 @@ import android.app.ActivityManager; import android.content.ComponentName; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.split.DefaultSplitAssetLoader; import android.content.pm.split.SplitAssetDependencyLoader; import android.content.pm.split.SplitAssetLoader; -import android.content.pm.split.DefaultSplitAssetLoader; import android.content.res.AssetManager; import android.content.res.Configuration; import android.content.res.Resources; @@ -148,8 +148,9 @@ public class PackageParser { private static final String PROPERTY_CHILD_PACKAGES_ENABLED = "persist.sys.child_packages_enabled"; + // TODO: Decide the correct default before O-MR1. private static final boolean MULTI_PACKAGE_APK_ENABLED = Build.IS_DEBUGGABLE && - SystemProperties.getBoolean(PROPERTY_CHILD_PACKAGES_ENABLED, false); + SystemProperties.getBoolean(PROPERTY_CHILD_PACKAGES_ENABLED, true); private static final int MAX_PACKAGES_PER_APK = 5; @@ -498,11 +499,25 @@ public class PackageParser { } } + /** + * Cached parse state for new components. + * + * Allows reuse of the same parse argument records to avoid GC pressure. Lifetime is carefully + * scoped to the parsing of a single application element. + */ + private static class CachedComponentArgs { + ParseComponentArgs mActivityArgs; + ParseComponentArgs mActivityAliasArgs; + ParseComponentArgs mServiceArgs; + ParseComponentArgs mProviderArgs; + } + + /** + * Cached state for parsing instrumentation to avoid GC pressure. + * + * Must be manually reset to null for each new manifest. + */ private ParsePackageItemArgs mParseInstrumentationArgs; - private ParseComponentArgs mParseActivityArgs; - private ParseComponentArgs mParseActivityAliasArgs; - private ParseComponentArgs mParseServiceArgs; - private ParseComponentArgs mParseProviderArgs; /** If set to true, we will only allow package files that exactly match * the DTD. Otherwise, we try to get as much from the package as we @@ -1335,9 +1350,6 @@ public class PackageParser { parsePackageSplitNames(parser, attrs); mParseInstrumentationArgs = null; - mParseActivityArgs = null; - mParseServiceArgs = null; - mParseProviderArgs = null; int type; @@ -2044,9 +2056,6 @@ public class PackageParser { XmlResourceParser parser, int flags, String[] outError) throws XmlPullParserException, IOException { mParseInstrumentationArgs = null; - mParseActivityArgs = null; - mParseServiceArgs = null; - mParseProviderArgs = null; int type; boolean foundApp = false; @@ -3638,6 +3647,9 @@ public class PackageParser { } final int innerDepth = parser.getDepth(); + // IMPORTANT: These must only be cached for a single <application> to avoid components + // getting added to the wrong package. + final CachedComponentArgs cachedArgs = new CachedComponentArgs(); int type; while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) { @@ -3647,7 +3659,7 @@ public class PackageParser { String tagName = parser.getName(); if (tagName.equals("activity")) { - Activity a = parseActivity(owner, res, parser, flags, outError, false, + Activity a = parseActivity(owner, res, parser, flags, outError, cachedArgs, false, owner.baseHardwareAccelerated); if (a == null) { mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; @@ -3657,7 +3669,8 @@ public class PackageParser { owner.activities.add(a); } else if (tagName.equals("receiver")) { - Activity a = parseActivity(owner, res, parser, flags, outError, true, false); + Activity a = parseActivity(owner, res, parser, flags, outError, cachedArgs, + true, false); if (a == null) { mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return false; @@ -3666,7 +3679,7 @@ public class PackageParser { owner.receivers.add(a); } else if (tagName.equals("service")) { - Service s = parseService(owner, res, parser, flags, outError); + Service s = parseService(owner, res, parser, flags, outError, cachedArgs); if (s == null) { mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return false; @@ -3675,7 +3688,7 @@ public class PackageParser { owner.services.add(s); } else if (tagName.equals("provider")) { - Provider p = parseProvider(owner, res, parser, flags, outError); + Provider p = parseProvider(owner, res, parser, flags, outError, cachedArgs); if (p == null) { mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return false; @@ -3684,7 +3697,7 @@ public class PackageParser { owner.providers.add(p); } else if (tagName.equals("activity-alias")) { - Activity a = parseActivityAlias(owner, res, parser, flags, outError); + Activity a = parseActivityAlias(owner, res, parser, flags, outError, cachedArgs); if (a == null) { mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return false; @@ -3892,9 +3905,12 @@ public class PackageParser { ComponentInfo parsedComponent = null; + // IMPORTANT: These must only be cached for a single <application> to avoid components + // getting added to the wrong package. + final CachedComponentArgs cachedArgs = new CachedComponentArgs(); String tagName = parser.getName(); if (tagName.equals("activity")) { - Activity a = parseActivity(owner, res, parser, flags, outError, false, + Activity a = parseActivity(owner, res, parser, flags, outError, cachedArgs, false, owner.baseHardwareAccelerated); if (a == null) { mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; @@ -3905,7 +3921,8 @@ public class PackageParser { parsedComponent = a.info; } else if (tagName.equals("receiver")) { - Activity a = parseActivity(owner, res, parser, flags, outError, true, false); + Activity a = parseActivity(owner, res, parser, flags, outError, cachedArgs, + true, false); if (a == null) { mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return false; @@ -3915,7 +3932,7 @@ public class PackageParser { parsedComponent = a.info; } else if (tagName.equals("service")) { - Service s = parseService(owner, res, parser, flags, outError); + Service s = parseService(owner, res, parser, flags, outError, cachedArgs); if (s == null) { mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return false; @@ -3925,7 +3942,7 @@ public class PackageParser { parsedComponent = s.info; } else if (tagName.equals("provider")) { - Provider p = parseProvider(owner, res, parser, flags, outError); + Provider p = parseProvider(owner, res, parser, flags, outError, cachedArgs); if (p == null) { mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return false; @@ -3935,7 +3952,7 @@ public class PackageParser { parsedComponent = p.info; } else if (tagName.equals("activity-alias")) { - Activity a = parseActivityAlias(owner, res, parser, flags, outError); + Activity a = parseActivityAlias(owner, res, parser, flags, outError, cachedArgs); if (a == null) { mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return false; @@ -4081,13 +4098,13 @@ public class PackageParser { } private Activity parseActivity(Package owner, Resources res, - XmlResourceParser parser, int flags, String[] outError, + XmlResourceParser parser, int flags, String[] outError, CachedComponentArgs cachedArgs, boolean receiver, boolean hardwareAccelerated) throws XmlPullParserException, IOException { TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestActivity); - if (mParseActivityArgs == null) { - mParseActivityArgs = new ParseComponentArgs(owner, outError, + if (cachedArgs.mActivityArgs == null) { + cachedArgs.mActivityArgs = new ParseComponentArgs(owner, outError, R.styleable.AndroidManifestActivity_name, R.styleable.AndroidManifestActivity_label, R.styleable.AndroidManifestActivity_icon, @@ -4100,11 +4117,11 @@ public class PackageParser { R.styleable.AndroidManifestActivity_enabled); } - mParseActivityArgs.tag = receiver ? "<receiver>" : "<activity>"; - mParseActivityArgs.sa = sa; - mParseActivityArgs.flags = flags; + cachedArgs.mActivityArgs.tag = receiver ? "<receiver>" : "<activity>"; + cachedArgs.mActivityArgs.sa = sa; + cachedArgs.mActivityArgs.flags = flags; - Activity a = new Activity(mParseActivityArgs, new ActivityInfo()); + Activity a = new Activity(cachedArgs.mActivityArgs, new ActivityInfo()); if (outError[0] != null) { sa.recycle(); return null; @@ -4576,7 +4593,8 @@ public class PackageParser { } private Activity parseActivityAlias(Package owner, Resources res, - XmlResourceParser parser, int flags, String[] outError) + XmlResourceParser parser, int flags, String[] outError, + CachedComponentArgs cachedArgs) throws XmlPullParserException, IOException { TypedArray sa = res.obtainAttributes(parser, com.android.internal.R.styleable.AndroidManifestActivityAlias); @@ -4597,8 +4615,8 @@ public class PackageParser { return null; } - if (mParseActivityAliasArgs == null) { - mParseActivityAliasArgs = new ParseComponentArgs(owner, outError, + if (cachedArgs.mActivityAliasArgs == null) { + cachedArgs.mActivityAliasArgs = new ParseComponentArgs(owner, outError, com.android.internal.R.styleable.AndroidManifestActivityAlias_name, com.android.internal.R.styleable.AndroidManifestActivityAlias_label, com.android.internal.R.styleable.AndroidManifestActivityAlias_icon, @@ -4609,11 +4627,11 @@ public class PackageParser { 0, com.android.internal.R.styleable.AndroidManifestActivityAlias_description, com.android.internal.R.styleable.AndroidManifestActivityAlias_enabled); - mParseActivityAliasArgs.tag = "<activity-alias>"; + cachedArgs.mActivityAliasArgs.tag = "<activity-alias>"; } - mParseActivityAliasArgs.sa = sa; - mParseActivityAliasArgs.flags = flags; + cachedArgs.mActivityAliasArgs.sa = sa; + cachedArgs.mActivityAliasArgs.flags = flags; Activity target = null; @@ -4660,7 +4678,7 @@ public class PackageParser { info.maxAspectRatio = target.info.maxAspectRatio; info.encryptionAware = info.directBootAware = target.info.directBootAware; - Activity a = new Activity(mParseActivityAliasArgs, info); + Activity a = new Activity(cachedArgs.mActivityAliasArgs, info); if (outError[0] != null) { sa.recycle(); return null; @@ -4766,13 +4784,14 @@ public class PackageParser { } private Provider parseProvider(Package owner, Resources res, - XmlResourceParser parser, int flags, String[] outError) + XmlResourceParser parser, int flags, String[] outError, + CachedComponentArgs cachedArgs) throws XmlPullParserException, IOException { TypedArray sa = res.obtainAttributes(parser, com.android.internal.R.styleable.AndroidManifestProvider); - if (mParseProviderArgs == null) { - mParseProviderArgs = new ParseComponentArgs(owner, outError, + if (cachedArgs.mProviderArgs == null) { + cachedArgs.mProviderArgs = new ParseComponentArgs(owner, outError, com.android.internal.R.styleable.AndroidManifestProvider_name, com.android.internal.R.styleable.AndroidManifestProvider_label, com.android.internal.R.styleable.AndroidManifestProvider_icon, @@ -4783,13 +4802,13 @@ public class PackageParser { com.android.internal.R.styleable.AndroidManifestProvider_process, com.android.internal.R.styleable.AndroidManifestProvider_description, com.android.internal.R.styleable.AndroidManifestProvider_enabled); - mParseProviderArgs.tag = "<provider>"; + cachedArgs.mProviderArgs.tag = "<provider>"; } - mParseProviderArgs.sa = sa; - mParseProviderArgs.flags = flags; + cachedArgs.mProviderArgs.sa = sa; + cachedArgs.mProviderArgs.flags = flags; - Provider p = new Provider(mParseProviderArgs, new ProviderInfo()); + Provider p = new Provider(cachedArgs.mProviderArgs, new ProviderInfo()); if (outError[0] != null) { sa.recycle(); return null; @@ -5120,13 +5139,14 @@ public class PackageParser { } private Service parseService(Package owner, Resources res, - XmlResourceParser parser, int flags, String[] outError) + XmlResourceParser parser, int flags, String[] outError, + CachedComponentArgs cachedArgs) throws XmlPullParserException, IOException { TypedArray sa = res.obtainAttributes(parser, com.android.internal.R.styleable.AndroidManifestService); - if (mParseServiceArgs == null) { - mParseServiceArgs = new ParseComponentArgs(owner, outError, + if (cachedArgs.mServiceArgs == null) { + cachedArgs.mServiceArgs = new ParseComponentArgs(owner, outError, com.android.internal.R.styleable.AndroidManifestService_name, com.android.internal.R.styleable.AndroidManifestService_label, com.android.internal.R.styleable.AndroidManifestService_icon, @@ -5137,13 +5157,13 @@ public class PackageParser { com.android.internal.R.styleable.AndroidManifestService_process, com.android.internal.R.styleable.AndroidManifestService_description, com.android.internal.R.styleable.AndroidManifestService_enabled); - mParseServiceArgs.tag = "<service>"; + cachedArgs.mServiceArgs.tag = "<service>"; } - mParseServiceArgs.sa = sa; - mParseServiceArgs.flags = flags; + cachedArgs.mServiceArgs.sa = sa; + cachedArgs.mServiceArgs.flags = flags; - Service s = new Service(mParseServiceArgs, new ServiceInfo()); + Service s = new Service(cachedArgs.mServiceArgs, new ServiceInfo()); if (outError[0] != null) { sa.recycle(); return null; diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 784ed7a25a47..4e70f4b12a5e 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -3973,6 +3973,15 @@ public final class Settings { }; /** + * Keys we no longer back up under the current schema, but want to continue to + * process when restoring historical backup datasets. + * + * @hide + */ + public static final String[] LEGACY_RESTORE_SETTINGS = { + }; + + /** * These are all public system settings * * @hide @@ -7100,6 +7109,10 @@ public final class Settings { NOTIFICATION_BADGING }; + /** @hide */ + public static final String[] LEGACY_RESTORE_SETTINGS = { + }; + /** * These entries are considered common between the personal and the managed profile, * since the managed profile doesn't get to change them. @@ -10103,6 +10116,10 @@ public final class Settings { BLUETOOTH_ON }; + /** @hide */ + public static final String[] LEGACY_RESTORE_SETTINGS = { + }; + private static final ContentProviderHolder sProviderHolder = new ContentProviderHolder(CONTENT_URI); diff --git a/core/java/android/text/InputFilter.java b/core/java/android/text/InputFilter.java index bff09a20e595..d773158ed0cb 100644 --- a/core/java/android/text/InputFilter.java +++ b/core/java/android/text/InputFilter.java @@ -16,6 +16,10 @@ package android.text; +import android.annotation.Nullable; + +import java.util.Locale; + /** * InputFilters can be attached to {@link Editable}s to constrain the * changes that can be made to them. @@ -33,40 +37,122 @@ public interface InputFilter * as this is what happens when you delete text. Also beware that * you should not attempt to make any changes to <code>dest</code> * from this method; you may only examine it for context. - * + * * Note: If <var>source</var> is an instance of {@link Spanned} or - * {@link Spannable}, the span objects in the <var>source</var> should be - * copied into the filtered result (i.e. the non-null return value). - * {@link TextUtils#copySpansFrom} can be used for convenience. + * {@link Spannable}, the span objects in the <var>source</var> should be + * copied into the filtered result (i.e. the non-null return value). + * {@link TextUtils#copySpansFrom} can be used for convenience if the + * span boundary indices would be remaining identical relative to the source. */ public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend); /** - * This filter will capitalize all the lower case letters that are added - * through edits. + * This filter will capitalize all the lowercase and titlecase letters that are added + * through edits. (Note that if there are no lowercase or titlecase letters in the input, the + * text would not be transformed, even if the result of capitalization of the string is + * different from the string.) */ public static class AllCaps implements InputFilter { + private final Locale mLocale; + + public AllCaps() { + mLocale = null; + } + + /** + * Constructs a locale-specific AllCaps filter, to make sure capitalization rules of that + * locale are used for transforming the sequence. + */ + public AllCaps(@Nullable Locale locale) { + mLocale = locale; + } + public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) { - for (int i = start; i < end; i++) { - if (Character.isLowerCase(source.charAt(i))) { - char[] v = new char[end - start]; - TextUtils.getChars(source, start, end, v, 0); - String s = new String(v).toUpperCase(); - - if (source instanceof Spanned) { - SpannableString sp = new SpannableString(s); - TextUtils.copySpansFrom((Spanned) source, - start, end, null, sp, 0); - return sp; - } else { - return s; - } + final CharSequence wrapper = new CharSequenceWrapper(source, start, end); + + boolean lowerOrTitleFound = false; + final int length = end - start; + for (int i = 0, cp; i < length; i += Character.charCount(cp)) { + // We access 'wrapper' instead of 'source' to make sure no code unit beyond 'end' is + // ever accessed. + cp = Character.codePointAt(wrapper, i); + if (Character.isLowerCase(cp) || Character.isTitleCase(cp)) { + lowerOrTitleFound = true; + break; } } + if (!lowerOrTitleFound) { + return null; // keep original + } + + final boolean copySpans = source instanceof Spanned; + final CharSequence upper = TextUtils.toUpperCase(mLocale, wrapper, copySpans); + if (upper == wrapper) { + // Nothing was changed in the uppercasing operation. This is weird, since + // we had found at least one lowercase or titlecase character. But we can't + // do anything better than keeping the original in this case. + return null; // keep original + } + // Return a SpannableString or String for backward compatibility. + return copySpans ? new SpannableString(upper) : upper.toString(); + } + + private static class CharSequenceWrapper implements CharSequence, Spanned { + private final CharSequence mSource; + private final int mStart, mEnd; + private final int mLength; + + CharSequenceWrapper(CharSequence source, int start, int end) { + mSource = source; + mStart = start; + mEnd = end; + mLength = end - start; + } - return null; // keep original + public int length() { + return mLength; + } + + public char charAt(int index) { + if (index < 0 || index >= mLength) { + throw new IndexOutOfBoundsException(); + } + return mSource.charAt(mStart + index); + } + + public CharSequence subSequence(int start, int end) { + if (start < 0 || end < 0 || end > mLength || start > end) { + throw new IndexOutOfBoundsException(); + } + return new CharSequenceWrapper(mSource, mStart + start, mStart + end); + } + + public String toString() { + return mSource.subSequence(mStart, mEnd).toString(); + } + + public <T> T[] getSpans(int start, int end, Class<T> type) { + return ((Spanned) mSource).getSpans(mStart + start, mStart + end, type); + } + + public int getSpanStart(Object tag) { + return ((Spanned) mSource).getSpanStart(tag) - mStart; + } + + public int getSpanEnd(Object tag) { + return ((Spanned) mSource).getSpanEnd(tag) - mStart; + } + + public int getSpanFlags(Object tag) { + return ((Spanned) mSource).getSpanFlags(tag); + } + + public int nextSpanTransition(int start, int limit, Class type) { + return ((Spanned) mSource).nextSpanTransition(mStart + start, mStart + limit, type) + - mStart; + } } } diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java index e4ed62a1ead9..1aaf73e6e12c 100644 --- a/core/java/android/text/TextLine.java +++ b/core/java/android/text/TextLine.java @@ -827,7 +827,9 @@ class TextLine { underlineXLeft, underlineXRight, y); } if (info.isUnderlineText) { - drawUnderline(wp, c, wp.getColor(), ((Paint) wp).getUnderlineThickness(), + final float thickness = + Math.max(((Paint) wp).getUnderlineThickness(), 1.0f); + drawUnderline(wp, c, wp.getColor(), thickness, underlineXLeft, underlineXRight, y); } } @@ -986,7 +988,17 @@ class TextLine { return 0f; } + final boolean needsSpanMeasurement; if (mSpanned == null) { + needsSpanMeasurement = false; + } else { + mMetricAffectingSpanSpanSet.init(mSpanned, mStart + start, mStart + limit); + mCharacterStyleSpanSet.init(mSpanned, mStart + start, mStart + limit); + needsSpanMeasurement = mMetricAffectingSpanSpanSet.numberOfSpans != 0 + || mCharacterStyleSpanSet.numberOfSpans != 0; + } + + if (!needsSpanMeasurement) { final TextPaint wp = mWorkPaint; wp.set(mPaint); wp.setHyphenEdit(adjustHyphenEdit(start, limit, wp.getHyphenEdit())); @@ -994,9 +1006,6 @@ class TextLine { y, bottom, fmi, needWidth, measureLimit, null); } - mMetricAffectingSpanSpanSet.init(mSpanned, mStart + start, mStart + limit); - mCharacterStyleSpanSet.init(mSpanned, mStart + start, mStart + limit); - // Shaping needs to take into account context up to metric boundaries, // but rendering needs to take into account character style boundaries. // So we iterate through metric runs to get metric bounds, diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java index 3baadd4c64c5..440c88e8cabb 100644 --- a/core/java/android/text/TextUtils.java +++ b/core/java/android/text/TextUtils.java @@ -23,6 +23,8 @@ import android.annotation.PluralsRes; import android.content.Context; import android.content.res.Resources; import android.icu.lang.UCharacter; +import android.icu.text.CaseMap; +import android.icu.text.Edits; import android.icu.util.ULocale; import android.os.Parcel; import android.os.Parcelable; @@ -1072,6 +1074,75 @@ public class TextUtils { } } + /** + * Transforms a CharSequences to uppercase, copying the sources spans and keeping them spans as + * much as possible close to their relative original places. In the case the the uppercase + * string is identical to the sources, the source itself is returned instead of being copied. + * + * If copySpans is set, source must be an instance of Spanned. + * + * {@hide} + */ + @NonNull + public static CharSequence toUpperCase(@Nullable Locale locale, @NonNull CharSequence source, + boolean copySpans) { + final Edits edits = new Edits(); + if (!copySpans) { // No spans. Just uppercase the characters. + final StringBuilder result = CaseMap.toUpper().apply( + locale, source, new StringBuilder(), edits); + return edits.hasChanges() ? result : source; + } + + final SpannableStringBuilder result = CaseMap.toUpper().apply( + locale, source, new SpannableStringBuilder(), edits); + if (!edits.hasChanges()) { + // No changes happened while capitalizing. We can return the source as it was. + return source; + } + + final Edits.Iterator iterator = edits.getFineIterator(); + final int sourceLength = source.length(); + final Spanned spanned = (Spanned) source; + final Object[] spans = spanned.getSpans(0, sourceLength, Object.class); + for (Object span : spans) { + final int sourceStart = spanned.getSpanStart(span); + final int sourceEnd = spanned.getSpanEnd(span); + final int flags = spanned.getSpanFlags(span); + // Make sure the indices are not at the end of the string, since in that case + // iterator.findSourceIndex() would fail. + final int destStart = sourceStart == sourceLength ? result.length() : + toUpperMapToDest(iterator, sourceStart); + final int destEnd = sourceEnd == sourceLength ? result.length() : + toUpperMapToDest(iterator, sourceEnd); + result.setSpan(span, destStart, destEnd, flags); + } + return result; + } + + // helper method for toUpperCase() + private static int toUpperMapToDest(Edits.Iterator iterator, int sourceIndex) { + // Guaranteed to succeed if sourceIndex < source.length(). + iterator.findSourceIndex(sourceIndex); + if (sourceIndex == iterator.sourceIndex()) { + return iterator.destinationIndex(); + } + // We handle the situation differently depending on if we are in the changed slice or an + // unchanged one: In an unchanged slice, we can find the exact location the span + // boundary was before and map there. + // + // But in a changed slice, we need to treat the whole destination slice as an atomic unit. + // We adjust the span boundary to the end of that slice to reduce of the chance of adjacent + // spans in the source overlapping in the result. (The choice for the end vs the beginning + // is somewhat arbitrary, but was taken because we except to see slightly more spans only + // affecting a base character compared to spans only affecting a combining character.) + if (iterator.hasChange()) { + return iterator.destinationIndex() + iterator.newLength(); + } else { + // Move the index 1:1 along with this unchanged piece of text. + return iterator.destinationIndex() + (sourceIndex - iterator.sourceIndex()); + } + } + public enum TruncateAt { START, MIDDLE, diff --git a/core/java/android/text/method/AllCapsTransformationMethod.java b/core/java/android/text/method/AllCapsTransformationMethod.java index 15f40d5121ef..c807e7da4f1b 100644 --- a/core/java/android/text/method/AllCapsTransformationMethod.java +++ b/core/java/android/text/method/AllCapsTransformationMethod.java @@ -15,12 +15,12 @@ */ package android.text.method; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.Context; import android.graphics.Rect; -import android.icu.text.CaseMap; -import android.icu.text.Edits; -import android.text.SpannableStringBuilder; import android.text.Spanned; +import android.text.TextUtils; import android.util.Log; import android.view.View; import android.widget.TextView; @@ -38,12 +38,12 @@ public class AllCapsTransformationMethod implements TransformationMethod2 { private boolean mEnabled; private Locale mLocale; - public AllCapsTransformationMethod(Context context) { + public AllCapsTransformationMethod(@NonNull Context context) { mLocale = context.getResources().getConfiguration().getLocales().get(0); } @Override - public CharSequence getTransformation(CharSequence source, View view) { + public CharSequence getTransformation(@Nullable CharSequence source, View view) { if (!mEnabled) { Log.w(TAG, "Caller did not enable length changes; not transforming text"); return source; @@ -60,61 +60,8 @@ public class AllCapsTransformationMethod implements TransformationMethod2 { if (locale == null) { locale = mLocale; } - - if (!(source instanceof Spanned)) { // No spans - return CaseMap.toUpper().apply( - locale, source, new StringBuilder(), - null /* we don't need the edits */); - } - - final Edits edits = new Edits(); - final SpannableStringBuilder result = CaseMap.toUpper().apply( - locale, source, new SpannableStringBuilder(), edits); - if (!edits.hasChanges()) { - // No changes happened while capitalizing. We can return the source as it was. - return source; - } - - final Edits.Iterator iterator = edits.getFineIterator(); - final Spanned spanned = (Spanned) source; - final int sourceLength = source.length(); - final Object[] spans = spanned.getSpans(0, sourceLength, Object.class); - for (Object span : spans) { - final int sourceStart = spanned.getSpanStart(span); - final int sourceEnd = spanned.getSpanEnd(span); - final int flags = spanned.getSpanFlags(span); - // Make sure the indexes are not at the end of the string, since in that case - // iterator.findSourceIndex() would fail. - final int destStart = sourceStart == sourceLength ? result.length() : - mapToDest(iterator, sourceStart); - final int destEnd = sourceEnd == sourceLength ? result.length() : - mapToDest(iterator, sourceEnd); - result.setSpan(span, destStart, destEnd, flags); - } - return result; - } - - private static int mapToDest(Edits.Iterator iterator, int sourceIndex) { - // Guaranteed to succeed if sourceIndex < source.length(). - iterator.findSourceIndex(sourceIndex); - if (sourceIndex == iterator.sourceIndex()) { - return iterator.destinationIndex(); - } - // We handle the situation differently depending on if we are in the changed slice or an - // unchanged one: In an unchanged slice, we can find the exact location the span - // boundary was before and map there. - // - // But in a changed slice, we need to treat the whole destination slice as an atomic unit. - // We adjust the span boundary to the end of that slice to reduce of the chance of adjacent - // spans in the source overlapping in the result. (The choice for the end vs the beginning - // is somewhat arbitrary, but was taken because we except to see slightly more spans only - // affecting a base character compared to spans only affecting a combining character.) - if (iterator.hasChange()) { - return iterator.destinationIndex() + iterator.newLength(); - } else { - // Move the index 1:1 along with this unchanged piece of text. - return iterator.destinationIndex() + (sourceIndex - iterator.sourceIndex()); - } + final boolean copySpans = source instanceof Spanned; + return TextUtils.toUpperCase(locale, source, copySpans); } @Override diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp index fa25a8f0c596..b93697398020 100644 --- a/core/jni/android/graphics/Paint.cpp +++ b/core/jni/android/graphics/Paint.cpp @@ -961,6 +961,30 @@ namespace PaintGlue { return SkScalarToFloat(metrics.fDescent); } + static jfloat getUnderlinePosition(jlong paintHandle, jlong typefaceHandle) { + Paint::FontMetrics metrics; + getMetricsInternal(paintHandle, typefaceHandle, &metrics); + SkScalar position; + if (metrics.hasUnderlinePosition(&position)) { + return SkScalarToFloat(position); + } else { + const SkScalar textSize = reinterpret_cast<Paint*>(paintHandle)->getTextSize(); + return SkScalarToFloat(Paint::kStdUnderline_Top * textSize); + } + } + + static jfloat getUnderlineThickness(jlong paintHandle, jlong typefaceHandle) { + Paint::FontMetrics metrics; + getMetricsInternal(paintHandle, typefaceHandle, &metrics); + SkScalar thickness; + if (metrics.hasUnderlineThickness(&thickness)) { + return SkScalarToFloat(thickness); + } else { + const SkScalar textSize = reinterpret_cast<Paint*>(paintHandle)->getTextSize(); + return SkScalarToFloat(Paint::kStdUnderline_Thickness * textSize); + } + } + static void setShadowLayer(jlong paintHandle, jfloat radius, jfloat dx, jfloat dy, jint color) { Paint* paint = reinterpret_cast<Paint*>(paintHandle); @@ -1072,6 +1096,8 @@ static const JNINativeMethod methods[] = { {"nSetHyphenEdit", "(JI)V", (void*) PaintGlue::setHyphenEdit}, {"nAscent","(JJ)F", (void*) PaintGlue::ascent}, {"nDescent","(JJ)F", (void*) PaintGlue::descent}, + {"nGetUnderlinePosition","(JJ)F", (void*) PaintGlue::getUnderlinePosition}, + {"nGetUnderlineThickness","(JJ)F", (void*) PaintGlue::getUnderlineThickness}, {"nSetShadowLayer", "(JFFFI)V", (void*)PaintGlue::setShadowLayer}, {"nHasShadowLayer", "(J)Z", (void*)PaintGlue::hasShadowLayer} }; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 004ecfcd12cf..958592b581a7 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2616,6 +2616,12 @@ <permission android:name="android.permission.TV_VIRTUAL_REMOTE_CONTROLLER" android:protectionLevel="signature|privileged" /> + <!-- Allows an application to change HDMI CEC active source. + <p>Not for use by third-party applications. + @hide --> + <permission android:name="android.permission.CHANGE_HDMI_CEC_ACTIVE_SOURCE" + android:protectionLevel="signature|privileged" /> + <!-- @SystemApi Allows an application to modify parental controls <p>Not for use by third-party applications. @hide --> diff --git a/core/tests/coretests/apks/install_multi_package/Android.mk b/core/tests/coretests/apks/install_multi_package/Android.mk new file mode 100644 index 000000000000..2813dad2339a --- /dev/null +++ b/core/tests/coretests/apks/install_multi_package/Android.mk @@ -0,0 +1,12 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := tests + +LOCAL_SRC_FILES := $(call all-subdir-java-files) + +LOCAL_PACKAGE_NAME := install_multi_package + +include $(FrameworkCoreTests_BUILD_PACKAGE) +#include $(BUILD_PACKAGE) + diff --git a/core/tests/coretests/apks/install_multi_package/AndroidManifest.xml b/core/tests/coretests/apks/install_multi_package/AndroidManifest.xml new file mode 100644 index 000000000000..5164cae9e5c0 --- /dev/null +++ b/core/tests/coretests/apks/install_multi_package/AndroidManifest.xml @@ -0,0 +1,104 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2011 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. +--> +<manifest + xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.frameworks.coretests.install_multi_package"> + +<!-- + This manifest is has child packages with components. +--> + + <uses-feature + android:name="com.android.frameworks.coretests.nonexistent" /> + <uses-configuration + android:reqFiveWayNav="false" /> + + <instrumentation + android:name="android.test.InstrumentationTestRunner" + android:targetPackage="com.android.frameworks.coretests" + android:label="Frameworks Core Tests" /> + + <permission + android:label="test permission" + android:name="test_permission" + android:protectionLevel="normal" /> + <uses-permission android:name="android.permission.INTERNET" /> + +<!-- + NOTE: This declares a child package, application, then another child + package, to test potential bugs that are order-dependent. Also, each + one varies the order. +--> + + <package package="com.android.frameworks.coretests.install_multi_package.first_child"> + <uses-permission android:name="android.permission.NFC" /> + <!-- NOTE: A declared permission is ignored since the tag is not whitelisted. --> + <permission + android:label="test permission" + android:name="first_child_permission" + android:protectionLevel="signature" /> + <application + android:hasCode="true"> + <activity + android:name="com.android.frameworks.coretests.FirstChildTestActivity"> + </activity> + <provider + android:name="com.android.frameworks.coretests.FirstChildTestProvider" + android:authorities="com.android.frameworks.coretests.testprovider" /> + <receiver + android:name="com.android.frameworks.coretests.FirstChildTestReceiver" /> + <service + android:name="com.android.frameworks.coretests.FirstChildTestService" /> + </application> + </package> + + <application + android:hasCode="true"> + <service + android:name="com.android.frameworks.coretests.TestService" /> + <activity + android:name="com.android.frameworks.coretests.TestActivity"> + </activity> + <provider + android:name="com.android.frameworks.coretests.TestProvider" + android:authorities="com.android.frameworks.coretests.testprovider" /> + <receiver + android:name="com.android.frameworks.coretests.TestReceiver" /> + </application> + + <package package="com.android.frameworks.coretests.blah.second_child"> + <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> + <uses-permission-sdk-23 android:name="android.permission.READ_CONTACTS" /> + <!-- NOTE: A declared permission is ignored since the tag is not whitelisted. --> + <permission + android:label="test permission" + android:name="second_child_permission" + android:protectionLevel="dangerous" /> + <application + android:hasCode="true"> + <receiver + android:name="com.android.frameworks.coretests.SecondChildTestReceiver" /> + <service + android:name="com.android.frameworks.coretests.SecondChildTestService" /> + <activity + android:name="com.android.frameworks.coretests.SecondChildTestActivity"> + </activity> + <provider + android:name="com.android.frameworks.coretests.SecondChildTestProvider" + android:authorities="com.android.frameworks.coretests.testprovider" /> + </application> + </package> +</manifest> diff --git a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/FirstChildTestActivity.java b/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/FirstChildTestActivity.java new file mode 100644 index 000000000000..57afcb0e1a0d --- /dev/null +++ b/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/FirstChildTestActivity.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2011 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.frameworks.coretests; + +import android.app.Activity; + +public class FirstChildTestActivity extends Activity { + +} diff --git a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/FirstChildTestProvider.java b/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/FirstChildTestProvider.java new file mode 100644 index 000000000000..2816865b2f1f --- /dev/null +++ b/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/FirstChildTestProvider.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2011 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.frameworks.coretests; + +import android.content.ContentProvider; +import android.content.ContentValues; +import android.database.Cursor; +import android.net.Uri; + +public class FirstChildTestProvider extends ContentProvider { + + @Override + public boolean onCreate() { + return false; + } + + @Override + public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, + String sortOrder) { + return null; + } + + @Override + public String getType(Uri uri) { + return null; + } + + @Override + public Uri insert(Uri uri, ContentValues values) { + return null; + } + + @Override + public int delete(Uri uri, String selection, String[] selectionArgs) { + return 0; + } + + @Override + public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + return 0; + } +} diff --git a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/FirstChildTestReceiver.java b/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/FirstChildTestReceiver.java new file mode 100644 index 000000000000..ffe84b73dd37 --- /dev/null +++ b/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/FirstChildTestReceiver.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2011 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.frameworks.coretests; + +import android.content.ContentProvider; +import android.content.ContentValues; +import android.database.Cursor; +import android.net.Uri; + +public class FirstChildTestReceiver extends ContentProvider { + + @Override + public boolean onCreate() { + return false; + } + + @Override + public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, + String sortOrder) { + return null; + } + + @Override + public String getType(Uri uri) { + return null; + } + + @Override + public Uri insert(Uri uri, ContentValues values) { + return null; + } + + @Override + public int delete(Uri uri, String selection, String[] selectionArgs) { + return 0; + } + + @Override + public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + return 0; + } +} diff --git a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/FirstChildTestService.java b/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/FirstChildTestService.java new file mode 100644 index 000000000000..faa6e9cff2b2 --- /dev/null +++ b/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/FirstChildTestService.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2011 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.frameworks.coretests; + +import android.app.Service; +import android.content.Intent; +import android.os.IBinder; + +public class FirstChildTestService extends Service { + + @Override + public IBinder onBind(Intent intent) { + return null; + } +} diff --git a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/SecondChildTestActivity.java b/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/SecondChildTestActivity.java new file mode 100644 index 000000000000..e89f26489959 --- /dev/null +++ b/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/SecondChildTestActivity.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2011 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.frameworks.coretests; + +import android.app.Activity; + +public class SecondChildTestActivity extends Activity { + +} diff --git a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/SecondChildTestProvider.java b/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/SecondChildTestProvider.java new file mode 100644 index 000000000000..2bd40a5df94d --- /dev/null +++ b/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/SecondChildTestProvider.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2011 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.frameworks.coretests; + +import android.content.ContentProvider; +import android.content.ContentValues; +import android.database.Cursor; +import android.net.Uri; + +public class SecondChildTestProvider extends ContentProvider { + + @Override + public boolean onCreate() { + return false; + } + + @Override + public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, + String sortOrder) { + return null; + } + + @Override + public String getType(Uri uri) { + return null; + } + + @Override + public Uri insert(Uri uri, ContentValues values) { + return null; + } + + @Override + public int delete(Uri uri, String selection, String[] selectionArgs) { + return 0; + } + + @Override + public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + return 0; + } +} diff --git a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/SecondChildTestReceiver.java b/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/SecondChildTestReceiver.java new file mode 100644 index 000000000000..a6c4ddc90c6a --- /dev/null +++ b/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/SecondChildTestReceiver.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2011 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.frameworks.coretests; + +import android.content.ContentProvider; +import android.content.ContentValues; +import android.database.Cursor; +import android.net.Uri; + +public class SecondChildTestReceiver extends ContentProvider { + + @Override + public boolean onCreate() { + return false; + } + + @Override + public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, + String sortOrder) { + return null; + } + + @Override + public String getType(Uri uri) { + return null; + } + + @Override + public Uri insert(Uri uri, ContentValues values) { + return null; + } + + @Override + public int delete(Uri uri, String selection, String[] selectionArgs) { + return 0; + } + + @Override + public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + return 0; + } +} diff --git a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/SecondChildTestService.java b/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/SecondChildTestService.java new file mode 100644 index 000000000000..1e721aa8ae5b --- /dev/null +++ b/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/SecondChildTestService.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2011 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.frameworks.coretests; + +import android.app.Service; +import android.content.Intent; +import android.os.IBinder; + +public class SecondChildTestService extends Service { + + @Override + public IBinder onBind(Intent intent) { + return null; + } +} diff --git a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/TestActivity.java b/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/TestActivity.java new file mode 100644 index 000000000000..10d0551a3a6f --- /dev/null +++ b/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/TestActivity.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2011 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.frameworks.coretests; + +import android.app.Activity; + +public class TestActivity extends Activity { + +} diff --git a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/TestProvider.java b/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/TestProvider.java new file mode 100644 index 000000000000..59f9f10c6efe --- /dev/null +++ b/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/TestProvider.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2011 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.frameworks.coretests; + +import android.content.ContentProvider; +import android.content.ContentValues; +import android.database.Cursor; +import android.net.Uri; + +public class TestProvider extends ContentProvider { + + @Override + public boolean onCreate() { + return false; + } + + @Override + public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, + String sortOrder) { + return null; + } + + @Override + public String getType(Uri uri) { + return null; + } + + @Override + public Uri insert(Uri uri, ContentValues values) { + return null; + } + + @Override + public int delete(Uri uri, String selection, String[] selectionArgs) { + return 0; + } + + @Override + public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + return 0; + } +} diff --git a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/TestReceiver.java b/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/TestReceiver.java new file mode 100644 index 000000000000..21f6263a38bc --- /dev/null +++ b/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/TestReceiver.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2011 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.frameworks.coretests; + +import android.content.ContentProvider; +import android.content.ContentValues; +import android.database.Cursor; +import android.net.Uri; + +public class TestReceiver extends ContentProvider { + + @Override + public boolean onCreate() { + return false; + } + + @Override + public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, + String sortOrder) { + return null; + } + + @Override + public String getType(Uri uri) { + return null; + } + + @Override + public Uri insert(Uri uri, ContentValues values) { + return null; + } + + @Override + public int delete(Uri uri, String selection, String[] selectionArgs) { + return 0; + } + + @Override + public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + return 0; + } +} diff --git a/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/TestService.java b/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/TestService.java new file mode 100644 index 000000000000..b330e75308f9 --- /dev/null +++ b/core/tests/coretests/apks/install_multi_package/src/com/android/frameworks/coretests/TestService.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2011 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.frameworks.coretests; + +import android.app.Service; +import android.content.Intent; +import android.os.IBinder; + +public class TestService extends Service { + + @Override + public IBinder onBind(Intent intent) { + return null; + } +} diff --git a/core/tests/coretests/assets/fonts/underlineTestFont.ttf b/core/tests/coretests/assets/fonts/underlineTestFont.ttf Binary files differnew file mode 100644 index 000000000000..a46a38e0b397 --- /dev/null +++ b/core/tests/coretests/assets/fonts/underlineTestFont.ttf diff --git a/core/tests/coretests/assets/fonts/underlineTestFont.ttx b/core/tests/coretests/assets/fonts/underlineTestFont.ttx new file mode 100644 index 000000000000..d7376b06703e --- /dev/null +++ b/core/tests/coretests/assets/fonts/underlineTestFont.ttx @@ -0,0 +1,163 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 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. +--> +<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.9"> + + <GlyphOrder> + <GlyphID id="0" name=".notdef"/> + <GlyphID id="1" name="a"/> + </GlyphOrder> + + <head> + <tableVersion value="1.0"/> + <fontRevision value="1.0"/> + <checkSumAdjustment value="0"/> + <magicNumber value="0x5f0f3cf5"/> + <flags value="00000000 00000011"/> + <unitsPerEm value="1000"/> + <created value="Fri Mar 17 07:26:00 2017"/> + <macStyle value="00000000 00000000"/> + <lowestRecPPEM value="7"/> + <fontDirectionHint value="2"/> + <glyphDataFormat value="0"/> + </head> + + <hhea> + <tableVersion value="0x00010000"/> + <ascent value="1000"/> + <descent value="-200"/> + <lineGap value="0"/> + <caretSlopeRise value="1"/> + <caretSlopeRun value="0"/> + <caretOffset value="0"/> + <reserved0 value="0"/> + <reserved1 value="0"/> + <reserved2 value="0"/> + <reserved3 value="0"/> + <metricDataFormat value="0"/> + </hhea> + + <maxp> + <tableVersion value="0x10000"/> + <maxZones value="0"/> + <maxTwilightPoints value="0"/> + <maxStorage value="0"/> + <maxFunctionDefs value="0"/> + <maxInstructionDefs value="0"/> + <maxStackElements value="0"/> + <maxSizeOfInstructions value="0"/> + <maxComponentElements value="0"/> + </maxp> + + <OS_2> + <version value="3"/> + <xAvgCharWidth value="594"/> + <usWeightClass value="400"/> + <usWidthClass value="5"/> + <fsType value="00000000 00001000"/> + <ySubscriptXSize value="650"/> + <ySubscriptYSize value="600"/> + <ySubscriptXOffset value="0"/> + <ySubscriptYOffset value="75"/> + <ySuperscriptXSize value="650"/> + <ySuperscriptYSize value="600"/> + <ySuperscriptXOffset value="0"/> + <ySuperscriptYOffset value="350"/> + <yStrikeoutSize value="50"/> + <yStrikeoutPosition value="300"/> + <sFamilyClass value="0"/> + <panose> + <bFamilyType value="0"/> + <bSerifStyle value="0"/> + <bWeight value="5"/> + <bProportion value="0"/> + <bContrast value="0"/> + <bStrokeVariation value="0"/> + <bArmStyle value="0"/> + <bLetterForm value="0"/> + <bMidline value="0"/> + <bXHeight value="0"/> + </panose> + <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/> + <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/> + <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/> + <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/> + <achVendID value="UKWN"/> + <fsSelection value="00000000 01000000"/> + <sTypoAscender value="800"/> + <sTypoDescender value="-200"/> + <sTypoLineGap value="200"/> + <usWinAscent value="1000"/> + <usWinDescent value="200"/> + <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/> + <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/> + <sxHeight value="500"/> + <sCapHeight value="700"/> + <usDefaultChar value="0"/> + <usBreakChar value="32"/> + <usMaxContext value="0"/> + </OS_2> + + <hmtx> + <mtx name=".notdef" width="500" lsb="93"/> + <mtx name="a" width="500" lsb="93"/> + </hmtx> + + <cmap> + <tableVersion version="0"/> + <cmap_format_4 platformID="3" platEncID="10" language="0"> + <map code="0x61" name="a"/><!-- LATIN SMALL LETTER A --> + </cmap_format_4> + </cmap> + + <loca> + <!-- The 'loca' table will be calculated by the compiler --> + </loca> + + <glyf> + <TTGlyph name=".notdef"/><!-- contains no outline data --> + + <TTGlyph name="a"/><!-- contains no outline data --> + + </glyf> + + <name> + <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409"> + Sample Font + </namerecord> + <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409"> + Regular + </namerecord> + <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409"> + Sample Font + </namerecord> + <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409"> + SampleFont-Regular + </namerecord> + </name> + + <post> + <formatType value="3.0"/> + <italicAngle value="0.0"/> + <underlinePosition value="-200"/> + <underlineThickness value="300"/> + <isFixedPitch value="0"/> + <minMemType42 value="0"/> + <maxMemType42 value="0"/> + <minMemType1 value="0"/> + <maxMemType1 value="0"/> + </post> + +</ttFont> diff --git a/core/tests/coretests/src/android/content/pm/PackageParserTest.java b/core/tests/coretests/src/android/content/pm/PackageParserTest.java index b9bd193ad48c..73c153f2754a 100644 --- a/core/tests/coretests/src/android/content/pm/PackageParserTest.java +++ b/core/tests/coretests/src/android/content/pm/PackageParserTest.java @@ -19,14 +19,27 @@ package android.content.pm; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import android.content.Context; +import android.content.pm.PackageParser.Component; +import android.content.pm.PackageParser.Package; +import android.content.pm.PackageParser.Permission; import android.os.Build; +import android.os.FileUtils; +import android.support.test.InstrumentationRegistry; import android.support.test.runner.AndroidJUnit4; import android.test.suitebuilder.annotation.SmallTest; +import com.android.frameworks.coretests.R; + import org.junit.Test; import org.junit.runner.RunWith; +import java.io.File; +import java.io.InputStream; +import java.util.Arrays; + @SmallTest @RunWith(AndroidJUnit4.class) public class PackageParserTest { @@ -257,4 +270,117 @@ public class PackageParserTest { PackageParser.getActivityConfigChanges(configChanges, recreateOnConfigChanges); assertEquals(0x0083, finalConfigChanges); // Should be 10000011. } + + /** + * Attempts to parse a package. + * + * APKs are put into coretests/apks/packageparser_*. + * + * @param apkName temporary file name to store apk extracted from resources + * @param apkResourceId identifier of the apk as a resource + */ + Package parsePackage(String apkFileName, int apkResourceId) throws Exception { + // Copy the resource to a file. + Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); + File outFile = new File(context.getFilesDir(), apkFileName); + try { + InputStream is = context.getResources().openRawResource(apkResourceId); + assertTrue(FileUtils.copyToFile(is, outFile)); + return new PackageParser().parsePackage(outFile, 0 /* flags */); + } finally { + outFile.delete(); + } + } + + /** + * Asserts basic properties about a component. + */ + private void assertComponent(String className, String packageName, int numIntents, + Component<?> component) { + assertEquals(className, component.className); + assertEquals(packageName, component.owner.packageName); + assertEquals(numIntents, component.intents.size()); + } + + /** + * Asserts four regularly-named components of each type: one Activity, one Service, one + * Provider, and one Receiver. + * @param template templated string with %s subbed with Activity, Service, Provider, Receiver + */ + private void assertOneComponentOfEachType(String template, Package p) { + String packageName = p.packageName; + + assertEquals(1, p.activities.size()); + assertComponent(String.format(template, "Activity"), + packageName, 0 /* intents */, p.activities.get(0)); + assertEquals(1, p.services.size()); + assertComponent(String.format(template, "Service"), + packageName, 0 /* intents */, p.services.get(0)); + assertEquals(1, p.providers.size()); + assertComponent(String.format(template, "Provider"), + packageName, 0 /* intents */, p.providers.get(0)); + assertEquals(1, p.receivers.size()); + assertComponent(String.format(template, "Receiver"), + packageName, 0 /* intents */, p.receivers.get(0)); + } + + private void assertPermission(String name, String packageName, int protectionLevel, + Permission permission) { + assertEquals(packageName, permission.owner.packageName); + assertEquals(name, permission.info.name); + assertEquals(protectionLevel, permission.info.protectionLevel); + } + + @Test + public void testPackageWithComponents() throws Exception { + Package p = parsePackage( + "install_complete_package_info.apk", R.raw.install_complete_package_info); + String packageName = "com.android.frameworks.coretests.install_complete_package_info"; + + assertEquals(packageName, p.packageName); + assertEquals(1, p.permissions.size()); + assertPermission( + "com.android.frameworks.coretests.install_complete_package_info.test_permission", + packageName, PermissionInfo.PROTECTION_NORMAL, p.permissions.get(0)); + + assertOneComponentOfEachType("com.android.frameworks.coretests.Test%s", p); + } + + @Test + public void testMultiPackageComponents() throws Exception { + String parentName = "com.android.frameworks.coretests.install_multi_package"; + String firstChildName = + "com.android.frameworks.coretests.install_multi_package.first_child"; + String secondChildName = // NOTE: intentionally inconsistent! + "com.android.frameworks.coretests.blah.second_child"; + + Package parent = parsePackage("install_multi_package.apk", R.raw.install_multi_package); + assertEquals(parentName, parent.packageName); + assertEquals(2, parent.childPackages.size()); + assertOneComponentOfEachType("com.android.frameworks.coretests.Test%s", parent); + assertEquals(1, parent.permissions.size()); + assertPermission(parentName + ".test_permission", parentName, + PermissionInfo.PROTECTION_NORMAL, parent.permissions.get(0)); + assertEquals(Arrays.asList("android.permission.INTERNET"), + parent.requestedPermissions); + + Package firstChild = parent.childPackages.get(0); + assertEquals(firstChildName, firstChild.packageName); + assertOneComponentOfEachType( + "com.android.frameworks.coretests.FirstChildTest%s", firstChild); + assertEquals(0, firstChild.permissions.size()); // Child APKs cannot declare permissions. + assertEquals(Arrays.asList("android.permission.NFC"), + firstChild.requestedPermissions); + + Package secondChild = parent.childPackages.get(1); + assertEquals(secondChildName, secondChild.packageName); + assertOneComponentOfEachType( + "com.android.frameworks.coretests.SecondChildTest%s", secondChild); + assertEquals(0, secondChild.permissions.size()); // Child APKs cannot declare permissions. + assertEquals( + Arrays.asList( + "android.permission.ACCESS_NETWORK_STATE", + "android.permission.READ_CONTACTS"), + secondChild.requestedPermissions); + } } diff --git a/core/tests/coretests/src/android/graphics/PaintTest.java b/core/tests/coretests/src/android/graphics/PaintTest.java index 5811ca0fd266..2f28606790cf 100644 --- a/core/tests/coretests/src/android/graphics/PaintTest.java +++ b/core/tests/coretests/src/android/graphics/PaintTest.java @@ -16,6 +16,8 @@ package android.graphics; +import static org.junit.Assert.assertNotEquals; + import android.graphics.Paint; import android.test.InstrumentationTestCase; import android.test.suitebuilder.annotation.SmallTest; @@ -148,7 +150,10 @@ public class PaintTest extends InstrumentationTestCase { " U+" + Integer.toHexString(vs) + ")"; final String testString = codePointsToString(new int[] {testCase.mBaseCodepoint, vs}); - if (testCase.mVariationSelectors.contains(vs)) { + if (vs == 0xFE0E // U+FE0E is the text presentation emoji. hasGlyph is expected to + // return true for that variation selector if the font has the base + // glyph. + || testCase.mVariationSelectors.contains(vs)) { assertTrue(signature + " is expected to be true", p.hasGlyph(testString)); } else { assertFalse(signature + " is expected to be false", p.hasGlyph(testString)); @@ -365,4 +370,31 @@ public class PaintTest extends InstrumentationTestCase { p.setWordSpacing(-2.0f); assertEquals(-2.0f, p.getWordSpacing()); } + + public void testGetUnderlinePositionAndThickness() { + final Typeface fontTypeface = Typeface.createFromAsset( + getInstrumentation().getContext().getAssets(), "fonts/underlineTestFont.ttf"); + final Paint p = new Paint(); + final int textSize = 100; + p.setTextSize(textSize); + + final float origPosition = p.getUnderlinePosition(); + final float origThickness = p.getUnderlineThickness(); + + p.setTypeface(fontTypeface); + assertNotEquals(origPosition, p.getUnderlinePosition()); + assertNotEquals(origThickness, p.getUnderlineThickness()); + + // -200 (underlinePosition in 'post' table, negative means below the baseline) + // ÷ 1000 (unitsPerEm in 'head' table) + // × 100 (text size) + // × -1 (negated, since we consider below the baseline positive) + // = 20 + assertEquals(20.0f, p.getUnderlinePosition(), 0.5f); + // 300 (underlineThickness in 'post' table) + // ÷ 1000 (unitsPerEm in 'head' table) + // × 100 (text size) + // = 30 + assertEquals(30.0f, p.getUnderlineThickness(), 0.5f); + } } diff --git a/core/tests/coretests/src/android/text/TextUtilsTest.java b/core/tests/coretests/src/android/text/TextUtilsTest.java index 312c4fb4b23f..4cc648def549 100644 --- a/core/tests/coretests/src/android/text/TextUtilsTest.java +++ b/core/tests/coretests/src/android/text/TextUtilsTest.java @@ -20,26 +20,28 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import android.support.test.runner.AndroidJUnit4; -import com.google.android.collect.Lists; - import android.os.Parcel; import android.support.test.filters.LargeTest; import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; import android.test.MoreAsserts; import android.text.style.StyleSpan; import android.text.util.Rfc822Token; import android.text.util.Rfc822Tokenizer; import android.view.View; +import com.google.android.collect.Lists; + +import org.junit.Test; +import org.junit.runner.RunWith; + import java.util.ArrayList; import java.util.List; import java.util.Locale; -import org.junit.Test; -import org.junit.runner.RunWith; /** * TextUtilsTest tests {@link TextUtils}. @@ -580,4 +582,84 @@ public class TextUtilsTest { assertEquals(View.LAYOUT_DIRECTION_RTL, TextUtils.getLayoutDirectionFromLocale(Locale.forLanguageTag("tr-Arab"))); } + + @Test + public void testToUpperCase() { + { + final CharSequence result = TextUtils.toUpperCase(null, "abc", false); + assertEquals(StringBuilder.class, result.getClass()); + assertEquals("ABC", result.toString()); + } + { + final SpannableString str = new SpannableString("abc"); + Object span = new Object(); + str.setSpan(span, 1, 2, Spanned.SPAN_INCLUSIVE_INCLUSIVE); + + final CharSequence result = TextUtils.toUpperCase(null, str, true /* copySpans */); + assertEquals(SpannableStringBuilder.class, result.getClass()); + assertEquals("ABC", result.toString()); + final Spanned spanned = (Spanned) result; + final Object[] resultSpans = spanned.getSpans(0, result.length(), Object.class); + assertEquals(1, resultSpans.length); + assertSame(span, resultSpans[0]); + assertEquals(1, spanned.getSpanStart(span)); + assertEquals(2, spanned.getSpanEnd(span)); + assertEquals(Spanned.SPAN_INCLUSIVE_INCLUSIVE, spanned.getSpanFlags(span)); + } + { + final Locale turkish = new Locale("tr", "TR"); + final CharSequence result = TextUtils.toUpperCase(turkish, "i", false); + assertEquals(StringBuilder.class, result.getClass()); + assertEquals("İ", result.toString()); + } + { + final String str = "ABC"; + assertSame(str, TextUtils.toUpperCase(null, str, false)); + } + { + final SpannableString str = new SpannableString("ABC"); + str.setSpan(new Object(), 1, 2, Spanned.SPAN_INCLUSIVE_INCLUSIVE); + assertSame(str, TextUtils.toUpperCase(null, str, true /* copySpans */)); + } + } + + // Copied from cts/tests/tests/widget/src/android/widget/cts/TextViewTest.java and modified + // for the TextUtils.toUpperCase method. + @Test + public void testToUpperCase_SpansArePreserved() { + final Locale greek = new Locale("el", "GR"); + final String lowerString = "ι\u0301ριδα"; // ίριδα with first letter decomposed + final String upperString = "ΙΡΙΔΑ"; // uppercased + // expected lowercase to uppercase index map + final int[] indexMap = {0, 1, 1, 2, 3, 4, 5}; + final int flags = Spanned.SPAN_INCLUSIVE_INCLUSIVE; + + final Spannable source = new SpannableString(lowerString); + source.setSpan(new Object(), 0, 1, flags); + source.setSpan(new Object(), 1, 2, flags); + source.setSpan(new Object(), 2, 3, flags); + source.setSpan(new Object(), 3, 4, flags); + source.setSpan(new Object(), 4, 5, flags); + source.setSpan(new Object(), 5, 6, flags); + source.setSpan(new Object(), 0, 2, flags); + source.setSpan(new Object(), 1, 3, flags); + source.setSpan(new Object(), 2, 4, flags); + source.setSpan(new Object(), 0, 6, flags); + final Object[] sourceSpans = source.getSpans(0, source.length(), Object.class); + + final CharSequence uppercase = TextUtils.toUpperCase(greek, source, true /* copySpans */); + assertEquals(SpannableStringBuilder.class, uppercase.getClass()); + final Spanned result = (Spanned) uppercase; + + assertEquals(upperString, result.toString()); + final Object[] resultSpans = result.getSpans(0, result.length(), Object.class); + assertEquals(sourceSpans.length, resultSpans.length); + for (int i = 0; i < sourceSpans.length; i++) { + assertSame(sourceSpans[i], resultSpans[i]); + final Object span = sourceSpans[i]; + assertEquals(indexMap[source.getSpanStart(span)], result.getSpanStart(span)); + assertEquals(indexMap[source.getSpanEnd(span)], result.getSpanEnd(span)); + assertEquals(source.getSpanFlags(span), result.getSpanFlags(span)); + } + } } diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java index e1eb69a27e33..1fd56974783e 100644 --- a/graphics/java/android/graphics/Paint.java +++ b/graphics/java/android/graphics/Paint.java @@ -822,18 +822,14 @@ public class Paint { * @hide */ public float getUnderlinePosition() { - // kStdUnderline_Offset = 1/9, defined in SkTextFormatParams.h - // TODO: replace with position from post and MVAR tables (b/62353930). - return (1.0f / 9.0f) * getTextSize(); + return nGetUnderlinePosition(mNativePaint, mNativeTypeface); } /** * @hide */ public float getUnderlineThickness() { - // kStdUnderline_Thickness = 1/18, defined in SkTextFormatParams.h - // TODO: replace with thickness from post and MVAR tables (b/62353930). - return (1.0f / 18.0f) * getTextSize(); + return nGetUnderlineThickness(mNativePaint, mNativeTypeface); } /** @@ -2997,5 +2993,9 @@ public class Paint { @CriticalNative private static native float nDescent(long paintPtr, long typefacePtr); @CriticalNative + private static native float nGetUnderlinePosition(long paintPtr, long typefacePtr); + @CriticalNative + private static native float nGetUnderlineThickness(long paintPtr, long typefacePtr); + @CriticalNative private static native void nSetTextSize(long paintPtr, float textSize); } diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp index 679cb5021e27..4507c5018c26 100644 --- a/libs/hwui/hwui/Canvas.cpp +++ b/libs/hwui/hwui/Canvas.cpp @@ -46,23 +46,31 @@ void Canvas::drawTextDecorations(float x, float y, float length, const SkPaint& flags = paint.getFlags(); } if (flags & (SkPaint::kUnderlineText_ReserveFlag | SkPaint::kStrikeThruText_ReserveFlag)) { - // Same values used by Skia - const float kStdStrikeThru_Offset = (-6.0f / 21.0f); - const float kStdUnderline_Offset = (1.0f / 9.0f); - const float kStdUnderline_Thickness = (1.0f / 18.0f); - - SkScalar left = x; - SkScalar right = x + length; - float textSize = paint.getTextSize(); - float strokeWidth = fmax(textSize * kStdUnderline_Thickness, 1.0f); + + const SkScalar left = x; + const SkScalar right = x + length; if (flags & SkPaint::kUnderlineText_ReserveFlag) { - SkScalar top = y + textSize * kStdUnderline_Offset - 0.5f * strokeWidth; - SkScalar bottom = y + textSize * kStdUnderline_Offset + 0.5f * strokeWidth; + Paint::FontMetrics metrics; + paint.getFontMetrics(&metrics); + SkScalar position; + if (!metrics.hasUnderlinePosition(&position)) { + position = paint.getTextSize() * Paint::kStdUnderline_Top; + } + SkScalar thickness; + if (!metrics.hasUnderlineThickness(&thickness)) { + thickness = paint.getTextSize() * Paint::kStdUnderline_Thickness; + } + const float strokeWidth = fmax(thickness, 1.0f); + const SkScalar top = y + position; + const SkScalar bottom = top + strokeWidth; drawRect(left, top, right, bottom, paint); } if (flags & SkPaint::kStrikeThruText_ReserveFlag) { - SkScalar top = y + textSize * kStdStrikeThru_Offset - 0.5f * strokeWidth; - SkScalar bottom = y + textSize * kStdStrikeThru_Offset + 0.5f * strokeWidth; + const float textSize = paint.getTextSize(); + const float position = textSize * Paint::kStdStrikeThru_Offset; + const float strokeWidth = fmax(textSize * Paint::kStdUnderline_Thickness, 1.0f); + const SkScalar top = y + position - 0.5f * strokeWidth; + const SkScalar bottom = y + position + 0.5f * strokeWidth; drawRect(left, top, right, bottom, paint); } } diff --git a/libs/hwui/hwui/Paint.h b/libs/hwui/hwui/Paint.h index c9b5f0031a7b..4305025272b2 100644 --- a/libs/hwui/hwui/Paint.h +++ b/libs/hwui/hwui/Paint.h @@ -28,6 +28,15 @@ namespace android { class ANDROID_API Paint : public SkPaint { public: + // Default values for underlined and strikethrough text, + // as defined by Skia in SkTextFormatParams.h. + constexpr static float kStdStrikeThru_Offset = (-6.0f / 21.0f); + constexpr static float kStdUnderline_Offset = (1.0f / 9.0f); + constexpr static float kStdUnderline_Thickness = (1.0f / 18.0f); + + constexpr static float kStdUnderline_Top = + kStdUnderline_Offset - 0.5f * kStdUnderline_Thickness; + Paint(); Paint(const Paint& paint); Paint(const SkPaint& paint); // NOLINT(implicit) diff --git a/lowpan/java/android/net/lowpan/LowpanBeaconInfo.java b/lowpan/java/android/net/lowpan/LowpanBeaconInfo.java index b344527e4be5..e18abd5bf23f 100644 --- a/lowpan/java/android/net/lowpan/LowpanBeaconInfo.java +++ b/lowpan/java/android/net/lowpan/LowpanBeaconInfo.java @@ -26,7 +26,7 @@ import java.util.TreeSet; * * @hide */ -//@SystemApi +// @SystemApi public class LowpanBeaconInfo extends LowpanIdentity { private int mRssi = UNKNOWN; @@ -104,7 +104,7 @@ public class LowpanBeaconInfo extends LowpanIdentity { } public Collection<Integer> getFlags() { - return mFlags.clone(); + return (Collection<Integer>) mFlags.clone(); } public boolean isFlagSet(int flag) { diff --git a/lowpan/java/android/net/lowpan/LowpanChannelInfo.java b/lowpan/java/android/net/lowpan/LowpanChannelInfo.java index 50afe6d3a4c0..621affee6d06 100644 --- a/lowpan/java/android/net/lowpan/LowpanChannelInfo.java +++ b/lowpan/java/android/net/lowpan/LowpanChannelInfo.java @@ -16,14 +16,12 @@ package android.net.lowpan; - /** Provides detailed information about a given channel. */ -//@SystemApi +// @SystemApi public class LowpanChannelInfo { public static final int UNKNOWN_POWER = Integer.MAX_VALUE; - ////////////////////////////////////////////////////////////////////////// // Instance Variables private String mName = null; @@ -33,7 +31,6 @@ public class LowpanChannelInfo { private float mSpectrumBandwidth = 0.0f; private int mMaxTransmitPower = UNKNOWN_POWER; - ////////////////////////////////////////////////////////////////////////// // Public Getters and Setters public String getName() { diff --git a/lowpan/java/android/net/lowpan/LowpanCommissioningSession.java b/lowpan/java/android/net/lowpan/LowpanCommissioningSession.java index 9cad00c3415a..1da085ddb294 100644 --- a/lowpan/java/android/net/lowpan/LowpanCommissioningSession.java +++ b/lowpan/java/android/net/lowpan/LowpanCommissioningSession.java @@ -30,7 +30,7 @@ import java.net.InetSocketAddress; * * @hide */ -//@SystemApi +// @SystemApi public abstract class LowpanCommissioningSession { public LowpanCommissioningSession() {} @@ -39,7 +39,7 @@ public abstract class LowpanCommissioningSession { * * @hide */ - //@SystemApi + // @SystemApi public class Callback { public void onReceiveFromCommissioner(@NonNull byte[] packet) {}; diff --git a/lowpan/java/android/net/lowpan/LowpanCredential.java b/lowpan/java/android/net/lowpan/LowpanCredential.java index dea4d7888884..ca8602151515 100644 --- a/lowpan/java/android/net/lowpan/LowpanCredential.java +++ b/lowpan/java/android/net/lowpan/LowpanCredential.java @@ -16,7 +16,6 @@ package android.net.lowpan; - import java.util.Map; /** @@ -24,7 +23,7 @@ import java.util.Map; * * @hide */ -//@SystemApi +// @SystemApi public class LowpanCredential { public static final int UNSPECIFIED_KEY_INDEX = 0; @@ -84,8 +83,7 @@ public class LowpanCredential { void addToMap(Map<String, Object> parameters) throws LowpanException { if (isMasterKey()) { LowpanProperties.KEY_NETWORK_MASTER_KEY.putInMap(parameters, getMasterKey()); - LowpanProperties.KEY_NETWORK_MASTER_KEY_INDEX.putInMap( - parameters, getMasterKeyIndex()); + LowpanProperties.KEY_NETWORK_MASTER_KEY_INDEX.putInMap(parameters, getMasterKeyIndex()); } else { throw new LowpanException("Unsupported Network Credential"); } diff --git a/lowpan/java/android/net/lowpan/LowpanEnergyScanResult.java b/lowpan/java/android/net/lowpan/LowpanEnergyScanResult.java index c680687d0e09..91ed19c488eb 100644 --- a/lowpan/java/android/net/lowpan/LowpanEnergyScanResult.java +++ b/lowpan/java/android/net/lowpan/LowpanEnergyScanResult.java @@ -16,13 +16,12 @@ package android.net.lowpan; - /** * Describes the result from one channel of an energy scan. * * @hide */ -//@SystemApi +// @SystemApi public class LowpanEnergyScanResult { public static final int UNKNOWN = Integer.MAX_VALUE; diff --git a/lowpan/java/android/net/lowpan/LowpanException.java b/lowpan/java/android/net/lowpan/LowpanException.java index 8ff37f926899..b43b2fe087e1 100644 --- a/lowpan/java/android/net/lowpan/LowpanException.java +++ b/lowpan/java/android/net/lowpan/LowpanException.java @@ -28,7 +28,7 @@ import android.util.AndroidException; * @see LowpanInterface * @hide */ -//@SystemApi +// @SystemApi public class LowpanException extends AndroidException { // Make the eclipse warning about serializable exceptions go away private static final long serialVersionUID = 0x31863cbe562b0e11l; // randomly generated diff --git a/lowpan/java/android/net/lowpan/LowpanIdentity.java b/lowpan/java/android/net/lowpan/LowpanIdentity.java index 2e7b560fda5e..9be45ef14b98 100644 --- a/lowpan/java/android/net/lowpan/LowpanIdentity.java +++ b/lowpan/java/android/net/lowpan/LowpanIdentity.java @@ -24,10 +24,9 @@ import java.util.Map; * * @hide */ -//@SystemApi +// @SystemApi public class LowpanIdentity { - ////////////////////////////////////////////////////////////////////////// // Constants /** @hide */ @@ -41,11 +40,10 @@ public class LowpanIdentity { public static final int UNKNOWN = Integer.MAX_VALUE; - ////////////////////////////////////////////////////////////////////////// // Builder /** @hide */ - //@SystemApi + // @SystemApi public static class Builder { private final LowpanIdentity identity = new LowpanIdentity(); @@ -102,7 +100,6 @@ public class LowpanIdentity { LowpanIdentity() {} - ////////////////////////////////////////////////////////////////////////// // Instance Variables private String mName = null; @@ -111,7 +108,6 @@ public class LowpanIdentity { private int mPanid = UNKNOWN; private int mChannel = UNKNOWN; - ////////////////////////////////////////////////////////////////////////// // Public Getters and Setters public String getName() { @@ -140,12 +136,10 @@ public class LowpanIdentity { LowpanProperties.KEY_NETWORK_NAME.putInMap(parameters, networkInfo.getName()); } if (networkInfo.getPanid() != LowpanIdentity.UNKNOWN) { - LowpanProperties.KEY_NETWORK_PANID.putInMap( - parameters, networkInfo.getPanid()); + LowpanProperties.KEY_NETWORK_PANID.putInMap(parameters, networkInfo.getPanid()); } if (networkInfo.getChannel() != LowpanIdentity.UNKNOWN) { - LowpanProperties.KEY_CHANNEL.putInMap( - parameters, networkInfo.getChannel()); + LowpanProperties.KEY_CHANNEL.putInMap(parameters, networkInfo.getChannel()); } if (networkInfo.getXpanid() != null) { LowpanProperties.KEY_NETWORK_XPANID.putInMap(parameters, networkInfo.getXpanid()); diff --git a/lowpan/java/android/net/lowpan/LowpanInterface.java b/lowpan/java/android/net/lowpan/LowpanInterface.java index cd548190fa17..2bb4ecd380f2 100644 --- a/lowpan/java/android/net/lowpan/LowpanInterface.java +++ b/lowpan/java/android/net/lowpan/LowpanInterface.java @@ -33,7 +33,7 @@ import java.util.Set; * * @hide */ -//@SystemApi +// @SystemApi public class LowpanInterface { private static final String TAG = LowpanInterface.class.getSimpleName(); @@ -170,7 +170,7 @@ public class LowpanInterface { * * @hide */ - //@SystemApi + // @SystemApi public abstract static class Callback { public void onConnectedChanged(boolean value) {} @@ -244,7 +244,6 @@ public class LowpanInterface { LowpanException.throwAsPublicException(t); } - ////////////////////////////////////////////////////////////////////////// // Private Property Helpers void setProperties(Map properties) throws LowpanException { @@ -311,10 +310,9 @@ public class LowpanInterface { boolean getPropertyAsBoolean(LowpanProperty<Boolean> key) throws LowpanException { Boolean value = getProperty(key); - return (value != null) ? value : 0; + return (value != null) ? value : false; } - ////////////////////////////////////////////////////////////////////////// // Public Actions /** @@ -424,7 +422,6 @@ public class LowpanInterface { } } - ////////////////////////////////////////////////////////////////////////// // Public Getters and Setters /** @@ -448,13 +445,13 @@ public class LowpanInterface { return ""; } - /** - * Indicates if the interface is enabled or disabled. - * - * @see #setEnabled - * @see android.net.lowpan.LowpanException#LOWPAN_DISABLED - */ - public boolean isEnabled() { + /** + * Indicates if the interface is enabled or disabled. + * + * @see #setEnabled + * @see android.net.lowpan.LowpanException#LOWPAN_DISABLED + */ + public boolean isEnabled() { try { return getPropertyAsBoolean(LowpanProperties.KEY_INTERFACE_ENABLED); } catch (LowpanException x) { @@ -462,16 +459,16 @@ public class LowpanInterface { } } - /** - * Enables or disables the LoWPAN interface. When disabled, the interface is put into a low-power - * state and all commands that require the NCP to be queried will fail with {@link - * android.net.lowpan.LowpanException#LOWPAN_DISABLED}. - * - * @see #isEnabled - * @see android.net.lowpan.LowpanException#LOWPAN_DISABLED - * @hide - */ - public void setEnabled(boolean enabled) throws LowpanException { + /** + * Enables or disables the LoWPAN interface. When disabled, the interface is put into a + * low-power state and all commands that require the NCP to be queried will fail with {@link + * android.net.lowpan.LowpanException#LOWPAN_DISABLED}. + * + * @see #isEnabled + * @see android.net.lowpan.LowpanException#LOWPAN_DISABLED + * @hide + */ + public void setEnabled(boolean enabled) throws LowpanException { setProperty(LowpanProperties.KEY_INTERFACE_ENABLED, enabled); } @@ -628,7 +625,6 @@ public class LowpanInterface { setProperties(map); } - ////////////////////////////////////////////////////////////////////////// // Listener Support /** @@ -644,7 +640,7 @@ public class LowpanInterface { public void registerCallback(@NonNull Callback cb, @Nullable Handler handler) { ILowpanInterfaceListener.Stub listenerBinder = new ILowpanInterfaceListener.Stub() { - public void onPropertiesChanged(Map<String, Object> properties) { + public void onPropertiesChanged(Map properties) { Runnable runnable = new Runnable() { @Override @@ -724,36 +720,35 @@ public class LowpanInterface { */ public void unregisterCallback(Callback cb) { int hashCode = System.identityHashCode(cb); - ILowpanInterfaceListener listenerBinder = mListenerMap.get(hashCode); + synchronized (mListenerMap) { + ILowpanInterfaceListener listenerBinder = mListenerMap.get(hashCode); - if (listenerBinder != null) { - synchronized (mListenerMap) { + if (listenerBinder != null) { mListenerMap.remove(hashCode); - } - try { - mBinder.removeListener(listenerBinder); - } catch (RemoteException x) { - // Catch and ignore all binder exceptions - Log.e(TAG, x.toString()); + + try { + mBinder.removeListener(listenerBinder); + } catch (RemoteException x) { + // Catch and ignore all binder exceptions + Log.e(TAG, x.toString()); + } } } } - ////////////////////////////////////////////////////////////////////////// - // Active and Passive Scanning + // Active and Passive Scanning - /** - * Creates a new {@link android.net.lowpan.LowpanScanner} object for this interface. - * - * <p>This method allocates a new unique object for each call. - * - * @see android.net.lowpan.LowpanScanner - */ - public @NonNull LowpanScanner createScanner() { + /** + * Creates a new {@link android.net.lowpan.LowpanScanner} object for this interface. + * + * <p>This method allocates a new unique object for each call. + * + * @see android.net.lowpan.LowpanScanner + */ + public @NonNull LowpanScanner createScanner() { return new LowpanScanner(mBinder); } - ////////////////////////////////////////////////////////////////////////// // Route Management /** diff --git a/lowpan/java/android/net/lowpan/LowpanManager.java b/lowpan/java/android/net/lowpan/LowpanManager.java index b58608da7c21..ecdda49f718a 100644 --- a/lowpan/java/android/net/lowpan/LowpanManager.java +++ b/lowpan/java/android/net/lowpan/LowpanManager.java @@ -33,32 +33,24 @@ import java.util.HashMap; * * @hide */ -//@SystemApi +// @SystemApi public class LowpanManager { private static final String TAG = LowpanManager.class.getSimpleName(); - ////////////////////////////////////////////////////////////////////////// - // Public Classes - /** @hide */ - //@SystemApi + // @SystemApi public abstract static class Callback { - public void onInterfaceAdded(LowpanInterface lowpan_interface) {} + public void onInterfaceAdded(LowpanInterface lowpanInterface) {} - public void onInterfaceRemoved(LowpanInterface lowpan_interface) {} + public void onInterfaceRemoved(LowpanInterface lowpanInterface) {} } - ////////////////////////////////////////////////////////////////////////// - // Instance Variables - + private Context mContext; private ILowpanManager mManager; private HashMap<Integer, ILowpanManagerListener> mListenerMap = new HashMap<>(); - ////////////////////////////////////////////////////////////////////////// - private static LowpanManager sSingletonInstance; - ////////////////////////////////////////////////////////////////////////// // Static Methods /** Returns a reference to the LowpanManager object, allocating it if necessary. */ @@ -75,7 +67,6 @@ public class LowpanManager { return sSingletonInstance; } - ////////////////////////////////////////////////////////////////////////// // Constructors /** @@ -84,28 +75,34 @@ public class LowpanManager { */ private LowpanManager() {} - ////////////////////////////////////////////////////////////////////////// // Private Methods /** * Returns a reference to the ILowpanManager interface, provided by the LoWPAN Manager Service. */ @Nullable - private ILowpanManager getILowpanManager() { + private synchronized ILowpanManager getILowpanManager() { + // Use a local reference of this object for thread safety. ILowpanManager manager = mManager; + if (manager == null) { IBinder serviceBinder = new ServiceManager().getService(ILowpanManager.LOWPAN_SERVICE_NAME); - mManager = manager = ILowpanManager.Stub.asInterface(serviceBinder); + + manager = ILowpanManager.Stub.asInterface(serviceBinder); + + mManager = manager; // Add any listeners synchronized (mListenerMap) { - for (Integer hashObj : mListenerMap.keySet()) { + for (ILowpanManagerListener listener : mListenerMap.values()) { try { - manager.addListener(mListenerMap.get(hashObj)); + manager.addListener(listener); + } catch (RemoteException x) { // Consider any failure here as implying the manager is defunct - mManager = manager = null; + mManager = null; + manager = null; } } } @@ -113,7 +110,6 @@ public class LowpanManager { return manager; } - ////////////////////////////////////////////////////////////////////////// // Public Methods /** @@ -141,6 +137,7 @@ public class LowpanManager { manager = getILowpanManager(); } } + return ret; } @@ -151,7 +148,7 @@ public class LowpanManager { @Nullable public LowpanInterface getInterface() { String[] ifaceList = getInterfaceList(); - if (ifaceList != null && ifaceList.length > 0) { + if (ifaceList.length > 0) { return getInterface(ifaceList[0]); } return null; @@ -165,24 +162,16 @@ public class LowpanManager { public String[] getInterfaceList() { ILowpanManager manager = getILowpanManager(); - if (manager != null) { + // Maximum number of tries is two. We should only try + // more than once if our manager has died or there + // was some sort of AIDL buffer full event. + for (int i = 0; i < 2 && manager != null; i++) { try { return manager.getInterfaceList(); - } catch (RemoteException x) { // In all of the cases when we get this exception, we reconnect and try again mManager = null; - try { - manager = getILowpanManager(); - if (manager != null) { - return manager.getInterfaceList(); - } - } catch (RemoteException ex) { - // Something weird is going on, so we log it - // and fall back thru to returning an empty array. - Log.e(TAG, ex.toString()); - mManager = null; - } + manager = getILowpanManager(); } } @@ -200,14 +189,14 @@ public class LowpanManager { throws LowpanException { ILowpanManagerListener.Stub listenerBinder = new ILowpanManagerListener.Stub() { - public void onInterfaceAdded(ILowpanInterface lowpan_interface) { + public void onInterfaceAdded(ILowpanInterface lowpanInterface) { Runnable runnable = new Runnable() { @Override public void run() { cb.onInterfaceAdded( LowpanInterface.getInterfaceFromBinder( - lowpan_interface.asBinder())); + lowpanInterface.asBinder())); } }; @@ -218,14 +207,14 @@ public class LowpanManager { } } - public void onInterfaceRemoved(ILowpanInterface lowpan_interface) { + public void onInterfaceRemoved(ILowpanInterface lowpanInterface) { Runnable runnable = new Runnable() { @Override public void run() { cb.onInterfaceRemoved( LowpanInterface.getInterfaceFromBinder( - lowpan_interface.asBinder())); + lowpanInterface.asBinder())); } }; diff --git a/lowpan/java/android/net/lowpan/LowpanProvision.java b/lowpan/java/android/net/lowpan/LowpanProvision.java index ace1f9c05c4d..7028807679a1 100644 --- a/lowpan/java/android/net/lowpan/LowpanProvision.java +++ b/lowpan/java/android/net/lowpan/LowpanProvision.java @@ -25,14 +25,13 @@ import java.util.Map; * * @hide */ -//@SystemApi +// @SystemApi public class LowpanProvision { - ////////////////////////////////////////////////////////////////////////// // Builder /** @hide */ - //@SystemApi + // @SystemApi public static class Builder { private final LowpanProvision provision = new LowpanProvision(); @@ -53,13 +52,11 @@ public class LowpanProvision { private LowpanProvision() {} - ////////////////////////////////////////////////////////////////////////// // Instance Variables private LowpanIdentity mIdentity = new LowpanIdentity(); private LowpanCredential mCredential = null; - ////////////////////////////////////////////////////////////////////////// // Public Getters and Setters @NonNull @@ -72,7 +69,6 @@ public class LowpanProvision { return mCredential; } - ////////////////////////////////////////////////////////////////////////// // LoWPAN-Internal Methods static void addToMap(Map<String, Object> parameters, LowpanProvision provision) diff --git a/lowpan/java/android/net/lowpan/LowpanScanner.java b/lowpan/java/android/net/lowpan/LowpanScanner.java index 754f72e3cb84..e0df55d9af3f 100644 --- a/lowpan/java/android/net/lowpan/LowpanScanner.java +++ b/lowpan/java/android/net/lowpan/LowpanScanner.java @@ -25,7 +25,6 @@ import android.util.Log; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; -import java.util.List; import java.util.Map; /** @@ -36,11 +35,10 @@ import java.util.Map; * @see LowpanInterface * @hide */ -//@SystemApi +// @SystemApi public class LowpanScanner { private static final String TAG = LowpanInterface.class.getSimpleName(); - ////////////////////////////////////////////////////////////////////////// // Public Classes /** @@ -48,7 +46,7 @@ public class LowpanScanner { * * @hide */ - //@SystemApi + // @SystemApi public abstract static class Callback { public void onNetScanBeacon(LowpanBeaconInfo beacon) {} @@ -57,16 +55,14 @@ public class LowpanScanner { public void onScanFinished() {} } - ////////////////////////////////////////////////////////////////////////// // Instance Variables private ILowpanInterface mBinder; private Callback mCallback = null; private Handler mHandler = null; - private List<Integer> mChannelMask = null; + private ArrayList<Integer> mChannelMask = null; private int mTxPower = Integer.MAX_VALUE; - ////////////////////////////////////////////////////////////////////////// // Constructors/Accessors and Exception Glue LowpanScanner(@NonNull ILowpanInterface binder) { @@ -74,7 +70,7 @@ public class LowpanScanner { } /** Sets an instance of {@link LowpanScanner.Callback} to receive events. */ - public void setCallback(@Nullable Callback cb, @Nullable Handler handler) { + public synchronized void setCallback(@Nullable Callback cb, @Nullable Handler handler) { mCallback = cb; mHandler = handler; } @@ -110,7 +106,7 @@ public class LowpanScanner { * @return the current channel mask, or <code>null</code> if no channel mask is currently set. */ public @Nullable Collection<Integer> getChannelMask() { - return mChannelMask.clone(); + return (Collection<Integer>) mChannelMask.clone(); } /** @@ -179,17 +175,24 @@ public class LowpanScanner { ILowpanNetScanCallback binderListener = new ILowpanNetScanCallback.Stub() { public void onNetScanBeacon(Map parameters) { - Callback callback = mCallback; - Handler handler = mHandler; + Callback callback; + Handler handler; + + synchronized (LowpanScanner.this) { + callback = mCallback; + handler = mHandler; + } if (callback == null) { return; } - Runnable runnable = () -> callback.onNetScanBeacon( - new LowpanBeaconInfo.Builder() - .updateFromMap(parameters) - .build()); + Runnable runnable = + () -> + callback.onNetScanBeacon( + new LowpanBeaconInfo.Builder() + .updateFromMap(parameters) + .build()); if (handler != null) { handler.post(runnable); @@ -199,8 +202,13 @@ public class LowpanScanner { } public void onNetScanFinished() { - Callback callback = mCallback; - Handler handler = mHandler; + Callback callback; + Handler handler; + + synchronized (LowpanScanner.this) { + callback = mCallback; + handler = mHandler; + } if (callback == null) { return; @@ -218,7 +226,7 @@ public class LowpanScanner { try { mBinder.startNetScan(map, binderListener); - } catch (ServiceSpecificException|RemoteException x) { + } catch (ServiceSpecificException | RemoteException x) { LowpanException.throwAsPublicException(x); } } @@ -257,7 +265,8 @@ public class LowpanScanner { return; } - Runnable runnable = () -> { + Runnable runnable = + () -> { if (callback != null) { LowpanEnergyScanResult result = new LowpanEnergyScanResult(); diff --git a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java index 2703fbf00a50..20b12b4823ac 100644 --- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java +++ b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java @@ -47,6 +47,9 @@ import android.webkit.WebViewClient; import android.widget.ProgressBar; import android.widget.TextView; +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; + import java.io.IOException; import java.net.HttpURLConnection; import java.net.MalformedURLException; @@ -63,7 +66,14 @@ public class CaptivePortalLoginActivity extends Activity { private static final int SOCKET_TIMEOUT_MS = 10000; - private enum Result { DISMISSED, UNWANTED, WANTED_AS_IS }; + private enum Result { + DISMISSED(MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_RESULT_DISMISSED), + UNWANTED(MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_RESULT_UNWANTED), + WANTED_AS_IS(MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_RESULT_WANTED_AS_IS); + + final int metricsEvent; + Result(int metricsEvent) { this.metricsEvent = metricsEvent; } + }; private URL mUrl; private String mUserAgent; @@ -77,6 +87,9 @@ public class CaptivePortalLoginActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + + logMetricsEvent(MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_ACTIVITY); + mCm = ConnectivityManager.from(this); mNetwork = getIntent().getParcelableExtra(ConnectivityManager.EXTRA_NETWORK); mCaptivePortal = getIntent().getParcelableExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL); @@ -173,6 +186,7 @@ public class CaptivePortalLoginActivity extends Activity { mCm.unregisterNetworkCallback(mNetworkCallback); mNetworkCallback = null; } + logMetricsEvent(result.metricsEvent); switch (result) { case DISMISSED: mCaptivePortal.reportCaptivePortalDismissed(); @@ -381,6 +395,7 @@ public class CaptivePortalLoginActivity extends Activity { @Override public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { + logMetricsEvent(MetricsEvent.CAPTIVE_PORTAL_LOGIN_ACTIVITY_SSL_ERROR); Log.w(TAG, "SSL error (error: " + error.getPrimaryError() + " host: " + // Only show host to avoid leaking private info. Uri.parse(error.getUrl()).getHost() + " certificate: " + @@ -492,4 +507,8 @@ public class CaptivePortalLoginActivity extends Activity { } return url.getHost(); } + + private void logMetricsEvent(int event) { + MetricsLogger.action(this, event, getPackageName()); + } } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java index 533c52b20f4e..96f51c16796f 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java @@ -16,6 +16,7 @@ package com.android.providers.settings; +import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.backup.BackupAgentHelper; import android.app.backup.BackupDataInput; @@ -30,7 +31,6 @@ import android.net.NetworkPolicyManager; import android.net.Uri; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiManager; -import android.os.Handler; import android.os.ParcelFileDescriptor; import android.os.UserHandle; import android.provider.Settings; @@ -590,14 +590,18 @@ public class SettingsBackupAgent extends BackupAgentHelper { Log.i(TAG, "restoreSettings: " + contentUri); } - // Figure out the white list and redirects to the global table. + // Figure out the white list and redirects to the global table. We restore anything + // in either the backup whitelist or the legacy-restore whitelist for this table. final String[] whitelist; if (contentUri.equals(Settings.Secure.CONTENT_URI)) { - whitelist = Settings.Secure.SETTINGS_TO_BACKUP; + whitelist = concat(Settings.Secure.SETTINGS_TO_BACKUP, + Settings.Secure.LEGACY_RESTORE_SETTINGS); } else if (contentUri.equals(Settings.System.CONTENT_URI)) { - whitelist = Settings.System.SETTINGS_TO_BACKUP; + whitelist = concat(Settings.System.SETTINGS_TO_BACKUP, + Settings.System.LEGACY_RESTORE_SETTINGS); } else if (contentUri.equals(Settings.Global.CONTENT_URI)) { - whitelist = Settings.Global.SETTINGS_TO_BACKUP; + whitelist = concat(Settings.Global.SETTINGS_TO_BACKUP, + Settings.Global.LEGACY_RESTORE_SETTINGS); } else { throw new IllegalArgumentException("Unknown URI: " + contentUri); } @@ -648,6 +652,18 @@ public class SettingsBackupAgent extends BackupAgentHelper { } } + private final String[] concat(String[] first, @Nullable String[] second) { + if (second == null || second.length == 0) { + return first; + } + final int firstLen = first.length; + final int secondLen = second.length; + String[] both = new String[firstLen + secondLen]; + System.arraycopy(first, 0, both, 0, firstLen); + System.arraycopy(second, 0, both, firstLen, secondLen); + return both; + } + /** * Restores the owner info enabled and other settings in LockSettings. * diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index f5d7dd8d9acd..1df2c807f64f 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -2895,7 +2895,7 @@ public class SettingsProvider extends ContentProvider { } private final class UpgradeController { - private static final int SETTINGS_VERSION = 145; + private static final int SETTINGS_VERSION = 146; private final int mUserId; @@ -3478,22 +3478,25 @@ public class SettingsProvider extends ContentProvider { } if (currentVersion == 144) { - // Version 145: Set the default value for WIFI_WAKEUP_AVAILABLE. + // Version 145: Removed + currentVersion = 145; + } + + if (currentVersion == 145) { + // Version 146: Set the default value for WIFI_WAKEUP_AVAILABLE. if (userId == UserHandle.USER_SYSTEM) { final SettingsState globalSettings = getGlobalSettingsLocked(); final Setting currentSetting = globalSettings.getSettingLocked( Settings.Global.WIFI_WAKEUP_AVAILABLE); - if (currentSetting.isNull()) { - final int defaultValue = getContext().getResources().getInteger( - com.android.internal.R.integer.config_wifi_wakeup_available); - globalSettings.insertSettingLocked( - Settings.Global.WIFI_WAKEUP_AVAILABLE, - String.valueOf(defaultValue), - null, true, SettingsState.SYSTEM_PACKAGE_NAME); - } + final int defaultValue = getContext().getResources().getInteger( + com.android.internal.R.integer.config_wifi_wakeup_available); + globalSettings.insertSettingLocked( + Settings.Global.WIFI_WAKEUP_AVAILABLE, + String.valueOf(defaultValue), + null, true, SettingsState.SYSTEM_PACKAGE_NAME); } - currentVersion = 145; + currentVersion = 146; } // vXXX: Add new settings above this point. diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index d3cbe4aa578f..81ca23082367 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -262,6 +262,9 @@ <!-- Doze: alpha to apply to small icons when dozing --> <integer name="doze_small_icon_alpha">222</integer><!-- 87% of 0xff --> + <!-- Doze: whether the double tap sensor reports 2D touch coordinates --> + <bool name="doze_double_tap_reports_touch_coordinates">false</bool> + <!-- Hotspot tile: number of days to show after feature is used. --> <integer name="days_to_show_hotspot_tile">30</integer> diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java index 3e424d05373a..5aaa6c78f5fd 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java @@ -38,6 +38,8 @@ public interface DozeHost { void setAnimateWakeup(boolean animateWakeup); + void onDoubleTap(float x, float y); + interface Callback { default void onNotificationHeadsUp() {} default void onPowerSaveChanged(boolean active) {} diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java index 73f522244a60..23da716706d3 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java @@ -81,17 +81,18 @@ public class DozeSensors { mSensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION), null /* setting */, dozeParameters.getPulseOnSigMotion(), - DozeLog.PULSE_REASON_SENSOR_SIGMOTION), + DozeLog.PULSE_REASON_SENSOR_SIGMOTION, false /* touchCoords */), mPickupSensor = new TriggerSensor( mSensorManager.getDefaultSensor(Sensor.TYPE_PICK_UP_GESTURE), Settings.Secure.DOZE_PULSE_ON_PICK_UP, config.pulseOnPickupAvailable(), - DozeLog.PULSE_REASON_SENSOR_PICKUP), + DozeLog.PULSE_REASON_SENSOR_PICKUP, false /* touchCoords */), new TriggerSensor( findSensorWithType(config.doubleTapSensorType()), Settings.Secure.DOZE_PULSE_ON_DOUBLE_TAP, true /* configured */, - DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP) + DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP, + dozeParameters.doubleTapReportsTouchCoordinates()) }; mProxSensor = new ProxSensor(); @@ -207,16 +208,19 @@ public class DozeSensors { final boolean mConfigured; final int mPulseReason; final String mSetting; + final boolean mReportsTouchCoordinates; private boolean mRequested; private boolean mRegistered; private boolean mDisabled; - public TriggerSensor(Sensor sensor, String setting, boolean configured, int pulseReason) { + public TriggerSensor(Sensor sensor, String setting, boolean configured, int pulseReason, + boolean reportsTouchCoordinates) { mSensor = sensor; mSetting = setting; mConfigured = configured; mPulseReason = pulseReason; + mReportsTouchCoordinates = reportsTouchCoordinates; } public void setListening(boolean listen) { @@ -276,7 +280,13 @@ public class DozeSensors { } mRegistered = false; - mCallback.onSensorPulse(mPulseReason, sensorPerformsProxCheck); + float screenX = -1; + float screenY = -1; + if (mReportsTouchCoordinates && event.values.length >= 2) { + screenX = event.values[0]; + screenY = event.values[1]; + } + mCallback.onSensorPulse(mPulseReason, sensorPerformsProxCheck, screenX, screenY); updateListener(); // reregister, this sensor only fires once })); } @@ -309,7 +319,12 @@ public class DozeSensors { * Called when a sensor requests a pulse * @param pulseReason Requesting sensor, e.g. {@link DozeLog#PULSE_REASON_SENSOR_PICKUP} * @param sensorPerformedProxCheck true if the sensor already checked for FAR proximity. + * @param screenX the location on the screen where the sensor fired or -1 + * if the sensor doesn't support reporting screen locations. + * @param screenY the location on the screen where the sensor fired or -1 + * if the sensor doesn't support reporting screen locations. */ - void onSensorPulse(int pulseReason, boolean sensorPerformedProxCheck); + void onSensorPulse(int pulseReason, boolean sensorPerformedProxCheck, + float screenX, float screenY); } } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java index 693a690d7f59..ae936db7b0e2 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java @@ -98,12 +98,14 @@ public class DozeTriggers implements DozeMachine.Part { requestPulse(DozeLog.PULSE_REASON_NOTIFICATION, false /* performedProxCheck */); } - private void onSensor(int pulseReason, boolean sensorPerformedProxCheck) { + private void onSensor(int pulseReason, boolean sensorPerformedProxCheck, + float screenX, float screenY) { boolean isDoubleTap = pulseReason == DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP; boolean isPickup = pulseReason == DozeLog.PULSE_REASON_SENSOR_PICKUP; if (mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT)) { if (isDoubleTap) { + mDozeHost.onDoubleTap(screenX, screenY); mMachine.wakeUp(); } else { mDozeHost.extendPulse(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java index 76177a310a06..4a6bafc50e52 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java @@ -51,7 +51,14 @@ import com.android.systemui.statusbar.phone.NavigationBarView; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.UserSwitcherController; - +import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.systemui.classifier.FalsingLog; +import com.android.systemui.classifier.FalsingManager; +import com.android.systemui.Prefs; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.Map; /** * A status bar (and navigation bar) tailored for the automotive use case. */ @@ -71,6 +78,7 @@ public class CarStatusBar extends StatusBar implements private ConnectedDeviceSignalController mConnectedDeviceSignalController; private CarNavigationBarView mNavigationBarView; + private final Object mQueueLock = new Object(); @Override public void start() { super.start(); @@ -170,6 +178,43 @@ public class CarStatusBar extends StatusBar implements } @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + //When executing dump() funciton simultaneously, we need to serialize them + //to get mStackScroller's position correctly. + synchronized (mQueueLock) { + pw.println(" mStackScroller: " + viewInfo(mStackScroller)); + pw.println(" mStackScroller: " + viewInfo(mStackScroller) + + " scroll " + mStackScroller.getScrollX() + + "," + mStackScroller.getScrollY()); + } + + pw.print(" mTaskStackListener="); pw.println(mTaskStackListener); + pw.print(" mController="); + pw.println(mController); + pw.print(" mFullscreenUserSwitcher="); pw.println(mFullscreenUserSwitcher); + pw.print(" mCarBatteryController="); + pw.println(mCarBatteryController); + pw.print(" mBatteryMeterView="); + pw.println(mBatteryMeterView); + pw.print(" mConnectedDeviceSignalController="); + pw.println(mConnectedDeviceSignalController); + pw.print(" mNavigationBarView="); + pw.println(mNavigationBarView); + + if (KeyguardUpdateMonitor.getInstance(mContext) != null) { + KeyguardUpdateMonitor.getInstance(mContext).dump(fd, pw, args); + } + + FalsingManager.getInstance(mContext).dump(pw); + FalsingLog.dump(pw); + + pw.println("SharedPreferences:"); + for (Map.Entry<String, ?> entry : Prefs.getAll(mContext).entrySet()) { + pw.print(" "); pw.print(entry.getKey()); pw.print("="); pw.println(entry.getValue()); + } + } + + @Override public NavigationBarView getNavigationBarView() { return mNavigationBarView; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java index d7e7abe4fd9c..6b7397b3f8ca 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java @@ -157,6 +157,10 @@ public class DozeParameters { return 2 * getPulseVisibleDuration(); } + public boolean doubleTapReportsTouchCoordinates() { + return mContext.getResources().getBoolean(R.bool.doze_double_tap_reports_touch_coordinates); + } + /** * Parses a spec of the form `1,2,3,!5,*`. The resulting object will match numbers that are diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java index 021e11acbcf5..3937dd3eea2a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java @@ -517,14 +517,14 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { mAnimationsEnabled = enabled; } - public void setReplacingIcons(ArrayMap<String, ArrayList<StatusBarIcon>> replacingIcons) { - mReplacingIcons = replacingIcons; - } - public void setDarkOffsetX(int offsetX) { mDarkOffsetX = offsetX; } + public void setReplacingIcons(ArrayMap<String, ArrayList<StatusBarIcon>> replacingIcons) { + mReplacingIcons = replacingIcons; + } + public class IconState extends ViewState { public float iconAppearAmount = 1.0f; public float clampedAppearAmount = 1.0f; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index e8b6e079e261..0ac221f7f088 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -32,7 +32,6 @@ import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSLUCE import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT; import static com.android.systemui.statusbar.phone.BarTransitions.MODE_WARNING; -import android.R.style; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.annotation.NonNull; @@ -5325,6 +5324,37 @@ public class StatusBar extends SystemUI implements DemoMode, mAnimateWakeup = animateWakeup; } + @Override + public void onDoubleTap(float screenX, float screenY) { + if (screenX > 0 && screenY > 0 && mAmbientIndicationContainer != null + && mAmbientIndicationContainer.getVisibility() == View.VISIBLE) { + mAmbientIndicationContainer.getLocationOnScreen(mTmpInt2); + float viewX = screenX - mTmpInt2[0]; + float viewY = screenY - mTmpInt2[1]; + if (0 <= viewX && viewX <= mAmbientIndicationContainer.getWidth() + && 0 <= viewY && viewY <= mAmbientIndicationContainer.getHeight()) { + dispatchDoubleTap(viewX, viewY); + } + } + } + + public void dispatchDoubleTap(float viewX, float viewY) { + dispatchTap(mAmbientIndicationContainer, viewX, viewY); + dispatchTap(mAmbientIndicationContainer, viewX, viewY); + } + + private void dispatchTap(View view, float x, float y) { + long now = SystemClock.elapsedRealtime(); + dispatchTouchEvent(view, x, y, now, MotionEvent.ACTION_DOWN); + dispatchTouchEvent(view, x, y, now, MotionEvent.ACTION_UP); + } + + private void dispatchTouchEvent(View view, float x, float y, long now, int action) { + MotionEvent ev = MotionEvent.obtain(now, now, action, x, y, 0 /* meta */); + view.dispatchTouchEvent(ev); + ev.recycle(); + } + private boolean shouldAnimateWakeup() { return mAnimateWakeup; } @@ -6866,7 +6896,8 @@ public class StatusBar extends SystemUI implements DemoMode, // If mAlwaysExpandNonGroupedNotification is false, then only expand the // very first notification and if it's not a child of grouped notifications. row.setSystemExpanded(mAlwaysExpandNonGroupedNotification - || (visibleNotifications == 0 && !isChildNotification)); + || (visibleNotifications == 0 && !isChildNotification + && !row.isLowPriority())); } entry.row.setShowAmbient(isDozing()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java index 56c07f905446..23451106a20c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java @@ -28,6 +28,8 @@ class DozeHostFake implements DozeHost { boolean pulseExtended; boolean animateWakeup; boolean dozing; + float doubleTapX; + float doubleTapY; @Override public void addCallback(@NonNull Callback callback) { @@ -88,4 +90,10 @@ class DozeHostFake implements DozeHost { public void setAnimateWakeup(boolean animateWakeup) { this.animateWakeup = animateWakeup; } + + @Override + public void onDoubleTap(float x, float y) { + doubleTapX = y; + doubleTapY = y; + } } diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto index 11ea841a10f5..fa2b1ee83419 100644 --- a/proto/src/metrics_constants.proto +++ b/proto/src/metrics_constants.proto @@ -4090,6 +4090,11 @@ message MetricsEvent { // OS: O DR SETTINGS_ASSIST_GESTURE_FIRST_TIME = 1012; + // CaptivePortalLoginActivity displays SSL error page + // CATEGORY: GLOBAL_SYSTEM_UI + // OS: O DR + CAPTIVE_PORTAL_LOGIN_ACTIVITY_SSL_ERROR = 1013; + // Add new aosp constants above this line. // END OF AOSP CONSTANTS } diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java index 25a0772efd7f..1afde550f027 100644 --- a/services/core/java/com/android/server/tv/TvInputManagerService.java +++ b/services/core/java/com/android/server/tv/TvInputManagerService.java @@ -1218,6 +1218,12 @@ public final class TvInputManagerService extends SystemService { @Override public void setMainSession(IBinder sessionToken, int userId) { + if (mContext.checkCallingPermission( + android.Manifest.permission.CHANGE_HDMI_CEC_ACTIVE_SOURCE) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException( + "The caller does not have CHANGE_HDMI_CEC_ACTIVE_SOURCE permission"); + } if (DEBUG) { Slog.d(TAG, "setMainSession(sessionToken=" + sessionToken + ")"); } diff --git a/telephony/java/android/telephony/Telephony.java b/telephony/java/android/telephony/Telephony.java index 3282f5f31960..765c73beedf4 100644 --- a/telephony/java/android/telephony/Telephony.java +++ b/telephony/java/android/telephony/Telephony.java @@ -1176,6 +1176,29 @@ public final class Telephony { } /** + * Base column for the table that contain Carrier Public key. + * @hide + */ + public interface CarrierColumns extends BaseColumns { + + public static final String MCC = "mcc"; + public static final String MNC = "mnc"; + public static final String KEY_TYPE = "key_type"; + public static final String MVNO_TYPE = "mvno_type"; + public static final String MVNO_MATCH_DATA = "mvno_match_data"; + public static final String PUBLIC_KEY = "public_key"; + public static final String KEY_IDENTIFIER = "key_identifier"; + public static final String EXPIRATION_TIME = "expiration_time"; + public static final String LAST_MODIFIED = "last_modified"; + + /** + * The {@code content://} style URL for this table. + * @hide + */ + public static final Uri CONTENT_URI = Uri.parse("content://carrier_information/carrier"); + } + + /** * Base columns for tables that contain MMSs. */ public interface BaseMmsColumns extends BaseColumns { diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp index 1d2e3a4f2df0..5978cdd154b9 100644 --- a/tools/aapt2/Main.cpp +++ b/tools/aapt2/Main.cpp @@ -27,7 +27,7 @@ namespace aapt { static const char* sMajorVersion = "2"; // Update minor version whenever a feature or flag is added. -static const char* sMinorVersion = "16"; +static const char* sMinorVersion = "17"; int PrintVersion() { std::cerr << "Android Asset Packaging Tool (aapt) " << sMajorVersion << "." diff --git a/tools/aapt2/readme.md b/tools/aapt2/readme.md index 0290e30dfced..49cb8d4ca827 100644 --- a/tools/aapt2/readme.md +++ b/tools/aapt2/readme.md @@ -1,9 +1,19 @@ # Android Asset Packaging Tool 2.0 (AAPT2) release notes ## Version 2.17 -### `aapt2 compile ...` -- Fixed an issue where symlinks would not be followed when compiling PNGs. (bug 62144459) +### `aapt2 ...` +- Fixed issue where symlinks would not be followed when compiling PNGs. (bug 62144459) - Fixed issue where overlays that declared `<add-resource>` did not compile. (bug 38355988) +- Fixed issue where `%n` in a string resource was interpreted as a format argument. (bug 37132275) +- Allow empty resources to compile, giving them a value of `""` or `@null`, depending on the + accepted formats. (bug 38425050) +- Resources declared via `<item>` with no format attribute were changed to accept all + resource types. (bug 62260121) +- Allow `<layout>` element under `<activity>` in AndroidManifest.xml. (bug 62189611) +- Fix issue where `--no-version-vector` did not apply to `pathInterpolator` and `objectAnimator`. + (bug 62211148) +- Fix issue where overlaid `<style>` would not be merged, and would replace the original resource + instead. This fix brings behavior in-line with AAPT. (bug 38355988) ## Version 2.16 ### `aapt2 link ...` |