summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResults.java8
-rw-r--r--apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResultsReporter.java8
-rw-r--r--apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java8
-rw-r--r--apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java (renamed from apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTest.java)12
-rw-r--r--api/current.txt5
-rw-r--r--api/system-current.txt23
-rw-r--r--api/test-current.txt5
-rw-r--r--core/java/android/app/ActivityThread.java17
-rw-r--r--core/java/android/app/IActivityManager.aidl4
-rw-r--r--core/java/android/app/IApplicationThread.aidl3
-rw-r--r--core/java/android/app/backup/RestoreDescription.java2
-rw-r--r--core/java/android/content/pm/ActivityInfo.java10
-rw-r--r--core/java/android/content/pm/PackageManager.java10
-rw-r--r--core/java/android/content/pm/PackageParser.java83
-rw-r--r--core/java/android/content/res/Configuration.java2
-rw-r--r--core/java/android/content/res/Resources.java3
-rw-r--r--core/java/android/database/ContentObserver.java10
-rw-r--r--core/java/android/hardware/location/NanoApp.java4
-rw-r--r--core/java/android/hardware/radio/RadioManager.java12
-rw-r--r--core/java/android/hardware/radio/RadioTuner.java19
-rw-r--r--core/java/android/net/LinkAddress.java20
-rw-r--r--core/java/android/net/metrics/IpManagerEvent.java2
-rw-r--r--core/java/android/os/Debug.java7
-rw-r--r--core/java/android/os/HwBinder.java5
-rw-r--r--core/java/android/os/RecoverySystem.java5
-rwxr-xr-xcore/java/android/provider/Settings.java6
-rw-r--r--core/java/android/text/FontConfig.java8
-rw-r--r--core/java/android/text/format/Formatter.java251
-rw-r--r--core/java/android/view/WindowManagerPolicy.java13
-rw-r--r--core/java/android/view/textclassifier/TextClassification.java18
-rw-r--r--core/java/android/webkit/SafeBrowsingResponse.java6
-rw-r--r--core/java/android/webkit/WebView.java8
-rw-r--r--core/java/android/webkit/WebViewFactoryProvider.java6
-rw-r--r--core/java/com/android/internal/app/ResolverComparator.java35
-rw-r--r--core/java/com/android/internal/colorextraction/ColorExtractor.java55
-rw-r--r--core/java/com/android/internal/colorextraction/types/ExtractionType.java3
-rw-r--r--core/java/com/android/internal/colorextraction/types/Tonal.java110
-rw-r--r--core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java14
-rw-r--r--core/java/com/android/server/BootReceiver.java1
-rw-r--r--core/jni/android_os_Debug.cpp54
-rw-r--r--core/jni/android_os_HwBinder.cpp17
-rw-r--r--core/jni/android_text_AndroidBidi.cpp2
-rw-r--r--core/res/res/values-bs/strings.xml4
-rw-r--r--core/res/res/values-es/strings.xml6
-rw-r--r--core/res/res/values-eu/strings.xml3
-rw-r--r--core/res/res/values-fr-rCA/strings.xml3
-rw-r--r--core/res/res/values-fr/strings.xml2
-rw-r--r--core/res/res/values-gl/strings.xml3
-rw-r--r--core/res/res/values-hi/strings.xml6
-rw-r--r--core/res/res/values-hy/strings.xml4
-rw-r--r--core/res/res/values-iw/strings.xml3
-rw-r--r--core/res/res/values-lo/strings.xml3
-rw-r--r--core/res/res/values-sl/strings.xml3
-rw-r--r--core/res/res/values-tr/strings.xml3
-rw-r--r--core/res/res/values-zh-rCN/strings.xml5
-rw-r--r--core/res/res/values/config.xml3
-rw-r--r--core/res/res/values/strings.xml20
-rw-r--r--core/res/res/values/symbols.xml6
-rw-r--r--core/tests/coretests/assets/fonts/a3em.ttfbin0 -> 1784 bytes
-rw-r--r--core/tests/coretests/assets/fonts/a3em.ttx187
-rw-r--r--core/tests/coretests/assets/fonts/all2em.ttfbin0 -> 1780 bytes
-rw-r--r--core/tests/coretests/assets/fonts/all2em.ttx184
-rw-r--r--core/tests/coretests/assets/fonts/b3em.ttfbin0 -> 1784 bytes
-rw-r--r--core/tests/coretests/assets/fonts/b3em.ttx187
-rw-r--r--core/tests/coretests/assets/fonts/c3em.ttfbin0 -> 1784 bytes
-rw-r--r--core/tests/coretests/assets/fonts/c3em.ttx187
-rw-r--r--core/tests/coretests/assets/fonts/no_coverage.ttfbin0 -> 1768 bytes
-rw-r--r--core/tests/coretests/assets/fonts/no_coverage.ttx180
-rw-r--r--core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java469
-rw-r--r--core/tests/coretests/src/android/provider/SettingsBackupTest.java1
-rw-r--r--graphics/java/android/graphics/FontListParser.java3
-rw-r--r--graphics/java/android/graphics/Typeface.java492
-rw-r--r--libs/hwui/hwui/Typeface.cpp2
-rw-r--r--lowpan/java/android/net/lowpan/LowpanCommissioningSession.java2
-rw-r--r--packages/SettingsLib/res/layout/preference_category_material_settings.xml1
-rw-r--r--packages/SettingsLib/res/values/attrs.xml5
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java11
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java11
-rw-r--r--packages/SystemUI/res-keyguard/values/styles.xml2
-rw-r--r--packages/SystemUI/res/values/styles.xml1
-rw-r--r--packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeLog.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/SlashDrawable.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/SlashImageView.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/AnimatedImageView.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java54
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/colorextraction/SysuiColorExtractorTests.java9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/SlashImageViewTest.java21
-rw-r--r--proto/src/metrics_constants.proto16
-rw-r--r--services/backup/java/com/android/server/backup/BackupManagerService.java11
-rw-r--r--services/core/java/com/android/server/Watchdog.java5
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java11
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerShellCommand.java6
-rw-r--r--services/core/java/com/android/server/am/ActivityRecord.java24
-rw-r--r--services/core/java/com/android/server/camera/CameraServiceProxy.java28
-rw-r--r--services/core/java/com/android/server/connectivity/Tethering.java70
-rw-r--r--services/core/java/com/android/server/connectivity/tethering/OffloadController.java63
-rw-r--r--services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java20
-rw-r--r--services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java2
-rw-r--r--services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java58
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java21
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java10
-rw-r--r--services/core/java/com/android/server/timezone/PackageTracker.java40
-rw-r--r--services/core/java/com/android/server/wm/AppWindowToken.java6
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java6
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java18
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java46
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java14
-rw-r--r--services/core/jni/com_android_server_VibratorService.cpp10
-rw-r--r--services/java/com/android/server/SystemServer.java4
-rw-r--r--services/net/java/android/net/ip/IpManager.java141
-rw-r--r--services/net/java/android/net/util/NetworkConstants.java1
-rw-r--r--services/net/java/android/net/util/PrefixUtils.java74
-rw-r--r--services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java53
-rw-r--r--services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java37
-rw-r--r--services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java1
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java6
-rw-r--r--services/usb/java/com/android/server/usb/UsbDeviceManager.java4
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java41
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java15
-rw-r--r--tests/Internal/src/com/android/internal/colorextraction/ColorExtractorTest.java17
-rw-r--r--tests/Internal/src/com/android/internal/colorextraction/types/TonalTest.java31
-rw-r--r--tests/TouchLatency/.gitignore1
-rw-r--r--tests/TouchLatency/Android.mk12
-rw-r--r--tests/TouchLatency/TouchLatency.iml19
-rw-r--r--tests/TouchLatency/app/app.iml92
-rw-r--r--tests/TouchLatency/app/build.gradle5
-rw-r--r--tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java4
-rw-r--r--tests/TouchLatency/app/src/main/res/menu/menu_touch_latency.xml3
-rw-r--r--tests/TouchLatency/app/src/main/res/values/styles.xml2
-rw-r--r--tests/net/java/android/net/ip/IpManagerTest.java178
-rw-r--r--tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java89
-rw-r--r--tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java50
-rw-r--r--tests/radio/src/android/hardware/radio/tests/RadioTest.java5
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
new file mode 100644
index 000000000000..a601ce2ed932
--- /dev/null
+++ b/core/tests/coretests/assets/fonts/a3em.ttf
Binary files differ
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
new file mode 100644
index 000000000000..482f7552f510
--- /dev/null
+++ b/core/tests/coretests/assets/fonts/all2em.ttf
Binary files differ
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
new file mode 100644
index 000000000000..63948a22c113
--- /dev/null
+++ b/core/tests/coretests/assets/fonts/b3em.ttf
Binary files differ
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
new file mode 100644
index 000000000000..badc3e29868f
--- /dev/null
+++ b/core/tests/coretests/assets/fonts/c3em.ttf
Binary files differ
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
new file mode 100644
index 000000000000..c884881c5026
--- /dev/null
+++ b/core/tests/coretests/assets/fonts/no_coverage.ttf
Binary files differ
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