diff options
142 files changed, 3546 insertions, 948 deletions
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResults.java b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResults.java index 0d764ce29c74..e417ca791c45 100644 --- a/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResults.java +++ b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResults.java @@ -28,7 +28,13 @@ public class BenchmarkResults { mResults.add(TimeUnit.NANOSECONDS.toMillis(duration)); } - public Bundle getStats() { + public Bundle getStatsToReport() { + final Bundle stats = new Bundle(); + stats.putDouble("Mean (ms)", mean()); + return stats; + } + + public Bundle getStatsToLog() { final Bundle stats = new Bundle(); stats.putDouble("Mean (ms)", mean()); stats.putDouble("Median (ms)", median()); diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResultsReporter.java b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResultsReporter.java index 7472865e9a5a..d3a3ce54e378 100644 --- a/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResultsReporter.java +++ b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResultsReporter.java @@ -40,9 +40,11 @@ public class BenchmarkResultsReporter implements TestRule { @Override public void evaluate() throws Throwable { base.evaluate(); - final Bundle stats = mRunner.getStats(); - final String summary = getSummaryString(description.getMethodName(), stats); - logSummary(description.getTestClass().getSimpleName(), summary, mRunner.getAllDurations()); + final Bundle stats = mRunner.getStatsToReport(); + final String summary = getSummaryString(description.getMethodName(), + mRunner.getStatsToLog()); + logSummary(description.getTestClass().getSimpleName(), summary, + mRunner.getAllDurations()); stats.putString(Instrumentation.REPORT_KEY_STREAMRESULT, summary); InstrumentationRegistry.getInstrumentation().sendStatus( Activity.RESULT_OK, stats); diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java index ccadc9a8f6a9..c7bebf3847fe 100644 --- a/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java +++ b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java @@ -93,8 +93,12 @@ public class BenchmarkRunner { mState = RUNNING; } - public Bundle getStats() { - return mResults.getStats(); + public Bundle getStatsToReport() { + return mResults.getStatsToReport(); + } + + public Bundle getStatsToLog() { + return mResults.getStatsToLog(); } public ArrayList<Long> getAllDurations() { diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTest.java b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java index 6e802a9fc0c8..855be0859520 100644 --- a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTest.java +++ b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java @@ -49,19 +49,21 @@ import java.util.concurrent.TimeUnit; * make MultiUserPerfTests && * adb install -r \ * ${ANDROID_PRODUCT_OUT}/data/app/MultiUserPerfTests/MultiUserPerfTests.apk && - * adb shell am instrument -e class android.multiuser.UserLifecycleTest \ + * adb shell am instrument -e class android.multiuser.UserLifecycleTests \ * -w com.android.perftests.multiuser/android.support.test.runner.AndroidJUnitRunner * * or * - * bit MultiUserPerfTests:android.multiuser.UserLifecycleTest + * bit MultiUserPerfTests:android.multiuser.UserLifecycleTests * * Note: If you use bit for running the tests, benchmark results won't be printed on the host side. - * But in either case, results can be checked on the device side 'adb logcat -s UserLifecycleTest' + * But in either case, results can be checked on the device side 'adb logcat -s UserLifecycleTests' */ @LargeTest @RunWith(AndroidJUnit4.class) -public class UserLifecycleTest { +public class UserLifecycleTests { + private static final String TAG = UserLifecycleTests.class.getSimpleName(); + private final int TIMEOUT_IN_SECOND = 30; private final int CHECK_USER_REMOVED_INTERVAL_MS = 200; @@ -276,7 +278,7 @@ public class UserLifecycleTest { bootCompleteLatch.countDown(); } } - }, "UserLifecycleTest"); + }, TAG); } private void registerBroadcastReceiver(final String action, final CountDownLatch latch, diff --git a/api/current.txt b/api/current.txt index 06404c348744..21e66d3d6626 100644 --- a/api/current.txt +++ b/api/current.txt @@ -48596,6 +48596,9 @@ package android.webkit { public abstract class SafeBrowsingResponse { ctor public SafeBrowsingResponse(); + method public abstract void backToSafety(boolean); + method public abstract void proceed(boolean); + method public abstract void showInterstitial(boolean); } public class ServiceWorkerClient { @@ -49065,7 +49068,7 @@ package android.webkit { method public void setNetworkAvailable(boolean); method public deprecated void setPictureListener(android.webkit.WebView.PictureListener); method public void setRendererPriorityPolicy(int, boolean); - method public static void setSafeBrowsingWhiteList(java.lang.String[]); + method public static void setSafeBrowsingWhiteList(java.util.List<java.lang.String>); method public deprecated void setVerticalScrollbarOverlay(boolean); method public void setWebChromeClient(android.webkit.WebChromeClient); method public static void setWebContentsDebuggingEnabled(boolean); diff --git a/api/system-current.txt b/api/system-current.txt index b8202aaffa18..268d8ecc44ac 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -11399,6 +11399,7 @@ package android.content.pm { field public static final java.lang.String FEATURE_BACKUP = "android.software.backup"; field public static final java.lang.String FEATURE_BLUETOOTH = "android.hardware.bluetooth"; field public static final java.lang.String FEATURE_BLUETOOTH_LE = "android.hardware.bluetooth_le"; + field public static final java.lang.String FEATURE_BROADCAST_RADIO = "android.hardware.broadcastradio"; field public static final java.lang.String FEATURE_CAMERA = "android.hardware.camera"; field public static final java.lang.String FEATURE_CAMERA_ANY = "android.hardware.camera.any"; field public static final java.lang.String FEATURE_CAMERA_AUTOFOCUS = "android.hardware.camera.autofocus"; @@ -17316,7 +17317,10 @@ package android.hardware.radio { method public int getNumTuners(); method public java.lang.String getProduct(); method public java.lang.String getSerial(); + method public java.lang.String getServiceName(); + method public java.lang.String getVendorExension(); method public java.lang.String getVersion(); + method public boolean isBackgroundScanningSupported(); method public boolean isCaptureSupported(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.hardware.radio.RadioManager.ModuleProperties> CREATOR; @@ -17328,7 +17332,10 @@ package android.hardware.radio { method public android.hardware.radio.RadioMetadata getMetadata(); method public int getSignalStrength(); method public int getSubChannel(); + method public java.lang.String getVendorExension(); method public boolean isDigital(); + method public boolean isLive(); + method public boolean isMuted(); method public boolean isStereo(); method public boolean isTuned(); method public void writeToParcel(android.os.Parcel, int); @@ -17386,15 +17393,21 @@ package android.hardware.radio { method public abstract int getConfiguration(android.hardware.radio.RadioManager.BandConfig[]); method public abstract boolean getMute(); method public abstract int getProgramInformation(android.hardware.radio.RadioManager.ProgramInfo[]); + method public abstract java.util.List<android.hardware.radio.RadioManager.ProgramInfo> getProgramList(java.lang.String); method public abstract boolean hasControl(); + method public abstract boolean isAnalogForced(); method public abstract boolean isAntennaConnected(); method public abstract int scan(int, boolean); + method public abstract void setAnalogForced(boolean); method public abstract int setConfiguration(android.hardware.radio.RadioManager.BandConfig); method public abstract int setMute(boolean); + method public abstract boolean startBackgroundScan(); method public abstract int step(int, boolean); method public abstract int tune(int, int); field public static final int DIRECTION_DOWN = 1; // 0x1 field public static final int DIRECTION_UP = 0; // 0x0 + field public static final int ERROR_BACKGROUND_SCAN_FAILED = 6; // 0x6 + field public static final int ERROR_BACKGROUND_SCAN_UNAVAILABLE = 5; // 0x5 field public static final int ERROR_CANCELLED = 2; // 0x2 field public static final int ERROR_CONFIG = 4; // 0x4 field public static final int ERROR_HARDWARE_FAILURE = 0; // 0x0 @@ -17405,12 +17418,15 @@ package android.hardware.radio { public static abstract class RadioTuner.Callback { ctor public RadioTuner.Callback(); method public void onAntennaState(boolean); + method public void onBackgroundScanAvailabilityChange(boolean); + method public void onBackgroundScanComplete(); method public void onConfigurationChanged(android.hardware.radio.RadioManager.BandConfig); method public void onControlChanged(boolean); method public void onEmergencyAnnouncement(boolean); method public void onError(int); method public void onMetadataChanged(android.hardware.radio.RadioMetadata); method public void onProgramInfoChanged(android.hardware.radio.RadioManager.ProgramInfo); + method public void onProgramListChanged(); method public void onTrafficAnnouncement(boolean); } @@ -52207,6 +52223,9 @@ package android.webkit { public abstract class SafeBrowsingResponse { ctor public SafeBrowsingResponse(); + method public abstract void backToSafety(boolean); + method public abstract void proceed(boolean); + method public abstract void showInterstitial(boolean); } public class ServiceWorkerClient { @@ -52716,7 +52735,7 @@ package android.webkit { method public void setNetworkAvailable(boolean); method public deprecated void setPictureListener(android.webkit.WebView.PictureListener); method public void setRendererPriorityPolicy(int, boolean); - method public static void setSafeBrowsingWhiteList(java.lang.String[]); + method public static void setSafeBrowsingWhiteList(java.util.List<java.lang.String>); method public deprecated void setVerticalScrollbarOverlay(boolean); method public void setWebChromeClient(android.webkit.WebChromeClient); method public static void setWebContentsDebuggingEnabled(boolean); @@ -52918,7 +52937,7 @@ package android.webkit { method public abstract java.lang.String getDefaultUserAgent(android.content.Context); method public abstract void initSafeBrowsing(android.content.Context, android.webkit.ValueCallback<java.lang.Boolean>); method public abstract android.net.Uri[] parseFileChooserResult(int, android.content.Intent); - method public abstract void setSafeBrowsingWhiteList(java.lang.String[]); + method public abstract void setSafeBrowsingWhiteList(java.util.List<java.lang.String>); method public abstract void setWebContentsDebuggingEnabled(boolean); method public abstract void shutdownSafeBrowsing(); } diff --git a/api/test-current.txt b/api/test-current.txt index eda06b2396a7..c2a1231ee2f9 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -49026,6 +49026,9 @@ package android.webkit { public abstract class SafeBrowsingResponse { ctor public SafeBrowsingResponse(); + method public abstract void backToSafety(boolean); + method public abstract void proceed(boolean); + method public abstract void showInterstitial(boolean); } public class ServiceWorkerClient { @@ -49495,7 +49498,7 @@ package android.webkit { method public void setNetworkAvailable(boolean); method public deprecated void setPictureListener(android.webkit.WebView.PictureListener); method public void setRendererPriorityPolicy(int, boolean); - method public static void setSafeBrowsingWhiteList(java.lang.String[]); + method public static void setSafeBrowsingWhiteList(java.util.List<java.lang.String>); method public deprecated void setVerticalScrollbarOverlay(boolean); method public void setWebChromeClient(android.webkit.WebChromeClient); method public static void setWebContentsDebuggingEnabled(boolean); diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index c994e135f408..3d2e0619af92 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -658,6 +658,8 @@ public final class ActivityThread { } static final class DumpHeapData { + public boolean managed; + public boolean mallocInfo; public boolean runGc; String path; ParcelFileDescriptor fd; @@ -1025,12 +1027,15 @@ public final class ActivityThread { } @Override - public void dumpHeap(boolean managed, boolean runGc, String path, ParcelFileDescriptor fd) { + public void dumpHeap(boolean managed, boolean mallocInfo, boolean runGc, String path, + ParcelFileDescriptor fd) { DumpHeapData dhd = new DumpHeapData(); + dhd.managed = managed; + dhd.mallocInfo = mallocInfo; dhd.runGc = runGc; dhd.path = path; dhd.fd = fd; - sendMessage(H.DUMP_HEAP, dhd, managed ? 1 : 0, 0, true /*async*/); + sendMessage(H.DUMP_HEAP, dhd, 0, 0, true /*async*/); } public void attachAgent(String agent) { @@ -1762,7 +1767,7 @@ public final class ActivityThread { case SCHEDULE_CRASH: throw new RemoteServiceException((String)msg.obj); case DUMP_HEAP: - handleDumpHeap(msg.arg1 != 0, (DumpHeapData)msg.obj); + handleDumpHeap((DumpHeapData) msg.obj); break; case DUMP_ACTIVITY: handleDumpActivity((DumpComponentInfo)msg.obj); @@ -5173,13 +5178,13 @@ public final class ActivityThread { } } - static final void handleDumpHeap(boolean managed, DumpHeapData dhd) { + static void handleDumpHeap(DumpHeapData dhd) { if (dhd.runGc) { System.gc(); System.runFinalization(); System.gc(); } - if (managed) { + if (dhd.managed) { try { Debug.dumpHprofData(dhd.path, dhd.fd.getFileDescriptor()); } catch (IOException e) { @@ -5192,6 +5197,8 @@ public final class ActivityThread { Slog.w(TAG, "Failure closing profile fd", e); } } + } else if (dhd.mallocInfo) { + Debug.dumpNativeMallocInfo(dhd.fd.getFileDescriptor()); } else { Debug.dumpNativeHeap(dhd.fd.getFileDescriptor()); } diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 3be6f97504bd..df1a412d464e 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -277,8 +277,8 @@ interface IActivityManager { int checkGrantUriPermission(int callingUid, in String targetPkg, in Uri uri, int modeFlags, int userId); // Cause the specified process to dump the specified heap. - boolean dumpHeap(in String process, int userId, boolean managed, boolean runGc, in String path, - in ParcelFileDescriptor fd); + boolean dumpHeap(in String process, int userId, boolean managed, boolean mallocInfo, + boolean runGc, in String path, in ParcelFileDescriptor fd); int startActivities(in IApplicationThread caller, in String callingPackage, in Intent[] intents, in String[] resolvedTypes, in IBinder resultTo, in Bundle options, int userId); diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl index 7191fc471077..aeed7e1287d2 100644 --- a/core/java/android/app/IApplicationThread.aidl +++ b/core/java/android/app/IApplicationThread.aidl @@ -117,7 +117,8 @@ oneway interface IApplicationThread { void scheduleSuicide(); void dispatchPackageBroadcast(int cmd, in String[] packages); void scheduleCrash(in String msg); - void dumpHeap(boolean managed, boolean runGc, in String path, in ParcelFileDescriptor fd); + void dumpHeap(boolean managed, boolean mallocInfo, boolean runGc, in String path, + in ParcelFileDescriptor fd); void dumpActivity(in ParcelFileDescriptor fd, IBinder servicetoken, in String prefix, in String[] args); void clearDnsCache(); diff --git a/core/java/android/app/backup/RestoreDescription.java b/core/java/android/app/backup/RestoreDescription.java index 611ff07c8d0c..0250326e42f2 100644 --- a/core/java/android/app/backup/RestoreDescription.java +++ b/core/java/android/app/backup/RestoreDescription.java @@ -34,7 +34,7 @@ public class RestoreDescription implements Parcelable { private final String mPackageName; private final int mDataType; - private static final String NO_MORE_PACKAGES_SENTINEL = ""; + private static final String NO_MORE_PACKAGES_SENTINEL = "NO_MORE_PACKAGES"; /** * Return this constant RestoreDescription from BackupTransport.nextRestorePackage() diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index b75c39c0d350..bab39802da3f 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -782,6 +782,16 @@ public class ActivityInfo extends ComponentInfo * constant starts at the high bits. */ public static final int CONFIG_FONT_SCALE = 0x40000000; + /** + * Bit in {@link #configChanges} that indicates that the activity + * can itself handle changes to the rotation. Set from the + * {@link android.R.attr#configChanges} attribute. This is + * not a core resource configuration, but a higher-level value, so its + * constant starts at the high bits. + * @hide We do not want apps to handle this. It will eventually be moved out of + * {@link Configuration}. + */ + public static final int CONFIG_ROTATION = 0x20000000; /** @hide * Unfortunately the constants for config changes in native code are diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 827711afe8da..040f85b3c132 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -54,7 +54,6 @@ import android.os.UserHandle; import android.os.UserManager; import android.os.storage.StorageManager; import android.os.storage.VolumeInfo; -import android.provider.Settings; import android.util.AndroidException; import android.util.Log; @@ -1901,12 +1900,13 @@ public abstract class PackageManager { public static final String FEATURE_VULKAN_HARDWARE_VERSION = "android.hardware.vulkan.version"; /** - * The device includes broadcast radio tuner. - * - * @hide FutureFeature + * Feature for {@link #getSystemAvailableFeatures} and + * {@link #hasSystemFeature}: The device includes broadcast radio tuner. + * @hide */ + @SystemApi @SdkConstant(SdkConstantType.FEATURE) - public static final String FEATURE_RADIO = "android.hardware.radio"; + public static final String FEATURE_BROADCAST_RADIO = "android.hardware.broadcastradio"; /** * Feature for {@link #getSystemAvailableFeatures} and diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index eb6e0d8bd4de..426c4f26945c 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -202,6 +202,8 @@ public class PackageParser { // Temporary workaround; allow meta-data to expose components to instant apps private static final String META_DATA_INSTANT_APPS = "instantapps.clients.allowed"; + private static final String METADATA_MAX_ASPECT_RATIO = "android.max_aspect"; + /** * Bit mask of all the valid bits that can be set in recreateOnConfigChanges. * @hide @@ -3662,6 +3664,7 @@ public class PackageParser { // 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)) { if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { @@ -3839,6 +3842,10 @@ public class PackageParser { } } + // Must be ran after the entire {@link ApplicationInfo} has been fully processed and after + // every activity info has had a chance to set it from its attributes. + setMaxAspectRatio(owner); + modifySharedLibrariesForBackwardCompatibility(owner); if (hasDomainURLs(owner)) { @@ -4286,7 +4293,12 @@ public class PackageParser { a.info.flags |= FLAG_ALWAYS_FOCUSABLE; } - setActivityMaxAspectRatio(a.info, sa, owner); + if (sa.hasValue(R.styleable.AndroidManifestActivity_maxAspectRatio) + && sa.getType(R.styleable.AndroidManifestActivity_maxAspectRatio) + == TypedValue.TYPE_FLOAT) { + a.setMaxAspectRatio(sa.getFloat(R.styleable.AndroidManifestActivity_maxAspectRatio, + 0 /*default*/)); + } a.info.lockTaskLaunchMode = sa.getInt(R.styleable.AndroidManifestActivity_lockTaskMode, 0); @@ -4533,28 +4545,40 @@ public class PackageParser { } } - private void setActivityMaxAspectRatio(ActivityInfo aInfo, TypedArray sa, Package owner) { - if (aInfo.resizeMode == RESIZE_MODE_RESIZEABLE - || aInfo.resizeMode == RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION) { - // Resizeable activities can be put in any aspect ratio. - aInfo.maxAspectRatio = 0; - return; - } - + /** + * Sets every the max aspect ratio of every child activity that doesn't already have an aspect + * ratio set. + */ + private void setMaxAspectRatio(Package owner) { // Default to (1.86) 16.7:9 aspect ratio for pre-O apps and unset for O and greater. // NOTE: 16.7:9 was the max aspect ratio Android devices can support pre-O per the CDD. - float defaultMaxAspectRatio = owner.applicationInfo.targetSdkVersion < O + float maxAspectRatio = owner.applicationInfo.targetSdkVersion < O ? DEFAULT_PRE_O_MAX_ASPECT_RATIO : 0; - if (owner.applicationInfo.maxAspectRatio != 0 ) { + + if (owner.applicationInfo.maxAspectRatio != 0) { // Use the application max aspect ration as default if set. - defaultMaxAspectRatio = owner.applicationInfo.maxAspectRatio; + maxAspectRatio = owner.applicationInfo.maxAspectRatio; + } else if (owner.mAppMetaData != null + && owner.mAppMetaData.containsKey(METADATA_MAX_ASPECT_RATIO)) { + maxAspectRatio = owner.mAppMetaData.getFloat(METADATA_MAX_ASPECT_RATIO, maxAspectRatio); } - aInfo.maxAspectRatio = sa.getFloat( - R.styleable.AndroidManifestActivity_maxAspectRatio, defaultMaxAspectRatio); - if (aInfo.maxAspectRatio < 1.0f && aInfo.maxAspectRatio != 0) { - // Ignore any value lesser than 1.0. - aInfo.maxAspectRatio = 0; + for (Activity activity : owner.activities) { + // If the max aspect ratio for the activity has already been set, skip. + if (activity.hasMaxAspectRatio()) { + continue; + } + + // By default we prefer to use a values defined on the activity directly than values + // defined on the application. We do not check the styled attributes on the activity + // as it would have already been set when we processed the activity. We wait to process + // the meta data here since this method is called at the end of processing the + // application and all meta data is guaranteed. + final float activityAspectRatio = activity.metaData != null + ? activity.metaData.getFloat(METADATA_MAX_ASPECT_RATIO, maxAspectRatio) + : maxAspectRatio; + + activity.setMaxAspectRatio(activityAspectRatio); } } @@ -4696,6 +4720,7 @@ public class PackageParser { info.windowLayout = target.info.windowLayout; info.resizeMode = target.info.resizeMode; info.maxAspectRatio = target.info.maxAspectRatio; + info.encryptionAware = info.directBootAware = target.info.directBootAware; Activity a = new Activity(cachedArgs.mActivityAliasArgs, info); @@ -6980,6 +7005,11 @@ public class PackageParser { public final static class Activity extends Component<ActivityIntentInfo> implements Parcelable { public final ActivityInfo info; + private boolean mHasMaxAspectRatio; + + private boolean hasMaxAspectRatio() { + return mHasMaxAspectRatio; + } public Activity(final ParseComponentArgs args, final ActivityInfo _info) { super(args, _info); @@ -6992,6 +7022,23 @@ public class PackageParser { info.packageName = packageName; } + + private void setMaxAspectRatio(float maxAspectRatio) { + if (info.resizeMode == RESIZE_MODE_RESIZEABLE + || info.resizeMode == RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION) { + // Resizeable activities can be put in any aspect ratio. + return; + } + + if (maxAspectRatio < 1.0f && maxAspectRatio != 0) { + // Ignore any value lesser than 1.0. + return; + } + + info.maxAspectRatio = maxAspectRatio; + mHasMaxAspectRatio = true; + } + public String toString() { StringBuilder sb = new StringBuilder(128); sb.append("Activity{"); @@ -7011,11 +7058,13 @@ public class PackageParser { public void writeToParcel(Parcel dest, int flags) { super.writeToParcel(dest, flags); dest.writeParcelable(info, flags | Parcelable.PARCELABLE_ELIDE_DUPLICATES); + dest.writeBoolean(mHasMaxAspectRatio); } private Activity(Parcel in) { super(in); info = in.readParcelable(Object.class.getClassLoader()); + mHasMaxAspectRatio = in.readBoolean(); for (ActivityIntentInfo aii : intents) { aii.activity = this; diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java index ef279b86662a..68d4cd8c5f7d 100644 --- a/core/java/android/content/res/Configuration.java +++ b/core/java/android/content/res/Configuration.java @@ -1395,7 +1395,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration } if ((compareUndefined || delta.mRotation != ROTATION_UNDEFINED) && mRotation != delta.mRotation) { - changed |= ActivityInfo.CONFIG_ORIENTATION; + changed |= ActivityInfo.CONFIG_ROTATION; } if ((compareUndefined || getScreenLayoutNoDirection(delta.screenLayout) != (SCREENLAYOUT_SIZE_UNDEFINED | SCREENLAYOUT_LONG_UNDEFINED)) diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index 60226d5b6875..e173653cd961 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -19,6 +19,7 @@ package android.content.res; import android.animation.Animator; import android.animation.StateListAnimator; import android.annotation.AnimRes; +import android.annotation.AnimatorRes; import android.annotation.AnyRes; import android.annotation.ArrayRes; import android.annotation.AttrRes; @@ -1162,7 +1163,7 @@ public class Resources { * * @see #getXml */ - public XmlResourceParser getAnimation(@AnimRes int id) throws NotFoundException { + public XmlResourceParser getAnimation(@AnimatorRes @AnimRes int id) throws NotFoundException { return loadXmlResourceParser(id, "anim"); } diff --git a/core/java/android/database/ContentObserver.java b/core/java/android/database/ContentObserver.java index 4795e979f644..5f01e300bf42 100644 --- a/core/java/android/database/ContentObserver.java +++ b/core/java/android/database/ContentObserver.java @@ -193,11 +193,6 @@ public abstract class ContentObserver { */ private void dispatchChange(boolean selfChange, Uri uri, int userId) { if (mHandler == null) { - synchronized (mLock) { - if (mTransport == null) { - return; - } - } onChange(selfChange, uri, userId); } else { mHandler.post(new NotificationRunnable(selfChange, uri, userId)); @@ -218,11 +213,6 @@ public abstract class ContentObserver { @Override public void run() { - synchronized (mLock) { - if (mTransport == null) { - return; - } - } ContentObserver.this.onChange(mSelfChange, mUri, mUserId); } } diff --git a/core/java/android/hardware/location/NanoApp.java b/core/java/android/hardware/location/NanoApp.java index d5d428e95080..0465defc41ef 100644 --- a/core/java/android/hardware/location/NanoApp.java +++ b/core/java/android/hardware/location/NanoApp.java @@ -56,10 +56,10 @@ public class NanoApp { * {@link #setAppBinary(byte[])} and {@link #setAppId(long)} must be called * prior to passing this object to any managers. * - * @see #NanoApp(int, byte[]) + * @see #NanoApp(long, byte[]) */ public NanoApp() { - this(0, null); + this(0L, null); mAppIdSet = false; } diff --git a/core/java/android/hardware/radio/RadioManager.java b/core/java/android/hardware/radio/RadioManager.java index f697b89f6fdd..d25e752f8773 100644 --- a/core/java/android/hardware/radio/RadioManager.java +++ b/core/java/android/hardware/radio/RadioManager.java @@ -153,8 +153,6 @@ public class RadioManager { /** * Module service (driver) name as registered with HIDL. * @return the module service name. - * - * @hide FutureFeature */ public @NonNull String getServiceName() { return mServiceName; @@ -229,8 +227,6 @@ public class RadioManager { * * @return {@code true} if background scanning is supported (not necessary available * at a given time), {@code false} otherwise. - * - * @hide FutureFeature */ public boolean isBackgroundScanningSupported() { return mIsBgScanSupported; @@ -245,8 +241,6 @@ public class RadioManager { * * Client application MUST verify vendor/product name from the * ModuleProperties class before doing any interpretation of this value. - * - * @hide FutureFeature */ public @NonNull String getVendorExension() { return mVendorExension == null ? "" : mVendorExension; @@ -1324,8 +1318,6 @@ public class RadioManager { * {@code true} if the program is currently playing live stream. * This may result in a slightly altered reception parameters, * usually targetted at reduced latency. - * - * @hide FutureFeature */ public boolean isLive() { return (mFlags & FLAG_LIVE) != 0; @@ -1336,8 +1328,6 @@ public class RadioManager { * conditions or buffering. In this state volume knob MAY be disabled to * prevent user increasing volume too much. * It does NOT mean the user has muted audio. - * - * @hide FutureFeature */ public boolean isMuted() { return (mFlags & FLAG_MUTED) != 0; @@ -1366,8 +1356,6 @@ public class RadioManager { * * Client application MUST verify vendor/product name from the * ModuleProperties class before doing any interpretation of this value. - * - * @hide FutureFeature */ public @NonNull String getVendorExension() { return mVendorExension == null ? "" : mVendorExension; diff --git a/core/java/android/hardware/radio/RadioTuner.java b/core/java/android/hardware/radio/RadioTuner.java index 78d7b61e8d22..61e62eaabf8c 100644 --- a/core/java/android/hardware/radio/RadioTuner.java +++ b/core/java/android/hardware/radio/RadioTuner.java @@ -219,7 +219,6 @@ public abstract class RadioTuner { * is unavailable; ie. temporarily due to ongoing foreground playback in single-tuner device * or permanently if the feature is not supported * (see ModuleProperties#isBackgroundScanningSupported()). - * @hide FutureFeature */ public abstract boolean startBackgroundScan(); @@ -234,7 +233,6 @@ public abstract class RadioTuner { * @throws IllegalStateException if the scan is in progress or has not been started, * startBackgroundScan() call may fix it. * @throws IllegalArgumentException if the filter argument is not valid. - * @hide FutureFeature */ public abstract @NonNull List<RadioManager.ProgramInfo> getProgramList(@Nullable String filter); @@ -244,7 +242,6 @@ public abstract class RadioTuner { * @throws IllegalStateException if the switch is not supported at current * configuration. * @return {@code true} if analog is forced, {@code false} otherwise. - * @hide FutureFeature */ public abstract boolean isAnalogForced(); @@ -260,7 +257,6 @@ public abstract class RadioTuner { * @param isForced {@code true} to force analog, {@code false} for a default behaviour. * @throws IllegalStateException if the switch is not supported at current * configuration. - * @hide FutureFeature */ public abstract void setAnalogForced(boolean isForced); @@ -298,15 +294,9 @@ public abstract class RadioTuner { public static final int ERROR_SCAN_TIMEOUT = 3; /** The requested configuration could not be applied */ public static final int ERROR_CONFIG = 4; - /** - * Background scan was interrupted due to hardware becoming temporarily unavailable. - * @hide FutureFeature - */ + /** Background scan was interrupted due to hardware becoming temporarily unavailable. */ public static final int ERROR_BACKGROUND_SCAN_UNAVAILABLE = 5; - /** - * Background scan failed due to other error, ie. HW failure. - * @hide FutureFeature - */ + /** Background scan failed due to other error, ie. HW failure. */ public static final int ERROR_BACKGROUND_SCAN_FAILED = 6; /** @@ -373,14 +363,11 @@ public abstract class RadioTuner { * * @param isAvailable true, if the tuner turned temporarily background- * capable, false in the other case. - * @hide FutureFeature */ public void onBackgroundScanAvailabilityChange(boolean isAvailable) {} /** * Called when a background scan completes successfully. - * - * @hide FutureFeature */ public void onBackgroundScanComplete() {} @@ -388,8 +375,6 @@ public abstract class RadioTuner { * Called when available program list changed. * * Use getProgramList() to get the actual list. - * - * @hide FutureFeature */ public void onProgramListChanged() {} } diff --git a/core/java/android/net/LinkAddress.java b/core/java/android/net/LinkAddress.java index 6e74f14bd138..62de9911bc48 100644 --- a/core/java/android/net/LinkAddress.java +++ b/core/java/android/net/LinkAddress.java @@ -16,6 +16,15 @@ package android.net; +import static android.system.OsConstants.IFA_F_DADFAILED; +import static android.system.OsConstants.IFA_F_DEPRECATED; +import static android.system.OsConstants.IFA_F_OPTIMISTIC; +import static android.system.OsConstants.IFA_F_TENTATIVE; +import static android.system.OsConstants.RT_SCOPE_HOST; +import static android.system.OsConstants.RT_SCOPE_LINK; +import static android.system.OsConstants.RT_SCOPE_SITE; +import static android.system.OsConstants.RT_SCOPE_UNIVERSE; + import android.os.Parcel; import android.os.Parcelable; import android.util.Pair; @@ -26,15 +35,6 @@ import java.net.InetAddress; import java.net.InterfaceAddress; import java.net.UnknownHostException; -import static android.system.OsConstants.IFA_F_DADFAILED; -import static android.system.OsConstants.IFA_F_DEPRECATED; -import static android.system.OsConstants.IFA_F_OPTIMISTIC; -import static android.system.OsConstants.IFA_F_TENTATIVE; -import static android.system.OsConstants.RT_SCOPE_HOST; -import static android.system.OsConstants.RT_SCOPE_LINK; -import static android.system.OsConstants.RT_SCOPE_SITE; -import static android.system.OsConstants.RT_SCOPE_UNIVERSE; - /** * Identifies an IP address on a network link. * @@ -101,7 +101,7 @@ public class LinkAddress implements Parcelable { * Per RFC 4193 section 8, fc00::/7 identifies these addresses. */ private boolean isIPv6ULA() { - if (address != null && address instanceof Inet6Address) { + if (address instanceof Inet6Address) { byte[] bytes = address.getAddress(); return ((bytes[0] & (byte)0xfe) == (byte)0xfc); } diff --git a/core/java/android/net/metrics/IpManagerEvent.java b/core/java/android/net/metrics/IpManagerEvent.java index f5aea73cf2aa..a94b9284df3b 100644 --- a/core/java/android/net/metrics/IpManagerEvent.java +++ b/core/java/android/net/metrics/IpManagerEvent.java @@ -39,10 +39,12 @@ public final class IpManagerEvent implements Parcelable { public static final int ERROR_STARTING_IPV4 = 4; public static final int ERROR_STARTING_IPV6 = 5; public static final int ERROR_STARTING_IPREACHABILITYMONITOR = 6; + public static final int ERROR_INVALID_PROVISIONING = 7; @IntDef(value = { PROVISIONING_OK, PROVISIONING_FAIL, COMPLETE_LIFECYCLE, ERROR_STARTING_IPV4, ERROR_STARTING_IPV6, ERROR_STARTING_IPREACHABILITYMONITOR, + ERROR_INVALID_PROVISIONING, }) @Retention(RetentionPolicy.SOURCE) public @interface EventType {} diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java index f243f377cb56..de5974216f71 100644 --- a/core/java/android/os/Debug.java +++ b/core/java/android/os/Debug.java @@ -1814,6 +1814,13 @@ public final class Debug public static native void dumpNativeHeap(FileDescriptor fd); /** + * Writes malloc info data to the specified file descriptor. + * + * @hide + */ + public static native void dumpNativeMallocInfo(FileDescriptor fd); + + /** * Returns a count of the extant instances of a class. * * @hide diff --git a/core/java/android/os/HwBinder.java b/core/java/android/os/HwBinder.java index b09c51c50d26..866e20c26a0c 100644 --- a/core/java/android/os/HwBinder.java +++ b/core/java/android/os/HwBinder.java @@ -51,6 +51,11 @@ public abstract class HwBinder implements IHwBinder { String serviceName) throws RemoteException, NoSuchElementException; + public static native final void configureRpcThreadpool( + long maxThreads, boolean callerWillJoin); + + public static native final void joinRpcThreadpool(); + // Returns address of the "freeFunction". private static native final long native_init(); diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java index 2c6c7f96c517..1f8de044b280 100644 --- a/core/java/android/os/RecoverySystem.java +++ b/core/java/android/os/RecoverySystem.java @@ -808,7 +808,8 @@ public class RecoverySystem { HandlerThread euiccHandlerThread = new HandlerThread("euiccWipeFinishReceiverThread"); euiccHandlerThread.start(); Handler euiccHandler = new Handler(euiccHandlerThread.getLooper()); - context.registerReceiver(euiccWipeFinishReceiver, filterConsent, null, euiccHandler); + context.getApplicationContext() + .registerReceiver(euiccWipeFinishReceiver, filterConsent, null, euiccHandler); if (isWipeEuicc) { euiccManager.eraseSubscriptions(callbackIntent); } else { @@ -831,7 +832,7 @@ public class RecoverySystem { Log.e(TAG, "Timeout retaining eUICC data."); } } - context.unregisterReceiver(euiccWipeFinishReceiver); + context.getApplicationContext().unregisterReceiver(euiccWipeFinishReceiver); } catch (InterruptedException e) { Thread.currentThread().interrupt(); if (isWipeEuicc) { diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 8e3e3785f6c7..ffa38d47513f 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -6541,6 +6541,12 @@ public final class Settings { public static final String DOZE_PULSE_ON_PICK_UP = "doze_pulse_on_pick_up"; /** + * Whether the device should pulse on long press gesture. + * @hide + */ + public static final String DOZE_PULSE_ON_LONG_PRESS = "doze_pulse_on_long_press"; + + /** * Whether the device should pulse on double tap gesture. * @hide */ diff --git a/core/java/android/text/FontConfig.java b/core/java/android/text/FontConfig.java index ed583907123b..029f66e1d670 100644 --- a/core/java/android/text/FontConfig.java +++ b/core/java/android/text/FontConfig.java @@ -64,17 +64,19 @@ public final class FontConfig { private final int mWeight; private final boolean mIsItalic; private Uri mUri; + private final String mFallbackFor; /** * @hide */ public Font(@NonNull String fontName, int ttcIndex, @NonNull FontVariationAxis[] axes, - int weight, boolean isItalic) { + int weight, boolean isItalic, String fallbackFor) { mFontName = fontName; mTtcIndex = ttcIndex; mAxes = axes; mWeight = weight; mIsItalic = isItalic; + mFallbackFor = fallbackFor; } /** @@ -125,6 +127,10 @@ public final class FontConfig { public void setUri(@NonNull Uri uri) { mUri = uri; } + + public String getFallbackFor() { + return mFallbackFor; + } } /** diff --git a/core/java/android/text/format/Formatter.java b/core/java/android/text/format/Formatter.java index e5bc32bb4f0a..fc56455236a2 100644 --- a/core/java/android/text/format/Formatter.java +++ b/core/java/android/text/format/Formatter.java @@ -20,7 +20,11 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.content.res.Resources; +import android.icu.text.DecimalFormat; import android.icu.text.MeasureFormat; +import android.icu.text.NumberFormat; +import android.icu.text.UnicodeSet; +import android.icu.text.UnicodeSetSpanner; import android.icu.util.Measure; import android.icu.util.MeasureUnit; import android.net.NetworkUtils; @@ -28,6 +32,7 @@ import android.text.BidiFormatter; import android.text.TextUtils; import android.view.View; +import java.math.BigDecimal; import java.util.Locale; /** @@ -37,6 +42,8 @@ import java.util.Locale; public final class Formatter { /** {@hide} */ + public static final int FLAG_DEFAULT = 0; + /** {@hide} */ public static final int FLAG_SHORTER = 1 << 0; /** {@hide} */ public static final int FLAG_CALCULATE_ROUNDED = 1 << 1; @@ -58,7 +65,9 @@ public final class Formatter { return context.getResources().getConfiguration().getLocales().get(0); } - /* Wraps the source string in bidi formatting characters in RTL locales */ + /** + * Wraps the source string in bidi formatting characters in RTL locales. + */ private static String bidiWrap(@NonNull Context context, String source) { final Locale locale = localeFromContext(context); if (TextUtils.getLayoutDirectionFromLocale(locale) == View.LAYOUT_DIRECTION_RTL) { @@ -87,12 +96,7 @@ public final class Formatter { * @return formatted string with the number */ public static String formatFileSize(@Nullable Context context, long sizeBytes) { - if (context == null) { - return ""; - } - final BytesResult res = formatBytes(context.getResources(), sizeBytes, 0); - return bidiWrap(context, context.getString(com.android.internal.R.string.fileSizeSuffix, - res.value, res.units)); + return formatFileSize(context, sizeBytes, FLAG_DEFAULT); } /** @@ -100,88 +104,191 @@ public final class Formatter { * (showing fewer digits of precision). */ public static String formatShortFileSize(@Nullable Context context, long sizeBytes) { + return formatFileSize(context, sizeBytes, FLAG_SHORTER); + } + + private static String formatFileSize(@Nullable Context context, long sizeBytes, int flags) { if (context == null) { return ""; } - final BytesResult res = formatBytes(context.getResources(), sizeBytes, FLAG_SHORTER); - return bidiWrap(context, context.getString(com.android.internal.R.string.fileSizeSuffix, - res.value, res.units)); + final RoundedBytesResult res = RoundedBytesResult.roundBytes(sizeBytes, flags); + return bidiWrap(context, formatRoundedBytesResult(context, res)); } - /** {@hide} */ - public static BytesResult formatBytes(Resources res, long sizeBytes, int flags) { - final boolean isNegative = (sizeBytes < 0); - float result = isNegative ? -sizeBytes : sizeBytes; - int suffix = com.android.internal.R.string.byteShort; - long mult = 1; - if (result > 900) { - suffix = com.android.internal.R.string.kilobyteShort; - mult = 1000; - result = result / 1000; + private static String getSuffixOverride(@NonNull Resources res, MeasureUnit unit) { + if (unit == MeasureUnit.BYTE) { + return res.getString(com.android.internal.R.string.byteShort); + } else { // unit == PETABYTE + return res.getString(com.android.internal.R.string.petabyteShort); } - if (result > 900) { - suffix = com.android.internal.R.string.megabyteShort; - mult *= 1000; - result = result / 1000; + } + + private static NumberFormat getNumberFormatter(Locale locale, int fractionDigits) { + final NumberFormat numberFormatter = NumberFormat.getInstance(locale); + numberFormatter.setMinimumFractionDigits(fractionDigits); + numberFormatter.setMaximumFractionDigits(fractionDigits); + numberFormatter.setGroupingUsed(false); + if (numberFormatter instanceof DecimalFormat) { + // We do this only for DecimalFormat, since in the general NumberFormat case, calling + // setRoundingMode may throw an exception. + numberFormatter.setRoundingMode(BigDecimal.ROUND_HALF_UP); } - if (result > 900) { - suffix = com.android.internal.R.string.gigabyteShort; - mult *= 1000; - result = result / 1000; + return numberFormatter; + } + + private static String deleteFirstFromString(String source, String toDelete) { + final int location = source.indexOf(toDelete); + if (location == -1) { + return source; + } else { + return source.substring(0, location) + + source.substring(location + toDelete.length(), source.length()); } - if (result > 900) { - suffix = com.android.internal.R.string.terabyteShort; - mult *= 1000; - result = result / 1000; + } + + private static String formatMeasureShort(Locale locale, NumberFormat numberFormatter, + float value, MeasureUnit units) { + final MeasureFormat measureFormatter = MeasureFormat.getInstance( + locale, MeasureFormat.FormatWidth.SHORT, numberFormatter); + return measureFormatter.format(new Measure(value, units)); + } + + private static final UnicodeSetSpanner SPACES_AND_CONTROLS = + new UnicodeSetSpanner(new UnicodeSet("[[:Zs:][:Cf:]]").freeze()); + + private static String formatRoundedBytesResult( + @NonNull Context context, @NonNull RoundedBytesResult input) { + final Locale locale = localeFromContext(context); + final NumberFormat numberFormatter = getNumberFormatter(locale, input.fractionDigits); + if (input.units == MeasureUnit.BYTE || input.units == PETABYTE) { + // ICU spells out "byte" instead of "B", and can't format petabytes yet. + final String formattedNumber = numberFormatter.format(input.value); + return context.getString(com.android.internal.R.string.fileSizeSuffix, + formattedNumber, getSuffixOverride(context.getResources(), input.units)); + } else { + return formatMeasureShort(locale, numberFormatter, input.value, input.units); } - if (result > 900) { - suffix = com.android.internal.R.string.petabyteShort; - mult *= 1000; - result = result / 1000; + } + + /** {@hide} */ + public static BytesResult formatBytes(Resources res, long sizeBytes, int flags) { + final RoundedBytesResult rounded = RoundedBytesResult.roundBytes(sizeBytes, flags); + final Locale locale = res.getConfiguration().getLocales().get(0); + final NumberFormat numberFormatter = getNumberFormatter(locale, rounded.fractionDigits); + final String formattedNumber = numberFormatter.format(rounded.value); + final String units; + if (rounded.units == MeasureUnit.BYTE || rounded.units == PETABYTE) { + // ICU spells out "byte" instead of "B", and can't format petabytes yet. + units = getSuffixOverride(res, rounded.units); + } else { + // Since ICU does not give us access to the pattern, we need to extract the unit string + // from ICU, which we do by taking out the formatted number out of the formatted string + // and trimming the result of spaces and controls. + final String formattedMeasure = formatMeasureShort( + locale, numberFormatter, rounded.value, rounded.units); + final String numberRemoved = deleteFirstFromString(formattedMeasure, formattedNumber); + units = SPACES_AND_CONTROLS.trim(numberRemoved).toString(); } - // Note we calculate the rounded long by ourselves, but still let String.format() - // compute the rounded value. String.format("%f", 0.1) might not return "0.1" due to - // floating point errors. - final int roundFactor; - final String roundFormat; - if (mult == 1 || result >= 100) { - roundFactor = 1; - roundFormat = "%.0f"; - } else if (result < 1) { - roundFactor = 100; - roundFormat = "%.2f"; - } else if (result < 10) { - if ((flags & FLAG_SHORTER) != 0) { - roundFactor = 10; - roundFormat = "%.1f"; - } else { - roundFactor = 100; - roundFormat = "%.2f"; + return new BytesResult(formattedNumber, units, rounded.roundedBytes); + } + + /** + * ICU doesn't support PETABYTE yet. Fake it so that we can treat all units the same way. + * {@hide} + */ + public static final MeasureUnit PETABYTE = MeasureUnit.internalGetInstance( + "digital", "petabyte"); + + /** {@hide} */ + public static class RoundedBytesResult { + public final float value; + public final MeasureUnit units; + public final int fractionDigits; + public final long roundedBytes; + + private RoundedBytesResult( + float value, MeasureUnit units, int fractionDigits, long roundedBytes) { + this.value = value; + this.units = units; + this.fractionDigits = fractionDigits; + this.roundedBytes = roundedBytes; + } + + /** + * Returns a RoundedBytesResult object based on the input size in bytes and the rounding + * flags. The result can be used for formatting. + */ + public static RoundedBytesResult roundBytes(long sizeBytes, int flags) { + final boolean isNegative = (sizeBytes < 0); + float result = isNegative ? -sizeBytes : sizeBytes; + MeasureUnit units = MeasureUnit.BYTE; + long mult = 1; + if (result > 900) { + units = MeasureUnit.KILOBYTE; + mult = 1000; + result = result / 1000; + } + if (result > 900) { + units = MeasureUnit.MEGABYTE; + mult *= 1000; + result = result / 1000; + } + if (result > 900) { + units = MeasureUnit.GIGABYTE; + mult *= 1000; + result = result / 1000; + } + if (result > 900) { + units = MeasureUnit.TERABYTE; + mult *= 1000; + result = result / 1000; } - } else { // 10 <= result < 100 - if ((flags & FLAG_SHORTER) != 0) { + if (result > 900) { + units = PETABYTE; + mult *= 1000; + result = result / 1000; + } + // Note we calculate the rounded long by ourselves, but still let NumberFormat compute + // the rounded value. NumberFormat.format(0.1) might not return "0.1" due to floating + // point errors. + final int roundFactor; + final int roundDigits; + if (mult == 1 || result >= 100) { roundFactor = 1; - roundFormat = "%.0f"; - } else { + roundDigits = 0; + } else if (result < 1) { roundFactor = 100; - roundFormat = "%.2f"; + roundDigits = 2; + } else if (result < 10) { + if ((flags & FLAG_SHORTER) != 0) { + roundFactor = 10; + roundDigits = 1; + } else { + roundFactor = 100; + roundDigits = 2; + } + } else { // 10 <= result < 100 + if ((flags & FLAG_SHORTER) != 0) { + roundFactor = 1; + roundDigits = 0; + } else { + roundFactor = 100; + roundDigits = 2; + } } - } - - if (isNegative) { - result = -result; - } - final String roundedString = String.format(roundFormat, result); - // Note this might overflow if abs(result) >= Long.MAX_VALUE / 100, but that's like 80PB so - // it's okay (for now)... - final long roundedBytes = - (flags & FLAG_CALCULATE_ROUNDED) == 0 ? 0 - : (((long) Math.round(result * roundFactor)) * mult / roundFactor); + if (isNegative) { + result = -result; + } - final String units = res.getString(suffix); + // Note this might overflow if abs(result) >= Long.MAX_VALUE / 100, but that's like + // 80PB so it's okay (for now)... + final long roundedBytes = + (flags & FLAG_CALCULATE_ROUNDED) == 0 ? 0 + : (((long) Math.round(result * roundFactor)) * mult / roundFactor); - return new BytesResult(roundedString, units, roundedBytes); + return new RoundedBytesResult(result, units, roundDigits, roundedBytes); + } } /** diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index 7538f6561c1e..ba9e05c2d870 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -150,6 +150,11 @@ public interface WindowManagerPolicy { public final static int PRESENCE_INTERNAL = 1 << 0; public final static int PRESENCE_EXTERNAL = 1 << 1; + // Navigation bar position values + int NAV_BAR_LEFT = 1 << 0; + int NAV_BAR_RIGHT = 1 << 1; + int NAV_BAR_BOTTOM = 1 << 2; + public final static boolean WATCH_POINTER = false; /** @@ -1676,6 +1681,14 @@ public interface WindowManagerPolicy { public boolean isNavBarForcedShownLw(WindowState win); /** + * @return The side of the screen where navigation bar is positioned. + * @see #NAV_BAR_LEFT + * @see #NAV_BAR_RIGHT + * @see #NAV_BAR_BOTTOM + */ + int getNavBarPosition(); + + /** * Calculates the insets for the areas that could never be removed in Honeycomb, i.e. system * bar or button bar. See {@link #getNonDecorDisplayWidth}. * diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java index b6dd0b948739..d1e0ae5917f4 100644 --- a/core/java/android/view/textclassifier/TextClassification.java +++ b/core/java/android/view/textclassifier/TextClassification.java @@ -50,11 +50,11 @@ public final class TextClassification { private int mLogType; private TextClassification( - @NonNull String text, - Drawable icon, - String label, - Intent intent, - OnClickListener onClickListener, + @Nullable String text, + @Nullable Drawable icon, + @Nullable String label, + @Nullable Intent intent, + @Nullable OnClickListener onClickListener, @NonNull EntityConfidence<String> entityConfidence, int logType) { mText = text; @@ -70,7 +70,7 @@ public final class TextClassification { /** * Gets the classified text. */ - @NonNull + @Nullable public String getText() { return mText; } @@ -183,8 +183,8 @@ public final class TextClassification { /** * Sets the classified text. */ - public Builder setText(@NonNull String text) { - mText = Preconditions.checkNotNull(text); + public Builder setText(@Nullable String text) { + mText = text; return this; } @@ -197,7 +197,7 @@ public final class TextClassification { */ public Builder setEntityType( @NonNull @EntityType String type, - @FloatRange(from = 0.0, to = 1.0)float confidenceScore) { + @FloatRange(from = 0.0, to = 1.0) float confidenceScore) { mEntityConfidence.setEntityType(type, confidenceScore); return this; } diff --git a/core/java/android/webkit/SafeBrowsingResponse.java b/core/java/android/webkit/SafeBrowsingResponse.java index dc29d423c56c..3540f80a2bca 100644 --- a/core/java/android/webkit/SafeBrowsingResponse.java +++ b/core/java/android/webkit/SafeBrowsingResponse.java @@ -29,19 +29,19 @@ public abstract class SafeBrowsingResponse { * * @param allowReporting True if the interstitial should show a reporting checkbox. */ - abstract void showInterstitial(boolean allowReporting); + public abstract void showInterstitial(boolean allowReporting); /** * Act as if the user clicked "visit this unsafe site." * * @param report True to enable Safe Browsing reporting. */ - abstract void proceed(boolean report); + public abstract void proceed(boolean report); /** * Act as if the user clicked "back to safety." * * @param report True to enable Safe Browsing reporting. */ - abstract void backToSafety(boolean report); + public abstract void backToSafety(boolean report); } diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 5fed925dd956..ed2547fa30b2 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -67,6 +67,7 @@ import java.io.BufferedWriter; import java.io.File; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.List; import java.util.Map; /** @@ -1621,8 +1622,9 @@ public class WebView extends AbsoluteLayout } /** - * Starts Safe Browsing initialization. This should only be called once. - * @param context is the activity context the WebView will be used in. + * Starts Safe Browsing initialization. This should only be called once. This does not require + * an Activity Context, and will always use the application Context to do its work. + * @param context Application Context. * @param callback will be called with the value true if initialization is * successful. The callback will be run on the UI thread. */ @@ -1644,7 +1646,7 @@ public class WebView extends AbsoluteLayout * * @param urls the list of URLs */ - public static void setSafeBrowsingWhiteList(@Nullable String[] urls) { + public static void setSafeBrowsingWhiteList(@Nullable List<String> urls) { getFactory().getStatics().setSafeBrowsingWhiteList(urls); } diff --git a/core/java/android/webkit/WebViewFactoryProvider.java b/core/java/android/webkit/WebViewFactoryProvider.java index 7c938ae5bb70..9b31a0c7462d 100644 --- a/core/java/android/webkit/WebViewFactoryProvider.java +++ b/core/java/android/webkit/WebViewFactoryProvider.java @@ -21,6 +21,8 @@ import android.content.Context; import android.content.Intent; import android.net.Uri; +import java.util.List; + /** * This is the main entry-point into the WebView back end implementations, which the WebView * proxy class uses to instantiate all the other objects as needed. The backend must provide an @@ -89,9 +91,9 @@ public interface WebViewFactoryProvider { /** * Implement the API method - * {@link android.webkit.WebView#setSafeBrowsingWhiteList(String[])} + * {@link android.webkit.WebView#setSafeBrowsingWhiteList(List<String>)} */ - void setSafeBrowsingWhiteList(String[] urls); + void setSafeBrowsingWhiteList(List<String> urls); } Statics getStatics(); diff --git a/core/java/com/android/internal/app/ResolverComparator.java b/core/java/com/android/internal/app/ResolverComparator.java index a0f58a99f625..eda63b3da5b7 100644 --- a/core/java/com/android/internal/app/ResolverComparator.java +++ b/core/java/com/android/internal/app/ResolverComparator.java @@ -30,6 +30,7 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.content.SharedPreferences; import android.content.ServiceConnection; +import android.metrics.LogMaker; import android.os.Environment; import android.os.Handler; import android.os.IBinder; @@ -46,6 +47,8 @@ import android.text.TextUtils; import android.util.ArrayMap; import android.util.Log; import com.android.internal.app.ResolverActivity.ResolvedComponentInfo; +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import java.io.File; import java.lang.InterruptedException; @@ -99,6 +102,8 @@ class ResolverComparator implements Comparator<ResolvedComponentInfo> { private String mContentType; private String[] mAnnotations; private String mAction; + private ComponentName mResolvedRankerName; + private ComponentName mRankerServiceName; private IResolverRankerService mRanker; private ResolverRankerServiceConnection mConnection; private AfterCompute mAfterCompute; @@ -119,9 +124,17 @@ class ResolverComparator implements Comparator<ResolvedComponentInfo> { if (receivedTargets != null && mTargets != null && receivedTargets.size() == mTargets.size()) { final int size = mTargets.size(); + boolean isUpdated = false; for (int i = 0; i < size; ++i) { - mTargets.get(i).setSelectProbability( - receivedTargets.get(i).getSelectProbability()); + final float predictedProb = + receivedTargets.get(i).getSelectProbability(); + if (predictedProb != mTargets.get(i).getSelectProbability()) { + mTargets.get(i).setSelectProbability(predictedProb); + isUpdated = true; + } + } + if (isUpdated) { + mRankerServiceName = mResolvedRankerName; } } else { Log.e(TAG, "Sizes of sent and received ResolverTargets diff."); @@ -170,6 +183,7 @@ class ResolverComparator implements Comparator<ResolvedComponentInfo> { mContentType = intent.getType(); getContentAnnotations(intent); mAction = intent.getAction(); + mRankerServiceName = new ComponentName(mContext, this.getClass()); } // get annotations of content from intent. @@ -361,6 +375,7 @@ class ResolverComparator implements Comparator<ResolvedComponentInfo> { try { int selectedPos = new ArrayList<ComponentName>(mTargetsDict.keySet()) .indexOf(componentName); + logMetrics(selectedPos); if (selectedPos > 0) { mRanker.train(mTargets, selectedPos); } else { @@ -392,6 +407,19 @@ class ResolverComparator implements Comparator<ResolvedComponentInfo> { } } + // records metrics for evaluation. + private void logMetrics(int selectedPos) { + if (mRankerServiceName != null) { + MetricsLogger metricsLogger = new MetricsLogger(); + LogMaker log = new LogMaker(MetricsEvent.ACTION_TARGET_SELECTED); + log.setComponentName(mRankerServiceName); + int isCategoryUsed = (mAnnotations == null) ? 0 : 1; + log.addTaggedData(MetricsEvent.FIELD_IS_CATEGORY_USED, isCategoryUsed); + log.addTaggedData(MetricsEvent.FIELD_RANKED_POSITION, selectedPos); + metricsLogger.write(log); + } + } + // connect to a ranking service. private void initRanker(Context context) { synchronized (mLock) { @@ -454,6 +482,7 @@ class ResolverComparator implements Comparator<ResolvedComponentInfo> { if (DEBUG) { Log.d(TAG, "Succeeded to retrieve a ranker: " + componentName); } + mResolvedRankerName = componentName; intent.setComponent(componentName); return intent; } @@ -523,6 +552,8 @@ class ResolverComparator implements Comparator<ResolvedComponentInfo> { private void reset() { mTargetsDict.clear(); mTargets = null; + mRankerServiceName = new ComponentName(mContext, this.getClass()); + mResolvedRankerName = null; startWatchDog(WATCHDOG_TIMEOUT_MILLIS); initRanker(mContext); } diff --git a/core/java/com/android/internal/colorextraction/ColorExtractor.java b/core/java/com/android/internal/colorextraction/ColorExtractor.java index 2608698f5b63..04819a5999eb 100644 --- a/core/java/com/android/internal/colorextraction/ColorExtractor.java +++ b/core/java/com/android/internal/colorextraction/ColorExtractor.java @@ -43,10 +43,6 @@ public class ColorExtractor implements WallpaperManager.OnColorsChangedListener private static final String TAG = "ColorExtractor"; - public static final int FALLBACK_COLOR = 0xff83888d; - - private int mMainFallbackColor = FALLBACK_COLOR; - private int mSecondaryFallbackColor = FALLBACK_COLOR; private final SparseArray<GradientColors[]> mGradientColors; private final ArrayList<OnColorsChangedListener> mOnColorsChangedListeners; private final Context mContext; @@ -73,6 +69,9 @@ public class ColorExtractor implements WallpaperManager.OnColorsChangedListener } mOnColorsChangedListeners = new ArrayList<>(); + GradientColors[] systemColors = mGradientColors.get(WallpaperManager.FLAG_SYSTEM); + GradientColors[] lockColors = mGradientColors.get(WallpaperManager.FLAG_LOCK); + WallpaperManager wallpaperManager = mContext.getSystemService(WallpaperManager.class); if (wallpaperManager == null) { Log.w(TAG, "Can't listen to color changes!"); @@ -83,23 +82,18 @@ public class ColorExtractor implements WallpaperManager.OnColorsChangedListener Trace.beginSection("ColorExtractor#getWallpaperColors"); mSystemColors = wallpaperManager.getWallpaperColors(WallpaperManager.FLAG_SYSTEM); mLockColors = wallpaperManager.getWallpaperColors(WallpaperManager.FLAG_LOCK); - - GradientColors[] systemColors = mGradientColors.get( - WallpaperManager.FLAG_SYSTEM); - extractInto(mSystemColors, - systemColors[TYPE_NORMAL], - systemColors[TYPE_DARK], - systemColors[TYPE_EXTRA_DARK]); - - GradientColors[] lockColors = mGradientColors.get(WallpaperManager.FLAG_LOCK); - extractInto(mLockColors, - lockColors[TYPE_NORMAL], - lockColors[TYPE_DARK], - lockColors[TYPE_EXTRA_DARK]); - triggerColorsChanged(WallpaperManager.FLAG_SYSTEM - | WallpaperManager.FLAG_LOCK); Trace.endSection(); } + + // Initialize all gradients with the current colors + extractInto(mSystemColors, + systemColors[TYPE_NORMAL], + systemColors[TYPE_DARK], + systemColors[TYPE_EXTRA_DARK]); + extractInto(mLockColors, + lockColors[TYPE_NORMAL], + lockColors[TYPE_DARK], + lockColors[TYPE_EXTRA_DARK]); } /** @@ -181,25 +175,8 @@ public class ColorExtractor implements WallpaperManager.OnColorsChangedListener private void extractInto(WallpaperColors inWallpaperColors, GradientColors outGradientColorsNormal, GradientColors outGradientColorsDark, GradientColors outGradientColorsExtraDark) { - if (inWallpaperColors == null) { - applyFallback(outGradientColorsNormal); - applyFallback(outGradientColorsDark); - applyFallback(outGradientColorsExtraDark); - return; - } - boolean success = mExtractionType.extractInto(inWallpaperColors, outGradientColorsNormal, + mExtractionType.extractInto(inWallpaperColors, outGradientColorsNormal, outGradientColorsDark, outGradientColorsExtraDark); - if (!success) { - applyFallback(outGradientColorsNormal); - applyFallback(outGradientColorsDark); - applyFallback(outGradientColorsExtraDark); - } - } - - private void applyFallback(GradientColors outGradientColors) { - outGradientColors.setMainColor(mMainFallbackColor); - outGradientColors.setSecondaryColor(mSecondaryFallbackColor); - outGradientColors.setSupportsDarkText(false); } public void destroy() { @@ -218,8 +195,8 @@ public class ColorExtractor implements WallpaperManager.OnColorsChangedListener } public static class GradientColors { - private int mMainColor = FALLBACK_COLOR; - private int mSecondaryColor = FALLBACK_COLOR; + private int mMainColor; + private int mSecondaryColor; private boolean mSupportsDarkText; public void setMainColor(int mainColor) { diff --git a/core/java/com/android/internal/colorextraction/types/ExtractionType.java b/core/java/com/android/internal/colorextraction/types/ExtractionType.java index 762b54fb1e6b..7000e798f87b 100644 --- a/core/java/com/android/internal/colorextraction/types/ExtractionType.java +++ b/core/java/com/android/internal/colorextraction/types/ExtractionType.java @@ -38,9 +38,8 @@ public interface ExtractionType { * @param outGradientColorsNormal object that should receive normal colors * @param outGradientColorsDark object that should receive dark colors * @param outGradientColorsExtraDark object that should receive extra dark colors - * @return true if successful. */ - boolean extractInto(WallpaperColors inWallpaperColors, + void extractInto(WallpaperColors inWallpaperColors, ColorExtractor.GradientColors outGradientColorsNormal, ColorExtractor.GradientColors outGradientColorsDark, ColorExtractor.GradientColors outGradientColorsExtraDark); diff --git a/core/java/com/android/internal/colorextraction/types/Tonal.java b/core/java/com/android/internal/colorextraction/types/Tonal.java index b8ebe3000d8e..e78ca3844bed 100644 --- a/core/java/com/android/internal/colorextraction/types/Tonal.java +++ b/core/java/com/android/internal/colorextraction/types/Tonal.java @@ -44,24 +44,54 @@ public class Tonal implements ExtractionType { private static final boolean DEBUG = true; + public static final int MAIN_COLOR_LIGHT = 0xffe0e0e0; + public static final int SECONDARY_COLOR_LIGHT = 0xff9e9e9e; + public static final int MAIN_COLOR_DARK = 0xff212121; + public static final int SECONDARY_COLOR_DARK = 0xff000000; + // Temporary variable to avoid allocations private float[] mTmpHSL = new float[3]; /** - * Grab colors from WallpaperColors as set them into GradientColors + * Grab colors from WallpaperColors and set them into GradientColors. + * Also applies the default gradient in case extraction fails. + * + * @param inWallpaperColors Input. + * @param outColorsNormal Colors for normal theme. + * @param outColorsDark Colors for dar theme. + * @param outColorsExtraDark Colors for extra dark theme. + */ + public void extractInto(@Nullable WallpaperColors inWallpaperColors, + @NonNull GradientColors outColorsNormal, @NonNull GradientColors outColorsDark, + @NonNull GradientColors outColorsExtraDark) { + boolean success = runTonalExtraction(inWallpaperColors, outColorsNormal, outColorsDark, + outColorsExtraDark); + if (!success) { + applyFallback(inWallpaperColors, outColorsNormal, outColorsDark, outColorsExtraDark); + } + } + + /** + * Grab colors from WallpaperColors and set them into GradientColors. * - * @param inWallpaperColors input - * @param outColorsNormal colors for normal theme - * @param outColorsDark colors for dar theme - * @param outColorsExtraDark colors for extra dark theme - * @return true if successful + * @param inWallpaperColors Input. + * @param outColorsNormal Colors for normal theme. + * @param outColorsDark Colors for dar theme. + * @param outColorsExtraDark Colors for extra dark theme. + * @return True if succeeded or false if failed. */ - public boolean extractInto(@NonNull WallpaperColors inWallpaperColors, + private boolean runTonalExtraction(@Nullable WallpaperColors inWallpaperColors, @NonNull GradientColors outColorsNormal, @NonNull GradientColors outColorsDark, @NonNull GradientColors outColorsExtraDark) { + if (inWallpaperColors == null) { + return false; + } + final List<Color> mainColors = inWallpaperColors.getMainColors(); final int mainColorsSize = mainColors.size(); + final boolean supportsDarkText = (inWallpaperColors.getColorHints() & + WallpaperColors.HINT_SUPPORTS_DARK_TEXT) != 0; if (mainColorsSize == 0) { return false; @@ -120,7 +150,6 @@ public class Tonal implements ExtractionType { float[] s = fit(palette.s, hsl[1], fitIndex, 0.0f, 1.0f); float[] l = fit(palette.l, hsl[2], fitIndex, 0.0f, 1.0f); - final int textInversionIndex = h.length - 3; if (DEBUG) { StringBuilder builder = new StringBuilder("Tonal Palette - index: " + fitIndex + ". Main color: " + Integer.toHexString(getColorInt(fitIndex, h, s, l)) + @@ -135,21 +164,38 @@ public class Tonal implements ExtractionType { Log.d(TAG, builder.toString()); } + int primaryIndex = fitIndex; + int mainColor = getColorInt(primaryIndex, h, s, l); + + // We might want use the fallback in case the extracted color is brighter than our + // light fallback or darker than our dark fallback. + ColorUtils.colorToHSL(mainColor, mTmpHSL); + final float mainLuminosity = mTmpHSL[2]; + ColorUtils.colorToHSL(MAIN_COLOR_LIGHT, mTmpHSL); + final float lightLuminosity = mTmpHSL[2]; + if (mainLuminosity > lightLuminosity) { + return false; + } + ColorUtils.colorToHSL(MAIN_COLOR_DARK, mTmpHSL); + final float darkLuminosity = mTmpHSL[2]; + if (mainLuminosity < darkLuminosity) { + return false; + } + // Normal colors: // best fit + a 2 colors offset - int primaryIndex = fitIndex; + outColorsNormal.setMainColor(mainColor); int secondaryIndex = primaryIndex + (primaryIndex >= 2 ? -2 : 2); - outColorsNormal.setMainColor(getColorInt(primaryIndex, h, s, l)); outColorsNormal.setSecondaryColor(getColorInt(secondaryIndex, h, s, l)); // Dark colors: // Stops at 4th color, only lighter if dark text is supported - if (fitIndex < 2) { + if (supportsDarkText) { + primaryIndex = h.length - 1; + } else if (fitIndex < 2) { primaryIndex = 0; - } else if (fitIndex < textInversionIndex) { - primaryIndex = Math.min(fitIndex, 3); } else { - primaryIndex = h.length - 1; + primaryIndex = Math.min(fitIndex, 3); } secondaryIndex = primaryIndex + (primaryIndex >= 2 ? -2 : 2); outColorsDark.setMainColor(getColorInt(primaryIndex, h, s, l)); @@ -157,18 +203,17 @@ public class Tonal implements ExtractionType { // Extra Dark: // Stay close to dark colors until dark text is supported - if (fitIndex < 2) { + if (supportsDarkText) { + primaryIndex = h.length - 1; + } else if (fitIndex < 2) { primaryIndex = 0; - } else if (fitIndex < textInversionIndex) { - primaryIndex = 2; } else { - primaryIndex = h.length - 1; + primaryIndex = 2; } secondaryIndex = primaryIndex + (primaryIndex >= 2 ? -2 : 2); outColorsExtraDark.setMainColor(getColorInt(primaryIndex, h, s, l)); outColorsExtraDark.setSecondaryColor(getColorInt(secondaryIndex, h, s, l)); - final boolean supportsDarkText = fitIndex >= textInversionIndex; outColorsNormal.setSupportsDarkText(supportsDarkText); outColorsDark.setSupportsDarkText(supportsDarkText); outColorsExtraDark.setSupportsDarkText(supportsDarkText); @@ -181,6 +226,33 @@ public class Tonal implements ExtractionType { return true; } + private void applyFallback(@Nullable WallpaperColors inWallpaperColors, + GradientColors outColorsNormal, GradientColors outColorsDark, + GradientColors outColorsExtraDark) { + applyFallback(inWallpaperColors, outColorsNormal); + applyFallback(inWallpaperColors, outColorsDark); + applyFallback(inWallpaperColors, outColorsExtraDark); + } + + /** + * Sets the gradient to the light or dark fallbacks based on the current wallpaper colors. + * + * @param inWallpaperColors Colors to read. + * @param outGradientColors Destination. + */ + public static void applyFallback(@Nullable WallpaperColors inWallpaperColors, + @NonNull GradientColors outGradientColors) { + boolean light = inWallpaperColors != null + && (inWallpaperColors.getColorHints() & WallpaperColors.HINT_SUPPORTS_DARK_TEXT) + != 0; + int innerColor = light ? MAIN_COLOR_LIGHT : MAIN_COLOR_DARK; + int outerColor = light ? SECONDARY_COLOR_LIGHT : SECONDARY_COLOR_DARK; + + outGradientColors.setMainColor(innerColor); + outGradientColors.setSecondaryColor(outerColor); + outGradientColors.setSupportsDarkText(light); + } + private int getColorInt(int fitIndex, float[] h, float[] s, float[] l) { mTmpHSL[0] = fract(h[fitIndex]) * 360.0f; mTmpHSL[1] = s[fitIndex]; diff --git a/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java b/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java index 445a82c4b4f8..df9c27b9df14 100644 --- a/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java +++ b/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java @@ -36,6 +36,7 @@ public class AmbientDisplayConfiguration { return pulseOnNotificationEnabled(user) || pulseOnPickupEnabled(user) || pulseOnDoubleTapEnabled(user) + || pulseOnLongPressEnabled(user) || alwaysOnEnabled(user); } @@ -79,6 +80,19 @@ public class AmbientDisplayConfiguration { return mContext.getResources().getString(R.string.config_dozeDoubleTapSensorType); } + public String longPressSensorType() { + return mContext.getResources().getString(R.string.config_dozeLongPressSensorType); + } + + public boolean pulseOnLongPressEnabled(int user) { + return pulseOnLongPressAvailable() && boolSettingDefaultOff( + Settings.Secure.DOZE_PULSE_ON_LONG_PRESS, user); + } + + private boolean pulseOnLongPressAvailable() { + return !TextUtils.isEmpty(longPressSensorType()); + } + public boolean alwaysOnEnabled(int user) { return boolSettingDefaultOn(Settings.Secure.DOZE_ALWAYS_ON, user) && alwaysOnAvailable(); diff --git a/core/java/com/android/server/BootReceiver.java b/core/java/com/android/server/BootReceiver.java index 2cf58d7ac465..4f9e8a51b9a4 100644 --- a/core/java/com/android/server/BootReceiver.java +++ b/core/java/com/android/server/BootReceiver.java @@ -143,7 +143,6 @@ public class BootReceiver extends BroadcastReceiver { try { return FileUtils.readTextFile(lastHeaderFile, 0, null); } catch (IOException e) { - Slog.e(TAG, "Error reading " + lastHeaderFile, e); return null; } } diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp index 76140c046611..16a923098a27 100644 --- a/core/jni/android_os_Debug.cpp +++ b/core/jni/android_os_Debug.cpp @@ -48,10 +48,14 @@ namespace android { -using UniqueFile = std::unique_ptr<FILE, decltype(&fclose)>; +static void safeFclose(FILE* fp) { + if (fp) fclose(fp); +} + +using UniqueFile = std::unique_ptr<FILE, decltype(&safeFclose)>; static inline UniqueFile MakeUniqueFile(const char* path, const char* mode) { - return UniqueFile(fopen(path, mode), fclose); + return UniqueFile(fopen(path, mode), safeFclose); } enum { @@ -972,21 +976,16 @@ static void dumpNativeHeap(FILE* fp) fprintf(fp, "END\n"); } -/* - * Dump the native heap, writing human-readable output to the specified - * file descriptor. - */ -static void android_os_Debug_dumpNativeHeap(JNIEnv* env, jobject clazz, - jobject fileDescriptor) +static bool openFile(JNIEnv* env, jobject fileDescriptor, UniqueFile& fp) { if (fileDescriptor == NULL) { jniThrowNullPointerException(env, "fd == null"); - return; + return false; } int origFd = jniGetFDFromFileDescriptor(env, fileDescriptor); if (origFd < 0) { jniThrowRuntimeException(env, "Invalid file descriptor"); - return; + return false; } /* dup() the descriptor so we don't close the original with fclose() */ @@ -994,14 +993,28 @@ static void android_os_Debug_dumpNativeHeap(JNIEnv* env, jobject clazz, if (fd < 0) { ALOGW("dup(%d) failed: %s\n", origFd, strerror(errno)); jniThrowRuntimeException(env, "dup() failed"); - return; + return false; } - UniqueFile fp(fdopen(fd, "w"), fclose); + fp.reset(fdopen(fd, "w")); if (fp == nullptr) { ALOGW("fdopen(%d) failed: %s\n", fd, strerror(errno)); close(fd); jniThrowRuntimeException(env, "fdopen() failed"); + return false; + } + return true; +} + +/* + * Dump the native heap, writing human-readable output to the specified + * file descriptor. + */ +static void android_os_Debug_dumpNativeHeap(JNIEnv* env, jobject, + jobject fileDescriptor) +{ + UniqueFile fp(nullptr, safeFclose); + if (!openFile(env, fileDescriptor, fp)) { return; } @@ -1010,6 +1023,21 @@ static void android_os_Debug_dumpNativeHeap(JNIEnv* env, jobject clazz, ALOGD("Native heap dump complete.\n"); } +/* + * Dump the native malloc info, writing xml output to the specified + * file descriptor. + */ +static void android_os_Debug_dumpNativeMallocInfo(JNIEnv* env, jobject, + jobject fileDescriptor) +{ + UniqueFile fp(nullptr, safeFclose); + if (!openFile(env, fileDescriptor, fp)) { + return; + } + + malloc_info(0, fp.get()); +} + static bool dumpTraces(JNIEnv* env, jint pid, jstring fileName, jint timeoutSecs, DebuggerdDumpType dumpType) { const ScopedUtfChars fileNameChars(env, fileName); @@ -1070,6 +1098,8 @@ static const JNINativeMethod gMethods[] = { (void*) android_os_Debug_getMemInfo }, { "dumpNativeHeap", "(Ljava/io/FileDescriptor;)V", (void*) android_os_Debug_dumpNativeHeap }, + { "dumpNativeMallocInfo", "(Ljava/io/FileDescriptor;)V", + (void*) android_os_Debug_dumpNativeMallocInfo }, { "getBinderSentTransactions", "()I", (void*) android_os_Debug_getBinderSentTransactions }, { "getBinderReceivedTransactions", "()I", diff --git a/core/jni/android_os_HwBinder.cpp b/core/jni/android_os_HwBinder.cpp index 6c6fa66c422e..566312594d0b 100644 --- a/core/jni/android_os_HwBinder.cpp +++ b/core/jni/android_os_HwBinder.cpp @@ -42,6 +42,8 @@ using android::AndroidRuntime; using android::hardware::hidl_vec; using android::hardware::hidl_string; +using android::hardware::IPCThreadState; +using android::hardware::ProcessState; template<typename T> using Return = android::hardware::Return<T>; @@ -395,6 +397,15 @@ static jobject JHwBinder_native_getService( return JHwRemoteBinder::NewObject(env, service); } +void JHwBinder_native_configureRpcThreadpool(jlong maxThreads, jboolean callerWillJoin) { + CHECK(maxThreads > 0); + ProcessState::self()->setThreadPoolConfiguration(maxThreads, callerWillJoin /*callerJoinsPool*/); +} + +void JHwBinder_native_joinRpcThreadpool() { + IPCThreadState::self()->joinThreadPool(); +} + static JNINativeMethod gMethods[] = { { "native_init", "()J", (void *)JHwBinder_native_init }, { "native_setup", "()V", (void *)JHwBinder_native_setup }, @@ -408,6 +419,12 @@ static JNINativeMethod gMethods[] = { { "getService", "(Ljava/lang/String;Ljava/lang/String;)L" PACKAGE_PATH "/IHwBinder;", (void *)JHwBinder_native_getService }, + + { "configureRpcThreadpool", "(JZ)V", + (void *)JHwBinder_native_configureRpcThreadpool }, + + { "joinRpcThreadpool", "()V", + (void *)JHwBinder_native_joinRpcThreadpool }, }; namespace android { diff --git a/core/jni/android_text_AndroidBidi.cpp b/core/jni/android_text_AndroidBidi.cpp index d744b7c989ea..8b7157261ea2 100644 --- a/core/jni/android_text_AndroidBidi.cpp +++ b/core/jni/android_text_AndroidBidi.cpp @@ -42,7 +42,7 @@ static jint runBidi(JNIEnv* env, jobject obj, jint dir, jcharArray chsArray, // Set callbacks to override bidi classes of new emoji ubidi_setClassCallback( bidi, minikin::emojiBidiOverride, nullptr, nullptr, nullptr, &status); - ubidi_setPara(bidi, chs, n, dir, NULL, &status); + ubidi_setPara(bidi, reinterpret_cast<const UChar*>(chs), n, dir, NULL, &status); if (U_SUCCESS(status)) { for (int i = 0; i < n; ++i) { info[i] = ubidi_getLevelAt(bidi, i); diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml index 186cbbf3837e..1c1ec2565254 100644 --- a/core/res/res/values-bs/strings.xml +++ b/core/res/res/values-bs/strings.xml @@ -1509,7 +1509,7 @@ <string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Koristi prečicu"</string> <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Prečica za pristupačnost je uključila uslugu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Prečica za pristupačnost je isključila uslugu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> - <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Izaberite funkciju koja će se koristiti kada dodirnete dugme Pristupačnost:"</string> + <string name="accessibility_button_prompt_text" msgid="4234556536456854251">"Odaberite funkciju koja će se koristiti kada dodirnete dugme Pristupačnost:"</string> <string name="accessibility_button_instructional_text" msgid="6942300463612999993">"Da promijenite funkcije, dodirnite i držite dugme Pristupačnost."</string> <string name="accessibility_magnification_chooser_text" msgid="1227146738764986237">"Uvećanje"</string> <string name="user_switched" msgid="3768006783166984410">"Trenutni korisnik <xliff:g id="NAME">%1$s</xliff:g>."</string> @@ -1736,7 +1736,7 @@ <string name="user_creation_adding" msgid="4482658054622099197">"Da li dozvoljavate da <xliff:g id="APP">%1$s</xliff:g> kreira novog korisnika za <xliff:g id="ACCOUNT">%2$s</xliff:g> (Korisnik sa ovim nalogom već postoji)?"</string> <string name="language_selection_title" msgid="2680677278159281088">"Dodaj jezik"</string> <string name="country_selection_title" msgid="2954859441620215513">"Izbor regije"</string> - <string name="search_language_hint" msgid="7042102592055108574">"Ukucajte ime jezika"</string> + <string name="search_language_hint" msgid="7042102592055108574">"Upišite ime jezika"</string> <string name="language_picker_section_suggested" msgid="8414489646861640885">"Predloženo"</string> <string name="language_picker_section_all" msgid="3097279199511617537">"Svi jezici"</string> <string name="region_picker_section_all" msgid="8966316787153001779">"Sve regije"</string> diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml index e8df21f36558..6c5e7e566398 100644 --- a/core/res/res/values-es/strings.xml +++ b/core/res/res/values-es/strings.xml @@ -1296,7 +1296,7 @@ <string name="submit" msgid="1602335572089911941">"Enviar"</string> <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"Se ha habilitado el modo coche"</string> <string name="car_mode_disable_notification_message" msgid="6301524980144350051">"Toca para salir del modo coche."</string> - <string name="tethered_notification_title" msgid="3146694234398202601">"Compartir Internet/Zona Wi-Fi activado"</string> + <string name="tethered_notification_title" msgid="3146694234398202601">"Compartir conexión/Zona Wi-Fi activada"</string> <string name="tethered_notification_message" msgid="2113628520792055377">"Toca para configurar."</string> <string name="back_button_label" msgid="2300470004503343439">"Atrás"</string> <string name="next_button_label" msgid="1080555104677992408">"Siguiente"</string> @@ -1383,7 +1383,7 @@ <string name="data_usage_mobile_limit_snoozed_title" msgid="279240572165412168">"Límite de datos móviles superado"</string> <string name="data_usage_wifi_limit_snoozed_title" msgid="8743856006384825974">"Límite de datos Wi-Fi superado"</string> <string name="data_usage_limit_snoozed_body" msgid="7035490278298441767">"Límite superado en <xliff:g id="SIZE">%s</xliff:g>"</string> - <string name="data_usage_restricted_title" msgid="5965157361036321914">"Conexiones automáticas restringidas"</string> + <string name="data_usage_restricted_title" msgid="5965157361036321914">"Datos en segundo plano restringidos"</string> <string name="data_usage_restricted_body" msgid="469866376337242726">"Toca para quitar la restricción."</string> <string name="ssl_certificate" msgid="6510040486049237639">"Certificado de seguridad"</string> <string name="ssl_certificate_is_valid" msgid="6825263250774569373">"Este certificado es válido."</string> @@ -1618,7 +1618,7 @@ <string name="package_installed_device_owner" msgid="6875717669960212648">"Instalado por el administrador"</string> <string name="package_updated_device_owner" msgid="1847154566357862089">"Actualizado por el administrador"</string> <string name="package_deleted_device_owner" msgid="2307122077550236438">"Eliminado por el administrador"</string> - <string name="battery_saver_description" msgid="1960431123816253034">"Para mejorar la duración de la batería, la función de ahorro de batería reduce el rendimiento del dispositivo y limita la vibración, los servicios de ubicación y la mayor parte de la transmisión de datos en segundo plano. Es posible que las aplicaciones que se sincronizan, como las de correo y mensajes, no se actualicen a menos que las abras.\n\nLa función de ahorro de batería se desactiva automáticamente cuando el dispositivo se está cargando."</string> + <string name="battery_saver_description" msgid="1960431123816253034">"Para mejorar la duración de la batería, la función de ahorro de batería reduce el rendimiento del dispositivo y limita la vibración, los servicios de ubicación y la mayor parte de los datos en segundo plano. Es posible que las aplicaciones que se sincronizan, como las de correo y mensajes, no se actualicen a menos que las abras.\n\nLa función de ahorro de batería se desactiva automáticamente cuando el dispositivo se está cargando."</string> <string name="data_saver_description" msgid="6015391409098303235">"El ahorro de datos evita que algunas aplicaciones envíen o reciban datos en segundo plano, lo que permite reducir el uso de datos. Una aplicación activa podrá acceder a los datos, aunque con menos frecuencia. Esto significa que, por ejemplo, algunas imágenes no se muestren hasta que no las toques."</string> <string name="data_saver_enable_title" msgid="4674073932722787417">"¿Activar ahorro de datos?"</string> <string name="data_saver_enable_button" msgid="7147735965247211818">"Activar"</string> diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml index 36922d985056..b5234a8e3154 100644 --- a/core/res/res/values-eu/strings.xml +++ b/core/res/res/values-eu/strings.xml @@ -1770,8 +1770,7 @@ <string name="etws_primary_default_message_tsunami" msgid="1887685943498368548">"Ebakuatu kostaldeak eta ibaialdeak berehala eta joan toki seguru batera, adibidez, toki garai batera."</string> <string name="etws_primary_default_message_earthquake_and_tsunami" msgid="998797956848445862">"Ez larritu eta bilatu babesleku bat inguruan."</string> <string name="etws_primary_default_message_test" msgid="2709597093560037455">"Larrialdi-mezuen proba"</string> - <!-- no translation found for notification_reply_button_accessibility (3621714652387814344) --> - <skip /> + <string name="notification_reply_button_accessibility" msgid="3621714652387814344">"Erantzun"</string> <string name="etws_primary_default_message_others" msgid="6293148756130398971"></string> <string name="mmcc_authentication_reject" msgid="7729819349669603406">"Ez da onartzen SIM txartela"</string> <string name="mmcc_imsi_unknown_in_hlr" msgid="6321202257374418726">"Ez dago SIM txartelik"</string> diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml index a39bc3ff5b4e..c6881825bfe1 100644 --- a/core/res/res/values-fr-rCA/strings.xml +++ b/core/res/res/values-fr-rCA/strings.xml @@ -1769,8 +1769,7 @@ <string name="etws_primary_default_message_tsunami" msgid="1887685943498368548">"Évacuez immédiatement les zones côtières et les rives des fleuves, et réfugiez-vous dans un endroit plus sécuritaire, comme un terrain surélevé."</string> <string name="etws_primary_default_message_earthquake_and_tsunami" msgid="998797956848445862">"Restez calme et cherchez un abri à proximité."</string> <string name="etws_primary_default_message_test" msgid="2709597093560037455">"Test de messages d\'urgence"</string> - <!-- no translation found for notification_reply_button_accessibility (3621714652387814344) --> - <skip /> + <string name="notification_reply_button_accessibility" msgid="3621714652387814344">"Répondre"</string> <string name="etws_primary_default_message_others" msgid="6293148756130398971"></string> <string name="mmcc_authentication_reject" msgid="7729819349669603406">"Carte SIM non autorisée"</string> <string name="mmcc_imsi_unknown_in_hlr" msgid="6321202257374418726">"Carte SIM non configurée"</string> diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml index 728de0546f1d..a5c9b0f9c4fb 100644 --- a/core/res/res/values-fr/strings.xml +++ b/core/res/res/values-fr/strings.xml @@ -1420,7 +1420,7 @@ <string name="media_route_button_content_description" msgid="591703006349356016">"Caster"</string> <string name="media_route_chooser_title" msgid="1751618554539087622">"Connexion à l\'appareil"</string> <string name="media_route_chooser_title_for_remote_display" msgid="3395541745872017583">"Caster l\'écran sur l\'appareil"</string> - <string name="media_route_chooser_searching" msgid="4776236202610828706">"Recherche d\'appareils en cours…"</string> + <string name="media_route_chooser_searching" msgid="4776236202610828706">"Recherche d\'appareils…"</string> <string name="media_route_chooser_extended_settings" msgid="87015534236701604">"Paramètres"</string> <string name="media_route_controller_disconnect" msgid="8966120286374158649">"Déconnecter"</string> <string name="media_route_status_scanning" msgid="7279908761758293783">"Analyse en cours..."</string> diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml index 09104db004e4..8db169b0276e 100644 --- a/core/res/res/values-gl/strings.xml +++ b/core/res/res/values-gl/strings.xml @@ -1770,8 +1770,7 @@ <string name="etws_primary_default_message_tsunami" msgid="1887685943498368548">"Abandona de inmediato rexións costeiras e situadas na beira de ríos para dirixirte a un lugar máis seguro, como un terreo elevado."</string> <string name="etws_primary_default_message_earthquake_and_tsunami" msgid="998797956848445862">"Mantén a calma e busca refuxio cerca."</string> <string name="etws_primary_default_message_test" msgid="2709597093560037455">"Proba de mensaxes de emerxencia"</string> - <!-- no translation found for notification_reply_button_accessibility (3621714652387814344) --> - <skip /> + <string name="notification_reply_button_accessibility" msgid="3621714652387814344">"Responder"</string> <string name="etws_primary_default_message_others" msgid="6293148756130398971"></string> <string name="mmcc_authentication_reject" msgid="7729819349669603406">"Non se admite a tarxeta SIM"</string> <string name="mmcc_imsi_unknown_in_hlr" msgid="6321202257374418726">"Non se introduciu ningunha tarxeta SIM"</string> diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml index f0732d41fd37..bffede48084f 100644 --- a/core/res/res/values-hi/strings.xml +++ b/core/res/res/values-hi/strings.xml @@ -837,7 +837,7 @@ <string name="save_password_remember" msgid="6491879678996749466">"याद रखें"</string> <string name="save_password_never" msgid="8274330296785855105">"कभी नहीं"</string> <string name="open_permission_deny" msgid="7374036708316629800">"आपके पास इस पेज को खोलने की अनुमति नहीं है."</string> - <string name="text_copied" msgid="4985729524670131385">"लेख की क्लिपबोर्ड पर प्रतिलिपि बनाई गई."</string> + <string name="text_copied" msgid="4985729524670131385">"लेख को क्लिपबोर्ड पर कॉपी किया गया."</string> <string name="more_item_label" msgid="4650918923083320495">"अधिक"</string> <string name="prepend_shortcut_label" msgid="2572214461676015642">"मेनू+"</string> <string name="menu_space_shortcut_label" msgid="2410328639272162537">"space"</string> @@ -954,12 +954,12 @@ <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string> <string name="selectAll" msgid="6876518925844129331">"सभी को चुनें"</string> <string name="cut" msgid="3092569408438626261">"काटें"</string> - <string name="copy" msgid="2681946229533511987">"प्रतिलिपि बनाएं"</string> + <string name="copy" msgid="2681946229533511987">"कॉपी करें"</string> <string name="paste" msgid="5629880836805036433">"चिपकाएं"</string> <string name="paste_as_plain_text" msgid="5427792741908010675">"सादे पाठ के रूप में चिपकाएं"</string> <string name="replace" msgid="5781686059063148930">"बदलें•"</string> <string name="delete" msgid="6098684844021697789">"हटाएं"</string> - <string name="copyUrl" msgid="2538211579596067402">"URL की प्रतिलिपि बनाएं"</string> + <string name="copyUrl" msgid="2538211579596067402">"URL को कॉपी करें"</string> <string name="selectTextMode" msgid="1018691815143165326">"लेख को चुनें"</string> <string name="undo" msgid="7905788502491742328">"वापस लाएं"</string> <string name="redo" msgid="7759464876566803888">"फिर से करें"</string> diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml index 5b741ca5fb21..7cf7507e9100 100644 --- a/core/res/res/values-hy/strings.xml +++ b/core/res/res/values-hy/strings.xml @@ -567,8 +567,8 @@ <string name="policydesc_setGlobalProxy" msgid="8459859731153370499">"Կարգավորել, որ սարքի համընդհանուր պրոքսի-սերվերն օգտագործվի, երբ քաղաքականությունը միացված է: Միայն սարքի սեփականատերը կարող է կարգավորել համընդհանուր պրոքսի-սերվերը:"</string> <string name="policylab_expirePassword" msgid="5610055012328825874">"Նշել էկր կողպ գաղտնաբ սպառումը"</string> <string name="policydesc_expirePassword" msgid="5367525762204416046">"Փոխել էկրանի կողպման գաղտնաբառի, PIN-ի կամ նախշի փոփոխման հաճախականությունը:"</string> - <string name="policylab_encryptedStorage" msgid="8901326199909132915">"Կարգավորել պահոցի կոդավորումը"</string> - <string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Պահանջել, որ պահվող հավելվածների տվյալները լինեն կոդավորված:"</string> + <string name="policylab_encryptedStorage" msgid="8901326199909132915">"Կարգավորել պահոցի գաղտնագրումը"</string> + <string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Պահանջել, որ պահվող հավելվածների տվյալները լինեն գաղտնագրված:"</string> <string name="policylab_disableCamera" msgid="6395301023152297826">"Կասեցնել տեսախցիկները"</string> <string name="policydesc_disableCamera" msgid="2306349042834754597">"Կանխել բոլոր սարքերի ֆոտոխցիկների օգտագործումը:"</string> <string name="policylab_disableKeyguardFeatures" msgid="8552277871075367771">"Անջատել կողպման գործառույթները"</string> diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml index 938b03aac6fd..426442c0734c 100644 --- a/core/res/res/values-iw/strings.xml +++ b/core/res/res/values-iw/strings.xml @@ -1837,8 +1837,7 @@ <string name="etws_primary_default_message_tsunami" msgid="1887685943498368548">"יש להתפנות מיידית מאזורים הסמוכים לחופים ולנהרות למקום בטוח יותר, כגון שטח גבוה יותר."</string> <string name="etws_primary_default_message_earthquake_and_tsunami" msgid="998797956848445862">"הישאר רגוע וחפש מחסה בקרבת מקום."</string> <string name="etws_primary_default_message_test" msgid="2709597093560037455">"בדיקה של הודעות חירום"</string> - <!-- no translation found for notification_reply_button_accessibility (3621714652387814344) --> - <skip /> + <string name="notification_reply_button_accessibility" msgid="3621714652387814344">"השב"</string> <string name="etws_primary_default_message_others" msgid="6293148756130398971"></string> <string name="mmcc_authentication_reject" msgid="7729819349669603406">"כרטיס ה-SIM לא מורשה"</string> <string name="mmcc_imsi_unknown_in_hlr" msgid="6321202257374418726">"כרטיס ה-SIM לא מזוהה"</string> diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml index affd2874990d..52d5b0b4b8cf 100644 --- a/core/res/res/values-lo/strings.xml +++ b/core/res/res/values-lo/strings.xml @@ -1769,8 +1769,7 @@ <string name="etws_primary_default_message_tsunami" msgid="1887685943498368548">"ອົບພະຍົບອອກຈາກເຂດຊາຍຝັ່ງທະເລ ແລະ ບໍລິເວນແມ່ນ້ຳໄປບ່ອນທີ່ປອດໄພກວ່າ ເຊັ່ນ: ບ່ອນສູງ ໂດຍທັນທີ."</string> <string name="etws_primary_default_message_earthquake_and_tsunami" msgid="998797956848445862">"ໃຈເຢັນໆ ແລະ ຊອກຫາບ່ອນພັກຢູ່ໃກ້ໆ."</string> <string name="etws_primary_default_message_test" msgid="2709597093560037455">"ທົດສອບຂໍ້ຄວາມສຸກເສີນ"</string> - <!-- no translation found for notification_reply_button_accessibility (3621714652387814344) --> - <skip /> + <string name="notification_reply_button_accessibility" msgid="3621714652387814344">"ຕອບກັບ"</string> <string name="etws_primary_default_message_others" msgid="6293148756130398971"></string> <string name="mmcc_authentication_reject" msgid="7729819349669603406">"ບໍ່ອະນຸຍາດໃຫ້ໃຊ້ SIM"</string> <string name="mmcc_imsi_unknown_in_hlr" msgid="6321202257374418726">"ບໍ່ມີການນຳໃຊ້ SIM"</string> diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml index 48baf056fa3b..ac24366aadc1 100644 --- a/core/res/res/values-sl/strings.xml +++ b/core/res/res/values-sl/strings.xml @@ -1837,8 +1837,7 @@ <string name="etws_primary_default_message_tsunami" msgid="1887685943498368548">"Takoj se umaknite z obalnih območij in bregov rek na varnejše mesto, na primer na višje ležeča mesta."</string> <string name="etws_primary_default_message_earthquake_and_tsunami" msgid="998797956848445862">"Ostanite mirni in poiščite zavetje v bližini."</string> <string name="etws_primary_default_message_test" msgid="2709597093560037455">"Preskus sporočil v sili"</string> - <!-- no translation found for notification_reply_button_accessibility (3621714652387814344) --> - <skip /> + <string name="notification_reply_button_accessibility" msgid="3621714652387814344">"Odgovor"</string> <string name="etws_primary_default_message_others" msgid="6293148756130398971"></string> <string name="mmcc_authentication_reject" msgid="7729819349669603406">"Kartica SIM ni dovoljena"</string> <string name="mmcc_imsi_unknown_in_hlr" msgid="6321202257374418726">"Kartica SIM ni omogočena za uporabo"</string> diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml index aa6356248b73..48c714b64658 100644 --- a/core/res/res/values-tr/strings.xml +++ b/core/res/res/values-tr/strings.xml @@ -1769,8 +1769,7 @@ <string name="etws_primary_default_message_tsunami" msgid="1887685943498368548">"Kıyı kesimlerini ve nehir kenarlarını hemen boşaltarak yüksek yerler gibi daha güvenli bölgelere gidin."</string> <string name="etws_primary_default_message_earthquake_and_tsunami" msgid="998797956848445862">"Sakin olun ve yakınlarda sığınabileceğiniz bir yer bulun."</string> <string name="etws_primary_default_message_test" msgid="2709597093560037455">"Acil durum mesajları testi"</string> - <!-- no translation found for notification_reply_button_accessibility (3621714652387814344) --> - <skip /> + <string name="notification_reply_button_accessibility" msgid="3621714652387814344">"Yanıtla"</string> <string name="etws_primary_default_message_others" msgid="6293148756130398971"></string> <string name="mmcc_authentication_reject" msgid="7729819349669603406">"SIM\'e izin verilmiyor"</string> <string name="mmcc_imsi_unknown_in_hlr" msgid="6321202257374418726">"SIM için temel hazırlık yapılmadı"</string> diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml index 0a39be07eb22..dc0395adf6dd 100644 --- a/core/res/res/values-zh-rCN/strings.xml +++ b/core/res/res/values-zh-rCN/strings.xml @@ -999,7 +999,7 @@ <string name="whichSendApplicationNamed" msgid="2799370240005424391">"使用%1$s分享"</string> <string name="whichSendApplicationLabel" msgid="4579076294675975354">"分享"</string> <string name="whichSendToApplication" msgid="8272422260066642057">"通过以下应用发送"</string> - <string name="whichSendToApplicationNamed" msgid="7768387871529295325">"通过1$s发送"</string> + <string name="whichSendToApplicationNamed" msgid="7768387871529295325">"通过%1$s发送"</string> <string name="whichSendToApplicationLabel" msgid="8878962419005813500">"发送"</string> <string name="whichHomeApplication" msgid="4307587691506919691">"选择主屏幕应用"</string> <string name="whichHomeApplicationNamed" msgid="4493438593214760979">"将“%1$s”设为主屏幕应用"</string> @@ -1769,8 +1769,7 @@ <string name="etws_primary_default_message_tsunami" msgid="1887685943498368548">"请立即从沿海和河滨区域撤离到高地等较安全的地方。"</string> <string name="etws_primary_default_message_earthquake_and_tsunami" msgid="998797956848445862">"请保持冷静,并寻找附近的避难地点。"</string> <string name="etws_primary_default_message_test" msgid="2709597093560037455">"紧急消息测试"</string> - <!-- no translation found for notification_reply_button_accessibility (3621714652387814344) --> - <skip /> + <string name="notification_reply_button_accessibility" msgid="3621714652387814344">"回复"</string> <string name="etws_primary_default_message_others" msgid="6293148756130398971"></string> <string name="mmcc_authentication_reject" msgid="7729819349669603406">"不受允许的 SIM 卡"</string> <string name="mmcc_imsi_unknown_in_hlr" msgid="6321202257374418726">"未配置的 SIM 卡"</string> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index e10570ff9610..10ffa5d74df7 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1884,6 +1884,9 @@ <!-- Type of the double tap sensor. Empty if double tap is not supported. --> <string name="config_dozeDoubleTapSensorType" translatable="false"></string> + <!-- Type of the long press sensor. Empty if long press is not supported. --> + <string name="config_dozeLongPressSensorType" translatable="false"></string> + <!-- Control whether the always on display mode is available. This should only be enabled on devices where the display has be tuned to be power efficient in DOZE and/or DOZE_SUSPEND states. --> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 18e8af7836d2..8c26db43fc41 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -20,23 +20,13 @@ <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <!-- Suffix added to a number to signify size in bytes. --> <string name="byteShort">B</string> - <!-- Suffix added to a number to signify size in kilobytes (1000 bytes). - If you retain the Latin script for the localization, please use the lowercase - 'k', as it signifies 1000 bytes as opposed to 1024 bytes. --> - <string name="kilobyteShort">kB</string> - <!-- Suffix added to a number to signify size in megabytes. --> - <string name="megabyteShort">MB</string> - <!-- Suffix added to a number to signify size in gigabytes. --> - <string name="gigabyteShort">GB</string> - <!-- Suffix added to a number to signify size in terabytes. --> - <string name="terabyteShort">TB</string> <!-- Suffix added to a number to signify size in petabytes. --> <string name="petabyteShort">PB</string> - <!-- Format string used to add a suffix like "kB" or "MB" to a number - to display a size in kilobytes, megabytes, or other size units. - Some languages (like French) will want to add a space between - the placeholders. --> - <string name="fileSizeSuffix"><xliff:g id="number" example="123">%1$s</xliff:g> <xliff:g id="unit" example="MB">%2$s</xliff:g></string> + <!-- Format string used to add a suffix like "B" or "PB" to a number + to display a size in bytes or petabytes. + Some languages may want to remove the space between the placeholders + or replace it with a non-breaking space. --> + <string name="fileSizeSuffix"><xliff:g id="number" example="123">%1$s</xliff:g> <xliff:g id="unit" example="B">%2$s</xliff:g></string> <!-- Used in Contacts for a field that has no label and in Note Pad for a note with no name. --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index f8b6904e38c6..8c81f3f2a1f5 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -677,7 +677,6 @@ <java-symbol type="string" name="fileSizeSuffix" /> <java-symbol type="string" name="force_close" /> <java-symbol type="string" name="gadget_host_error_inflating" /> - <java-symbol type="string" name="gigabyteShort" /> <java-symbol type="string" name="gpsNotifMessage" /> <java-symbol type="string" name="gpsNotifTicker" /> <java-symbol type="string" name="gpsNotifTitle" /> @@ -733,7 +732,6 @@ <java-symbol type="string" name="keyboardview_keycode_enter" /> <java-symbol type="string" name="keyboardview_keycode_mode_change" /> <java-symbol type="string" name="keyboardview_keycode_shift" /> - <java-symbol type="string" name="kilobyteShort" /> <java-symbol type="string" name="last_month" /> <java-symbol type="string" name="launchBrowserDefault" /> <java-symbol type="string" name="lock_to_app_toast" /> @@ -754,7 +752,6 @@ <java-symbol type="string" name="lockscreen_emergency_call" /> <java-symbol type="string" name="lockscreen_return_to_call" /> <java-symbol type="string" name="low_memory" /> - <java-symbol type="string" name="megabyteShort" /> <java-symbol type="string" name="midnight" /> <java-symbol type="string" name="mismatchPin" /> <java-symbol type="string" name="mmiComplete" /> @@ -957,7 +954,6 @@ <java-symbol type="string" name="sync_really_delete" /> <java-symbol type="string" name="sync_too_many_deletes_desc" /> <java-symbol type="string" name="sync_undo_deletes" /> - <java-symbol type="string" name="terabyteShort" /> <java-symbol type="string" name="text_copied" /> <java-symbol type="string" name="time_of_day" /> <java-symbol type="string" name="time_picker_decrement_hour_button" /> @@ -3034,6 +3030,8 @@ <java-symbol type="array" name="config_hideWhenDisabled_packageNames" /> + <java-symbol type="string" name="config_dozeLongPressSensorType" /> + <java-symbol type="array" name="config_allowedGlobalInstantAppSettings" /> <java-symbol type="array" name="config_allowedSystemInstantAppSettings" /> <java-symbol type="array" name="config_allowedSecureInstantAppSettings" /> diff --git a/core/tests/coretests/assets/fonts/a3em.ttf b/core/tests/coretests/assets/fonts/a3em.ttf Binary files differnew file mode 100644 index 000000000000..a601ce2ed932 --- /dev/null +++ b/core/tests/coretests/assets/fonts/a3em.ttf diff --git a/core/tests/coretests/assets/fonts/a3em.ttx b/core/tests/coretests/assets/fonts/a3em.ttx new file mode 100644 index 000000000000..d3b9e1603764 --- /dev/null +++ b/core/tests/coretests/assets/fonts/a3em.ttx @@ -0,0 +1,187 @@ +<?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.0"> + + <GlyphOrder> + <GlyphID id="0" name=".notdef"/> + <GlyphID id="1" name="1em"/> + <GlyphID id="2" name="3em"/> + </GlyphOrder> + + <head> + <tableVersion value="1.0"/> + <fontRevision value="1.0"/> + <checkSumAdjustment value="0x640cdb2f"/> + <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="1.0"/> + <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> + <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex' + will be recalculated by the compiler --> + <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"/> + <usFirstCharIndex value="32"/> + <usLastCharIndex value="122"/> + <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="1em" width="1000" lsb="93"/> + <mtx name="3em" width="3000" lsb="93"/> + </hmtx> + + <cmap> + <tableVersion version="0"/> + <cmap_format_4 platformID="3" platEncID="10" language="0"> + <map code="0x0061" name="3em" /> + <map code="0x0062" name="1em" /> + <map code="0x0063" name="1em" /> + <map code="0x0064" name="1em" /> + <map code="0x0065" name="1em" /> + </cmap_format_4> + </cmap> + + <loca> + <!-- The 'loca' table will be calculated by the compiler --> + </loca> + + <glyf> + <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" /> + <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" /> + <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" /> + </glyf> + + <name> + <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409"> + Copyright (C) 2017 The Android Open Source Project + </namerecord> + <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> + <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409"> + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + 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. + </namerecord> + <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409"> + http://www.apache.org/licenses/LICENSE-2.0 + </namerecord> + </name> + + <post> + <formatType value="3.0"/> + <italicAngle value="0.0"/> + <underlinePosition value="-75"/> + <underlineThickness value="50"/> + <isFixedPitch value="0"/> + <minMemType42 value="0"/> + <maxMemType42 value="0"/> + <minMemType1 value="0"/> + <maxMemType1 value="0"/> + </post> + +</ttFont> diff --git a/core/tests/coretests/assets/fonts/all2em.ttf b/core/tests/coretests/assets/fonts/all2em.ttf Binary files differnew file mode 100644 index 000000000000..482f7552f510 --- /dev/null +++ b/core/tests/coretests/assets/fonts/all2em.ttf diff --git a/core/tests/coretests/assets/fonts/all2em.ttx b/core/tests/coretests/assets/fonts/all2em.ttx new file mode 100644 index 000000000000..fe95ff04d1e3 --- /dev/null +++ b/core/tests/coretests/assets/fonts/all2em.ttx @@ -0,0 +1,184 @@ +<?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.0"> + + <GlyphOrder> + <GlyphID id="0" name=".notdef"/> + <GlyphID id="1" name="2em"/> + </GlyphOrder> + + <head> + <tableVersion value="1.0"/> + <fontRevision value="1.0"/> + <checkSumAdjustment value="0x640cdb2f"/> + <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="1.0"/> + <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> + <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex' + will be recalculated by the compiler --> + <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"/> + <usFirstCharIndex value="32"/> + <usLastCharIndex value="122"/> + <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="2em" width="1000" lsb="93"/> + </hmtx> + + <cmap> + <tableVersion version="0"/> + <cmap_format_4 platformID="3" platEncID="10" language="0"> + <map code="0x0061" name="2em" /> + <map code="0x0062" name="2em" /> + <map code="0x0063" name="2em" /> + <map code="0x0064" name="2em" /> + <map code="0x0065" name="2em" /> + </cmap_format_4> + </cmap> + + <loca> + <!-- The 'loca' table will be calculated by the compiler --> + </loca> + + <glyf> + <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" /> + <TTGlyph name="2em" xMin="0" yMin="0" xMax="0" yMax="0" /> + </glyf> + + <name> + <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409"> + Copyright (C) 2017 The Android Open Source Project + </namerecord> + <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> + <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409"> + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + 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. + </namerecord> + <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409"> + http://www.apache.org/licenses/LICENSE-2.0 + </namerecord> + </name> + + <post> + <formatType value="3.0"/> + <italicAngle value="0.0"/> + <underlinePosition value="-75"/> + <underlineThickness value="50"/> + <isFixedPitch value="0"/> + <minMemType42 value="0"/> + <maxMemType42 value="0"/> + <minMemType1 value="0"/> + <maxMemType1 value="0"/> + </post> + +</ttFont> diff --git a/core/tests/coretests/assets/fonts/b3em.ttf b/core/tests/coretests/assets/fonts/b3em.ttf Binary files differnew file mode 100644 index 000000000000..63948a22c113 --- /dev/null +++ b/core/tests/coretests/assets/fonts/b3em.ttf diff --git a/core/tests/coretests/assets/fonts/b3em.ttx b/core/tests/coretests/assets/fonts/b3em.ttx new file mode 100644 index 000000000000..b5a77ef09bd3 --- /dev/null +++ b/core/tests/coretests/assets/fonts/b3em.ttx @@ -0,0 +1,187 @@ +<?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.0"> + + <GlyphOrder> + <GlyphID id="0" name=".notdef"/> + <GlyphID id="1" name="1em"/> + <GlyphID id="2" name="3em"/> + </GlyphOrder> + + <head> + <tableVersion value="1.0"/> + <fontRevision value="1.0"/> + <checkSumAdjustment value="0x640cdb2f"/> + <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="1.0"/> + <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> + <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex' + will be recalculated by the compiler --> + <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"/> + <usFirstCharIndex value="32"/> + <usLastCharIndex value="122"/> + <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="1em" width="1000" lsb="93"/> + <mtx name="3em" width="3000" lsb="93"/> + </hmtx> + + <cmap> + <tableVersion version="0"/> + <cmap_format_4 platformID="3" platEncID="10" language="0"> + <map code="0x0061" name="1em" /> + <map code="0x0062" name="3em" /> + <map code="0x0063" name="1em" /> + <map code="0x0064" name="1em" /> + <map code="0x0065" name="1em" /> + </cmap_format_4> + </cmap> + + <loca> + <!-- The 'loca' table will be calculated by the compiler --> + </loca> + + <glyf> + <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" /> + <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" /> + <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" /> + </glyf> + + <name> + <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409"> + Copyright (C) 2017 The Android Open Source Project + </namerecord> + <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> + <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409"> + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + 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. + </namerecord> + <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409"> + http://www.apache.org/licenses/LICENSE-2.0 + </namerecord> + </name> + + <post> + <formatType value="3.0"/> + <italicAngle value="0.0"/> + <underlinePosition value="-75"/> + <underlineThickness value="50"/> + <isFixedPitch value="0"/> + <minMemType42 value="0"/> + <maxMemType42 value="0"/> + <minMemType1 value="0"/> + <maxMemType1 value="0"/> + </post> + +</ttFont> diff --git a/core/tests/coretests/assets/fonts/c3em.ttf b/core/tests/coretests/assets/fonts/c3em.ttf Binary files differnew file mode 100644 index 000000000000..badc3e29868f --- /dev/null +++ b/core/tests/coretests/assets/fonts/c3em.ttf diff --git a/core/tests/coretests/assets/fonts/c3em.ttx b/core/tests/coretests/assets/fonts/c3em.ttx new file mode 100644 index 000000000000..f5ed8e556332 --- /dev/null +++ b/core/tests/coretests/assets/fonts/c3em.ttx @@ -0,0 +1,187 @@ +<?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.0"> + + <GlyphOrder> + <GlyphID id="0" name=".notdef"/> + <GlyphID id="1" name="1em"/> + <GlyphID id="2" name="3em"/> + </GlyphOrder> + + <head> + <tableVersion value="1.0"/> + <fontRevision value="1.0"/> + <checkSumAdjustment value="0x640cdb2f"/> + <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="1.0"/> + <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> + <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex' + will be recalculated by the compiler --> + <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"/> + <usFirstCharIndex value="32"/> + <usLastCharIndex value="122"/> + <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="1em" width="1000" lsb="93"/> + <mtx name="3em" width="3000" lsb="93"/> + </hmtx> + + <cmap> + <tableVersion version="0"/> + <cmap_format_4 platformID="3" platEncID="10" language="0"> + <map code="0x0061" name="1em" /> + <map code="0x0062" name="1em" /> + <map code="0x0063" name="3em" /> + <map code="0x0064" name="1em" /> + <map code="0x0065" name="1em" /> + </cmap_format_4> + </cmap> + + <loca> + <!-- The 'loca' table will be calculated by the compiler --> + </loca> + + <glyf> + <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" /> + <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" /> + <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" /> + </glyf> + + <name> + <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409"> + Copyright (C) 2017 The Android Open Source Project + </namerecord> + <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> + <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409"> + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + 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. + </namerecord> + <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409"> + http://www.apache.org/licenses/LICENSE-2.0 + </namerecord> + </name> + + <post> + <formatType value="3.0"/> + <italicAngle value="0.0"/> + <underlinePosition value="-75"/> + <underlineThickness value="50"/> + <isFixedPitch value="0"/> + <minMemType42 value="0"/> + <maxMemType42 value="0"/> + <minMemType1 value="0"/> + <maxMemType1 value="0"/> + </post> + +</ttFont> diff --git a/core/tests/coretests/assets/fonts/no_coverage.ttf b/core/tests/coretests/assets/fonts/no_coverage.ttf Binary files differnew file mode 100644 index 000000000000..c884881c5026 --- /dev/null +++ b/core/tests/coretests/assets/fonts/no_coverage.ttf diff --git a/core/tests/coretests/assets/fonts/no_coverage.ttx b/core/tests/coretests/assets/fonts/no_coverage.ttx new file mode 100644 index 000000000000..3be5f8626bf1 --- /dev/null +++ b/core/tests/coretests/assets/fonts/no_coverage.ttx @@ -0,0 +1,180 @@ +<?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.0"> + + <GlyphOrder> + <GlyphID id="0" name=".notdef"/> + <GlyphID id="1" name="dummy"/> + </GlyphOrder> + + <head> + <tableVersion value="1.0"/> + <fontRevision value="1.0"/> + <checkSumAdjustment value="0x640cdb2f"/> + <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="1.0"/> + <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> + <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex' + will be recalculated by the compiler --> + <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"/> + <usFirstCharIndex value="32"/> + <usLastCharIndex value="122"/> + <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="dummy" width="500" lsb="93"/> + </hmtx> + + <cmap> + <tableVersion version="0"/> + <cmap_format_4 platformID="3" platEncID="10" language="0"> + <map code="0xFFFD" name="dummy" /> <!-- dummy entry --> + </cmap_format_4> + </cmap> + + <loca> + <!-- The 'loca' table will be calculated by the compiler --> + </loca> + + <glyf> + <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" /> + <TTGlyph name="dummy" xMin="0" yMin="0" xMax="0" yMax="0" /> + </glyf> + + <name> + <namerecord nameID="0" platformID="3" platEncID="1" langID="0x409"> + Copyright (C) 2017 The Android Open Source Project + </namerecord> + <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> + <namerecord nameID="13" platformID="3" platEncID="1" langID="0x409"> + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + 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. + </namerecord> + <namerecord nameID="14" platformID="3" platEncID="1" langID="0x409"> + http://www.apache.org/licenses/LICENSE-2.0 + </namerecord> + </name> + + <post> + <formatType value="3.0"/> + <italicAngle value="0.0"/> + <underlinePosition value="-75"/> + <underlineThickness value="50"/> + <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/graphics/TypefaceSystemFallbackTest.java b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java new file mode 100644 index 000000000000..ca4f7d43caf4 --- /dev/null +++ b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java @@ -0,0 +1,469 @@ +/* + * 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. + */ + +package android.graphics; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import android.content.Context; +import android.content.res.AssetManager; +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; +import android.util.ArrayMap; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class TypefaceSystemFallbackTest { + private static final String SYSTEM_FONT_DIR = "/system/fonts/"; + private static final String SYSTEM_FONTS_XML = "/system/etc/fonts.xml"; + + private static final String[] TEST_FONT_FILES = { + "a3em.ttf", // Supports "a","b","c". The width of "a" is 3em, others are 1em. + "b3em.ttf", // Supports "a","b","c". The width of "b" is 3em, others are 1em. + "c3em.ttf", // Supports "a","b","c". The width of "c" is 3em, others are 1em. + "all2em.ttf", // Supports "a,","b","c". All of them have the same width of 2em. + "no_coverage.ttf", // This font doesn't support any characters. + }; + private static final String TEST_FONTS_XML; + private static final String TEST_FONT_DIR; + + private static final float GLYPH_1EM_WIDTH; + private static final float GLYPH_2EM_WIDTH; + private static final float GLYPH_3EM_WIDTH; + + static { + final Context targetCtx = InstrumentationRegistry.getInstrumentation().getTargetContext(); + final File cacheDir = new File(targetCtx.getCacheDir(), "TypefaceSystemFallbackTest"); + if (!cacheDir.isDirectory()) { + cacheDir.mkdirs(); + } + TEST_FONT_DIR = cacheDir.getAbsolutePath() + "/"; + TEST_FONTS_XML = new File(cacheDir, "fonts.xml").getAbsolutePath(); + + final AssetManager am = + InstrumentationRegistry.getInstrumentation().getContext().getAssets(); + final Paint paint = new Paint(); + paint.setTypeface(new Typeface.Builder(am, "fonts/a3em.ttf").build()); + GLYPH_3EM_WIDTH = paint.measureText("a"); + GLYPH_1EM_WIDTH = paint.measureText("b"); + + paint.setTypeface(new Typeface.Builder(am, "fonts/all2em.ttf").build()); + GLYPH_2EM_WIDTH = paint.measureText("a"); + } + + @Before + public void setUp() { + final AssetManager am = + InstrumentationRegistry.getInstrumentation().getContext().getAssets(); + for (final String fontFile : TEST_FONT_FILES) { + final String sourceInAsset = "fonts/" + fontFile; + final File outInCache = new File(TEST_FONT_DIR, fontFile); + try (InputStream is = am.open(sourceInAsset)) { + Files.copy(is, outInCache.toPath(), StandardCopyOption.REPLACE_EXISTING); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + + @After + public void tearDown() { + for (final String fontFile : TEST_FONT_FILES) { + final File outInCache = new File(TEST_FONT_DIR, fontFile); + outInCache.delete(); + } + } + + private static void buildSystemFallback(String xml, + ArrayMap<String, Typeface> fontMap, ArrayMap<String, FontFamily[]> fallbackMap) { + try (FileOutputStream fos = new FileOutputStream(TEST_FONTS_XML)) { + fos.write(xml.getBytes(Charset.forName("UTF-8"))); + } catch (IOException e) { + throw new RuntimeException(e); + } + Typeface.buildSystemFallback(TEST_FONTS_XML, TEST_FONT_DIR, fontMap, fallbackMap); + } + + @Test + public void testBuildSystemFallback() { + final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); + final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); + + Typeface.buildSystemFallback(SYSTEM_FONTS_XML, SYSTEM_FONT_DIR, fontMap, fallbackMap); + + assertFalse(fontMap.isEmpty()); + assertFalse(fallbackMap.isEmpty()); + } + + @Test + public void testBuildSystemFallback_NonExistentFontShouldBeIgnored() { + final String xml = "<?xml version='1.0' encoding='UTF-8'?>" + + "<familyset version='22'>" + + " <family name='sans-serif'>" + + " <font weight='400' style='normal'>a3em.ttf</font>" + + " <font weight='400' style='normal'>NoSuchFont.ttf</font>" + + " </family>" + + " <family name='NoSuchFont'>" + + " <font weight='400' style='normal'>NoSuchFont.ttf</font>" + + " </family>" + + " <family>" + + " <font weight='400' style='normal'>NoSuchFont.ttf</font>" + + " </family>" + + "</familyset>"; + final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); + final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); + + buildSystemFallback(xml, fontMap, fallbackMap); + + assertEquals(1, fontMap.size()); + assertTrue(fontMap.containsKey("sans-serif")); + assertEquals(1, fallbackMap.size()); + assertTrue(fallbackMap.containsKey("sans-serif")); + } + + @Test + public void testBuildSystemFallback_NamedFamily() { + final String xml = "<?xml version='1.0' encoding='UTF-8'?>" + + "<familyset version='22'>" + + " <family name='sans-serif'>" + + " <font weight='400' style='normal'>a3em.ttf</font>" + + " </family>" + + " <family name='test'>" + + " <font weight='400' style='normal'>b3em.ttf</font>" + + " </family>" + + " <family name='test2'>" + + " <font weight='400' style='normal'>c3em.ttf</font>" + + " </family>" + + " <family>" + + " <font weight='400' style='normal'>all2em.ttf</font>" + + " </family>" + + "</familyset>"; + final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); + final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); + + buildSystemFallback(xml, fontMap, fallbackMap); + + final Paint paint = new Paint(); + + final Typeface sansSerifTypeface = fontMap.get("sans-serif"); + assertNotNull(sansSerifTypeface); + paint.setTypeface(sansSerifTypeface); + assertEquals(GLYPH_3EM_WIDTH, paint.measureText("a"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f); + + final Typeface testTypeface = fontMap.get("test"); + assertNotNull(testTypeface); + paint.setTypeface(testTypeface); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f); + assertEquals(GLYPH_3EM_WIDTH, paint.measureText("b"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f); + + final Typeface test2Typeface = fontMap.get("test2"); + assertNotNull(test2Typeface); + paint.setTypeface(test2Typeface); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f); + assertEquals(GLYPH_3EM_WIDTH, paint.measureText("c"), 0.0f); + } + + @Test + public void testBuildSystemFallback_defaultFallback() { + final String xml = "<?xml version='1.0' encoding='UTF-8'?>" + + "<familyset version='22'>" + + " <family name='sans-serif'>" + + " <font weight='400' style='normal'>no_coverage.ttf</font>" + + " </family>" + + " <family name='test'>" + + " <font weight='400' style='normal'>no_coverage.ttf</font>" + + " </family>" + + " <family>" + + " <font weight='400' style='normal'>a3em.ttf</font>" + + " </family>" + + " <family>" + + " <font weight='400' style='normal'>all2em.ttf</font>" + + " </family>" + + "</familyset>"; + final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); + final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); + + buildSystemFallback(xml, fontMap, fallbackMap); + + final Paint paint = new Paint(); + + final Typeface sansSerifTypeface = fontMap.get("sans-serif"); + assertNotNull(sansSerifTypeface); + paint.setTypeface(sansSerifTypeface); + assertEquals(GLYPH_3EM_WIDTH, paint.measureText("a"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f); + + final Typeface testTypeface = fontMap.get("test"); + assertNotNull(testTypeface); + paint.setTypeface(testTypeface); + assertEquals(GLYPH_3EM_WIDTH, paint.measureText("a"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f); + } + + @Test + public void testBuildSystemFallback_namedFallbackFamily() { + final String xml = "<?xml version='1.0' encoding='UTF-8'?>" + + "<familyset version='22'>" + + " <family name='sans-serif'>" + + " <font weight='400' style='normal'>no_coverage.ttf</font>" + + " </family>" + + " <family name='test'>" + + " <font weight='400' style='normal'>no_coverage.ttf</font>" + + " </family>" + + " <family name='test2'>" + + " <font weight='400' style='normal'>no_coverage.ttf</font>" + + " </family>" + + " <family>" + + " <font weight='400' style='normal' fallbackFor='test'>a3em.ttf</font>" + + " </family>" + + " <family>" + + " <font weight='400' style='normal' fallbackFor='test2'>b3em.ttf</font>" + + " </family>" + + " <family>" + + " <font weight='400' style='normal'>all2em.ttf</font>" + + " </family>" + + "</familyset>"; + final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); + final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); + + buildSystemFallback(xml, fontMap, fallbackMap); + + final Paint paint = new Paint(); + + final Typeface sansSerifTypeface = fontMap.get("sans-serif"); + assertNotNull(sansSerifTypeface); + paint.setTypeface(sansSerifTypeface); + assertEquals(GLYPH_2EM_WIDTH, paint.measureText("a"), 0.0f); + assertEquals(GLYPH_2EM_WIDTH, paint.measureText("b"), 0.0f); + assertEquals(GLYPH_2EM_WIDTH, paint.measureText("c"), 0.0f); + + final Typeface testTypeface = fontMap.get("test"); + assertNotNull(testTypeface); + paint.setTypeface(testTypeface); + assertEquals(GLYPH_3EM_WIDTH, paint.measureText("a"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f); + + final Typeface test2Typeface = fontMap.get("test2"); + assertNotNull(test2Typeface); + paint.setTypeface(test2Typeface); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f); + assertEquals(GLYPH_3EM_WIDTH, paint.measureText("b"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f); + } + + @Test + public void testBuildSystemFallback_namedFallbackFamily2() { + final String xml = "<?xml version='1.0' encoding='UTF-8'?>" + + "<familyset version='22'>" + + " <family name='sans-serif'>" + + " <font weight='400' style='normal'>no_coverage.ttf</font>" + + " </family>" + + " <family name='test'>" + + " <font weight='400' style='normal'>no_coverage.ttf</font>" + + " </family>" + + " <family name='test2'>" + + " <font weight='400' style='normal'>no_coverage.ttf</font>" + + " </family>" + + " <family>" + + " <font weight='400' style='normal' fallbackFor='test'>a3em.ttf</font>" + + " <font weight='400' style='normal'>b3em.ttf</font>" + + " </family>" + + " <family>" + + " <font weight='400' style='normal'>all2em.ttf</font>" + + " </family>" + + "</familyset>"; + final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); + final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); + + buildSystemFallback(xml, fontMap, fallbackMap); + + final Paint paint = new Paint(); + + final Typeface sansSerifTypeface = fontMap.get("sans-serif"); + assertNotNull(sansSerifTypeface); + paint.setTypeface(sansSerifTypeface); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f); + assertEquals(GLYPH_3EM_WIDTH, paint.measureText("b"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f); + + final Typeface testTypeface = fontMap.get("test"); + assertNotNull(testTypeface); + paint.setTypeface(testTypeface); + assertEquals(GLYPH_3EM_WIDTH, paint.measureText("a"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f); + + final Typeface test2Typeface = fontMap.get("test2"); + assertNotNull(test2Typeface); + paint.setTypeface(test2Typeface); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f); + assertEquals(GLYPH_3EM_WIDTH, paint.measureText("b"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f); + } + + @Test + public void testBuildSystemFallback_ImplicitSansSerifFallback() { + final String xml = "<?xml version='1.0' encoding='UTF-8'?>" + + "<familyset version='22'>" + + " <family name='sans-serif'>" + + " <font weight='400' style='normal'>a3em.ttf</font>" + + " </family>" + + " <family name='test'>" + + " <font weight='400' style='normal'>no_coverage.ttf</font>" + + " </family>" + + " <family name='test2'>" + + " <font weight='400' style='normal'>no_coverage.ttf</font>" + + " </family>" + + " <family>" + + " <font weight='400' style='normal'>all2em.ttf</font>" + + " </family>" + + "</familyset>"; + final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); + final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); + + buildSystemFallback(xml, fontMap, fallbackMap); + + final Paint paint = new Paint(); + + final Typeface testTypeface = fontMap.get("test"); + assertNotNull(testTypeface); + paint.setTypeface(testTypeface); + assertEquals(GLYPH_3EM_WIDTH, paint.measureText("a"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f); + + final Typeface test2Typeface = fontMap.get("test2"); + assertNotNull(test2Typeface); + paint.setTypeface(test2Typeface); + assertEquals(GLYPH_3EM_WIDTH, paint.measureText("a"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f); + } + + @Test + public void testBuildSystemFallback_ElegantFallback() { + final String xml = "<?xml version='1.0' encoding='UTF-8'?>" + + "<familyset version='22'>" + + " <family name='sans-serif'>" + + " <font weight='400' style='normal'>no_coverage.ttf</font>" + + " </family>" + + " <family name='serif'>" + + " <font weight='400' style='normal'>no_coverage.ttf</font>" + + " </family>" + + " <family variant='elegant'>" + + " <font weight='400' style='normal'>a3em.ttf</font>" + + " </family>" + + " <family variant='compact'>" + + " <font weight='400' style='normal'>b3em.ttf</font>" + + " </family>" + + "</familyset>"; + final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); + final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); + + buildSystemFallback(xml, fontMap, fallbackMap); + + final Paint paint = new Paint(); + + final Typeface testTypeface = fontMap.get("serif"); + assertNotNull(testTypeface); + paint.setTypeface(testTypeface); + paint.setElegantTextHeight(true); + assertEquals(GLYPH_3EM_WIDTH, paint.measureText("a"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f); + + paint.setElegantTextHeight(false); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f); + assertEquals(GLYPH_3EM_WIDTH, paint.measureText("b"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f); + } + + @Test + public void testBuildSystemFallback_ElegantFallback_customFallback() { + final String xml = "<?xml version='1.0' encoding='UTF-8'?>" + + "<familyset version='22'>" + + " <family name='sans-serif'>" + + " <font weight='400' style='normal'>no_coverage.ttf</font>" + + " </family>" + + " <family name='serif'>" + + " <font weight='400' style='normal'>no_coverage.ttf</font>" + + " </family>" + + " <family variant='elegant'>" + + " <font weight='400' style='normal'>a3em.ttf</font>" + + " <font weight='400' style='normal' fallbackFor='serif'>b3em.ttf</font>" + + " </family>" + + " <family variant='compact'>" + + " <font weight='400' style='normal'>c3em.ttf</font>" + + " </family>" + + "</familyset>"; + final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); + final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); + + buildSystemFallback(xml, fontMap, fallbackMap); + + final Paint paint = new Paint(); + + Typeface testTypeface = fontMap.get("serif"); + assertNotNull(testTypeface); + paint.setTypeface(testTypeface); + paint.setElegantTextHeight(true); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f); + assertEquals(GLYPH_3EM_WIDTH, paint.measureText("b"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f); + + paint.setElegantTextHeight(false); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f); + assertEquals(GLYPH_3EM_WIDTH, paint.measureText("c"), 0.0f); + + testTypeface = fontMap.get("sans-serif"); + assertNotNull(testTypeface); + paint.setTypeface(testTypeface); + paint.setElegantTextHeight(true); + assertEquals(GLYPH_3EM_WIDTH, paint.measureText("a"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f); + + paint.setElegantTextHeight(false); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f); + assertEquals(GLYPH_3EM_WIDTH, paint.measureText("c"), 0.0f); + } +} diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index 5af239616103..0001d7aba2ef 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -436,6 +436,7 @@ public class SettingsBackupTest { Settings.Secure.DISABLED_SYSTEM_INPUT_METHODS, Settings.Secure.DISPLAY_DENSITY_FORCED, Settings.Secure.DOZE_ALWAYS_ON, + Settings.Secure.DOZE_PULSE_ON_LONG_PRESS, Settings.Secure.EMERGENCY_ASSISTANCE_APPLICATION, Settings.Secure.ENABLED_NOTIFICATION_ASSISTANT, Settings.Secure.ENABLED_NOTIFICATION_LISTENERS, diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java index 7c07a302dfe9..80a9324d04f3 100644 --- a/graphics/java/android/graphics/FontListParser.java +++ b/graphics/java/android/graphics/FontListParser.java @@ -111,6 +111,7 @@ public class FontListParser { String weightStr = parser.getAttributeValue(null, "weight"); int weight = weightStr == null ? 400 : Integer.parseInt(weightStr); boolean isItalic = "italic".equals(parser.getAttributeValue(null, "style")); + String fallbackFor = parser.getAttributeValue(null, "fallbackFor"); StringBuilder filename = new StringBuilder(); while (parser.next() != XmlPullParser.END_TAG) { if (parser.getEventType() == XmlPullParser.TEXT) { @@ -126,7 +127,7 @@ public class FontListParser { } String sanitizedName = FILENAME_WHITESPACE_PATTERN.matcher(filename).replaceAll(""); return new FontConfig.Font(sanitizedName, index, - axes.toArray(new FontVariationAxis[axes.size()]), weight, isItalic); + axes.toArray(new FontVariationAxis[axes.size()]), weight, isItalic, fallbackFor); } private static FontVariationAxis readAxis(XmlPullParser parser) diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java index c4b56c333c64..1d8b5830aa92 100644 --- a/graphics/java/android/graphics/Typeface.java +++ b/graphics/java/android/graphics/Typeface.java @@ -38,6 +38,7 @@ import android.os.ResultReceiver; import android.provider.FontRequest; import android.provider.FontsContract; import android.text.FontConfig; +import android.util.ArrayMap; import android.util.Base64; import android.util.Log; import android.util.LongSparseArray; @@ -45,6 +46,7 @@ import android.util.LruCache; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; import libcore.io.IoUtils; @@ -105,12 +107,10 @@ public class Typeface { private static final LruCache<String, Typeface> sDynamicTypefaceCache = new LruCache<>(16); static Typeface sDefaultTypeface; - static Map<String, Typeface> sSystemFontMap; - static FontFamily[] sFallbackFonts; + static final Map<String, Typeface> sSystemFontMap; + static final Map<String, FontFamily[]> sSystemFallbackMap; private static final Object sLock = new Object(); - static final String FONTS_CONFIG = "fonts.xml"; - /** * @hide */ @@ -129,6 +129,7 @@ public class Typeface { // Must be the same as the C++ constant in core/jni/android/graphics/FontFamily.cpp /** @hide */ public static final int RESOLVE_BY_FONT_TABLE = -1; + private static final String DEFAULT_FAMILY = "sans-serif"; // Style value for building typeface. private static final int STYLE_NORMAL = 0; @@ -163,28 +164,27 @@ public class Typeface { */ @Nullable public static Typeface createFromResources(AssetManager mgr, String path, int cookie) { - if (sFallbackFonts != null) { - synchronized (sDynamicTypefaceCache) { - final String key = Builder.createAssetUid( - mgr, path, 0 /* ttcIndex */, null /* axes */, - RESOLVE_BY_FONT_TABLE /* weight */, RESOLVE_BY_FONT_TABLE /* italic */); - Typeface typeface = sDynamicTypefaceCache.get(key); - if (typeface != null) return typeface; - - FontFamily fontFamily = new FontFamily(); - // TODO: introduce ttc index and variation settings to resource type font. - if (fontFamily.addFontFromAssetManager(mgr, path, cookie, false /* isAsset */, - 0 /* ttcIndex */, RESOLVE_BY_FONT_TABLE /* weight */, - RESOLVE_BY_FONT_TABLE /* italic */, null /* axes */)) { - if (!fontFamily.freeze()) { - return null; - } - FontFamily[] families = {fontFamily}; - typeface = createFromFamiliesWithDefault(families, - RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE); - sDynamicTypefaceCache.put(key, typeface); - return typeface; + synchronized (sDynamicTypefaceCache) { + final String key = Builder.createAssetUid( + mgr, path, 0 /* ttcIndex */, null /* axes */, + RESOLVE_BY_FONT_TABLE /* weight */, RESOLVE_BY_FONT_TABLE /* italic */, + DEFAULT_FAMILY); + Typeface typeface = sDynamicTypefaceCache.get(key); + if (typeface != null) return typeface; + + FontFamily fontFamily = new FontFamily(); + // TODO: introduce ttc index and variation settings to resource type font. + if (fontFamily.addFontFromAssetManager(mgr, path, cookie, false /* isAsset */, + 0 /* ttcIndex */, RESOLVE_BY_FONT_TABLE /* weight */, + RESOLVE_BY_FONT_TABLE /* italic */, null /* axes */)) { + if (!fontFamily.freeze()) { + return null; } + FontFamily[] families = {fontFamily}; + typeface = createFromFamiliesWithDefault(families, DEFAULT_FAMILY, + RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE); + sDynamicTypefaceCache.put(key, typeface); + return typeface; } } return null; @@ -197,61 +197,57 @@ public class Typeface { @Nullable public static Typeface createFromResources( FamilyResourceEntry entry, AssetManager mgr, String path) { - if (sFallbackFonts != null) { - if (entry instanceof ProviderResourceEntry) { - final ProviderResourceEntry providerEntry = (ProviderResourceEntry) entry; - // Downloadable font - List<List<String>> givenCerts = providerEntry.getCerts(); - List<List<byte[]>> certs = new ArrayList<>(); - if (givenCerts != null) { - for (int i = 0; i < givenCerts.size(); i++) { - List<String> certSet = givenCerts.get(i); - List<byte[]> byteArraySet = new ArrayList<>(); - for (int j = 0; j < certSet.size(); j++) { - byteArraySet.add(Base64.decode(certSet.get(j), Base64.DEFAULT)); - } - certs.add(byteArraySet); + if (entry instanceof ProviderResourceEntry) { + final ProviderResourceEntry providerEntry = (ProviderResourceEntry) entry; + // Downloadable font + List<List<String>> givenCerts = providerEntry.getCerts(); + List<List<byte[]>> certs = new ArrayList<>(); + if (givenCerts != null) { + for (int i = 0; i < givenCerts.size(); i++) { + List<String> certSet = givenCerts.get(i); + List<byte[]> byteArraySet = new ArrayList<>(); + for (int j = 0; j < certSet.size(); j++) { + byteArraySet.add(Base64.decode(certSet.get(j), Base64.DEFAULT)); } + certs.add(byteArraySet); } - // Downloaded font and it wasn't cached, request it again and return a - // default font instead (nothing we can do now). - FontRequest request = new FontRequest(providerEntry.getAuthority(), - providerEntry.getPackage(), providerEntry.getQuery(), certs); - Typeface typeface = FontsContract.getFontSync(request); - return typeface == null ? DEFAULT : typeface; } + // Downloaded font and it wasn't cached, request it again and return a + // default font instead (nothing we can do now). + FontRequest request = new FontRequest(providerEntry.getAuthority(), + providerEntry.getPackage(), providerEntry.getQuery(), certs); + Typeface typeface = FontsContract.getFontSync(request); + return typeface == null ? DEFAULT : typeface; + } - Typeface typeface = findFromCache(mgr, path); - if (typeface != null) return typeface; + Typeface typeface = findFromCache(mgr, path); + if (typeface != null) return typeface; - // family is FontFamilyFilesResourceEntry - final FontFamilyFilesResourceEntry filesEntry = - (FontFamilyFilesResourceEntry) entry; + // family is FontFamilyFilesResourceEntry + final FontFamilyFilesResourceEntry filesEntry = (FontFamilyFilesResourceEntry) entry; - FontFamily fontFamily = new FontFamily(); - for (final FontFileResourceEntry fontFile : filesEntry.getEntries()) { - // TODO: Add ttc and variation font support. (b/37853920) - if (!fontFamily.addFontFromAssetManager(mgr, fontFile.getFileName(), - 0 /* resourceCookie */, false /* isAsset */, 0 /* ttcIndex */, - fontFile.getWeight(), fontFile.getItalic(), null /* axes */)) { - return null; - } - } - if (!fontFamily.freeze()) { + FontFamily fontFamily = new FontFamily(); + for (final FontFileResourceEntry fontFile : filesEntry.getEntries()) { + // TODO: Add ttc and variation font support. (b/37853920) + if (!fontFamily.addFontFromAssetManager(mgr, fontFile.getFileName(), + 0 /* resourceCookie */, false /* isAsset */, 0 /* ttcIndex */, + fontFile.getWeight(), fontFile.getItalic(), null /* axes */)) { return null; } - FontFamily[] familyChain = { fontFamily }; - typeface = createFromFamiliesWithDefault(familyChain, - RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE); - synchronized (sDynamicTypefaceCache) { - final String key = Builder.createAssetUid(mgr, path, 0 /* ttcIndex */, - null /* axes */, RESOLVE_BY_FONT_TABLE /* weight */, - RESOLVE_BY_FONT_TABLE /* italic */); - sDynamicTypefaceCache.put(key, typeface); - } - return typeface; } - return null; + if (!fontFamily.freeze()) { + return null; + } + FontFamily[] familyChain = { fontFamily }; + typeface = createFromFamiliesWithDefault(familyChain, DEFAULT_FAMILY, + RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE); + synchronized (sDynamicTypefaceCache) { + final String key = Builder.createAssetUid(mgr, path, 0 /* ttcIndex */, + null /* axes */, RESOLVE_BY_FONT_TABLE /* weight */, + RESOLVE_BY_FONT_TABLE /* italic */, DEFAULT_FAMILY); + sDynamicTypefaceCache.put(key, typeface); + } + return typeface; } /** @@ -261,7 +257,8 @@ public class Typeface { public static Typeface findFromCache(AssetManager mgr, String path) { synchronized (sDynamicTypefaceCache) { final String key = Builder.createAssetUid(mgr, path, 0 /* ttcIndex */, null /* axes */, - RESOLVE_BY_FONT_TABLE /* weight */, RESOLVE_BY_FONT_TABLE /* italic */); + RESOLVE_BY_FONT_TABLE /* weight */, RESOLVE_BY_FONT_TABLE /* italic */, + DEFAULT_FAMILY); Typeface typeface = sDynamicTypefaceCache.get(key); if (typeface != null) { return typeface; @@ -498,7 +495,7 @@ public class Typeface { * @return Unique id for a given AssetManager and asset path. */ private static String createAssetUid(final AssetManager mgr, String path, int ttcIndex, - @Nullable FontVariationAxis[] axes, int weight, int italic) { + @Nullable FontVariationAxis[] axes, int weight, int italic, String fallback) { final SparseArray<String> pkgs = mgr.getAssignedPackageIdentifiers(); final StringBuilder builder = new StringBuilder(); final int size = pkgs.size(); @@ -513,7 +510,11 @@ public class Typeface { builder.append(Integer.toString(weight)); builder.append("-"); builder.append(Integer.toString(italic)); - builder.append("-"); + // Family name may contain hyphen. Use double hyphen for avoiding key conflicts before + // and after appending falblack name. + builder.append("--"); + builder.append(fallback); + builder.append("--"); if (axes != null) { for (FontVariationAxis axis : axes) { builder.append(axis.getTag()); @@ -593,13 +594,15 @@ public class Typeface { return resolveFallbackTypeface(); } FontFamily[] families = { fontFamily }; - return createFromFamiliesWithDefault(families, mWeight, mItalic); + return createFromFamiliesWithDefault(families, mFallbackFamilyName, mWeight, + mItalic); } catch (IOException e) { return resolveFallbackTypeface(); } } else if (mAssetManager != null) { // Builder is created with asset manager. final String key = createAssetUid( - mAssetManager, mPath, mTtcIndex, mAxes, mWeight, mItalic); + mAssetManager, mPath, mTtcIndex, mAxes, mWeight, mItalic, + mFallbackFamilyName); synchronized (sLock) { Typeface typeface = sDynamicTypefaceCache.get(key); if (typeface != null) return typeface; @@ -613,7 +616,8 @@ public class Typeface { return resolveFallbackTypeface(); } FontFamily[] families = { fontFamily }; - typeface = createFromFamiliesWithDefault(families, mWeight, mItalic); + typeface = createFromFamiliesWithDefault(families, mFallbackFamilyName, + mWeight, mItalic); sDynamicTypefaceCache.put(key, typeface); return typeface; } @@ -627,7 +631,8 @@ public class Typeface { return resolveFallbackTypeface(); } FontFamily[] families = { fontFamily }; - return createFromFamiliesWithDefault(families, mWeight, mItalic); + return createFromFamiliesWithDefault(families, mFallbackFamilyName, mWeight, + mItalic); } else if (mFonts != null) { final FontFamily fontFamily = new FontFamily(); boolean atLeastOneFont = false; @@ -653,7 +658,8 @@ public class Typeface { } fontFamily.freeze(); FontFamily[] families = { fontFamily }; - return createFromFamiliesWithDefault(families, mWeight, mItalic); + return createFromFamiliesWithDefault(families, mFallbackFamilyName, mWeight, + mItalic); } // Must not reach here. @@ -673,10 +679,7 @@ public class Typeface { * @return The best matching typeface. */ public static Typeface create(String familyName, int style) { - if (sSystemFontMap != null) { - return create(sSystemFontMap.get(familyName), style); - } - return null; + return create(sSystemFontMap.get(familyName), style); } /** @@ -751,34 +754,33 @@ public class Typeface { if (path == null) { throw new NullPointerException(); // for backward compatibility } - if (sFallbackFonts != null) { - synchronized (sLock) { - Typeface typeface = new Builder(mgr, path).build(); - if (typeface != null) return typeface; + synchronized (sLock) { + Typeface typeface = new Builder(mgr, path).build(); + if (typeface != null) return typeface; - final String key = Builder.createAssetUid(mgr, path, 0 /* ttcIndex */, - null /* axes */, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE); - typeface = sDynamicTypefaceCache.get(key); - if (typeface != null) return typeface; + final String key = Builder.createAssetUid(mgr, path, 0 /* ttcIndex */, + null /* axes */, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE, + DEFAULT_FAMILY); + typeface = sDynamicTypefaceCache.get(key); + if (typeface != null) return typeface; - final FontFamily fontFamily = new FontFamily(); - if (fontFamily.addFontFromAssetManager(mgr, path, 0, true /* isAsset */, - 0 /* ttc index */, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE, - null /* axes */)) { - // Due to backward compatibility, even if the font is not supported by our font - // stack, we need to place the empty font at the first place. The typeface with - // empty font behaves different from default typeface especially in fallback - // font selection. - fontFamily.allowUnsupportedFont(); - fontFamily.freeze(); - final FontFamily[] families = { fontFamily }; - typeface = createFromFamiliesWithDefault(families, RESOLVE_BY_FONT_TABLE, - RESOLVE_BY_FONT_TABLE); - sDynamicTypefaceCache.put(key, typeface); - return typeface; - } else { - fontFamily.abortCreation(); - } + final FontFamily fontFamily = new FontFamily(); + if (fontFamily.addFontFromAssetManager(mgr, path, 0, true /* isAsset */, + 0 /* ttc index */, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE, + null /* axes */)) { + // Due to backward compatibility, even if the font is not supported by our font + // stack, we need to place the empty font at the first place. The typeface with + // empty font behaves different from default typeface especially in fallback + // font selection. + fontFamily.allowUnsupportedFont(); + fontFamily.freeze(); + final FontFamily[] families = { fontFamily }; + typeface = createFromFamiliesWithDefault(families, DEFAULT_FAMILY, + RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE); + sDynamicTypefaceCache.put(key, typeface); + return typeface; + } else { + fontFamily.abortCreation(); } } throw new RuntimeException("Font asset not found " + path); @@ -815,22 +817,20 @@ public class Typeface { * @return The new typeface. */ public static Typeface createFromFile(@Nullable String path) { - if (sFallbackFonts != null) { - final FontFamily fontFamily = new FontFamily(); - if (fontFamily.addFont(path, 0 /* ttcIndex */, null /* axes */, - RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE)) { - // Due to backward compatibility, even if the font is not supported by our font - // stack, we need to place the empty font at the first place. The typeface with - // empty font behaves different from default typeface especially in fallback font - // selection. - fontFamily.allowUnsupportedFont(); - fontFamily.freeze(); - FontFamily[] families = { fontFamily }; - return createFromFamiliesWithDefault(families, RESOLVE_BY_FONT_TABLE, - RESOLVE_BY_FONT_TABLE); - } else { - fontFamily.abortCreation(); - } + final FontFamily fontFamily = new FontFamily(); + if (fontFamily.addFont(path, 0 /* ttcIndex */, null /* axes */, + RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE)) { + // Due to backward compatibility, even if the font is not supported by our font + // stack, we need to place the empty font at the first place. The typeface with + // empty font behaves different from default typeface especially in fallback font + // selection. + fontFamily.allowUnsupportedFont(); + fontFamily.freeze(); + FontFamily[] families = { fontFamily }; + return createFromFamiliesWithDefault(families, DEFAULT_FAMILY, + RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE); + } else { + fontFamily.abortCreation(); } throw new RuntimeException("Font not found " + path); } @@ -852,6 +852,8 @@ public class Typeface { /** * Create a new typeface from an array of font families, including * also the font families in the fallback list. + * @param fallbackName the family name. If given families don't support characters, the + * characters will be rendered with this family. * @param weight the weight for this family. {@link RESOLVE_BY_FONT_TABLE} can be used. In that * case, the table information in the first family's font is used. If the first * family has multiple fonts, the closest to the regular weight and upright font @@ -863,13 +865,17 @@ public class Typeface { * @param families array of font families */ private static Typeface createFromFamiliesWithDefault(FontFamily[] families, - int weight, int italic) { - long[] ptrArray = new long[families.length + sFallbackFonts.length]; + String fallbackName, int weight, int italic) { + FontFamily[] fallback = sSystemFallbackMap.get(fallbackName); + if (fallback == null) { + fallback = sSystemFallbackMap.get(DEFAULT_FAMILY); + } + long[] ptrArray = new long[families.length + fallback.length]; for (int i = 0; i < families.length; i++) { ptrArray[i] = families[i].mNativePtr; } - for (int i = 0; i < sFallbackFonts.length; i++) { - ptrArray[i + families.length] = sFallbackFonts[i].mNativePtr; + for (int i = 0; i < fallback.length; i++) { + ptrArray[i + families.length] = fallback[i].mNativePtr; } return new Typeface(nativeCreateFromArray(ptrArray, weight, italic)); } @@ -885,113 +891,189 @@ public class Typeface { mWeight = nativeGetWeight(ni); } - private static FontFamily makeFamilyFromParsed(FontConfig.Family family, - Map<String, ByteBuffer> bufferForPath) { - FontFamily fontFamily = new FontFamily(family.getLanguage(), family.getVariant()); - for (FontConfig.Font font : family.getFonts()) { - String fullPathName = "/system/fonts/" + font.getFontName(); - ByteBuffer fontBuffer = bufferForPath.get(fullPathName); - if (fontBuffer == null) { - try (FileInputStream file = new FileInputStream(fullPathName)) { - FileChannel fileChannel = file.getChannel(); - long fontSize = fileChannel.size(); - fontBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fontSize); - bufferForPath.put(fullPathName, fontBuffer); - } catch (IOException e) { - Log.e(TAG, "Error mapping font file " + fullPathName); + private static @Nullable ByteBuffer mmap(String fullPath) { + try (FileInputStream file = new FileInputStream(fullPath)) { + final FileChannel fileChannel = file.getChannel(); + final long fontSize = fileChannel.size(); + return fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fontSize); + } catch (IOException e) { + Log.e(TAG, "Error mapping font file " + fullPath); + return null; + } + } + + private static @Nullable FontFamily createFontFamily( + String familyName, List<FontConfig.Font> fonts, String languageTag, int variant, + Map<String, ByteBuffer> cache, String fontDir) { + final FontFamily family = new FontFamily(languageTag, variant); + for (int i = 0; i < fonts.size(); i++) { + final FontConfig.Font font = fonts.get(i); + final String fullPath = fontDir + font.getFontName(); + ByteBuffer buffer = cache.get(fullPath); + if (buffer == null) { + if (cache.containsKey(fullPath)) { + continue; // Already failed to mmap. Skip it. + } + buffer = mmap(fullPath); + cache.put(fullPath, buffer); + if (buffer == null) { continue; } } - if (!fontFamily.addFontFromBuffer(fontBuffer, font.getTtcIndex(), font.getAxes(), + if (!family.addFontFromBuffer(buffer, font.getTtcIndex(), font.getAxes(), font.getWeight(), font.isItalic() ? STYLE_ITALIC : STYLE_NORMAL)) { - Log.e(TAG, "Error creating font " + fullPathName + "#" + font.getTtcIndex()); + Log.e(TAG, "Error creating font " + fullPath + "#" + font.getTtcIndex()); } } - if (!fontFamily.freeze()) { - // Treat as system error since reaching here means that a system pre-installed font - // can't be used by our font stack. - Log.e(TAG, "Unable to load Family: " + family.getName() + ":" + family.getLanguage()); + if (!family.freeze()) { + Log.e(TAG, "Unable to load Family: " + familyName + " : " + languageTag); return null; } - return fontFamily; + return family; } - /* - * (non-Javadoc) + private static void pushFamilyToFallback(FontConfig.Family xmlFamily, + ArrayMap<String, ArrayList<FontFamily>> fallbackMap, + Map<String, ByteBuffer> cache, + String fontDir) { + + final String languageTag = xmlFamily.getLanguage(); + final int variant = xmlFamily.getVariant(); + + final ArrayList<FontConfig.Font> defaultFonts = new ArrayList<>(); + final ArrayMap<String, ArrayList<FontConfig.Font>> specificFallbackFonts = new ArrayMap<>(); + + // Collect default fallback and specific fallback fonts. + for (final FontConfig.Font font : xmlFamily.getFonts()) { + final String fallbackName = font.getFallbackFor(); + if (fallbackName == null) { + defaultFonts.add(font); + } else { + ArrayList<FontConfig.Font> fallback = specificFallbackFonts.get(fallbackName); + if (fallback == null) { + fallback = new ArrayList<>(); + specificFallbackFonts.put(fallbackName, fallback); + } + fallback.add(font); + } + } + + final FontFamily defaultFamily = defaultFonts.isEmpty() ? null : createFontFamily( + xmlFamily.getName(), defaultFonts, languageTag, variant, cache, fontDir); + + // Insert family into fallback map. + for (int i = 0; i < fallbackMap.size(); i++) { + final ArrayList<FontConfig.Font> fallback = + specificFallbackFonts.get(fallbackMap.keyAt(i)); + if (fallback == null) { + if (defaultFamily != null) { + fallbackMap.valueAt(i).add(defaultFamily); + } + } else { + final FontFamily family = createFontFamily( + xmlFamily.getName(), fallback, languageTag, variant, cache, fontDir); + if (family != null) { + fallbackMap.valueAt(i).add(family); + } + } + } + } + + /** + * Build the system fallback from xml file. * - * This should only be called once, from the static class initializer block. + * @param xmlPath A full path string to the fonts.xml file. + * @param fontDir A full path string to the system font directory. This must end with + * slash('/'). + * @param fontMap An output system font map. Caller must pass empty map. + * @param fallbackMap An output system fallback map. Caller must pass empty map. + * @hide */ - private static void init() { - // Load font config and initialize Minikin state - File systemFontConfigLocation = getSystemFontConfigLocation(); - File configFilename = new File(systemFontConfigLocation, FONTS_CONFIG); + @VisibleForTesting + public static void buildSystemFallback(String xmlPath, String fontDir, + ArrayMap<String, Typeface> fontMap, ArrayMap<String, FontFamily[]> fallbackMap) { try { - FileInputStream fontsIn = new FileInputStream(configFilename); - FontConfig fontConfig = FontListParser.parse(fontsIn); - - Map<String, ByteBuffer> bufferForPath = new HashMap<String, ByteBuffer>(); - - List<FontFamily> familyList = new ArrayList<FontFamily>(); - // Note that the default typeface is always present in the fallback list; - // this is an enhancement from pre-Minikin behavior. - for (int i = 0; i < fontConfig.getFamilies().length; i++) { - FontConfig.Family f = fontConfig.getFamilies()[i]; - if (i == 0 || f.getName() == null) { - FontFamily family = makeFamilyFromParsed(f, bufferForPath); - if (family != null) { - familyList.add(family); - } + final FileInputStream fontsIn = new FileInputStream(xmlPath); + final FontConfig fontConfig = FontListParser.parse(fontsIn); + + final HashMap<String, ByteBuffer> bufferCache = new HashMap<String, ByteBuffer>(); + final FontConfig.Family[] xmlFamilies = fontConfig.getFamilies(); + + final ArrayMap<String, ArrayList<FontFamily>> fallbackListMap = new ArrayMap<>(); + // First traverse families which have a 'name' attribute to create fallback map. + for (final FontConfig.Family xmlFamily : xmlFamilies) { + final String familyName = xmlFamily.getName(); + if (familyName == null) { + continue; + } + final FontFamily family = createFontFamily( + xmlFamily.getName(), Arrays.asList(xmlFamily.getFonts()), + xmlFamily.getLanguage(), xmlFamily.getVariant(), bufferCache, fontDir); + if (family == null) { + continue; } + final ArrayList<FontFamily> fallback = new ArrayList<>(); + fallback.add(family); + fallbackListMap.put(familyName, fallback); } - sFallbackFonts = familyList.toArray(new FontFamily[familyList.size()]); - setDefault(Typeface.createFromFamilies(sFallbackFonts)); - - Map<String, Typeface> systemFonts = new HashMap<String, Typeface>(); - for (int i = 0; i < fontConfig.getFamilies().length; i++) { - Typeface typeface; - FontConfig.Family f = fontConfig.getFamilies()[i]; - if (f.getName() != null) { - if (i == 0) { - // The first entry is the default typeface; no sense in - // duplicating the corresponding FontFamily. - typeface = sDefaultTypeface; - } else { - FontFamily fontFamily = makeFamilyFromParsed(f, bufferForPath); - if (fontFamily == null) { - continue; - } - FontFamily[] families = { fontFamily }; - typeface = Typeface.createFromFamiliesWithDefault(families, - RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE); - } - systemFonts.put(f.getName(), typeface); + + // Then, add fallback fonts to the each fallback map. + for (int i = 0; i < xmlFamilies.length; i++) { + final FontConfig.Family xmlFamily = xmlFamilies[i]; + // The first family (usually the sans-serif family) is always placed immediately + // after the primary family in the fallback. + if (i == 0 || xmlFamily.getName() == null) { + pushFamilyToFallback(xmlFamily, fallbackListMap, bufferCache, fontDir); + } + } + + // Build the font map and fallback map. + for (int i = 0; i < fallbackListMap.size(); i++) { + final String fallbackName = fallbackListMap.keyAt(i); + final List<FontFamily> familyList = fallbackListMap.valueAt(i); + final FontFamily[] families = familyList.toArray(new FontFamily[familyList.size()]); + + fallbackMap.put(fallbackName, families); + final long[] ptrArray = new long[families.length]; + for (int j = 0; j < families.length; j++) { + ptrArray[j] = families[j].mNativePtr; } + fontMap.put(fallbackName, new Typeface(nativeCreateFromArray( + ptrArray, RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE))); } - for (FontConfig.Alias alias : fontConfig.getAliases()) { - Typeface base = systemFonts.get(alias.getToName()); + + // Insert alias to font maps. + for (final FontConfig.Alias alias : fontConfig.getAliases()) { + Typeface base = fontMap.get(alias.getToName()); Typeface newFace = base; int weight = alias.getWeight(); if (weight != 400) { newFace = new Typeface(nativeCreateWeightAlias(base.native_instance, weight)); } - systemFonts.put(alias.getName(), newFace); + fontMap.put(alias.getName(), newFace); } - sSystemFontMap = systemFonts; - } catch (RuntimeException e) { Log.w(TAG, "Didn't create default family (most likely, non-Minikin build)", e); // TODO: normal in non-Minikin case, remove or make error when Minikin-only } catch (FileNotFoundException e) { - Log.e(TAG, "Error opening " + configFilename, e); + Log.e(TAG, "Error opening " + xmlPath, e); } catch (IOException e) { - Log.e(TAG, "Error reading " + configFilename, e); + Log.e(TAG, "Error reading " + xmlPath, e); } catch (XmlPullParserException e) { - Log.e(TAG, "XML parse exception for " + configFilename, e); + Log.e(TAG, "XML parse exception for " + xmlPath, e); } } static { - init(); + final ArrayMap<String, Typeface> systemFontMap = new ArrayMap<>(); + final ArrayMap<String, FontFamily[]> systemFallbackMap = new ArrayMap<>(); + buildSystemFallback("/system/etc/fonts.xml", "/system/fonts/", systemFontMap, + systemFallbackMap); + sSystemFontMap = Collections.unmodifiableMap(systemFontMap); + sSystemFallbackMap = Collections.unmodifiableMap(systemFallbackMap); + + setDefault(sSystemFontMap.get(DEFAULT_FAMILY)); + // Set up defaults and typefaces exposed in public API DEFAULT = create((String) null, 0); DEFAULT_BOLD = create((String) null, Typeface.BOLD); @@ -1008,10 +1090,6 @@ public class Typeface { } - private static File getSystemFontConfigLocation() { - return new File("/system/etc/"); - } - @Override protected void finalize() throws Throwable { try { diff --git a/libs/hwui/hwui/Typeface.cpp b/libs/hwui/hwui/Typeface.cpp index f66bb045373c..d96e376b0b70 100644 --- a/libs/hwui/hwui/Typeface.cpp +++ b/libs/hwui/hwui/Typeface.cpp @@ -68,7 +68,7 @@ static minikin::FontStyle computeRelativeStyle(int baseWeight, SkTypeface::Style Typeface* gDefaultTypeface = NULL; Typeface* Typeface::resolveDefault(Typeface* src) { - LOG_ALWAYS_FATAL_IF(gDefaultTypeface == nullptr); + LOG_ALWAYS_FATAL_IF(src == nullptr && gDefaultTypeface == nullptr); return src == nullptr ? gDefaultTypeface : src; } diff --git a/lowpan/java/android/net/lowpan/LowpanCommissioningSession.java b/lowpan/java/android/net/lowpan/LowpanCommissioningSession.java index 1da085ddb294..ee75f1aece5c 100644 --- a/lowpan/java/android/net/lowpan/LowpanCommissioningSession.java +++ b/lowpan/java/android/net/lowpan/LowpanCommissioningSession.java @@ -40,7 +40,7 @@ public abstract class LowpanCommissioningSession { * @hide */ // @SystemApi - public class Callback { + public static abstract class Callback { public void onReceiveFromCommissioner(@NonNull byte[] packet) {}; public void onClosed() {}; diff --git a/packages/SettingsLib/res/layout/preference_category_material_settings.xml b/packages/SettingsLib/res/layout/preference_category_material_settings.xml index 741435ec9b43..245e3b741630 100644 --- a/packages/SettingsLib/res/layout/preference_category_material_settings.xml +++ b/packages/SettingsLib/res/layout/preference_category_material_settings.xml @@ -51,6 +51,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:textAppearance="@android:style/TextAppearance.Material.Body2" + android:textAlignment="viewStart" android:textColor="?android:attr/colorAccent" android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"/> <TextView diff --git a/packages/SettingsLib/res/values/attrs.xml b/packages/SettingsLib/res/values/attrs.xml index 6d852df2f907..a8a179358744 100644 --- a/packages/SettingsLib/res/values/attrs.xml +++ b/packages/SettingsLib/res/values/attrs.xml @@ -48,9 +48,4 @@ <attr name="footerPreferenceStyle" format="reference" /> - <declare-styleable name="PreferenceImageView"> - <attr name="maxWidth" format="dimension" /> - <attr name="maxHeight" format="dimension" /> - </declare-styleable> - </resources> diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java index 9ccd33226258..5a35da96375a 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java @@ -365,7 +365,7 @@ public class WifiTracker { mConnectivityManager.unregisterNetworkCallback(mNetworkCallback); mRegistered = false; } - unregisterAndClearScoreCache(); + unregisterScoreCache(); pauseScanning(); mContext.getContentResolver().unregisterContentObserver(mObserver); @@ -375,11 +375,14 @@ public class WifiTracker { mStaleScanResults = true; } - private void unregisterAndClearScoreCache() { + private void unregisterScoreCache() { mNetworkScoreManager.unregisterNetworkScoreCache(NetworkKey.TYPE_WIFI, mScoreCache); - mScoreCache.clearScores(); - // Synchronize on mLock to avoid concurrent modification during updateAccessPoints + // We do not want to clear the existing scores in the cache, as this method is called during + // stop tracking on activity pause. Hence, on resumption we want the ability to show the + // last known, potentially stale, scores. However, by clearing requested scores, the scores + // will be requested again upon resumption of tracking, and if any changes have occurred + // the listeners (UI) will be updated accordingly. synchronized (mLock) { mRequestedScores.clear(); } diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java index b6d0c457db7c..c87d01a7da8e 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java @@ -473,6 +473,17 @@ public class WifiTrackerTest { } @Test + public void stopTracking_shouldNotClearExistingScores() + throws InterruptedException { + // Start the tracker and inject the initial scan results and then stop tracking + WifiTracker tracker = createTrackerWithImmediateBroadcastsAndInjectInitialScanResults(); + updateScoresAndWaitForAccessPointsChangedCallback(tracker); + tracker.stopTracking(); + + assertThat(mScoreCacheCaptor.getValue().getScoredNetwork(NETWORK_KEY_1)).isNotNull(); + } + + @Test public void scoreCacheUpdateScoresShouldTriggerOnAccessPointsChanged() throws InterruptedException { WifiTracker tracker = createMockedWifiTracker(); diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml index 795f20ee814e..9eceeb40d6d5 100644 --- a/packages/SystemUI/res-keyguard/values/styles.xml +++ b/packages/SystemUI/res-keyguard/values/styles.xml @@ -38,7 +38,7 @@ <item name="android:paddingBottom">-16dp</item> </style> <style name="Keyguard.ImageButton.NumPadEnter" parent="@android:style/Widget.ImageButton"> - <item name="android:tint">?attr/bgProtectTextColor</item> + <item name="android:tint">@color/background_protected</item> </style> <style name="Widget.TextView.NumPadKey.Klondike" parent="Widget.TextView.NumPadKey"> <item name="android:textSize">12sp</item> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index e9dd7e66b96a..37310d254c75 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -308,6 +308,7 @@ <item name="darkIconTheme">@style/DualToneDarkTheme</item> <item name="bgProtectTextColor">?android:attr/textColorPrimaryInverse</item> <item name="bgProtectSecondaryTextColor">?android:attr/textColorSecondaryInverse</item> + <item name="android:colorControlHighlight">?android:attr/textColorSecondaryInverse</item> <item name="*android:lockPatternStyle">@style/LockPatternStyle</item> </style> diff --git a/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java b/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java index ccb81172c75c..3c895abd5e88 100644 --- a/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java +++ b/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java @@ -16,6 +16,7 @@ package com.android.systemui.colorextraction; +import android.app.WallpaperColors; import android.app.WallpaperManager; import android.content.Context; import android.os.Handler; @@ -47,10 +48,10 @@ public class SysuiColorExtractor extends ColorExtractor { @VisibleForTesting public SysuiColorExtractor(Context context, ExtractionType type, boolean registerVisibility) { super(context, type); - mWpHiddenColors = new GradientColors(); - mWpHiddenColors.setMainColor(FALLBACK_COLOR); - mWpHiddenColors.setSecondaryColor(FALLBACK_COLOR); + + WallpaperColors systemColors = getWallpaperColors(WallpaperManager.FLAG_SYSTEM); + updateDefaultGradients(systemColors); if (registerVisibility) { try { @@ -71,6 +72,24 @@ public class SysuiColorExtractor extends ColorExtractor { } } + private void updateDefaultGradients(WallpaperColors colors) { + Tonal.applyFallback(colors, mWpHiddenColors); + } + + @Override + public void onColorsChanged(WallpaperColors colors, int which) { + super.onColorsChanged(colors, which); + + if ((which & WallpaperManager.FLAG_SYSTEM) != 0) { + updateDefaultGradients(colors); + } + } + + @VisibleForTesting + GradientColors getFallbackColors() { + return mWpHiddenColors; + } + /** * Get TYPE_NORMAL colors when wallpaper is visible, or fallback otherwise. * diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java index ce0a151aff28..0993ace8cfcc 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java @@ -35,7 +35,7 @@ public class DozeLog { private static final int SIZE = Build.IS_DEBUGGABLE ? 400 : 50; static final SimpleDateFormat FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS"); - private static final int PULSE_REASONS = 5; + private static final int PULSE_REASONS = 6; public static final int PULSE_REASON_NONE = -1; public static final int PULSE_REASON_INTENT = 0; @@ -43,6 +43,7 @@ public class DozeLog { public static final int PULSE_REASON_SENSOR_SIGMOTION = 2; public static final int PULSE_REASON_SENSOR_PICKUP = 3; public static final int PULSE_REASON_SENSOR_DOUBLE_TAP = 4; + public static final int PULSE_REASON_SENSOR_LONG_PRESS = 5; private static boolean sRegisterKeyguardCallback = true; @@ -179,6 +180,7 @@ public class DozeLog { case PULSE_REASON_SENSOR_SIGMOTION: return "sigmotion"; case PULSE_REASON_SENSOR_PICKUP: return "pickup"; case PULSE_REASON_SENSOR_DOUBLE_TAP: return "doubletap"; + case PULSE_REASON_SENSOR_LONG_PRESS: return "longpress"; default: throw new IllegalArgumentException("bad reason: " + pulseReason); } } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java index 545a1ea11be3..0d5527cf6cd9 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java @@ -98,7 +98,14 @@ public class DozeSensors { Settings.Secure.DOZE_PULSE_ON_DOUBLE_TAP, true /* configured */, DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP, - dozeParameters.doubleTapReportsTouchCoordinates()) + dozeParameters.doubleTapReportsTouchCoordinates()), + new TriggerSensor( + findSensorWithType(config.longPressSensorType()), + Settings.Secure.DOZE_PULSE_ON_LONG_PRESS, + false /* settingDef */, + true /* configured */, + DozeLog.PULSE_REASON_SENSOR_LONG_PRESS, + true /* reports touch coordinates */), }; mProxSensor = new ProxSensor(); @@ -263,6 +270,7 @@ public class DozeSensors { final int mPulseReason; final String mSetting; final boolean mReportsTouchCoordinates; + final boolean mSettingDefault; private boolean mRequested; private boolean mRegistered; @@ -270,8 +278,15 @@ public class DozeSensors { public TriggerSensor(Sensor sensor, String setting, boolean configured, int pulseReason, boolean reportsTouchCoordinates) { + this(sensor, setting, true /* settingDef */, configured, pulseReason, + reportsTouchCoordinates); + } + + public TriggerSensor(Sensor sensor, String setting, boolean settingDef, + boolean configured, int pulseReason, boolean reportsTouchCoordinates) { mSensor = sensor; mSetting = setting; + mSettingDefault = settingDef; mConfigured = configured; mPulseReason = pulseReason; mReportsTouchCoordinates = reportsTouchCoordinates; @@ -305,7 +320,7 @@ public class DozeSensors { if (TextUtils.isEmpty(mSetting)) { return true; } - return Settings.Secure.getIntForUser(mResolver, mSetting, 1, + return Settings.Secure.getIntForUser(mResolver, mSetting, mSettingDefault ? 1 : 0, UserHandle.USER_CURRENT) != 0; } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java index ec6caf183c49..d1f5337b80b0 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java @@ -123,8 +123,9 @@ public class DozeTriggers implements DozeMachine.Part { float screenX, float screenY) { boolean isDoubleTap = pulseReason == DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP; boolean isPickup = pulseReason == DozeLog.PULSE_REASON_SENSOR_PICKUP; + boolean isLongPress = pulseReason == DozeLog.PULSE_REASON_SENSOR_LONG_PRESS; - if (mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT)) { + if (mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT) && !isLongPress) { proximityCheckThenCall((result) -> { if (result == ProximityCheck.RESULT_NEAR) { // In pocket, drop event. diff --git a/packages/SystemUI/src/com/android/systemui/qs/SlashDrawable.java b/packages/SystemUI/src/com/android/systemui/qs/SlashDrawable.java index a10aa5413ca9..b8535a3f5a40 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/SlashDrawable.java +++ b/packages/SystemUI/src/com/android/systemui/qs/SlashDrawable.java @@ -61,6 +61,7 @@ public class SlashDrawable extends Drawable { private boolean mSlashed; private Mode mTintMode; private ColorStateList mTintList; + private boolean mAnimationEnabled = true; public SlashDrawable(Drawable d) { mDrawable = d; @@ -97,6 +98,10 @@ public class SlashDrawable extends Drawable { invalidateSelf(); } + public void setAnimationEnabled(boolean enabled) { + mAnimationEnabled = enabled; + } + // Animate this value on change private float mCurrentSlashLength; private final FloatProperty mSlashLengthProp = new FloatProperty<SlashDrawable>("slashLength") { @@ -119,12 +124,15 @@ public class SlashDrawable extends Drawable { final float end = mSlashed ? SLASH_HEIGHT / SCALE : 0f; final float start = mSlashed ? 0f : SLASH_HEIGHT / SCALE; - ObjectAnimator anim = ObjectAnimator.ofFloat(this, mSlashLengthProp, start, end); - anim.addUpdateListener((ValueAnimator valueAnimator) -> { + if (mAnimationEnabled) { + ObjectAnimator anim = ObjectAnimator.ofFloat(this, mSlashLengthProp, start, end); + anim.addUpdateListener((ValueAnimator valueAnimator) -> invalidateSelf()); + anim.setDuration(QS_ANIM_LENGTH); + anim.start(); + } else { + mCurrentSlashLength = end; invalidateSelf(); - }); - anim.setDuration(QS_ANIM_LENGTH); - anim.start(); + } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java index 5ab3927ff098..8074cb9b0443 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java @@ -98,10 +98,14 @@ public class QSIconViewImpl extends QSIconView { d.setAutoMirrored(false); d.setLayoutDirection(getLayoutDirection()); } - iv.setImageDrawable(d); - if (state.slash != null && iv instanceof SlashImageView) { - ((SlashImageView) iv).setState(state.slash); + + if (iv instanceof SlashImageView) { + ((SlashImageView) iv).setAnimationEnabled(shouldAnimate); + ((SlashImageView) iv).setState(state.slash, d); + } else { + iv.setImageDrawable(d); } + iv.setTag(R.id.qs_icon_tag, state.icon); iv.setTag(R.id.qs_slash_tag, state.slash); iv.setPadding(0, padding, 0, padding); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SlashImageView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SlashImageView.java index 315a815af100..13912fe0c16d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SlashImageView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/SlashImageView.java @@ -14,8 +14,10 @@ package com.android.systemui.qs.tileimpl; +import android.annotation.Nullable; import android.content.Context; import android.graphics.drawable.Drawable; +import android.support.annotation.NonNull; import android.widget.ImageView; import com.android.internal.annotations.VisibleForTesting; @@ -26,6 +28,7 @@ public class SlashImageView extends ImageView { @VisibleForTesting protected SlashDrawable mSlash; + private boolean mAnimationEnabled = true; public SlashImageView(Context context) { super(context); @@ -34,6 +37,7 @@ public class SlashImageView extends ImageView { private void ensureSlashDrawable() { if (mSlash == null) { mSlash = new SlashDrawable(getDrawable()); + mSlash.setAnimationEnabled(mAnimationEnabled); super.setImageDrawable(mSlash); } } @@ -46,13 +50,28 @@ public class SlashImageView extends ImageView { } else if (mSlash == null) { super.setImageDrawable(drawable); } else { + mSlash.setAnimationEnabled(mAnimationEnabled); mSlash.setDrawable(drawable); } } - public void setState(SlashState slashState) { + public void setAnimationEnabled(boolean enabled) { + mAnimationEnabled = enabled; + } + + private void setSlashState(@NonNull SlashState slashState) { ensureSlashDrawable(); mSlash.setRotation(slashState.rotation); mSlash.setSlashed(slashState.isSlashed); } + + public void setState(@Nullable SlashState state, @Nullable Drawable drawable) { + if (state != null) { + setImageDrawable(drawable); + setSlashState(state); + } else { + mSlash = null; + setImageDrawable(drawable); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AnimatedImageView.java b/packages/SystemUI/src/com/android/systemui/statusbar/AnimatedImageView.java index e5aad0336061..ba92c451f764 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/AnimatedImageView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/AnimatedImageView.java @@ -32,7 +32,7 @@ public class AnimatedImageView extends ImageView { private final boolean mHasOverlappingRendering; AnimationDrawable mAnim; boolean mAttached; - private boolean mAllowAnimation; + private boolean mAllowAnimation = true; // Tracks the last image that was set, so that we don't refresh the image if it is exactly // the same as the previous one. If this is a resid, we track that. If it's a drawable, we diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java index a60102854618..e5f68ad60089 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java @@ -298,7 +298,8 @@ public class NotificationShelf extends ActivatableNotificationView implements private void updateNotificationClipHeight(ExpandableNotificationRow row, float notificationClipEnd) { float viewEnd = row.getTranslationY() + row.getActualHeight(); - boolean isPinned = row.isPinned() || row.isHeadsUpAnimatingAway(); + boolean isPinned = (row.isPinned() || row.isHeadsUpAnimatingAway()) + && !mAmbientState.isDozingAndNotPulsing(row); if (viewEnd > notificationClipEnd && (mAmbientState.isShadeExpanded() || !isPinned)) { int clipBottomAmount = (int) (viewEnd - notificationClipEnd); @@ -450,7 +451,7 @@ public class NotificationShelf extends ActivatableNotificationView implements ? fullTransitionAmount : transitionAmount; iconState.clampedAppearAmount = clampedAmount; - float contentTransformationAmount = !row.isAboveShelf() + float contentTransformationAmount = !mAmbientState.isAboveShelf(row) && (isLastChild || iconState.translateContent) ? iconTransitionAmount : 0.0f; @@ -519,7 +520,7 @@ public class NotificationShelf extends ActivatableNotificationView implements iconState.scaleY = 1.0f; iconState.hidden = false; } - if (row.isAboveShelf() || (!row.isInShelf() && (isLastChild && row.areGutsExposed() + if (mAmbientState.isAboveShelf(row) || (!row.isInShelf() && (isLastChild && row.areGutsExposed() || row.getTranslationZ() > mAmbientState.getBaseZHeight()))) { iconState.hidden = true; } 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 e0dbcc6987bc..3f5f4da2525f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -4607,11 +4607,13 @@ public class StatusBar extends SystemUI implements DemoMode, try { mOverlayManager.setEnabled("com.android.systemui.theme.lightwallpaper", useDarkText, mCurrentUserId); - mStatusBarWindowManager.setKeyguardDark(useDarkText); } catch (RemoteException e) { Log.w(TAG, "Can't change theme", e); } } + + // Make sure we have the correct navbar/statusbar colors. + mStatusBarWindowManager.setKeyguardDark(useDarkText); } private void updateDozingState() { @@ -5347,6 +5349,12 @@ public class StatusBar extends SystemUI implements DemoMode, @Override public void pulseWhileDozing(@NonNull PulseCallback callback, int reason) { + if (reason == DozeLog.PULSE_REASON_SENSOR_LONG_PRESS) { + mPowerManager.wakeUp(SystemClock.uptimeMillis(), "com.android.systemui:NODOZE"); + startAssist(new Bundle()); + return; + } + mDozeScrimController.pulse(new PulseCallback() { @Override @@ -7246,6 +7254,9 @@ public class StatusBar extends SystemUI implements DemoMode, if (mAccessibilityManager.isTouchExplorationEnabled()) { if (DEBUG) Log.d(TAG, "No peeking: accessible fullscreen: " + sbn.getKey()); return false; + } else if (mDozing) { + // We never want heads up when we are dozing. + return false; } else { // we only allow head-up on the lockscreen if it doesn't have a fullscreen intent return !mStatusBarKeyguardViewManager.isShowing() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java index ba1e7c2d86c5..4d8da441c039 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java @@ -21,11 +21,15 @@ import android.view.View; import com.android.systemui.R; import com.android.systemui.statusbar.ActivatableNotificationView; +import com.android.systemui.statusbar.ExpandableNotificationRow; +import com.android.systemui.statusbar.ExpandableView; +import com.android.systemui.statusbar.NotificationData; import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.policy.HeadsUpManager; import java.util.ArrayList; +import java.util.Collection; /** * A global state to track all input states for the algorithm. @@ -59,7 +63,7 @@ public class AmbientState { private boolean mPanelTracking; private boolean mExpansionChanging; private boolean mPanelFullWidth; - private boolean mHasPulsingNotifications; + private Collection<HeadsUpManager.HeadsUpEntry> mPulsing; private boolean mUnlockHintRunning; private boolean mQsCustomizerShowing; private int mIntrinsicPadding; @@ -290,11 +294,23 @@ public class AmbientState { } public boolean hasPulsingNotifications() { - return mHasPulsingNotifications; + return mPulsing != null; } - public void setHasPulsingNotifications(boolean hasPulsing) { - mHasPulsingNotifications = hasPulsing; + public void setPulsing(Collection<HeadsUpManager.HeadsUpEntry> hasPulsing) { + mPulsing = hasPulsing; + } + + public boolean isPulsing(NotificationData.Entry entry) { + if (mPulsing == null) { + return false; + } + for (HeadsUpManager.HeadsUpEntry e : mPulsing) { + if (e.entry == entry) { + return true; + } + } + return false; } public boolean isPanelTracking() { @@ -332,4 +348,34 @@ public class AmbientState { public int getIntrinsicPadding() { return mIntrinsicPadding; } + + /** + * Similar to the normal is above shelf logic but doesn't allow it to be above in AOD1. + * + * @param expandableView the view to check + */ + public boolean isAboveShelf(ExpandableView expandableView) { + if (!(expandableView instanceof ExpandableNotificationRow)) { + return expandableView.isAboveShelf(); + } + ExpandableNotificationRow row = (ExpandableNotificationRow) expandableView; + return row.isAboveShelf() && !isDozingAndNotPulsing(row); + } + + /** + * @return whether a view is dozing and not pulsing right now + */ + public boolean isDozingAndNotPulsing(ExpandableView view) { + if (view instanceof ExpandableNotificationRow) { + return isDozingAndNotPulsing((ExpandableNotificationRow) view); + } + return false; + } + + /** + * @return whether a row is dozing and not pulsing right now + */ + public boolean isDozingAndNotPulsing(ExpandableNotificationRow row) { + return isDark() && !isPulsing(row.getEntry()); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java index 74523e28c460..00973911ac0d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java @@ -805,7 +805,7 @@ public class NotificationStackScrollLayout extends ViewGroup */ private float getAppearStartPosition() { if (mTrackingHeadsUp && mFirstVisibleBackgroundChild != null) { - if (mFirstVisibleBackgroundChild.isAboveShelf()) { + if (mAmbientState.isAboveShelf(mFirstVisibleBackgroundChild)) { // If we ever expanded beyond the first notification, it's allowed to merge into // the shelf return mFirstVisibleBackgroundChild.getPinnedHeadsUpHeight(); @@ -823,7 +823,8 @@ public class NotificationStackScrollLayout extends ViewGroup int notGoneChildCount = getNotGoneChildCount(); if (mEmptyShadeView.getVisibility() == GONE && notGoneChildCount != 0) { int minNotificationsForShelf = 1; - if (mTrackingHeadsUp || mHeadsUpManager.hasPinnedHeadsUp()) { + if (mTrackingHeadsUp + || (mHeadsUpManager.hasPinnedHeadsUp() && !mAmbientState.isDark())) { appearPosition = mHeadsUpManager.getTopHeadsUpPinnedHeight(); minNotificationsForShelf = 2; } else { @@ -2008,12 +2009,7 @@ public class NotificationStackScrollLayout extends ViewGroup } private boolean isPulsing(NotificationData.Entry entry) { - for (HeadsUpManager.HeadsUpEntry e : mPulsing) { - if (e.entry == entry) { - return true; - } - } - return false; + return mAmbientState.isPulsing(entry); } public boolean hasPulsingNotifications() { @@ -4148,7 +4144,7 @@ public class NotificationStackScrollLayout extends ViewGroup return; } mPulsing = pulsing; - mAmbientState.setHasPulsingNotifications(hasPulsingNotifications()); + mAmbientState.setPulsing(pulsing); updateNotificationAnimationStates(); updateContentHeight(); notifyHeightChangeListener(mShelf); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java index 8235bc7dde07..f4197a3496ea 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java @@ -413,7 +413,7 @@ public class StackScrollAlgorithm { if (mIsExpanded) { // Ensure that the heads up is always visible even when scrolled off clampHunToTop(ambientState, row, childState); - if (i == 0 && row.isAboveShelf()) { + if (i == 0 && ambientState.isAboveShelf(row)) { // the first hun can't get off screen. clampHunToMaxTranslation(ambientState, row, childState); childState.hidden = false; @@ -515,7 +515,7 @@ public class StackScrollAlgorithm { ExpandableViewState childViewState = resultState.getViewStateForView(child); int zDistanceBetweenElements = ambientState.getZDistanceBetweenElements(); float baseZ = ambientState.getBaseZHeight(); - if (child.mustStayOnScreen() + if (child.mustStayOnScreen() && !ambientState.isDozingAndNotPulsing(child) && childViewState.yTranslation < ambientState.getTopPadding() + ambientState.getStackTranslation()) { if (childrenOnTop != 0.0f) { @@ -527,7 +527,7 @@ public class StackScrollAlgorithm { } childViewState.zTranslation = baseZ + childrenOnTop * zDistanceBetweenElements; - } else if (i == 0 && child.isAboveShelf()) { + } else if (i == 0 && ambientState.isAboveShelf(child)) { // In case this is a new view that has never been measured before, we don't want to // elevate if we are currently expanded more then the notification int shelfHeight = ambientState.getShelf().getIntrinsicHeight(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java b/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java index a81188af85ef..690186e91f55 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java +++ b/packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java @@ -48,14 +48,12 @@ public class SysuiColorExtractorTests extends SysuiTestCase { @Test public void getColors_usesGreyIfWallpaperNotVisible() { - ColorExtractor.GradientColors fallbackColors = new ColorExtractor.GradientColors(); - fallbackColors.setMainColor(ColorExtractor.FALLBACK_COLOR); - fallbackColors.setSecondaryColor(ColorExtractor.FALLBACK_COLOR); - SysuiColorExtractor extractor = new SysuiColorExtractor(getContext(), new Tonal(), false); simulateEvent(extractor); extractor.setWallpaperVisible(false); + ColorExtractor.GradientColors fallbackColors = extractor.getFallbackColors(); + for (int which : sWhich) { for (int type : sTypes) { assertEquals("Not using fallback!", extractor.getColors(which, type), @@ -76,7 +74,6 @@ public class SysuiColorExtractorTests extends SysuiTestCase { outGradientColorsNormal.set(colors); outGradientColorsDark.set(colors); outGradientColorsExtraDark.set(colors); - return true; }, false); simulateEvent(extractor); extractor.setWallpaperVisible(true); @@ -91,7 +88,7 @@ public class SysuiColorExtractorTests extends SysuiTestCase { private void simulateEvent(SysuiColorExtractor extractor) { // Let's fake a color event - extractor.onColorsChanged(new WallpaperColors(Color.valueOf(Color.BLACK), null, null, 0), + extractor.onColorsChanged(new WallpaperColors(Color.valueOf(Color.GREEN), null, null, 0), WallpaperManager.FLAG_SYSTEM | WallpaperManager.FLAG_LOCK); } }
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/SlashImageViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/SlashImageViewTest.java index aef584f8d986..9fe3e10b752e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/SlashImageViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/SlashImageViewTest.java @@ -38,16 +38,29 @@ public class SlashImageViewTest extends SysuiTestCase { private TestableSlashImageView mSlashView; @Test - public void testSetSlashStateCreatesSlashDrawable() { + public void testSetNonNullSlashStateCreatesSlashDrawable() { SlashState mockState = mock(SlashState.class); Drawable mockDrawable = mock(Drawable.class); mSlashView = new TestableSlashImageView(mContext); assertTrue(mSlashView.getSlashDrawable() == null); - mSlashView.setImageDrawable(mockDrawable); - mSlashView.setState(mockState); + mSlashView.setState(mockState, mockDrawable); + + assertTrue(mSlashView.getSlashDrawable() != null); + } + + @Test + public void testSetNullSlashStateRemovesSlashDrawable() { + SlashState mockState = mock(SlashState.class); + Drawable mockDrawable = mock(Drawable.class); + mSlashView = new TestableSlashImageView(mContext); + mSlashView.setState(mockState, mockDrawable); assertTrue(mSlashView.getSlashDrawable() != null); + + mSlashView.setState(null, mockDrawable); + + assertTrue(mSlashView.getSlashDrawable() == null); } @Test @@ -57,7 +70,7 @@ public class SlashImageViewTest extends SysuiTestCase { mSlashView = new TestableSlashImageView(mContext); mSlashView.setImageDrawable(mockDrawable); - mSlashView.setState(mockState); + mSlashView.setState(mockState, mockDrawable); mSlashView.setImageDrawable(null); assertTrue(mSlashView.getSlashDrawable() == null); diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto index 87ac46b9bfda..829217868f84 100644 --- a/proto/src/metrics_constants.proto +++ b/proto/src/metrics_constants.proto @@ -4218,6 +4218,22 @@ message MetricsEvent { // OS: O MR ACTION_SETTINGS_SMS_MIRRORING = 1084; + // ACTION: Chooser picked a ranked target. + // CATEGORY: GLOBAL_SYSTEM_UI + // OS: O MR + ACTION_TARGET_SELECTED = 1085; + + // FIELD - is category used in Chooser: 1. + // Type: int encoded boolean + // CATEGORY: GLOBAL_SYSTEM_UI + // OS: O MR + FIELD_IS_CATEGORY_USED = 1086; + + // FIELD - ranked position of selected target for Chooser. + // CATEGORY: GLOBAL_SYSTEM_UI + // OS: O MR + FIELD_RANKED_POSITION = 1087; + // Add new aosp constants above this line. // END OF AOSP CONSTANTS } diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java index 9486c157bacf..a02802859ca7 100644 --- a/services/backup/java/com/android/server/backup/BackupManagerService.java +++ b/services/backup/java/com/android/server/backup/BackupManagerService.java @@ -4875,6 +4875,7 @@ public class BackupManagerService implements BackupManagerServiceInterface { final int N = mPackages.size(); final byte[] buffer = new byte[8192]; for (int i = 0; i < N; i++) { + mBackupRunner = null; PackageInfo currentPackage = mPackages.get(i); String packageName = currentPackage.packageName; if (DEBUG) { @@ -5058,7 +5059,13 @@ public class BackupManagerService implements BackupManagerServiceInterface { } EventLog.writeEvent(EventLogTags.FULL_BACKUP_AGENT_FAILURE, packageName, "transport rejected"); - // Do nothing, clean up, and continue looping. + // This failure state can come either a-priori from the transport, or + // from the preflight pass. If we got as far as preflight, we now need + // to tear down the target process. + if (mBackupRunner != null) { + tearDownAgentAndKill(currentPackage.applicationInfo); + } + // ... and continue looping. } else if (backupPackageStatus == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) { sendBackupOnPackageResult(mBackupObserver, packageName, BackupManager.ERROR_TRANSPORT_QUOTA_EXCEEDED); @@ -5067,6 +5074,7 @@ public class BackupManagerService implements BackupManagerServiceInterface { EventLog.writeEvent(EventLogTags.FULL_BACKUP_QUOTA_EXCEEDED, packageName); } + tearDownAgentAndKill(currentPackage.applicationInfo); // Do nothing, clean up, and continue looping. } else if (backupPackageStatus == BackupTransport.AGENT_ERROR) { sendBackupOnPackageResult(mBackupObserver, packageName, @@ -5090,6 +5098,7 @@ public class BackupManagerService implements BackupManagerServiceInterface { EventLog.writeEvent(EventLogTags.FULL_BACKUP_TRANSPORT_FAILURE); // Abort entire backup pass. backupRunStatus = BackupManager.ERROR_TRANSPORT_ABORTED; + tearDownAgentAndKill(currentPackage.applicationInfo); return; } else { // Success! diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java index b18fa322bd2b..aceedf1ab805 100644 --- a/services/core/java/com/android/server/Watchdog.java +++ b/services/core/java/com/android/server/Watchdog.java @@ -85,8 +85,9 @@ public class Watchdog extends Thread { "android.hardware.bluetooth@1.0::IBluetoothHci", "android.hardware.camera.provider@2.4::ICameraProvider", "android.hardware.graphics.composer@2.1::IComposer", - "android.hardware.vr@1.0::IVr", - "android.hardware.media.omx@1.0::IOmx" + "android.hardware.media.omx@1.0::IOmx", + "android.hardware.sensors@1.0::ISensors", + "android.hardware.vr@1.0::IVr" ); static Watchdog sWatchdog; diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 04df10442aa6..03adcc417898 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -12499,10 +12499,10 @@ public class ActivityManagerService extends IActivityManager.Stub switch (mWakefulness) { case PowerManagerInternal.WAKEFULNESS_AWAKE: case PowerManagerInternal.WAKEFULNESS_DREAMING: - case PowerManagerInternal.WAKEFULNESS_DOZING: // Pause applications whenever the lock screen is shown or any sleep // tokens have been acquired. return mKeyguardController.isKeyguardShowing() || !mSleepTokens.isEmpty(); + case PowerManagerInternal.WAKEFULNESS_DOZING: case PowerManagerInternal.WAKEFULNESS_ASLEEP: default: // If we're asleep then pause applications unconditionally. @@ -21631,7 +21631,8 @@ public class ActivityManagerService extends IActivityManager.Stub if (DEBUG_PSS) Slog.d(TAG_PSS, "Requesting dump heap from " + myProc + " to " + heapdumpFile); - thread.dumpHeap(/* managed=*/ true, /* runGc= */ false, + thread.dumpHeap(/* managed= */ true, + /* mallocInfo= */ false, /* runGc= */ false, heapdumpFile.toString(), fd); } catch (RemoteException e) { } @@ -23394,8 +23395,8 @@ public class ActivityManagerService extends IActivityManager.Stub return proc; } - public boolean dumpHeap(String process, int userId, boolean managed, boolean runGc, - String path, ParcelFileDescriptor fd) throws RemoteException { + public boolean dumpHeap(String process, int userId, boolean managed, boolean mallocInfo, + boolean runGc, String path, ParcelFileDescriptor fd) throws RemoteException { try { synchronized (this) { @@ -23423,7 +23424,7 @@ public class ActivityManagerService extends IActivityManager.Stub } } - proc.thread.dumpHeap(managed, runGc, path, fd); + proc.thread.dumpHeap(managed, mallocInfo, runGc, path, fd); fd = null; return true; } diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index 5d242963dfb4..83eea9840043 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -784,6 +784,7 @@ final class ActivityManagerShellCommand extends ShellCommand { int runDumpHeap(PrintWriter pw) throws RemoteException { final PrintWriter err = getErrPrintWriter(); boolean managed = true; + boolean mallocInfo = false; int userId = UserHandle.USER_CURRENT; boolean runGc = false; @@ -799,6 +800,9 @@ final class ActivityManagerShellCommand extends ShellCommand { managed = false; } else if (opt.equals("-g")) { runGc = true; + } else if (opt.equals("-m")) { + managed = false; + mallocInfo = true; } else { err.println("Error: Unknown option: " + opt); return -1; @@ -814,7 +818,7 @@ final class ActivityManagerShellCommand extends ShellCommand { return -1; } - if (!mInterface.dumpHeap(process, userId, managed, runGc, heapFile, fd)) { + if (!mInterface.dumpHeap(process, userId, managed, mallocInfo, runGc, heapFile, fd)) { err.println("HEAP DUMP FAILED on process " + process); return -1; } diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index 4a573691b656..6f68c7a1df5f 100644 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -42,6 +42,7 @@ import static android.content.Intent.CATEGORY_LAUNCHER; import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; import static android.content.Intent.FLAG_ACTIVITY_NO_HISTORY; import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION; +import static android.content.pm.ActivityInfo.CONFIG_ROTATION; import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT; import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE; import static android.content.pm.ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE; @@ -76,6 +77,7 @@ import static android.os.Build.VERSION_CODES.HONEYCOMB; import static android.os.Build.VERSION_CODES.O; import static android.os.Process.SYSTEM_UID; import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER; +import static android.view.WindowManagerPolicy.NAV_BAR_LEFT; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SAVED_STATE; @@ -155,6 +157,7 @@ import android.view.IAppTransitionAnimationSpecsFuture; import android.view.IApplicationToken; import android.view.WindowManager.LayoutParams; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.ResolverActivity; import com.android.internal.content.ReferrerIntent; import com.android.internal.util.XmlUtils; @@ -2303,10 +2306,12 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo outBounds.setEmpty(); final float maxAspectRatio = info.maxAspectRatio; final ActivityStack stack = getStack(); - if (task == null || stack == null || !task.mFullscreen || maxAspectRatio == 0) { + if (task == null || stack == null || !task.mFullscreen || maxAspectRatio == 0 + || isInVrUiMode(getConfiguration())) { // We don't set override configuration if that activity task isn't fullscreen. I.e. the // activity is in multi-window mode. Or, there isn't a max aspect ratio specified for - // the activity. This is indicated by an empty {@link outBounds}. + // the activity. This is indicated by an empty {@link outBounds}. We also don't set it + // if we are in VR mode. return; } @@ -2342,6 +2347,17 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo // Compute configuration based on max supported width and height. outBounds.set(0, 0, maxActivityWidth, maxActivityHeight); + // Position the activity frame on the opposite side of the nav bar. + final int navBarPosition = service.mWindowManager.getNavBarPosition(); + final int left = navBarPosition == NAV_BAR_LEFT + ? configuration.appBounds.right - outBounds.width() : 0; + outBounds.offsetTo(left, 0 /* top */); + } + + /** Get bounds of the activity. */ + @VisibleForTesting + Rect getBounds() { + return new Rect(mBounds); } /** @@ -2565,6 +2581,10 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo changes &= ~CONFIG_SMALLEST_SCREEN_SIZE; } } + // We don't want rotation to cause relaunches. + if ((changes & CONFIG_ROTATION) != 0) { + changes &= ~CONFIG_ROTATION; + } return changes; } diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java index d1558254a1fb..82b25665160f 100644 --- a/services/core/java/com/android/server/camera/CameraServiceProxy.java +++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java @@ -56,12 +56,6 @@ public class CameraServiceProxy extends SystemService public static final String CAMERA_SERVICE_PROXY_BINDER_NAME = "media.camera.proxy"; - // State arguments to use with the notifyCameraState call from camera service: - public static final int CAMERA_STATE_OPEN = 0; - public static final int CAMERA_STATE_ACTIVE = 1; - public static final int CAMERA_STATE_IDLE = 2; - public static final int CAMERA_STATE_CLOSED = 3; - // Flags arguments to NFC adapter to enable/disable NFC public static final int DISABLE_POLLING_FLAGS = 0x1000; public static final int ENABLE_POLLING_FLAGS = 0x0000; @@ -123,9 +117,11 @@ public class CameraServiceProxy extends SystemService } @Override - public void notifyCameraState(String cameraId, int newCameraState) { + public void notifyCameraState(String cameraId, int newCameraState, int facing, + String clientName) { String state = cameraStateToString(newCameraState); - if (DEBUG) Slog.v(TAG, "Camera " + cameraId + " state now " + state); + if (DEBUG) Slog.v(TAG, "Camera " + cameraId + " facing " + facing + " state now " + + state + " for client " + clientName); updateActivityCount(cameraId, newCameraState); } @@ -282,13 +278,13 @@ public class CameraServiceProxy extends SystemService synchronized(mLock) { boolean wasEmpty = mActiveCameraIds.isEmpty(); switch (newCameraState) { - case CAMERA_STATE_OPEN: + case ICameraServiceProxy.CAMERA_STATE_OPEN: break; - case CAMERA_STATE_ACTIVE: + case ICameraServiceProxy.CAMERA_STATE_ACTIVE: mActiveCameraIds.add(cameraId); break; - case CAMERA_STATE_IDLE: - case CAMERA_STATE_CLOSED: + case ICameraServiceProxy.CAMERA_STATE_IDLE: + case ICameraServiceProxy.CAMERA_STATE_CLOSED: mActiveCameraIds.remove(cameraId); break; } @@ -328,10 +324,10 @@ public class CameraServiceProxy extends SystemService private static String cameraStateToString(int newCameraState) { switch (newCameraState) { - case CAMERA_STATE_OPEN: return "CAMERA_STATE_OPEN"; - case CAMERA_STATE_ACTIVE: return "CAMERA_STATE_ACTIVE"; - case CAMERA_STATE_IDLE: return "CAMERA_STATE_IDLE"; - case CAMERA_STATE_CLOSED: return "CAMERA_STATE_CLOSED"; + case ICameraServiceProxy.CAMERA_STATE_OPEN: return "CAMERA_STATE_OPEN"; + case ICameraServiceProxy.CAMERA_STATE_ACTIVE: return "CAMERA_STATE_ACTIVE"; + case ICameraServiceProxy.CAMERA_STATE_IDLE: return "CAMERA_STATE_IDLE"; + case ICameraServiceProxy.CAMERA_STATE_CLOSED: return "CAMERA_STATE_CLOSED"; default: break; } return "CAMERA_STATE_UNKNOWN"; diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java index 0a9dba72ce82..1fb944fda7c9 100644 --- a/services/core/java/com/android/server/connectivity/Tethering.java +++ b/services/core/java/com/android/server/connectivity/Tethering.java @@ -57,6 +57,7 @@ import android.net.NetworkRequest; import android.net.NetworkState; import android.net.NetworkUtils; import android.net.RouteInfo; +import android.net.util.PrefixUtils; import android.net.util.SharedLog; import android.net.wifi.WifiManager; import android.os.Binder; @@ -216,10 +217,10 @@ public class Tethering extends BaseNetworkObserver { mContext.getContentResolver(), mLog); mUpstreamNetworkMonitor = new UpstreamNetworkMonitor( - mContext, mTetherMasterSM, mLog, TetherMasterSM.EVENT_UPSTREAM_CALLBACK ); + mContext, mTetherMasterSM, mLog, TetherMasterSM.EVENT_UPSTREAM_CALLBACK); mForwardedDownstreams = new HashSet<>(); mSimChange = new SimChangeListener( - mContext, mTetherMasterSM.getHandler(), () -> reevaluateSimCardProvisioning()); + mContext, smHandler, () -> reevaluateSimCardProvisioning()); mStateReceiver = new StateReceiver(); IntentFilter filter = new IntentFilter(); @@ -227,13 +228,13 @@ public class Tethering extends BaseNetworkObserver { filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); filter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); - mContext.registerReceiver(mStateReceiver, filter, null, mTetherMasterSM.getHandler()); + mContext.registerReceiver(mStateReceiver, filter, null, smHandler); filter = new IntentFilter(); filter.addAction(Intent.ACTION_MEDIA_SHARED); filter.addAction(Intent.ACTION_MEDIA_UNSHARED); filter.addDataScheme("file"); - mContext.registerReceiver(mStateReceiver, filter, null, mTetherMasterSM.getHandler()); + mContext.registerReceiver(mStateReceiver, filter, null, smHandler); // load device config info updateConfiguration(); @@ -1142,12 +1143,6 @@ public class Tethering extends BaseNetworkObserver { } } - private void startOffloadController() { - mOffloadController.start(); - mOffloadController.updateExemptPrefixes( - mUpstreamNetworkMonitor.getOffloadExemptPrefixes()); - } - class TetherMasterSM extends StateMachine { private static final int BASE_MASTER = Protocol.BASE_TETHERING; // an interface SM has requested Tethering/Local Hotspot @@ -1165,14 +1160,14 @@ public class Tethering extends BaseNetworkObserver { static final int CMD_CLEAR_ERROR = BASE_MASTER + 6; static final int EVENT_IFACE_UPDATE_LINKPROPERTIES = BASE_MASTER + 7; - private State mInitialState; - private State mTetherModeAliveState; + private final State mInitialState; + private final State mTetherModeAliveState; - private State mSetIpForwardingEnabledErrorState; - private State mSetIpForwardingDisabledErrorState; - private State mStartTetheringErrorState; - private State mStopTetheringErrorState; - private State mSetDnsForwardersErrorState; + private final State mSetIpForwardingEnabledErrorState; + private final State mSetIpForwardingDisabledErrorState; + private final State mStartTetheringErrorState; + private final State mStopTetheringErrorState; + private final State mSetDnsForwardersErrorState; // This list is a little subtle. It contains all the interfaces that currently are // requesting tethering, regardless of whether these interfaces are still members of @@ -1212,22 +1207,46 @@ public class Tethering extends BaseNetworkObserver { mNotifyList = new ArrayList<>(); mIPv6TetheringCoordinator = new IPv6TetheringCoordinator(mNotifyList, mLog); + setInitialState(mInitialState); } + private void startOffloadController() { + mOffloadController.start(); + sendOffloadExemptPrefixes(); + } + + private void sendOffloadExemptPrefixes() { + sendOffloadExemptPrefixes(mUpstreamNetworkMonitor.getLocalPrefixes()); + } + + private void sendOffloadExemptPrefixes(Set<IpPrefix> localPrefixes) { + // Add in well-known minimum set. + PrefixUtils.addNonForwardablePrefixes(localPrefixes); + // Add tragically hardcoded prefixes. + localPrefixes.add(PrefixUtils.DEFAULT_WIFI_P2P_PREFIX); + + // Add prefixes for all downstreams, regardless of IP serving mode. + for (TetherInterfaceStateMachine tism : mNotifyList) { + localPrefixes.addAll(PrefixUtils.localPrefixesFrom(tism.linkProperties())); + } + + mOffloadController.setLocalPrefixes(localPrefixes); + } + class InitialState extends State { @Override public boolean processMessage(Message message) { logMessage(this, message.what); switch (message.what) { case EVENT_IFACE_SERVING_STATE_ACTIVE: - TetherInterfaceStateMachine who = (TetherInterfaceStateMachine)message.obj; + TetherInterfaceStateMachine who = (TetherInterfaceStateMachine) message.obj; if (VDBG) Log.d(TAG, "Tether Mode requested by " + who); handleInterfaceServingStateActive(message.arg1, who); transitionTo(mTetherModeAliveState); break; case EVENT_IFACE_SERVING_STATE_INACTIVE: - who = (TetherInterfaceStateMachine)message.obj; + who = (TetherInterfaceStateMachine) message.obj; if (VDBG) Log.d(TAG, "Tether Mode unrequested by " + who); handleInterfaceServingStateInactive(who); break; @@ -1422,8 +1441,8 @@ public class Tethering extends BaseNetworkObserver { } private void handleUpstreamNetworkMonitorCallback(int arg1, Object o) { - if (arg1 == UpstreamNetworkMonitor.NOTIFY_EXEMPT_PREFIXES) { - mOffloadController.updateExemptPrefixes((Set<IpPrefix>) o); + if (arg1 == UpstreamNetworkMonitor.NOTIFY_LOCAL_PREFIXES) { + sendOffloadExemptPrefixes((Set<IpPrefix>) o); return; } @@ -1527,7 +1546,7 @@ public class Tethering extends BaseNetworkObserver { boolean retValue = true; switch (message.what) { case EVENT_IFACE_SERVING_STATE_ACTIVE: { - TetherInterfaceStateMachine who = (TetherInterfaceStateMachine)message.obj; + TetherInterfaceStateMachine who = (TetherInterfaceStateMachine) message.obj; if (VDBG) Log.d(TAG, "Tether Mode requested by " + who); handleInterfaceServingStateActive(message.arg1, who); who.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED, @@ -1541,7 +1560,7 @@ public class Tethering extends BaseNetworkObserver { break; } case EVENT_IFACE_SERVING_STATE_INACTIVE: { - TetherInterfaceStateMachine who = (TetherInterfaceStateMachine)message.obj; + TetherInterfaceStateMachine who = (TetherInterfaceStateMachine) message.obj; if (VDBG) Log.d(TAG, "Tether Mode unrequested by " + who); handleInterfaceServingStateInactive(who); @@ -1573,6 +1592,9 @@ public class Tethering extends BaseNetworkObserver { mOffloadController.notifyDownstreamLinkProperties(newLp); } else { mOffloadController.removeDownstreamInterface(newLp.getInterfaceName()); + // Another interface might be in local-only hotspot mode; + // resend all local prefixes to the OffloadController. + sendOffloadExemptPrefixes(); } break; } @@ -1614,7 +1636,7 @@ public class Tethering extends BaseNetworkObserver { boolean retValue = true; switch (message.what) { case EVENT_IFACE_SERVING_STATE_ACTIVE: - TetherInterfaceStateMachine who = (TetherInterfaceStateMachine)message.obj; + TetherInterfaceStateMachine who = (TetherInterfaceStateMachine) message.obj; who.sendMessage(mErrorNotification); break; case CMD_CLEAR_ERROR: diff --git a/services/core/java/com/android/server/connectivity/tethering/OffloadController.java b/services/core/java/com/android/server/connectivity/tethering/OffloadController.java index 2b0ded94eacf..b47386705a36 100644 --- a/services/core/java/com/android/server/connectivity/tethering/OffloadController.java +++ b/services/core/java/com/android/server/connectivity/tethering/OffloadController.java @@ -20,6 +20,7 @@ import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED; import android.content.ContentResolver; import android.net.IpPrefix; +import android.net.LinkAddress; import android.net.LinkProperties; import android.net.RouteInfo; import android.net.util.SharedLog; @@ -27,8 +28,11 @@ import android.os.Handler; import android.provider.Settings; import java.net.Inet4Address; +import java.net.Inet6Address; import java.net.InetAddress; import java.util.ArrayList; +import java.util.HashSet; +import java.util.Objects; import java.util.Set; /** @@ -47,7 +51,13 @@ public class OffloadController { private boolean mConfigInitialized; private boolean mControlInitialized; private LinkProperties mUpstreamLinkProperties; + // The complete set of offload-exempt prefixes passed in via Tethering from + // all upstream and downstream sources. private Set<IpPrefix> mExemptPrefixes; + // A strictly "smaller" set of prefixes, wherein offload-approved prefixes + // (e.g. downstream on-link prefixes) have been removed and replaced with + // prefixes representing only the locally-assigned IP addresses. + private Set<String> mLastLocalPrefixStrs; public OffloadController(Handler h, OffloadHardwareInterface hwi, ContentResolver contentResolver, SharedLog log) { @@ -55,6 +65,8 @@ public class OffloadController { mHwInterface = hwi; mContentResolver = contentResolver; mLog = log.forSubComponent(TAG); + mExemptPrefixes = new HashSet<>(); + mLastLocalPrefixStrs = new HashSet<>(); } public void start() { @@ -134,25 +146,22 @@ public class OffloadController { } public void setUpstreamLinkProperties(LinkProperties lp) { - if (!started()) return; + if (!started() || Objects.equals(mUpstreamLinkProperties, lp)) return; mUpstreamLinkProperties = (lp != null) ? new LinkProperties(lp) : null; // TODO: examine return code and decide what to do if programming // upstream parameters fails (probably just wait for a subsequent // onOffloadEvent() callback to tell us offload is available again and // then reapply all state). + computeAndPushLocalPrefixes(); pushUpstreamParameters(); } - public void updateExemptPrefixes(Set<IpPrefix> exemptPrefixes) { + public void setLocalPrefixes(Set<IpPrefix> localPrefixes) { if (!started()) return; - mExemptPrefixes = exemptPrefixes; - // TODO: - // - add IP addresses from all downstream link properties - // - add routes from all non-tethering downstream link properties - // - remove any 64share prefixes - // - push this to the HAL + mExemptPrefixes = localPrefixes; + computeAndPushLocalPrefixes(); } public void notifyDownstreamLinkProperties(LinkProperties lp) { @@ -215,4 +224,42 @@ public class OffloadController { return mHwInterface.setUpstreamParameters( iface, v4addr, v4gateway, (v6gateways.isEmpty() ? null : v6gateways)); } + + private boolean computeAndPushLocalPrefixes() { + final Set<String> localPrefixStrs = computeLocalPrefixStrings( + mExemptPrefixes, mUpstreamLinkProperties); + if (mLastLocalPrefixStrs.equals(localPrefixStrs)) return true; + + mLastLocalPrefixStrs = localPrefixStrs; + return mHwInterface.setLocalPrefixes(new ArrayList<>(localPrefixStrs)); + } + + // TODO: Factor in downstream LinkProperties once that information is available. + private static Set<String> computeLocalPrefixStrings( + Set<IpPrefix> localPrefixes, LinkProperties upstreamLinkProperties) { + // Create an editable copy. + final Set<IpPrefix> prefixSet = new HashSet<>(localPrefixes); + + // TODO: If a downstream interface (not currently passed in) is reusing + // the /64 of the upstream (64share) then: + // + // [a] remove that /64 from the local prefixes + // [b] add in /128s for IP addresses on the downstream interface + // [c] add in /128s for IP addresses on the upstream interface + // + // Until downstream information is available here, simply add /128s from + // the upstream network; they'll just be redundant with their /64. + if (upstreamLinkProperties != null) { + for (LinkAddress linkAddr : upstreamLinkProperties.getLinkAddresses()) { + if (!linkAddr.isGlobalPreferred()) continue; + final InetAddress ip = linkAddr.getAddress(); + if (!(ip instanceof Inet6Address)) continue; + prefixSet.add(new IpPrefix(ip, 128)); + } + } + + final HashSet<String> localPrefixStrs = new HashSet<>(); + for (IpPrefix pfx : prefixSet) localPrefixStrs.add(pfx.toString()); + return localPrefixStrs; + } } diff --git a/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java b/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java index b648f5182242..4df566f03d6d 100644 --- a/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java +++ b/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java @@ -168,6 +168,26 @@ public class OffloadHardwareInterface { return stats; } + public boolean setLocalPrefixes(ArrayList<String> localPrefixes) { + final String logmsg = String.format("setLocalPrefixes([%s])", + String.join(",", localPrefixes)); + + final CbResults results = new CbResults(); + try { + mOffloadControl.setLocalPrefixes(localPrefixes, + (boolean success, String errMsg) -> { + results.success = success; + results.errMsg = errMsg; + }); + } catch (RemoteException e) { + record(logmsg, e); + return false; + } + + record(logmsg, results); + return results.success; + } + public boolean setUpstreamParameters( String iface, String v4addr, String v4gateway, ArrayList<String> v6gws) { iface = (iface != null) ? iface : NO_INTERFACE_NAME; diff --git a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java index 4bac69ce7495..69678df4543d 100644 --- a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java +++ b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java @@ -161,6 +161,8 @@ public class TetherInterfaceStateMachine extends StateMachine { public int lastError() { return mLastError; } + public LinkProperties linkProperties() { return new LinkProperties(mLinkProperties); } + public void stop() { sendMessage(CMD_INTERFACE_DOWN); } public void unwanted() { sendMessage(CMD_TETHER_UNREQUESTED); } diff --git a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java index eb66767bfdfa..c5f752807cb7 100644 --- a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java +++ b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java @@ -34,6 +34,7 @@ import android.net.NetworkCapabilities; import android.net.NetworkRequest; import android.net.NetworkState; import android.net.util.NetworkConstants; +import android.net.util.PrefixUtils; import android.net.util.SharedLog; import android.util.Log; @@ -72,16 +73,11 @@ public class UpstreamNetworkMonitor { private static final boolean DBG = false; private static final boolean VDBG = false; - private static final IpPrefix[] MINIMUM_LOCAL_PREFIXES_SET = { - prefix("127.0.0.0/8"), prefix("169.254.0.0/16"), - prefix("::/3"), prefix("fe80::/64"), prefix("fc00::/7"), prefix("ff00::/8"), - }; - public static final int EVENT_ON_AVAILABLE = 1; public static final int EVENT_ON_CAPABILITIES = 2; public static final int EVENT_ON_LINKPROPERTIES = 3; public static final int EVENT_ON_LOST = 4; - public static final int NOTIFY_EXEMPT_PREFIXES = 10; + public static final int NOTIFY_LOCAL_PREFIXES = 10; private static final int CALLBACK_LISTEN_ALL = 1; private static final int CALLBACK_TRACK_DEFAULT = 2; @@ -93,7 +89,7 @@ public class UpstreamNetworkMonitor { private final Handler mHandler; private final int mWhat; private final HashMap<Network, NetworkState> mNetworkMap = new HashMap<>(); - private HashSet<IpPrefix> mOffloadExemptPrefixes; + private HashSet<IpPrefix> mLocalPrefixes; private ConnectivityManager mCM; private NetworkCallback mListenAllCallback; private NetworkCallback mDefaultNetworkCallback; @@ -107,7 +103,7 @@ public class UpstreamNetworkMonitor { mHandler = mTarget.getHandler(); mLog = log.forSubComponent(TAG); mWhat = what; - mOffloadExemptPrefixes = allOffloadExemptPrefixes(mNetworkMap.values()); + mLocalPrefixes = new HashSet<>(); } @VisibleForTesting @@ -223,8 +219,8 @@ public class UpstreamNetworkMonitor { return typeStatePair.ns; } - public Set<IpPrefix> getOffloadExemptPrefixes() { - return (Set<IpPrefix>) mOffloadExemptPrefixes.clone(); + public Set<IpPrefix> getLocalPrefixes() { + return (Set<IpPrefix>) mLocalPrefixes.clone(); } private void handleAvailable(int callbackType, Network network) { @@ -360,11 +356,11 @@ public class UpstreamNetworkMonitor { notifyTarget(EVENT_ON_LOST, mNetworkMap.remove(network)); } - private void recomputeOffloadExemptPrefixes() { - final HashSet<IpPrefix> exemptPrefixes = allOffloadExemptPrefixes(mNetworkMap.values()); - if (!mOffloadExemptPrefixes.equals(exemptPrefixes)) { - mOffloadExemptPrefixes = exemptPrefixes; - notifyTarget(NOTIFY_EXEMPT_PREFIXES, exemptPrefixes.clone()); + private void recomputeLocalPrefixes() { + final HashSet<IpPrefix> localPrefixes = allLocalPrefixes(mNetworkMap.values()); + if (!mLocalPrefixes.equals(localPrefixes)) { + mLocalPrefixes = localPrefixes; + notifyTarget(NOTIFY_LOCAL_PREFIXES, localPrefixes.clone()); } } @@ -402,7 +398,7 @@ public class UpstreamNetworkMonitor { @Override public void onLinkPropertiesChanged(Network network, LinkProperties newLp) { handleLinkProp(network, newLp); - recomputeOffloadExemptPrefixes(); + recomputeLocalPrefixes(); } // TODO: Handle onNetworkSuspended(); @@ -411,7 +407,7 @@ public class UpstreamNetworkMonitor { @Override public void onLost(Network network) { handleLost(mCallbackType, network); - recomputeOffloadExemptPrefixes(); + recomputeLocalPrefixes(); } } @@ -460,35 +456,15 @@ public class UpstreamNetworkMonitor { return result; } - private static HashSet<IpPrefix> allOffloadExemptPrefixes(Iterable<NetworkState> netStates) { + private static HashSet<IpPrefix> allLocalPrefixes(Iterable<NetworkState> netStates) { final HashSet<IpPrefix> prefixSet = new HashSet<>(); - addDefaultLocalPrefixes(prefixSet); - for (NetworkState ns : netStates) { - addOffloadExemptPrefixes(prefixSet, ns.linkProperties); + final LinkProperties lp = ns.linkProperties; + if (lp == null) continue; + prefixSet.addAll(PrefixUtils.localPrefixesFrom(lp)); } return prefixSet; } - - private static void addDefaultLocalPrefixes(Set<IpPrefix> prefixSet) { - Collections.addAll(prefixSet, MINIMUM_LOCAL_PREFIXES_SET); - } - - private static void addOffloadExemptPrefixes(Set<IpPrefix> prefixSet, LinkProperties lp) { - if (lp == null) return; - - for (LinkAddress linkAddr : lp.getAllLinkAddresses()) { - prefixSet.add(new IpPrefix(linkAddr.getAddress(), linkAddr.getPrefixLength())); - } - - // TODO: Consider adding other non-default routes associated with this - // network. Traffic to these destinations should perhaps not go through - // the Internet (upstream). - } - - private static IpPrefix prefix(String prefixStr) { - return new IpPrefix(prefixStr); - } } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index c57b2fe476b6..98a247975959 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -1083,6 +1083,25 @@ public class NotificationManagerService extends SystemService { } @VisibleForTesting + int getNotificationRecordCount() { + synchronized (mNotificationLock) { + int count = mNotificationList.size() + mNotificationsByKey.size() + + mSummaryByGroupKey.size() + mEnqueuedNotifications.size(); + // subtract duplicates + for (NotificationRecord posted : mNotificationList) { + if (mNotificationsByKey.containsKey(posted.getKey())) { + count--; + } + if (posted.sbn.isGroup() && posted.getNotification().isGroupSummary()) { + count --; + } + } + + return count; + } + } + + @VisibleForTesting void addNotification(NotificationRecord r) { mNotificationList.add(r); mNotificationsByKey.put(r.sbn.getKey(), r); @@ -4734,6 +4753,7 @@ public class NotificationManagerService extends SystemService { canceledNotifications = new ArrayList<>(); } notificationList.remove(i); + mNotificationsByKey.remove(r.getKey()); canceledNotifications.add(r); cancelNotificationLocked(r, sendDelete, reason, wasPosted); } @@ -4844,6 +4864,7 @@ public class NotificationManagerService extends SystemService { EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, childSbn.getId(), childSbn.getTag(), userId, 0, 0, reason, listenerName); notificationList.remove(i); + mNotificationsByKey.remove(childR.getKey()); cancelNotificationLocked(childR, sendDelete, reason, wasPosted); } } diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 8425d235fe66..75fc25aaec77 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -358,10 +358,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { private static final String SYSUI_SCREENSHOT_ERROR_RECEIVER = "com.android.systemui.screenshot.ScreenshotServiceErrorReceiver"; - private static final int NAV_BAR_BOTTOM = 0; - private static final int NAV_BAR_RIGHT = 1; - private static final int NAV_BAR_LEFT = 2; - /** * Keyguard stuff */ @@ -6943,6 +6939,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { } @Override + public int getNavBarPosition() { + // TODO(multi-display): Support system decor on secondary displays. + return mNavigationBarPosition; + } + + @Override public boolean isDockSideAllowed(int dockSide) { // We do not allow all dock sides at which the navigation bar touches the docked stack. diff --git a/services/core/java/com/android/server/timezone/PackageTracker.java b/services/core/java/com/android/server/timezone/PackageTracker.java index e8dfd779a715..9b4999667c88 100644 --- a/services/core/java/com/android/server/timezone/PackageTracker.java +++ b/services/core/java/com/android/server/timezone/PackageTracker.java @@ -164,33 +164,29 @@ public class PackageTracker implements IntentHelper.Listener { } // Validate the updater application package. - // TODO(nfuller) Uncomment or remove the code below. Currently an app stops being a priv-app - // after it is replaced by one in data so this check fails. http://b/35995024 - // try { - // if (!mPackageManagerHelper.isPrivilegedApp(mUpdateAppPackageName)) { - // throw failWithException( - // "Update app " + mUpdateAppPackageName + " must be a priv-app.", null); - // } - // } catch (PackageManager.NameNotFoundException e) { - // throw failWithException("Could not determine update app package details for " - // + mUpdateAppPackageName, e); - // } + try { + if (!mPackageManagerHelper.isPrivilegedApp(mUpdateAppPackageName)) { + throw logAndThrowRuntimeException( + "Update app " + mUpdateAppPackageName + " must be a priv-app.", null); + } + } catch (PackageManager.NameNotFoundException e) { + throw logAndThrowRuntimeException("Could not determine update app package details for " + + mUpdateAppPackageName, e); + } // TODO(nfuller) Consider permission checks. While an updated system app retains permissions // obtained by the system version it's not clear how to check them. Slog.d(TAG, "Update app " + mUpdateAppPackageName + " is valid."); // Validate the data application package. - // TODO(nfuller) Uncomment or remove the code below. Currently an app stops being a priv-app - // after it is replaced by one in data. http://b/35995024 - // try { - // if (!mPackageManagerHelper.isPrivilegedApp(mDataAppPackageName)) { - // throw failWithException( - // "Data app " + mDataAppPackageName + " must be a priv-app.", null); - // } - // } catch (PackageManager.NameNotFoundException e) { - // throw failWithException("Could not determine data app package details for " - // + mDataAppPackageName, e); - // } + try { + if (!mPackageManagerHelper.isPrivilegedApp(mDataAppPackageName)) { + throw logAndThrowRuntimeException( + "Data app " + mDataAppPackageName + " must be a priv-app.", null); + } + } catch (PackageManager.NameNotFoundException e) { + throw logAndThrowRuntimeException("Could not determine data app package details for " + + mDataAppPackageName, e); + } // TODO(nfuller) Consider permission checks. While an updated system app retains permissions // obtained by the system version it's not clear how to check them. Slog.d(TAG, "Data app " + mDataAppPackageName + " is valid."); diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index 839ee0ec7efd..b9d02a900d1c 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -1357,8 +1357,10 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree * @return {@code true} If all children have been considered, {@code false}. */ private boolean allDrawnStatesConsidered() { - for (WindowState child : mChildren) { - if (!child.getDrawnStatedEvaluated()) { + for (int i = mChildren.size() - 1; i >= 0; --i) { + final WindowState child = mChildren.get(i); + if (child.mightAffectAllDrawn(false /*visibleOnly*/ ) + && !child.getDrawnStateEvaluated()) { return false; } } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 9fe73815b380..05f4626259d7 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -1073,7 +1073,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } if (w.mHasSurface && !rotateSeamlessly) { if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Set mOrientationChanging of " + w); - w.mOrientationChanging = true; + w.setOrientationChanging(true); mService.mRoot.mOrientationChangeComplete = false; w.mLastFreezeDuration = 0; } @@ -2679,10 +2679,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_TIMEOUT; forAllWindows(w -> { - if (!w.mOrientationChanging) { + if (!w.getOrientationChanging()) { return; } - w.mOrientationChanging = false; + w.setOrientationChanging(false); w.mLastFreezeDuration = (int)(SystemClock.elapsedRealtime() - mService.mDisplayFreezeTime); Slog.w(TAG_WM, "Force clearing orientation change: " + w); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 730c08cc50c2..902c2ff78660 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -5796,7 +5796,7 @@ public class WindowManagerService extends IWindowManager.Stub // orientation. if (!okToDisplay() && mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_TIMEOUT) { if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Changing surface while display frozen: " + w); - w.mOrientationChanging = true; + w.setOrientationChanging(true); w.mLastFreezeDuration = 0; mRoot.mOrientationChangeComplete = false; if (mWindowsFreezingScreen == WINDOWS_FREEZING_SCREENS_NONE) { @@ -6299,6 +6299,22 @@ public class WindowManagerService extends IWindowManager.Stub } } + /** + * Used by ActivityManager to determine where to position an app with aspect ratio shorter then + * the screen is. + * @see WindowManagerPolicy#getNavBarPosition() + */ + public int getNavBarPosition() { + synchronized (mWindowMap) { + // Perform layout if it was scheduled before to make sure that we get correct nav bar + // position when doing rotations. + final DisplayContent defaultDisplayContent = getDefaultDisplayContentLocked(); + defaultDisplayContent.performLayout(false /* initial */, + false /* updateInputWindows */); + return mPolicy.getNavBarPosition(); + } + } + @Override public WindowManagerPolicy.InputConsumer createInputConsumer(Looper looper, String name, InputEventReceiver.Factory inputEventReceiverFactory) { diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index e848f06b9777..7decb11d777b 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -446,7 +446,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP * Set when the orientation is changing and this window has not yet * been updated for the new orientation. */ - boolean mOrientationChanging; + private boolean mOrientationChanging; /** * The orientation during the last visible call to relayout. If our @@ -690,7 +690,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP /** * Returns whether this {@link WindowState} has been considered for drawing by its parent. */ - boolean getDrawnStatedEvaluated() { + boolean getDrawnStateEvaluated() { return mDrawnStateEvaluated; } @@ -1189,7 +1189,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // then we need to hold off on unfreezing the display until this window has been // redrawn; to do that, we need to go through the process of getting informed by the // application when it has finished drawing. - if (mOrientationChanging || dragResizingChanged || isResizedWhileNotDragResizing()) { + if (getOrientationChanging() || dragResizingChanged + || isResizedWhileNotDragResizing()) { if (DEBUG_SURFACE_TRACE || DEBUG_ANIM || DEBUG_ORIENTATION || DEBUG_RESIZE) { Slog.v(TAG_WM, "Orientation or resize start waiting for draw" + ", mDrawState=DRAW_PENDING in " + this @@ -1204,17 +1205,33 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG_WM, "Resizing window " + this); mService.mResizingWindows.add(this); } - } else if (mOrientationChanging) { + } else if (getOrientationChanging()) { if (isDrawnLw()) { if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Orientation not waiting for draw in " + this + ", surfaceController " + winAnimator.mSurfaceController); - mOrientationChanging = false; + setOrientationChanging(false); mLastFreezeDuration = (int)(SystemClock.elapsedRealtime() - mService.mDisplayFreezeTime); } } } + boolean getOrientationChanging() { + // In addition to the local state flag, we must also consider the difference in the last + // reported configuration vs. the current state. If the client code has not been informed of + // the change, logic dependent on having finished processing the orientation, such as + // unfreezing, could be improperly triggered. + // TODO(b/62846907): Checking against {@link mLastReportedConfiguration} could be flaky as + // this is not necessarily what the client has processed yet. Find a + // better indicator consistent with the client. + return mOrientationChanging || (isVisible() + && getConfiguration().orientation != mLastReportedConfiguration.orientation); + } + + void setOrientationChanging(boolean changing) { + mOrientationChanging = changing; + } + DisplayContent getDisplayContent() { return mToken.getDisplayContent(); } @@ -2694,10 +2711,10 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mAppFreezing = false; - if (mHasSurface && !mOrientationChanging + if (mHasSurface && !getOrientationChanging() && mService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_TIMEOUT) { if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "set mOrientationChanging of " + this); - mOrientationChanging = true; + setOrientationChanging(true); mService.mRoot.mOrientationChangeComplete = false; } mLastFreezeDuration = 0; @@ -3116,7 +3133,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mWinAnimator.mSurfaceResized = false; mReportOrientationChanged = false; } catch (RemoteException e) { - mOrientationChanging = false; + setOrientationChanging(false); mLastFreezeDuration = (int)(SystemClock.elapsedRealtime() - mService.mDisplayFreezeTime); // We are assuming the hosting process is dead or in a zombie state. @@ -3375,7 +3392,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP pw.print(prefix); pw.print("mAppToken="); pw.println(mAppToken); pw.print(prefix); pw.print(" isAnimatingWithSavedSurface()="); pw.print(isAnimatingWithSavedSurface()); - pw.print(" mAppDied=");pw.println(mAppDied); + pw.print(" mAppDied=");pw.print(mAppDied); + pw.print(prefix); pw.print("drawnStateEvaluated="); + pw.print(getDrawnStateEvaluated()); + pw.print(prefix); pw.print("mightAffectAllDrawn="); + pw.println(mightAffectAllDrawn(false /*visibleOnly*/)); } pw.print(prefix); pw.print("mViewVisibility=0x"); pw.print(Integer.toHexString(mViewVisibility)); @@ -3477,10 +3498,13 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP pw.print(" mDestroying="); pw.print(mDestroying); pw.print(" mRemoved="); pw.println(mRemoved); } - if (mOrientationChanging || mAppFreezing || mTurnOnScreen + if (getOrientationChanging() || mAppFreezing || mTurnOnScreen || mReportOrientationChanged) { pw.print(prefix); pw.print("mOrientationChanging="); pw.print(mOrientationChanging); + pw.print(" configOrientationChanging="); + pw.print(mLastReportedConfiguration.orientation + != getConfiguration().orientation); pw.print(" mAppFreezing="); pw.print(mAppFreezing); pw.print(" mTurnOnScreen="); pw.print(mTurnOnScreen); pw.print(" mReportOrientationChanged="); pw.println(mReportOrientationChanged); @@ -3517,6 +3541,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (computeDragResizing()) { pw.print(prefix); pw.println("computeDragResizing=" + computeDragResizing()); } + pw.print(prefix); pw.println("isOnScreen=" + isOnScreen()); + pw.print(prefix); pw.println("isVisible=" + isVisible()); } @Override diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index cd55156a67a9..8f1065f75642 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -1527,11 +1527,11 @@ class WindowStateAnimator { // There is no need to wait for an animation change if our window is gone for layout // already as we'll never be visible. - if (w.mOrientationChanging && w.isGoneForLayoutLw()) { + if (w.getOrientationChanging() && w.isGoneForLayoutLw()) { if (DEBUG_ORIENTATION) { Slog.v(TAG, "Orientation change skips hidden " + w); } - w.mOrientationChanging = false; + w.setOrientationChanging(false); } return; } @@ -1564,8 +1564,8 @@ class WindowStateAnimator { // really hidden (gone for layout), there is no point in still waiting for it. // Note that this does introduce a potential glitch if the window becomes unhidden // before it has drawn for the new orientation. - if (w.mOrientationChanging && w.isGoneForLayoutLw()) { - w.mOrientationChanging = false; + if (w.getOrientationChanging() && w.isGoneForLayoutLw()) { + w.setOrientationChanging(false); if (DEBUG_ORIENTATION) Slog.v(TAG, "Orientation change skips hidden " + w); } @@ -1618,7 +1618,7 @@ class WindowStateAnimator { mAnimator.setPendingLayoutChanges(w.getDisplayId(), WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM); } else { - w.mOrientationChanging = false; + w.setOrientationChanging(false); } } if (hasSurface()) { @@ -1631,14 +1631,14 @@ class WindowStateAnimator { displayed = true; } - if (w.mOrientationChanging) { + if (w.getOrientationChanging()) { if (!w.isDrawnLw()) { mAnimator.mBulkUpdateParams &= ~SET_ORIENTATION_CHANGE_COMPLETE; mAnimator.mLastWindowFreezeSource = w; if (DEBUG_ORIENTATION) Slog.v(TAG, "Orientation continue waiting for draw in " + w); } else { - w.mOrientationChanging = false; + w.setOrientationChanging(false); if (DEBUG_ORIENTATION) Slog.v(TAG, "Orientation change complete in " + w); } } diff --git a/services/core/jni/com_android_server_VibratorService.cpp b/services/core/jni/com_android_server_VibratorService.cpp index cb8416b36be5..45e9cc785bf8 100644 --- a/services/core/jni/com_android_server_VibratorService.cpp +++ b/services/core/jni/com_android_server_VibratorService.cpp @@ -73,11 +73,13 @@ Return<R> halCall(Return<R> (I::* fn)(Args0...), Args1&&... args1) { ret = (sHal == nullptr) ? NullptrStatus<R>() : (*sHal.*fn)(std::forward<Args1>(args1)...); - if (!ret.isOk()) { - ALOGE("Failed to issue command to vibrator HAL. Retrying."); - // Restoring connection to the HAL. - sHal = I::tryGetService(); + if (ret.isOk()) { + break; } + + ALOGE("Failed to issue command to vibrator HAL. Retrying."); + // Restoring connection to the HAL. + sHal = I::tryGetService(); } return ret; } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index d5e0eb0e1e41..c2a5b6cff814 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -1213,8 +1213,8 @@ public final class SystemServer { mSystemServiceManager.startService(AudioService.Lifecycle.class); traceEnd(); - if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_RADIO)) { - traceBeginAndSlog("StartRadioService"); + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_BROADCAST_RADIO)) { + traceBeginAndSlog("StartBroadcastRadioService"); mSystemServiceManager.startService(RadioService.class); traceEnd(); } diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java index 41fccdb9b4a7..5aaec9e37c26 100644 --- a/services/net/java/android/net/ip/IpManager.java +++ b/services/net/java/android/net/ip/IpManager.java @@ -23,6 +23,7 @@ import android.content.Context; import android.net.DhcpResults; import android.net.INetd; import android.net.InterfaceConfiguration; +import android.net.IpPrefix; import android.net.LinkAddress; import android.net.LinkProperties.ProvisioningChange; import android.net.LinkProperties; @@ -35,6 +36,7 @@ import android.net.dhcp.DhcpClient; import android.net.metrics.IpConnectivityLog; import android.net.metrics.IpManagerEvent; import android.net.util.MultinetworkPolicyTracker; +import android.net.util.NetworkConstants; import android.net.util.SharedLog; import android.os.INetworkManagementService; import android.os.Message; @@ -51,17 +53,25 @@ import android.util.SparseArray; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.IState; +import com.android.internal.util.Preconditions; import com.android.internal.util.State; import com.android.internal.util.StateMachine; import com.android.server.net.NetlinkTracker; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.net.Inet4Address; +import java.net.Inet6Address; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; +import java.util.Collection; +import java.util.HashSet; import java.util.Objects; +import java.util.Set; import java.util.StringJoiner; +import java.util.function.Predicate; +import java.util.stream.Collectors; /** @@ -308,6 +318,11 @@ public class IpManager extends StateMachine { return this; } + public Builder withInitialConfiguration(InitialConfiguration initialConfig) { + mConfig.mInitialConfig = initialConfig; + return this; + } + public Builder withStaticConfiguration(StaticIpConfiguration staticConfig) { mConfig.mStaticIpConfig = staticConfig; return this; @@ -342,18 +357,20 @@ public class IpManager extends StateMachine { /* package */ boolean mEnableIPv6 = true; /* package */ boolean mUsingIpReachabilityMonitor = true; /* package */ int mRequestedPreDhcpActionMs; + /* package */ InitialConfiguration mInitialConfig; /* package */ StaticIpConfiguration mStaticIpConfig; /* package */ ApfCapabilities mApfCapabilities; /* package */ int mProvisioningTimeoutMs = DEFAULT_TIMEOUT_MS; /* package */ int mIPv6AddrGenMode = INetd.IPV6_ADDR_GEN_MODE_STABLE_PRIVACY; - public ProvisioningConfiguration() {} + public ProvisioningConfiguration() {} // used by Builder public ProvisioningConfiguration(ProvisioningConfiguration other) { mEnableIPv4 = other.mEnableIPv4; mEnableIPv6 = other.mEnableIPv6; mUsingIpReachabilityMonitor = other.mUsingIpReachabilityMonitor; mRequestedPreDhcpActionMs = other.mRequestedPreDhcpActionMs; + mInitialConfig = InitialConfiguration.copy(other.mInitialConfig); mStaticIpConfig = other.mStaticIpConfig; mApfCapabilities = other.mApfCapabilities; mProvisioningTimeoutMs = other.mProvisioningTimeoutMs; @@ -366,12 +383,124 @@ public class IpManager extends StateMachine { .add("mEnableIPv6: " + mEnableIPv6) .add("mUsingIpReachabilityMonitor: " + mUsingIpReachabilityMonitor) .add("mRequestedPreDhcpActionMs: " + mRequestedPreDhcpActionMs) + .add("mInitialConfig: " + mInitialConfig) .add("mStaticIpConfig: " + mStaticIpConfig) .add("mApfCapabilities: " + mApfCapabilities) .add("mProvisioningTimeoutMs: " + mProvisioningTimeoutMs) .add("mIPv6AddrGenMode: " + mIPv6AddrGenMode) .toString(); } + + public boolean isValid() { + return (mInitialConfig == null) || mInitialConfig.isValid(); + } + } + + public static class InitialConfiguration { + public final Set<LinkAddress> ipAddresses = new HashSet<>(); + public final Set<IpPrefix> directlyConnectedRoutes = new HashSet<>(); + public final Set<InetAddress> dnsServers = new HashSet<>(); + public Inet4Address gateway; // WiFi legacy behavior with static ipv4 config + + public static InitialConfiguration copy(InitialConfiguration config) { + if (config == null) { + return null; + } + InitialConfiguration configCopy = new InitialConfiguration(); + configCopy.ipAddresses.addAll(config.ipAddresses); + configCopy.directlyConnectedRoutes.addAll(config.directlyConnectedRoutes); + configCopy.dnsServers.addAll(config.dnsServers); + return configCopy; + } + + @Override + public String toString() { + return String.format( + "InitialConfiguration(IPs: {%s}, prefixes: {%s}, DNS: {%s}, v4 gateway: %s)", + join(", ", ipAddresses), join(", ", directlyConnectedRoutes), + join(", ", dnsServers), gateway); + } + + public boolean isValid() { + // For every IP address, there must be at least one prefix containing that address. + for (LinkAddress addr : ipAddresses) { + if (!any(directlyConnectedRoutes, (p) -> p.contains(addr.getAddress()))) { + return false; + } + } + // For every dns server, there must be at least one prefix containing that address. + for (InetAddress addr : dnsServers) { + if (!any(directlyConnectedRoutes, (p) -> p.contains(addr))) { + return false; + } + } + // All IPv6 LinkAddresses have an RFC7421-suitable prefix length + // (read: compliant with RFC4291#section2.5.4). + if (any(ipAddresses, not(InitialConfiguration::isPrefixLengthCompliant))) { + return false; + } + // If directlyConnectedRoutes contains an IPv6 default route + // then ipAddresses MUST contain at least one non-ULA GUA. + if (any(directlyConnectedRoutes, InitialConfiguration::isIPv6DefaultRoute) + && all(ipAddresses, not(InitialConfiguration::isIPv6GUA))) { + return false; + } + // The prefix length of routes in directlyConnectedRoutes be within reasonable + // bounds for IPv6: /48-/64 just as we’d accept in RIOs. + if (any(directlyConnectedRoutes, not(InitialConfiguration::isPrefixLengthCompliant))) { + return false; + } + // There no more than one IPv4 address + if (ipAddresses.stream().filter(Inet4Address.class::isInstance).count() > 1) { + return false; + } + + return true; + } + + private static boolean isPrefixLengthCompliant(LinkAddress addr) { + return (addr.getAddress() instanceof Inet4Address) + || isCompliantIPv6PrefixLength(addr.getPrefixLength()); + } + + private static boolean isPrefixLengthCompliant(IpPrefix prefix) { + return (prefix.getAddress() instanceof Inet4Address) + || isCompliantIPv6PrefixLength(prefix.getPrefixLength()); + } + + private static boolean isCompliantIPv6PrefixLength(int prefixLength) { + return (NetworkConstants.RFC6177_MIN_PREFIX_LENGTH <= prefixLength) + && (prefixLength <= NetworkConstants.RFC7421_PREFIX_LENGTH); + } + + private static boolean isIPv6DefaultRoute(IpPrefix prefix) { + return prefix.getAddress().equals(Inet6Address.ANY); + } + + private static boolean isIPv6GUA(LinkAddress addr) { + return (addr.getAddress() instanceof Inet6Address) && addr.isGlobalPreferred(); + } + + private static <T> boolean any(Iterable<T> coll, Predicate<T> fn) { + for (T t : coll) { + if (fn.test(t)) { + return true; + } + } + return false; + } + + private static <T> boolean all(Iterable<T> coll, Predicate<T> fn) { + return !any(coll, not(fn)); + } + + private static <T> Predicate<T> not(Predicate<T> fn) { + return (t) -> !fn.test(t); + } + + private static <T> String join(String delimiter, Collection<T> coll) { + return coll.stream().map(Object::toString).collect(Collectors.joining(delimiter)); + } } public static final String DUMP_ARG = "ipmanager"; @@ -436,8 +565,7 @@ public class IpManager extends StateMachine { private boolean mMulticastFiltering; private long mStartTimeMillis; - public IpManager(Context context, String ifName, Callback callback) - throws IllegalArgumentException { + public IpManager(Context context, String ifName, Callback callback) { this(context, ifName, callback, INetworkManagementService.Stub.asInterface( ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE))); } @@ -446,7 +574,7 @@ public class IpManager extends StateMachine { * An expanded constructor, useful for dependency injection. */ public IpManager(Context context, String ifName, Callback callback, - INetworkManagementService nwService) throws IllegalArgumentException { + INetworkManagementService nwService) { super(IpManager.class.getSimpleName() + "." + ifName); mTag = getName(); @@ -563,6 +691,11 @@ public class IpManager extends StateMachine { } public void startProvisioning(ProvisioningConfiguration req) { + if (!req.isValid()) { + doImmediateProvisioningFailure(IpManagerEvent.ERROR_INVALID_PROVISIONING); + return; + } + getNetworkInterface(); mCallback.setNeighborDiscoveryOffload(true); diff --git a/services/net/java/android/net/util/NetworkConstants.java b/services/net/java/android/net/util/NetworkConstants.java index a012e0cb0547..9b3bc3f0301a 100644 --- a/services/net/java/android/net/util/NetworkConstants.java +++ b/services/net/java/android/net/util/NetworkConstants.java @@ -102,6 +102,7 @@ public final class NetworkConstants { public static final int IPV6_ADDR_LEN = 16; public static final int IPV6_MIN_MTU = 1280; public static final int RFC7421_PREFIX_LENGTH = 64; + public static final int RFC6177_MIN_PREFIX_LENGTH = 48; /** * ICMPv6 constants. diff --git a/services/net/java/android/net/util/PrefixUtils.java b/services/net/java/android/net/util/PrefixUtils.java new file mode 100644 index 000000000000..962aab459a19 --- /dev/null +++ b/services/net/java/android/net/util/PrefixUtils.java @@ -0,0 +1,74 @@ +/* + * 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. + */ + +package android.net.util; + +import android.net.IpPrefix; +import android.net.LinkAddress; +import android.net.LinkProperties; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + + +/** + * @hide + */ +public class PrefixUtils { + private static final IpPrefix[] MIN_NON_FORWARDABLE_PREFIXES = { + pfx("127.0.0.0/8"), // IPv4 loopback + pfx("169.254.0.0/16"), // IPv4 link-local, RFC3927#section-8 + pfx("::/3"), + pfx("fe80::/64"), // IPv6 link-local + pfx("fc00::/7"), // IPv6 ULA + pfx("ff02::/8"), // IPv6 link-local multicast + }; + + public static final IpPrefix DEFAULT_WIFI_P2P_PREFIX = pfx("192.168.49.0/24"); + + public static Set<IpPrefix> getNonForwardablePrefixes() { + final HashSet<IpPrefix> prefixes = new HashSet<>(); + addNonForwardablePrefixes(prefixes); + return prefixes; + } + + public static void addNonForwardablePrefixes(Set<IpPrefix> prefixes) { + Collections.addAll(prefixes, MIN_NON_FORWARDABLE_PREFIXES); + } + + public static Set<IpPrefix> localPrefixesFrom(LinkProperties lp) { + final HashSet<IpPrefix> localPrefixes = new HashSet<>(); + if (lp == null) return localPrefixes; + + for (LinkAddress addr : lp.getAllLinkAddresses()) { + if (addr.getAddress().isLinkLocalAddress()) continue; + localPrefixes.add(asIpPrefix(addr)); + } + // TODO: Add directly-connected routes as well (ones from which we did + // not also form a LinkAddress)? + + return localPrefixes; + } + + public static IpPrefix asIpPrefix(LinkAddress addr) { + return new IpPrefix(addr.getAddress(), addr.getPrefixLength()); + } + + private static IpPrefix pfx(String prefixStr) { + return new IpPrefix(prefixStr); + } +} diff --git a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java index e79942762ebb..1a5814b668be 100644 --- a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -341,9 +341,9 @@ public class NotificationManagerServiceTest extends NotificationTestCase { mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag", 0, generateNotificationRecord(null).getNotification(), 0); waitForIdle(); - StatusBarNotification[] notifs = - mBinderService.getActiveNotifications(PKG); + StatusBarNotification[] notifs = mBinderService.getActiveNotifications(PKG); assertEquals(1, notifs.length); + assertEquals(1, mNotificationManagerService.getNotificationRecordCount()); } @Test @@ -355,6 +355,7 @@ public class NotificationManagerServiceTest extends NotificationTestCase { StatusBarNotification[] notifs = mBinderService.getActiveNotifications(PKG); assertEquals(0, notifs.length); + assertEquals(0, mNotificationManagerService.getNotificationRecordCount()); } @Test @@ -369,6 +370,7 @@ public class NotificationManagerServiceTest extends NotificationTestCase { StatusBarNotification[] notifs = mBinderService.getActiveNotifications(PKG); assertEquals(0, notifs.length); + assertEquals(0, mNotificationManagerService.getNotificationRecordCount()); } @Test @@ -381,6 +383,7 @@ public class NotificationManagerServiceTest extends NotificationTestCase { StatusBarNotification[] notifs = mBinderService.getActiveNotifications(sbn.getPackageName()); assertEquals(0, notifs.length); + assertEquals(0, mNotificationManagerService.getNotificationRecordCount()); } @Test @@ -393,6 +396,43 @@ public class NotificationManagerServiceTest extends NotificationTestCase { StatusBarNotification[] notifs = mBinderService.getActiveNotifications(sbn.getPackageName()); assertEquals(0, notifs.length); + assertEquals(0, mNotificationManagerService.getNotificationRecordCount()); + } + + @Test + public void testUserInitiatedClearAll_noLeak() throws Exception { + final NotificationRecord n = generateNotificationRecord( + mTestNotificationChannel, 1, "group", true); + + mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag", + n.sbn.getId(), n.sbn.getNotification(), n.sbn.getUserId()); + waitForIdle(); + + mNotificationManagerService.mNotificationDelegate.onClearAll(uid, Binder.getCallingPid(), + n.getUserId()); + waitForIdle(); + StatusBarNotification[] notifs = + mBinderService.getActiveNotifications(n.sbn.getPackageName()); + assertEquals(0, notifs.length); + assertEquals(0, mNotificationManagerService.getNotificationRecordCount()); + } + + @Test + public void testCancelAllNotificationsCancelsChildren() throws Exception { + final NotificationRecord parent = generateNotificationRecord( + mTestNotificationChannel, 1, "group1", true); + final NotificationRecord child = generateNotificationRecord( + mTestNotificationChannel, 2, "group1", false); + + mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag", + parent.sbn.getId(), parent.sbn.getNotification(), parent.sbn.getUserId()); + mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag", + child.sbn.getId(), child.sbn.getNotification(), child.sbn.getUserId()); + waitForIdle(); + + mBinderService.cancelAllNotifications(PKG, parent.sbn.getUserId()); + waitForIdle(); + assertEquals(0, mNotificationManagerService.getNotificationRecordCount()); } @Test @@ -404,6 +444,8 @@ public class NotificationManagerServiceTest extends NotificationTestCase { } mBinderService.cancelAllNotifications(PKG, sbn.getUserId()); waitForIdle(); + + assertEquals(0, mNotificationManagerService.getNotificationRecordCount()); } @Test @@ -430,6 +472,8 @@ public class NotificationManagerServiceTest extends NotificationTestCase { parentAsChild.sbn.getId(), parentAsChild.sbn.getNotification(), parentAsChild.sbn.getUserId()); waitForIdle(); + + assertEquals(0, mNotificationManagerService.getNotificationRecordCount()); } @Test @@ -443,6 +487,7 @@ public class NotificationManagerServiceTest extends NotificationTestCase { StatusBarNotification[] notifs = mBinderService.getActiveNotifications(sbn.getPackageName()); assertEquals(1, notifs.length); + assertEquals(1, mNotificationManagerService.getNotificationRecordCount()); } @Test @@ -456,6 +501,7 @@ public class NotificationManagerServiceTest extends NotificationTestCase { StatusBarNotification[] notifs = mBinderService.getActiveNotifications(sbn.getPackageName()); assertEquals(1, notifs.length); + assertEquals(1, mNotificationManagerService.getNotificationRecordCount()); } @Test @@ -468,6 +514,7 @@ public class NotificationManagerServiceTest extends NotificationTestCase { StatusBarNotification[] notifs = mBinderService.getActiveNotifications(sbn.getPackageName()); assertEquals(0, notifs.length); + assertEquals(0, mNotificationManagerService.getNotificationRecordCount()); } @Test @@ -481,6 +528,7 @@ public class NotificationManagerServiceTest extends NotificationTestCase { StatusBarNotification[] notifs = mBinderService.getActiveNotifications(sbn.getPackageName()); assertEquals(1, notifs.length); + assertEquals(1, mNotificationManagerService.getNotificationRecordCount()); } @Test @@ -597,6 +645,7 @@ public class NotificationManagerServiceTest extends NotificationTestCase { mBinderService.cancelNotificationWithTag(PKG, "tag", sbn.getId(), sbn.getUserId()); waitForIdle(); assertEquals(0, mBinderService.getActiveNotifications(sbn.getPackageName()).length); + assertEquals(0, mNotificationManagerService.getNotificationRecordCount()); } @Test diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java index f75d49cae574..2252c85dddf2 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java @@ -16,10 +16,15 @@ package com.android.server.am; +import static android.view.WindowManagerPolicy.NAV_BAR_BOTTOM; +import static android.view.WindowManagerPolicy.NAV_BAR_LEFT; +import static android.view.WindowManagerPolicy.NAV_BAR_RIGHT; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; +import static org.mockito.Mockito.when; import android.content.ComponentName; +import android.graphics.Rect; import android.platform.test.annotations.Presubmit; import android.support.test.filters.MediumTest; import android.support.test.runner.AndroidJUnit4; @@ -94,4 +99,36 @@ public class ActivityRecordTests extends ActivityTestsBase { return -1; } + + @Test + public void testPositionLimitedAspectRatioNavBarBottom() throws Exception { + verifyPositionWithLimitedAspectRatio(NAV_BAR_BOTTOM, new Rect(0, 0, 1000, 2000), 1.5f, + new Rect(0, 0, 1000, 1500)); + } + + @Test + public void testPositionLimitedAspectRatioNavBarLeft() throws Exception { + verifyPositionWithLimitedAspectRatio(NAV_BAR_LEFT, new Rect(0, 0, 2000, 1000), 1.5f, + new Rect(500, 0, 2000, 1000)); + } + + @Test + public void testPositionLimitedAspectRatioNavBarRight() throws Exception { + verifyPositionWithLimitedAspectRatio(NAV_BAR_RIGHT, new Rect(0, 0, 2000, 1000), 1.5f, + new Rect(0, 0, 1500, 1000)); + } + + private void verifyPositionWithLimitedAspectRatio(int navBarPosition, Rect taskBounds, + float aspectRatio, Rect expectedActivityBounds) { + final ActivityManagerService service = createActivityManagerService(); + final TaskRecord task = createTask(service, testActivityComponent, TEST_STACK_ID); + final ActivityRecord record = createActivity(service, testActivityComponent, task); + + // Verify with nav bar on the right. + when(service.mWindowManager.getNavBarPosition()).thenReturn(navBarPosition); + task.getConfiguration().setAppBounds(taskBounds); + record.info.maxAspectRatio = aspectRatio; + record.ensureActivityConfigurationLocked(0 /* globalChanges */, false /* preserveWindow */); + assertEquals(expectedActivityBounds, record.getBounds()); + } } diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java index bac121695ed9..16bc011f97b5 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java @@ -120,6 +120,7 @@ public class ActivityTestsBase { null /*_taskDescription*/, new ActivityManager.TaskThumbnailInfo()); final ActivityStack stack = service.mStackSupervisor.getStack(stackId, true /*createStaticStackIfNeeded*/, true /*onTop*/); + service.mStackSupervisor.setFocusStackUnchecked("test", stack); stack.addTask(task, true, "creating test task"); task.setStack(stack); task.setWindowContainerController(mock(TaskWindowContainerController.class)); diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index 3866e0ed4e4b..dcca72481f45 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -69,6 +69,7 @@ import android.os.Bundle; import android.os.Process; import android.os.UserHandle; import android.os.UserManager; +import android.platform.test.annotations.Presubmit; import android.provider.Settings; import android.security.KeyChain; import android.telephony.TelephonyManager; @@ -112,6 +113,7 @@ import java.util.concurrent.TimeUnit; * runtest -c com.android.server.devicepolicy.DevicePolicyManagerTest frameworks-services */ @SmallTest +@Presubmit public class DevicePolicyManagerTest extends DpmTestBase { private static final List<String> OWNER_SETUP_PERMISSIONS = Arrays.asList( permission.MANAGE_DEVICE_ADMINS, permission.MANAGE_PROFILE_AND_DEVICE_OWNERS, diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java index a4e56fc9f745..0a7a5f26d35e 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java +++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java @@ -17,6 +17,7 @@ package com.android.server.wm; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; +import static android.view.WindowManagerPolicy.NAV_BAR_BOTTOM; import static org.mockito.Matchers.anyInt; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; @@ -614,6 +615,11 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { } @Override + public int getNavBarPosition() { + return NAV_BAR_BOTTOM; + } + + @Override public void getNonDecorInsetsLw(int displayRotation, int displayWidth, int displayHeight, Rect outInsets) { diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java index a7180c989a6e..a5797c01691f 100644 --- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java +++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java @@ -1032,7 +1032,9 @@ public class UsbDeviceManager { if (DEBUG) { Slog.v(TAG, "Accessory mode enter timeout: " + mConnected); } - if (!mConnected) { + if (!mConnected || !UsbManager.containsFunction( + mCurrentFunctions, + UsbManager.USB_FUNCTION_ACCESSORY)) { notifyAccessoryModeExit(); } break; diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java index 7c074dadadf9..303a577767ad 100644 --- a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java +++ b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java @@ -118,27 +118,28 @@ public class UsbDescriptorParser { /** * @hide */ - public boolean parseDescriptors(byte[] descriptors) { - try { - mDescriptors.clear(); - - ByteStream stream = new ByteStream(descriptors); - while (stream.available() > 0) { - UsbDescriptor descriptor = allocDescriptor(stream); - if (descriptor != null) { - // Parse + public void parseDescriptors(byte[] descriptors) { + mDescriptors.clear(); + + ByteStream stream = new ByteStream(descriptors); + while (stream.available() > 0) { + UsbDescriptor descriptor = allocDescriptor(stream); + if (descriptor != null) { + // Parse + try { descriptor.parseRawDescriptors(stream); - mDescriptors.add(descriptor); - - // Clean up - descriptor.postParse(stream); + } catch (Exception ex) { + Log.e(TAG, "Exception parsing USB descriptors.", ex); } + + // Its OK to add the invalid descriptor as the postParse() + // routine will mark it as invalid. + mDescriptors.add(descriptor); + + // Clean up + descriptor.postParse(stream); } - return true; - } catch (Exception ex) { - Log.e(TAG, "Exception parsing USB descriptors.", ex); } - return false; } /** @@ -146,7 +147,11 @@ public class UsbDescriptorParser { */ public boolean parseDevice(String deviceAddr) { byte[] rawDescriptors = getRawDescriptors(deviceAddr); - return rawDescriptors != null && parseDescriptors(rawDescriptors); + if (rawDescriptors != null) { + parseDescriptors(rawDescriptors); + return true; + } + return false; } private native byte[] getRawDescriptors(String deviceAddr); diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 9c712f4c4ca2..50fab5dd784e 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -1495,6 +1495,17 @@ public class CarrierConfigManager { public static final String IMSI_KEY_EXPIRATION_DAYS_TIME_INT = "imsi_key_expiration_days_time_int"; + /** + * Key identifying if the CDMA Caller ID presentation and suppression MMI codes + * should be converted to 3GPP CLIR codes when a multimode (CDMA+UMTS+LTE) device is roaming + * on a 3GPP network. Specifically *67<number> will be converted to #31#<number> and + * *82<number> will be converted to *31#<number> before dialing a call when this key is + * set TRUE and device is roaming on a 3GPP network. + * @hide + */ + public static final String KEY_CONVERT_CDMA_CALLER_ID_MMI_CODES_WHILE_ROAMING_ON_3GPP_BOOL = + "convert_cdma_caller_id_mmi_codes_while_roaming_on_3gpp_bool"; + /** The default value for every variable. */ private final static PersistableBundle sDefaults; @@ -1745,10 +1756,10 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_DISABLE_VOICE_BARRING_NOTIFICATION_BOOL, false); sDefaults.putInt(IMSI_KEY_EXPIRATION_DAYS_TIME_INT, IMSI_ENCRYPTION_DAYS_TIME_DISABLED); sDefaults.putString(IMSI_KEY_DOWNLOAD_URL_STRING, null); + sDefaults.putBoolean(KEY_CONVERT_CDMA_CALLER_ID_MMI_CODES_WHILE_ROAMING_ON_3GPP_BOOL, + false); sDefaults.putStringArray(KEY_NON_ROAMING_OPERATOR_STRING_ARRAY, null); sDefaults.putStringArray(KEY_ROAMING_OPERATOR_STRING_ARRAY, null); - sDefaults.putInt(IMSI_KEY_EXPIRATION_DAYS_TIME_INT, IMSI_ENCRYPTION_DAYS_TIME_DISABLED); - sDefaults.putString(IMSI_KEY_DOWNLOAD_URL_STRING, null); } /** diff --git a/tests/Internal/src/com/android/internal/colorextraction/ColorExtractorTest.java b/tests/Internal/src/com/android/internal/colorextraction/ColorExtractorTest.java index 71821472f55e..0060901578cd 100644 --- a/tests/Internal/src/com/android/internal/colorextraction/ColorExtractorTest.java +++ b/tests/Internal/src/com/android/internal/colorextraction/ColorExtractorTest.java @@ -62,21 +62,6 @@ public class ColorExtractorTest { } @Test - public void getColors_usesFallbackIfFails() { - ExtractionType alwaysFail = - (inWallpaperColors, outGradientColorsNormal, outGradientColorsDark, - outGradientColorsExtraDark) -> false; - ColorExtractor extractor = new ColorExtractor(mContext, alwaysFail); - GradientColors colors = extractor.getColors(WallpaperManager.FLAG_SYSTEM); - - assertEquals("Should be using the fallback color.", - colors.getMainColor(), ColorExtractor.FALLBACK_COLOR); - assertEquals("Should be using the fallback color.", - colors.getSecondaryColor(), ColorExtractor.FALLBACK_COLOR); - assertFalse("Dark text support should be false.", colors.supportsDarkText()); - } - - @Test public void getColors_usesExtractedColors() { GradientColors colorsExpectedNormal = new GradientColors(); colorsExpectedNormal.setMainColor(Color.RED); @@ -96,8 +81,6 @@ public class ColorExtractorTest { outGradientColorsNormal.set(colorsExpectedNormal); outGradientColorsDark.set(colorsExpectedDark); outGradientColorsExtraDark.set(colorsExpectedExtraDark); - // Successful extraction - return true; }; ColorExtractor extractor = new ColorExtractor(mContext, type); diff --git a/tests/Internal/src/com/android/internal/colorextraction/types/TonalTest.java b/tests/Internal/src/com/android/internal/colorextraction/types/TonalTest.java index 1e3e8e91d9ef..d408b84109bc 100644 --- a/tests/Internal/src/com/android/internal/colorextraction/types/TonalTest.java +++ b/tests/Internal/src/com/android/internal/colorextraction/types/TonalTest.java @@ -40,6 +40,31 @@ import java.util.Arrays; public class TonalTest { @Test + public void extractInto_usesFallback() { + GradientColors normal = new GradientColors(); + Tonal tonal = new Tonal(); + tonal.extractInto(null, normal, new GradientColors(), + new GradientColors()); + assertFalse("Should use fallback color if WallpaperColors is null.", + normal.getMainColor() == Tonal.MAIN_COLOR_LIGHT); + } + + @Test + public void extractInto_usesFallbackWhenTooLightOrDark() { + GradientColors normal = new GradientColors(); + Tonal tonal = new Tonal(); + tonal.extractInto(new WallpaperColors(Color.valueOf(0xff000000), null, null, 0), + normal, new GradientColors(), new GradientColors()); + assertTrue("Should use fallback color if WallpaperColors is too dark.", + normal.getMainColor() == Tonal.MAIN_COLOR_DARK); + + tonal.extractInto(new WallpaperColors(Color.valueOf(0xffffffff), null, null, 0), + normal, new GradientColors(), new GradientColors()); + assertTrue("Should use fallback color if WallpaperColors is too light.", + normal.getMainColor() == Tonal.MAIN_COLOR_LIGHT); + } + + @Test public void colorRange_containsColor() { Tonal.ColorRange colorRange = new Tonal.ColorRange(new Range<>(0f, 50f), new Range<>(0f, 1f), new Range<>(0f, 1f)); @@ -72,8 +97,10 @@ public class TonalTest { // Make sure that palette generation will fail Tonal tonal = new Tonal(); - boolean success = tonal.extractInto(colors, new GradientColors(), new GradientColors(), + GradientColors normal = new GradientColors(); + tonal.extractInto(colors, normal, new GradientColors(), new GradientColors()); - assertFalse("Cannot generate a tonal palette from blacklisted colors ", success); + assertFalse("Cannot generate a tonal palette from blacklisted colors.", + normal.getMainColor() == Tonal.MAIN_COLOR_LIGHT); } } diff --git a/tests/TouchLatency/.gitignore b/tests/TouchLatency/.gitignore index cfb71643044b..bd79078a904e 100644 --- a/tests/TouchLatency/.gitignore +++ b/tests/TouchLatency/.gitignore @@ -3,3 +3,4 @@ /.idea .DS_Store /build +.iml diff --git a/tests/TouchLatency/Android.mk b/tests/TouchLatency/Android.mk index 6ad47057e8e8..969283df4af7 100644 --- a/tests/TouchLatency/Android.mk +++ b/tests/TouchLatency/Android.mk @@ -8,19 +8,11 @@ LOCAL_MANIFEST_FILE := app/src/main/AndroidManifest.xml # omit gradle 'build' dir LOCAL_SRC_FILES := $(call all-java-files-under,app/src/main/java) -# use appcompat/support lib from the tree, so improvements/ -# regressions are reflected in test data LOCAL_RESOURCE_DIR := \ - $(LOCAL_PATH)/app/src/main/res \ - frameworks/support/v7/appcompat/res + $(LOCAL_PATH)/app/src/main/res LOCAL_AAPT_FLAGS := \ - --auto-add-overlay \ - --extra-packages android.support.v7.appcompat - -LOCAL_STATIC_JAVA_LIBRARIES := \ - android-support-v4 \ - android-support-v7-appcompat + --auto-add-overlay LOCAL_PACKAGE_NAME := TouchLatency diff --git a/tests/TouchLatency/TouchLatency.iml b/tests/TouchLatency/TouchLatency.iml deleted file mode 100644 index cd87cea19f1d..000000000000 --- a/tests/TouchLatency/TouchLatency.iml +++ /dev/null @@ -1,19 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" external.system.module.group="" external.system.module.version="unspecified" type="JAVA_MODULE" version="4"> - <component name="FacetManager"> - <facet type="java-gradle" name="Java-Gradle"> - <configuration> - <option name="BUILD_FOLDER_PATH" value="$MODULE_DIR$/build" /> - <option name="BUILDABLE" value="false" /> - </configuration> - </facet> - </component> - <component name="NewModuleRootManager" inherit-compiler-output="true"> - <exclude-output /> - <content url="file://$MODULE_DIR$"> - <excludeFolder url="file://$MODULE_DIR$/.gradle" /> - </content> - <orderEntry type="inheritedJdk" /> - <orderEntry type="sourceFolder" forTests="false" /> - </component> -</module>
\ No newline at end of file diff --git a/tests/TouchLatency/app/app.iml b/tests/TouchLatency/app/app.iml deleted file mode 100644 index 689e5e0024da..000000000000 --- a/tests/TouchLatency/app/app.iml +++ /dev/null @@ -1,92 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<module external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" external.system.module.group="TouchLatency" external.system.module.version="unspecified" type="JAVA_MODULE" version="4"> - <component name="FacetManager"> - <facet type="android-gradle" name="Android-Gradle"> - <configuration> - <option name="GRADLE_PROJECT_PATH" value=":app" /> - </configuration> - </facet> - <facet type="android" name="Android"> - <configuration> - <option name="SELECTED_BUILD_VARIANT" value="debug" /> - <option name="SELECTED_TEST_ARTIFACT" value="_android_test_" /> - <option name="ASSEMBLE_TASK_NAME" value="assembleDebug" /> - <option name="COMPILE_JAVA_TASK_NAME" value="compileDebugSources" /> - <option name="SOURCE_GEN_TASK_NAME" value="generateDebugSources" /> - <option name="ASSEMBLE_TEST_TASK_NAME" value="assembleDebugAndroidTest" /> - <option name="TEST_SOURCE_GEN_TASK_NAME" value="generateDebugAndroidTestSources" /> - <option name="ALLOW_USER_CONFIGURATION" value="false" /> - <option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" /> - <option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" /> - <option name="RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/src/main/res" /> - <option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" /> - </configuration> - </facet> - </component> - <component name="NewModuleRootManager" inherit-compiler-output="false"> - <output url="file://$MODULE_DIR$/build/intermediates/classes/debug" /> - <output-test url="file://$MODULE_DIR$/build/intermediates/classes/androidTest/debug" /> - <exclude-output /> - <content url="file://$MODULE_DIR$"> - <sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/debug" isTestSource="false" generated="true" /> - <sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/debug" isTestSource="false" generated="true" /> - <sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" /> - <sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/debug" isTestSource="false" generated="true" /> - <sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" /> - <sourceFolder url="file://$MODULE_DIR$/build/generated/res/generated/debug" type="java-resource" /> - <sourceFolder url="file://$MODULE_DIR$/build/generated/source/r/androidTest/debug" isTestSource="true" generated="true" /> - <sourceFolder url="file://$MODULE_DIR$/build/generated/source/aidl/androidTest/debug" isTestSource="true" generated="true" /> - <sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/debug" isTestSource="true" generated="true" /> - <sourceFolder url="file://$MODULE_DIR$/build/generated/source/rs/androidTest/debug" isTestSource="true" generated="true" /> - <sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/androidTest/debug" type="java-test-resource" /> - <sourceFolder url="file://$MODULE_DIR$/build/generated/res/generated/androidTest/debug" type="java-test-resource" /> - <sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" /> - <sourceFolder url="file://$MODULE_DIR$/src/debug/resources" type="java-resource" /> - <sourceFolder url="file://$MODULE_DIR$/src/debug/assets" type="java-resource" /> - <sourceFolder url="file://$MODULE_DIR$/src/debug/aidl" isTestSource="false" /> - <sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" /> - <sourceFolder url="file://$MODULE_DIR$/src/debug/jni" isTestSource="false" /> - <sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" /> - <sourceFolder url="file://$MODULE_DIR$/src/main/res" type="java-resource" /> - <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" /> - <sourceFolder url="file://$MODULE_DIR$/src/main/assets" type="java-resource" /> - <sourceFolder url="file://$MODULE_DIR$/src/main/aidl" isTestSource="false" /> - <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" /> - <sourceFolder url="file://$MODULE_DIR$/src/main/jni" isTestSource="false" /> - <sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" /> - <sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" /> - <sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" /> - <sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" /> - <sourceFolder url="file://$MODULE_DIR$/src/androidTest/aidl" isTestSource="true" /> - <sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" /> - <sourceFolder url="file://$MODULE_DIR$/src/androidTest/jni" isTestSource="true" /> - <sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/assets" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/bundles" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/classes" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/coverage-instrumented-classes" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dependency-cache" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/dex-cache" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/incremental" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/jacoco" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/javaResources" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/libs" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/lint" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/ndk" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/pre-dexed" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/proguard" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" /> - <excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" /> - <excludeFolder url="file://$MODULE_DIR$/build/outputs" /> - <excludeFolder url="file://$MODULE_DIR$/build/tmp" /> - </content> - <orderEntry type="jdk" jdkName="Android API 21 Platform" jdkType="Android SDK" /> - <orderEntry type="sourceFolder" forTests="false" /> - <orderEntry type="library" exported="" name="appcompat-v7-21.0.3" level="project" /> - <orderEntry type="library" exported="" name="support-annotations-21.0.3" level="project" /> - <orderEntry type="library" exported="" name="support-v4-21.0.3" level="project" /> - </component> -</module>
\ No newline at end of file diff --git a/tests/TouchLatency/app/build.gradle b/tests/TouchLatency/app/build.gradle index 7133beb8efee..233711055eb8 100644 --- a/tests/TouchLatency/app/build.gradle +++ b/tests/TouchLatency/app/build.gradle @@ -18,8 +18,3 @@ android { } } } - -dependencies { - compile fileTree(dir: 'libs', include: ['*.jar']) - compile 'com.android.support:appcompat-v7:21.0.3' -} diff --git a/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java b/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java index 55440c849ebc..b763c78207de 100644 --- a/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java +++ b/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java @@ -16,12 +16,12 @@ package com.prefabulated.touchlatency; +import android.app.Activity; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.os.CountDownTimer; -import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.text.method.Touch; import android.util.AttributeSet; @@ -173,7 +173,7 @@ class TouchLatencyView extends View implements View.OnTouchListener { private float mVelocityX, mVelocityY; } -public class TouchLatencyActivity extends AppCompatActivity { +public class TouchLatencyActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { diff --git a/tests/TouchLatency/app/src/main/res/menu/menu_touch_latency.xml b/tests/TouchLatency/app/src/main/res/menu/menu_touch_latency.xml index 1824f4a68995..5aef72e8d383 100644 --- a/tests/TouchLatency/app/src/main/res/menu/menu_touch_latency.xml +++ b/tests/TouchLatency/app/src/main/res/menu/menu_touch_latency.xml @@ -14,8 +14,7 @@ limitations under the License. --> <menu xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" tools:context=".TouchLatencyActivity"> <item android:id="@+id/action_settings" android:title="@string/mode" - android:orderInCategory="100" app:showAsAction="always" /> + android:orderInCategory="100" android:showAsAction="always" /> </menu> diff --git a/tests/TouchLatency/app/src/main/res/values/styles.xml b/tests/TouchLatency/app/src/main/res/values/styles.xml index aa7c09fff40d..22da7c1d050b 100644 --- a/tests/TouchLatency/app/src/main/res/values/styles.xml +++ b/tests/TouchLatency/app/src/main/res/values/styles.xml @@ -16,7 +16,7 @@ <resources> <!-- Base application theme. --> - <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> + <style name="AppTheme" parent="@android:style/Theme.Material.Light.DarkActionBar"> <!-- Customize your theme here. --> </style> diff --git a/tests/net/java/android/net/ip/IpManagerTest.java b/tests/net/java/android/net/ip/IpManagerTest.java index 025b01740aad..e7dbfe3f5044 100644 --- a/tests/net/java/android/net/ip/IpManagerTest.java +++ b/tests/net/java/android/net/ip/IpManagerTest.java @@ -16,11 +16,26 @@ package android.net.ip; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.app.AlarmManager; import android.content.ContentResolver; import android.content.Context; import android.content.res.Resources; +import android.net.IpPrefix; +import android.net.LinkAddress; +import android.net.ip.IpManager.Callback; +import android.net.ip.IpManager.InitialConfiguration; +import android.net.ip.IpManager.ProvisioningConfiguration; import android.os.INetworkManagementService; import android.provider.Settings; import android.support.test.filters.SmallTest; @@ -31,11 +46,17 @@ import com.android.internal.util.test.FakeSettingsProvider; import com.android.internal.R; import org.junit.Before; -import org.junit.runner.RunWith; import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.util.HashSet; +import java.util.Set; + /** * Tests for IpManager. */ @@ -44,14 +65,20 @@ import org.mockito.MockitoAnnotations; public class IpManagerTest { private static final int DEFAULT_AVOIDBADWIFI_CONFIG_VALUE = 1; + private static final String VALID = "VALID"; + private static final String INVALID = "INVALID"; + @Mock private Context mContext; @Mock private INetworkManagementService mNMService; @Mock private Resources mResources; + @Mock private Callback mCb; + @Mock private AlarmManager mAlarm; private MockContentResolver mContentResolver; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); + when(mContext.getSystemService(eq(Context.ALARM_SERVICE))).thenReturn(mAlarm); when(mContext.getResources()).thenReturn(mResources); when(mResources.getInteger(R.integer.config_networkAvoidBadWifi)) .thenReturn(DEFAULT_AVOIDBADWIFI_CONFIG_VALUE); @@ -68,7 +95,152 @@ public class IpManagerTest { @Test public void testInvalidInterfaceDoesNotThrow() throws Exception { - final IpManager.Callback cb = new IpManager.Callback(); - final IpManager ipm = new IpManager(mContext, "test_wlan0", cb, mNMService); + final IpManager ipm = new IpManager(mContext, "test_wlan0", mCb, mNMService); + } + + @Test + public void testDefaultProvisioningConfiguration() throws Exception { + final String iface = "test_wlan0"; + final IpManager ipm = new IpManager(mContext, iface, mCb, mNMService); + ProvisioningConfiguration config = new ProvisioningConfiguration.Builder() + .withoutIPv4() + // TODO: mock IpReachabilityMonitor's dependencies (NetworkInterface, PowerManager) + // and enable it in this test + .withoutIpReachabilityMonitor() + .build(); + + ipm.startProvisioning(config); + verify(mCb, times(1)).setNeighborDiscoveryOffload(true); + verify(mCb, timeout(100).times(1)).setFallbackMulticastFilter(false); + verify(mCb, never()).onProvisioningFailure(any()); + + ipm.stop(); + verify(mNMService, timeout(100).times(1)).disableIpv6(iface); + verify(mNMService, timeout(100).times(1)).clearInterfaceAddresses(iface); + } + + @Test + public void testInitialConfigurations() throws Exception { + InitialConfigurationTestCase[] testcases = { + validConf("valid IPv4 configuration", + links("192.0.2.12/24"), prefixes("192.0.2.0/24"), dns("192.0.2.2")), + validConf("another valid IPv4 configuration", + links("192.0.2.12/24"), prefixes("192.0.2.0/24"), dns()), + validConf("valid IPv6 configurations", + links("2001:db8:dead:beef:f00::a0/64", "fe80::1/64"), + prefixes("2001:db8:dead:beef::/64", "fe80::/64"), + dns("2001:db8:dead:beef:f00::02")), + validConf("valid IPv6 configurations", + links("fe80::1/64"), prefixes("fe80::/64"), dns()), + validConf("valid IPv6/v4 configuration", + links("2001:db8:dead:beef:f00::a0/48", "192.0.2.12/24"), + prefixes("2001:db8:dead:beef::/64", "192.0.2.0/24"), + dns("192.0.2.2", "2001:db8:dead:beef:f00::02")), + validConf("valid IPv6 configuration without any GUA.", + links("fd00:1234:5678::1/48"), + prefixes("fd00:1234:5678::/48"), + dns("fd00:1234:5678::1000")), + + invalidConf("v4 addr and dns not in any prefix", + links("192.0.2.12/24"), prefixes("198.51.100.0/24"), dns("192.0.2.2")), + invalidConf("v4 addr not in any prefix", + links("198.51.2.12/24"), prefixes("198.51.100.0/24"), dns("192.0.2.2")), + invalidConf("v4 dns addr not in any prefix", + links("192.0.2.12/24"), prefixes("192.0.2.0/24"), dns("198.51.100.2")), + invalidConf("v6 addr not in any prefix", + links("2001:db8:dead:beef:f00::a0/64", "fe80::1/64"), + prefixes("2001:db8:dead:beef::/64"), + dns("2001:db8:dead:beef:f00::02")), + invalidConf("v6 dns addr not in any prefix", + links("fe80::1/64"), prefixes("fe80::/64"), dns("2001:db8:dead:beef:f00::02")), + invalidConf("default ipv6 route and no GUA", + links("fd01:1111:2222:3333::a0/128"), prefixes("::/0"), dns()), + invalidConf("invalid v6 prefix length", + links("2001:db8:dead:beef:f00::a0/128"), prefixes("2001:db8:dead:beef::/32"), + dns()), + invalidConf("another invalid v6 prefix length", + links("2001:db8:dead:beef:f00::a0/128"), prefixes("2001:db8:dead:beef::/72"), + dns()) + }; + + for (InitialConfigurationTestCase testcase : testcases) { + if (testcase.config.isValid() != testcase.isValid) { + fail(testcase.errorMessage()); + } + } + } + + static class InitialConfigurationTestCase { + String descr; + boolean isValid; + InitialConfiguration config; + public String errorMessage() { + return String.format("%s: expected configuration %s to be %s, but was %s", + descr, config, validString(isValid), validString(!isValid)); + } + } + + static String validString(boolean isValid) { + return isValid ? VALID : INVALID; + } + + static InitialConfigurationTestCase validConf(String descr, Set<LinkAddress> links, + Set<IpPrefix> prefixes, Set<InetAddress> dns) { + return confTestCase(descr, true, conf(links, prefixes, dns)); + } + + static InitialConfigurationTestCase invalidConf(String descr, Set<LinkAddress> links, + Set<IpPrefix> prefixes, Set<InetAddress> dns) { + return confTestCase(descr, false, conf(links, prefixes, dns)); + } + + static InitialConfigurationTestCase confTestCase( + String descr, boolean isValid, InitialConfiguration config) { + InitialConfigurationTestCase testcase = new InitialConfigurationTestCase(); + testcase.descr = descr; + testcase.isValid = isValid; + testcase.config = config; + return testcase; + } + + static InitialConfiguration conf( + Set<LinkAddress> links, Set<IpPrefix> prefixes, Set<InetAddress> dns) { + InitialConfiguration conf = new InitialConfiguration(); + conf.ipAddresses.addAll(links); + conf.directlyConnectedRoutes.addAll(prefixes); + conf.dnsServers.addAll(dns); + return conf; + } + + static Set<IpPrefix> prefixes(String... prefixes) { + return mapIntoSet(prefixes, IpPrefix::new); + } + + static Set<LinkAddress> links(String... addresses) { + return mapIntoSet(addresses, LinkAddress::new); + } + + static Set<InetAddress> ips(String... addresses) { + return mapIntoSet(addresses, InetAddress::getByName); + } + + static Set<InetAddress> dns(String... addresses) { + return ips(addresses); + } + + static <A, B> Set<B> mapIntoSet(A[] in, Fn<A, B> fn) { + Set<B> out = new HashSet<>(in.length); + for (A item : in) { + try { + out.add(fn.call(item)); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + return out; + } + + interface Fn<A,B> { + B call(A a) throws Exception; } } diff --git a/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java b/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java index 0dedf703f3bb..0e4a36ccfc7e 100644 --- a/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java +++ b/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java @@ -31,6 +31,7 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.content.pm.ApplicationInfo; +import android.net.IpPrefix; import android.net.LinkAddress; import android.net.LinkProperties; import android.net.RouteInfo; @@ -45,6 +46,8 @@ import com.android.internal.util.test.FakeSettingsProvider; import java.net.InetAddress; import java.util.ArrayList; +import java.util.HashSet; +import java.util.Set; import org.junit.After; import org.junit.Before; @@ -182,17 +185,43 @@ public class OffloadControllerTest { any(OffloadHardwareInterface.ControlCallback.class)); inOrder.verifyNoMoreInteractions(); + // In reality, the UpstreamNetworkMonitor would have passed down to us + // a covering set of local prefixes representing a minimum essential + // set plus all the prefixes on networks with network agents. + // + // We simulate that there, and then add upstream elements one by one + // and watch what happens. + final Set<IpPrefix> minimumLocalPrefixes = new HashSet<>(); + for (String s : new String[]{ + "127.0.0.0/8", "192.0.2.0/24", "fe80::/64", "2001:db8::/64"}) { + minimumLocalPrefixes.add(new IpPrefix(s)); + } + offload.setLocalPrefixes(minimumLocalPrefixes); + inOrder.verify(mHardware, times(1)).setLocalPrefixes(mStringArrayCaptor.capture()); + ArrayList<String> localPrefixes = mStringArrayCaptor.getValue(); + assertEquals(4, localPrefixes.size()); + assertTrue(localPrefixes.contains("127.0.0.0/8")); + assertTrue(localPrefixes.contains("192.0.2.0/24")); + assertTrue(localPrefixes.contains("fe80::/64")); + assertTrue(localPrefixes.contains("2001:db8::/64")); + inOrder.verifyNoMoreInteractions(); + offload.setUpstreamLinkProperties(null); - inOrder.verify(mHardware, times(1)).setUpstreamParameters( - eq(null), eq(null), eq(null), eq(null)); + // No change in local addresses means no call to setLocalPrefixes(). + inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture()); + // This LinkProperties value does not differ from the default upstream. + // There should be no extraneous call to setUpstreamParameters(). + inOrder.verify(mHardware, never()).setUpstreamParameters( + anyObject(), anyObject(), anyObject(), anyObject()); inOrder.verifyNoMoreInteractions(); - reset(mHardware); final LinkProperties lp = new LinkProperties(); final String testIfName = "rmnet_data17"; lp.setInterfaceName(testIfName); offload.setUpstreamLinkProperties(lp); + // No change in local addresses means no call to setLocalPrefixes(). + inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture()); inOrder.verify(mHardware, times(1)).setUpstreamParameters( eq(testIfName), eq(null), eq(null), eq(null)); inOrder.verifyNoMoreInteractions(); @@ -200,7 +229,15 @@ public class OffloadControllerTest { final String ipv4Addr = "192.0.2.5"; final String linkAddr = ipv4Addr + "/24"; lp.addLinkAddress(new LinkAddress(linkAddr)); + lp.addRoute(new RouteInfo(new IpPrefix("192.0.2.0/24"))); offload.setUpstreamLinkProperties(lp); + // IPv4 prefixes and addresses on the upstream are simply left as whole + // prefixes (already passed in from UpstreamNetworkMonitor code). If a + // tethering client sends traffic to the IPv4 default router or other + // clients on the upstream this will not be hardware-forwarded, and that + // should be fine for now. Ergo: no change in local addresses, no call + // to setLocalPrefixes(). + inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture()); inOrder.verify(mHardware, times(1)).setUpstreamParameters( eq(testIfName), eq(ipv4Addr), eq(null), eq(null)); inOrder.verifyNoMoreInteractions(); @@ -208,6 +245,8 @@ public class OffloadControllerTest { final String ipv4Gateway = "192.0.2.1"; lp.addRoute(new RouteInfo(InetAddress.getByName(ipv4Gateway))); offload.setUpstreamLinkProperties(lp); + // No change in local addresses means no call to setLocalPrefixes(). + inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture()); inOrder.verify(mHardware, times(1)).setUpstreamParameters( eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), eq(null)); inOrder.verifyNoMoreInteractions(); @@ -215,6 +254,8 @@ public class OffloadControllerTest { final String ipv6Gw1 = "fe80::cafe"; lp.addRoute(new RouteInfo(InetAddress.getByName(ipv6Gw1))); offload.setUpstreamLinkProperties(lp); + // No change in local addresses means no call to setLocalPrefixes(). + inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture()); inOrder.verify(mHardware, times(1)).setUpstreamParameters( eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), mStringArrayCaptor.capture()); ArrayList<String> v6gws = mStringArrayCaptor.getValue(); @@ -225,6 +266,8 @@ public class OffloadControllerTest { final String ipv6Gw2 = "fe80::d00d"; lp.addRoute(new RouteInfo(InetAddress.getByName(ipv6Gw2))); offload.setUpstreamLinkProperties(lp); + // No change in local addresses means no call to setLocalPrefixes(). + inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture()); inOrder.verify(mHardware, times(1)).setUpstreamParameters( eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), mStringArrayCaptor.capture()); v6gws = mStringArrayCaptor.getValue(); @@ -240,6 +283,8 @@ public class OffloadControllerTest { stacked.addRoute(new RouteInfo(InetAddress.getByName("fe80::bad:f00"))); assertTrue(lp.addStackedLink(stacked)); offload.setUpstreamLinkProperties(lp); + // No change in local addresses means no call to setLocalPrefixes(). + inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture()); inOrder.verify(mHardware, times(1)).setUpstreamParameters( eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), mStringArrayCaptor.capture()); v6gws = mStringArrayCaptor.getValue(); @@ -247,5 +292,43 @@ public class OffloadControllerTest { assertTrue(v6gws.contains(ipv6Gw1)); assertTrue(v6gws.contains(ipv6Gw2)); inOrder.verifyNoMoreInteractions(); + + // Add in some IPv6 upstream info. When there is a tethered downstream + // making use of the IPv6 prefix we would expect to see the /64 route + // removed from "local prefixes" and /128s added for the upstream IPv6 + // addresses. This is not yet implemented, and for now we simply + // expect to see these /128s. + lp.addRoute(new RouteInfo(new IpPrefix("2001:db8::/64"))); + // "2001:db8::/64" plus "assigned" ASCII in hex + lp.addLinkAddress(new LinkAddress("2001:db8::6173:7369:676e:6564/64")); + // "2001:db8::/64" plus "random" ASCII in hex + lp.addLinkAddress(new LinkAddress("2001:db8::7261:6e64:6f6d/64")); + offload.setUpstreamLinkProperties(lp); + inOrder.verify(mHardware, times(1)).setLocalPrefixes(mStringArrayCaptor.capture()); + localPrefixes = mStringArrayCaptor.getValue(); + assertEquals(6, localPrefixes.size()); + assertTrue(localPrefixes.contains("127.0.0.0/8")); + assertTrue(localPrefixes.contains("192.0.2.0/24")); + assertTrue(localPrefixes.contains("fe80::/64")); + assertTrue(localPrefixes.contains("2001:db8::/64")); + assertTrue(localPrefixes.contains("2001:db8::6173:7369:676e:6564/128")); + assertTrue(localPrefixes.contains("2001:db8::7261:6e64:6f6d/128")); + // The relevant parts of the LinkProperties have not changed, but at the + // moment we do not de-dup upstream LinkProperties this carefully. + inOrder.verify(mHardware, times(1)).setUpstreamParameters( + eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), mStringArrayCaptor.capture()); + v6gws = mStringArrayCaptor.getValue(); + assertEquals(2, v6gws.size()); + assertTrue(v6gws.contains(ipv6Gw1)); + assertTrue(v6gws.contains(ipv6Gw2)); + inOrder.verifyNoMoreInteractions(); + + // Completely identical LinkProperties updates are de-duped. + offload.setUpstreamLinkProperties(lp); + // This LinkProperties value does not differ from the default upstream. + // There should be no extraneous call to setUpstreamParameters(). + inOrder.verify(mHardware, never()).setUpstreamParameters( + anyObject(), anyObject(), anyObject(), anyObject()); + inOrder.verifyNoMoreInteractions(); } } diff --git a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java index 69c93b14a486..c3b9defdec4e 100644 --- a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java +++ b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java @@ -324,19 +324,14 @@ public class UpstreamNetworkMonitorTest { } @Test - public void testOffloadExemptPrefixes() throws Exception { + public void testLocalPrefixes() throws Exception { mUNM.start(); - // [0] Test minimum set of exempt prefixes. - Set<IpPrefix> exempt = mUNM.getOffloadExemptPrefixes(); - final String[] MINSET = { - "127.0.0.0/8", "169.254.0.0/16", - "::/3", "fe80::/64", "fc00::/7", "ff00::/8", - }; - assertPrefixSet(exempt, INCLUDES, MINSET); + // [0] Test minimum set of local prefixes. + Set<IpPrefix> local = mUNM.getLocalPrefixes(); + assertTrue(local.isEmpty()); + final Set<String> alreadySeen = new HashSet<>(); - Collections.addAll(alreadySeen, MINSET); - assertEquals(alreadySeen.size(), exempt.size()); // [1] Pretend Wi-Fi connects. final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, TRANSPORT_WIFI); @@ -355,15 +350,15 @@ public class UpstreamNetworkMonitorTest { wifiAgent.fakeConnect(); wifiAgent.sendLinkProperties(wifiLp); - exempt = mUNM.getOffloadExemptPrefixes(); - assertPrefixSet(exempt, INCLUDES, alreadySeen); + local = mUNM.getLocalPrefixes(); + assertPrefixSet(local, INCLUDES, alreadySeen); final String[] wifiLinkPrefixes = { - // Excludes link-local as that's already tested within MINSET. + // Link-local prefixes are excluded and dealt with elsewhere. "100.112.96.0/20", "2001:db8:4:fd00::/64", "fd6a:a640:60bf:e985::/64", }; - assertPrefixSet(exempt, INCLUDES, wifiLinkPrefixes); + assertPrefixSet(local, INCLUDES, wifiLinkPrefixes); Collections.addAll(alreadySeen, wifiLinkPrefixes); - assertEquals(alreadySeen.size(), exempt.size()); + assertEquals(alreadySeen.size(), local.size()); // [2] Pretend mobile connects. final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR); @@ -379,12 +374,12 @@ public class UpstreamNetworkMonitorTest { cellAgent.fakeConnect(); cellAgent.sendLinkProperties(cellLp); - exempt = mUNM.getOffloadExemptPrefixes(); - assertPrefixSet(exempt, INCLUDES, alreadySeen); + local = mUNM.getLocalPrefixes(); + assertPrefixSet(local, INCLUDES, alreadySeen); final String[] cellLinkPrefixes = { "10.102.211.32/27", "2001:db8:0:1::/64" }; - assertPrefixSet(exempt, INCLUDES, cellLinkPrefixes); + assertPrefixSet(local, INCLUDES, cellLinkPrefixes); Collections.addAll(alreadySeen, cellLinkPrefixes); - assertEquals(alreadySeen.size(), exempt.size()); + assertEquals(alreadySeen.size(), local.size()); // [3] Pretend DUN connects. final TestNetworkAgent dunAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR); @@ -401,21 +396,20 @@ public class UpstreamNetworkMonitorTest { dunAgent.fakeConnect(); dunAgent.sendLinkProperties(dunLp); - exempt = mUNM.getOffloadExemptPrefixes(); - assertPrefixSet(exempt, INCLUDES, alreadySeen); + local = mUNM.getLocalPrefixes(); + assertPrefixSet(local, INCLUDES, alreadySeen); final String[] dunLinkPrefixes = { "192.0.2.32/27", "2001:db8:1:2::/64" }; - assertPrefixSet(exempt, INCLUDES, dunLinkPrefixes); + assertPrefixSet(local, INCLUDES, dunLinkPrefixes); Collections.addAll(alreadySeen, dunLinkPrefixes); - assertEquals(alreadySeen.size(), exempt.size()); + assertEquals(alreadySeen.size(), local.size()); // [4] Pretend Wi-Fi disconnected. It's addresses/prefixes should no // longer be included (should be properly removed). wifiAgent.fakeDisconnect(); - exempt = mUNM.getOffloadExemptPrefixes(); - assertPrefixSet(exempt, INCLUDES, MINSET); - assertPrefixSet(exempt, EXCLUDES, wifiLinkPrefixes); - assertPrefixSet(exempt, INCLUDES, cellLinkPrefixes); - assertPrefixSet(exempt, INCLUDES, dunLinkPrefixes); + local = mUNM.getLocalPrefixes(); + assertPrefixSet(local, EXCLUDES, wifiLinkPrefixes); + assertPrefixSet(local, INCLUDES, cellLinkPrefixes); + assertPrefixSet(local, INCLUDES, dunLinkPrefixes); } private void assertSatisfiesLegacyType(int legacyType, NetworkState ns) { diff --git a/tests/radio/src/android/hardware/radio/tests/RadioTest.java b/tests/radio/src/android/hardware/radio/tests/RadioTest.java index b7a5ac4eb19b..e354a34942bb 100644 --- a/tests/radio/src/android/hardware/radio/tests/RadioTest.java +++ b/tests/radio/src/android/hardware/radio/tests/RadioTest.java @@ -22,6 +22,7 @@ import android.hardware.radio.RadioManager; import android.hardware.radio.RadioTuner; import android.support.test.InstrumentationRegistry; import android.support.test.runner.AndroidJUnit4; +import android.test.suitebuilder.annotation.MediumTest; import android.util.Log; import java.lang.reflect.Constructor; @@ -54,6 +55,7 @@ import static org.testng.Assert.assertThrows; * A test for broadcast radio API. */ @RunWith(AndroidJUnit4.class) +@MediumTest public class RadioTest { private static final String TAG = "RadioTest"; @@ -81,7 +83,8 @@ public class RadioTest { // check if radio is supported and skip the test if it's not PackageManager packageManager = mContext.getPackageManager(); - boolean isRadioSupported = packageManager.hasSystemFeature(PackageManager.FEATURE_RADIO); + boolean isRadioSupported = packageManager.hasSystemFeature( + PackageManager.FEATURE_BROADCAST_RADIO); assumeTrue(isRadioSupported); // Check radio access permission |