Merge "Fix issue #122604613: Fix appops dumpsys"
diff --git a/Android.bp b/Android.bp
index 93c94e3..15befae 100644
--- a/Android.bp
+++ b/Android.bp
@@ -739,6 +739,7 @@
"android.hardware.vibrator-V1.0-java",
"android.hardware.vibrator-V1.1-java",
"android.hardware.vibrator-V1.2-java",
+ "android.hardware.vibrator-V1.3-java",
"android.hardware.wifi-V1.0-java-constants",
"android.hardware.radio-V1.0-java",
"android.hardware.radio-V1.3-java",
@@ -872,7 +873,10 @@
local_include_dir: "core/java",
srcs: [
"core/java/android/net/INetworkStackConnector.aidl",
+ "core/java/android/net/INetworkStackStatusCallback.aidl",
"core/java/android/net/dhcp/DhcpServingParamsParcel.aidl",
+ "core/java/android/net/dhcp/IDhcpServer.aidl",
+ "core/java/android/net/dhcp/IDhcpServerCallbacks.aidl",
],
api_dir: "aidl/networkstack",
}
diff --git a/apct-tests/perftests/core/Android.mk b/apct-tests/perftests/core/Android.mk
index 77bacbe..3f87a1c 100644
--- a/apct-tests/perftests/core/Android.mk
+++ b/apct-tests/perftests/core/Android.mk
@@ -9,7 +9,7 @@
src/android/os/ISomeService.aidl
LOCAL_STATIC_JAVA_LIBRARIES := \
- android-support-test \
+ androidx.test.rules \
androidx.annotation_annotation \
apct-perftests-utils \
guava
diff --git a/apct-tests/perftests/core/AndroidManifest.xml b/apct-tests/perftests/core/AndroidManifest.xml
index 13c24d9..a564a4d 100644
--- a/apct-tests/perftests/core/AndroidManifest.xml
+++ b/apct-tests/perftests/core/AndroidManifest.xml
@@ -28,7 +28,7 @@
</application>
- <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.perftests.core"/>
</manifest>
diff --git a/apct-tests/perftests/core/src/android/accounts/AccountManagerPerfTest.java b/apct-tests/perftests/core/src/android/accounts/AccountManagerPerfTest.java
index b9411fa..e455e6a 100644
--- a/apct-tests/perftests/core/src/android/accounts/AccountManagerPerfTest.java
+++ b/apct-tests/perftests/core/src/android/accounts/AccountManagerPerfTest.java
@@ -23,10 +23,10 @@
import android.content.pm.PackageManager;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
-import android.util.Log;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
import org.junit.Test;
diff --git a/apct-tests/perftests/core/src/android/app/PendingIntentPerfTest.java b/apct-tests/perftests/core/src/android/app/PendingIntentPerfTest.java
index f8fd51d..b3f8359 100644
--- a/apct-tests/perftests/core/src/android/app/PendingIntentPerfTest.java
+++ b/apct-tests/perftests/core/src/android/app/PendingIntentPerfTest.java
@@ -21,9 +21,10 @@
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
import android.perftests.utils.StubActivity;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Rule;
@@ -33,7 +34,6 @@
// Due to b/71353150, you might get "java.lang.AssertionError: Binder ProxyMap has too many
// entries", but it's flaky. Adding "Runtime.getRuntime().gc()" between each iteration solves
// the problem, but it doesn't seem like it's currently needed.
-
@RunWith(AndroidJUnit4.class)
@LargeTest
public class PendingIntentPerfTest {
diff --git a/apct-tests/perftests/core/src/android/app/ResourcesPerfTest.java b/apct-tests/perftests/core/src/android/app/ResourcesPerfTest.java
index 9cdeb48..c3e43ee 100644
--- a/apct-tests/perftests/core/src/android/app/ResourcesPerfTest.java
+++ b/apct-tests/perftests/core/src/android/app/ResourcesPerfTest.java
@@ -23,7 +23,8 @@
import android.content.res.XmlResourceParser;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.support.test.filters.LargeTest;
+
+import androidx.test.filters.LargeTest;
import org.junit.After;
import org.junit.Before;
diff --git a/apct-tests/perftests/core/src/android/app/ResourcesThemePerfTest.java b/apct-tests/perftests/core/src/android/app/ResourcesThemePerfTest.java
index bb0627e5..1b07572 100644
--- a/apct-tests/perftests/core/src/android/app/ResourcesThemePerfTest.java
+++ b/apct-tests/perftests/core/src/android/app/ResourcesThemePerfTest.java
@@ -19,8 +19,9 @@
import android.content.res.Resources;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
import org.junit.Before;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/database/CursorWindowPerfTest.java b/apct-tests/perftests/core/src/android/database/CursorWindowPerfTest.java
index 897d0ae..c5ef80d 100644
--- a/apct-tests/perftests/core/src/android/database/CursorWindowPerfTest.java
+++ b/apct-tests/perftests/core/src/android/database/CursorWindowPerfTest.java
@@ -23,9 +23,10 @@
import android.database.sqlite.SQLiteDatabase;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
import org.junit.AfterClass;
import org.junit.BeforeClass;
diff --git a/apct-tests/perftests/core/src/android/database/SQLiteDatabaseIoPerfTest.java b/apct-tests/perftests/core/src/android/database/SQLiteDatabaseIoPerfTest.java
index 7c5316d..830302e 100644
--- a/apct-tests/perftests/core/src/android/database/SQLiteDatabaseIoPerfTest.java
+++ b/apct-tests/perftests/core/src/android/database/SQLiteDatabaseIoPerfTest.java
@@ -16,17 +16,20 @@
package android.database;
+import static org.junit.Assert.assertEquals;
+
import android.app.Activity;
import android.content.ContentValues;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
import android.util.ArrayMap;
import android.util.Log;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
import com.android.internal.util.Preconditions;
import org.junit.After;
@@ -40,8 +43,6 @@
import java.util.List;
import java.util.Map;
-import static org.junit.Assert.assertEquals;
-
/**
* Performance tests for measuring amount of data written during typical DB operations
*
diff --git a/apct-tests/perftests/core/src/android/database/SQLiteDatabasePerfTest.java b/apct-tests/perftests/core/src/android/database/SQLiteDatabasePerfTest.java
index e2b75c3..973e996 100644
--- a/apct-tests/perftests/core/src/android/database/SQLiteDatabasePerfTest.java
+++ b/apct-tests/perftests/core/src/android/database/SQLiteDatabasePerfTest.java
@@ -16,14 +16,18 @@
package android.database;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
import android.content.ContentValues;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
import org.junit.Before;
@@ -33,9 +37,6 @@
import java.util.Random;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
/**
* Performance tests for typical CRUD operations and loading rows into the Cursor
*
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/CanvasPerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/CanvasPerfTest.java
index c742df3..9f09305 100644
--- a/apct-tests/perftests/core/src/android/graphics/perftests/CanvasPerfTest.java
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/CanvasPerfTest.java
@@ -24,7 +24,8 @@
import android.graphics.RenderNode;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.support.test.filters.LargeTest;
+
+import androidx.test.filters.LargeTest;
import org.junit.Rule;
import org.junit.Test;
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/OutlinePerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/OutlinePerfTest.java
index f9c3758..10a5128 100644
--- a/apct-tests/perftests/core/src/android/graphics/perftests/OutlinePerfTest.java
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/OutlinePerfTest.java
@@ -19,7 +19,8 @@
import android.graphics.Outline;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.support.test.filters.LargeTest;
+
+import androidx.test.filters.LargeTest;
import org.junit.Rule;
import org.junit.Test;
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/PaintHasGlyphPerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/PaintHasGlyphPerfTest.java
index 26b8309..3a80020 100644
--- a/apct-tests/perftests/core/src/android/graphics/perftests/PaintHasGlyphPerfTest.java
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/PaintHasGlyphPerfTest.java
@@ -20,17 +20,18 @@
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
import android.perftests.utils.StubActivity;
-import android.support.test.filters.LargeTest;
-import android.support.test.rule.ActivityTestRule;
-import java.util.Arrays;
-import java.util.Collection;
+import androidx.test.filters.LargeTest;
+import androidx.test.rule.ActivityTestRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized.Parameters;
import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+import java.util.Arrays;
+import java.util.Collection;
@LargeTest
@RunWith(Parameterized.class)
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/PaintMeasureTextTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/PaintMeasureTextTest.java
index b9ee613..fc8c673 100644
--- a/apct-tests/perftests/core/src/android/graphics/perftests/PaintMeasureTextTest.java
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/PaintMeasureTextTest.java
@@ -19,9 +19,10 @@
import android.graphics.Canvas;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.support.test.filters.LargeTest;
import android.text.TextPaint;
+import androidx.test.filters.LargeTest;
+
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/PathPerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/PathPerfTest.java
index 7a49b4f..c6de9ec 100644
--- a/apct-tests/perftests/core/src/android/graphics/perftests/PathPerfTest.java
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/PathPerfTest.java
@@ -20,7 +20,8 @@
import android.graphics.RectF;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.support.test.filters.LargeTest;
+
+import androidx.test.filters.LargeTest;
import org.junit.Rule;
import org.junit.Test;
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/RenderNodePerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/RenderNodePerfTest.java
index 62dd124..e7fe235 100644
--- a/apct-tests/perftests/core/src/android/graphics/perftests/RenderNodePerfTest.java
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/RenderNodePerfTest.java
@@ -20,7 +20,8 @@
import android.graphics.RenderNode;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.support.test.filters.LargeTest;
+
+import androidx.test.filters.LargeTest;
import org.junit.Rule;
import org.junit.Test;
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceCreatePerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceCreatePerfTest.java
index 11ee599..d6e8ab2 100644
--- a/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceCreatePerfTest.java
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceCreatePerfTest.java
@@ -21,9 +21,14 @@
import android.graphics.Typeface;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
import java.io.File;
import java.io.FileOutputStream;
@@ -31,10 +36,6 @@
import java.io.InputStream;
import java.io.OutputStream;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
@LargeTest
@RunWith(AndroidJUnit4.class)
public class TypefaceCreatePerfTest {
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/VectorDrawablePerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/VectorDrawablePerfTest.java
index 5533782..3b2b8a9 100644
--- a/apct-tests/perftests/core/src/android/graphics/perftests/VectorDrawablePerfTest.java
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/VectorDrawablePerfTest.java
@@ -16,6 +16,8 @@
package android.graphics.perftests;
+import static junit.framework.Assert.assertTrue;
+
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.Canvas;
@@ -25,21 +27,17 @@
import android.perftests.utils.BitmapUtils;
import android.perftests.utils.PerfStatusReporter;
import android.perftests.utils.StubActivity;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.LargeTest;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
import com.android.perftests.core.R;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.io.IOException;
-
-import static junit.framework.Assert.assertTrue;
-
@RunWith(AndroidJUnit4.class)
@LargeTest
public class VectorDrawablePerfTest {
diff --git a/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java b/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java
index 99e4ba1..12e49e3 100644
--- a/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java
+++ b/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java
@@ -18,8 +18,9 @@
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
import com.android.internal.os.BinderCallsStats;
import com.android.internal.os.BinderInternal.CallSession;
@@ -31,7 +32,6 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-
/**
* Performance tests for {@link BinderCallsStats}
*/
diff --git a/apct-tests/perftests/core/src/android/os/CpuUsageTrackingPerfTest.java b/apct-tests/perftests/core/src/android/os/CpuUsageTrackingPerfTest.java
index 4961b4f..0d7b7ca 100644
--- a/apct-tests/perftests/core/src/android/os/CpuUsageTrackingPerfTest.java
+++ b/apct-tests/perftests/core/src/android/os/CpuUsageTrackingPerfTest.java
@@ -18,8 +18,9 @@
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
import org.junit.Test;
@@ -28,7 +29,6 @@
import java.nio.file.Files;
import java.nio.file.Paths;
-
/**
* Performance tests collecting CPU data different mechanisms.
*/
diff --git a/apct-tests/perftests/core/src/android/os/KernelCpuThreadReaderPerfTest.java b/apct-tests/perftests/core/src/android/os/KernelCpuThreadReaderPerfTest.java
index 9034034..11c7599 100644
--- a/apct-tests/perftests/core/src/android/os/KernelCpuThreadReaderPerfTest.java
+++ b/apct-tests/perftests/core/src/android/os/KernelCpuThreadReaderPerfTest.java
@@ -20,8 +20,9 @@
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
import com.android.internal.os.KernelCpuThreadReader;
@@ -29,7 +30,6 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-
/**
* Performance tests collecting per-thread CPU data.
*/
diff --git a/apct-tests/perftests/core/src/android/os/LooperStatsPerfTest.java b/apct-tests/perftests/core/src/android/os/LooperStatsPerfTest.java
index 0f880b7..162167d 100644
--- a/apct-tests/perftests/core/src/android/os/LooperStatsPerfTest.java
+++ b/apct-tests/perftests/core/src/android/os/LooperStatsPerfTest.java
@@ -19,8 +19,9 @@
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
import com.android.internal.os.CachedDeviceState;
import com.android.internal.os.LooperStats;
@@ -31,7 +32,6 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-
/**
* Performance tests for {@link LooperStats}.
*/
diff --git a/apct-tests/perftests/core/src/android/os/PackageManagerPerfTest.java b/apct-tests/perftests/core/src/android/os/PackageManagerPerfTest.java
index 145fbcd..3aa6749 100644
--- a/apct-tests/perftests/core/src/android/os/PackageManagerPerfTest.java
+++ b/apct-tests/perftests/core/src/android/os/PackageManagerPerfTest.java
@@ -16,20 +16,16 @@
package android.os;
-import static android.content.pm.PackageManager.PERMISSION_DENIED;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-
import android.content.ComponentName;
-import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
-import org.junit.Assert;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/apct-tests/perftests/core/src/android/os/ParcelArrayPerfTest.java b/apct-tests/perftests/core/src/android/os/ParcelArrayPerfTest.java
index a67aeca..af6d6b0 100644
--- a/apct-tests/perftests/core/src/android/os/ParcelArrayPerfTest.java
+++ b/apct-tests/perftests/core/src/android/os/ParcelArrayPerfTest.java
@@ -18,7 +18,8 @@
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.support.test.filters.LargeTest;
+
+import androidx.test.filters.LargeTest;
import org.junit.After;
import org.junit.Before;
diff --git a/apct-tests/perftests/core/src/android/os/ParcelPerfTest.java b/apct-tests/perftests/core/src/android/os/ParcelPerfTest.java
index 6e4c9c5..4db9262 100644
--- a/apct-tests/perftests/core/src/android/os/ParcelPerfTest.java
+++ b/apct-tests/perftests/core/src/android/os/ParcelPerfTest.java
@@ -16,10 +16,15 @@
package android.os;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
import org.junit.Before;
@@ -27,10 +32,6 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-
@RunWith(AndroidJUnit4.class)
@LargeTest
public class ParcelPerfTest {
diff --git a/apct-tests/perftests/core/src/android/os/PssPerfTest.java b/apct-tests/perftests/core/src/android/os/PssPerfTest.java
index 400115d..2cc294f 100644
--- a/apct-tests/perftests/core/src/android/os/PssPerfTest.java
+++ b/apct-tests/perftests/core/src/android/os/PssPerfTest.java
@@ -18,8 +18,9 @@
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
import org.junit.Test;
diff --git a/apct-tests/perftests/core/src/android/os/SharedPreferencesTest.java b/apct-tests/perftests/core/src/android/os/SharedPreferencesTest.java
index 099134f..dd479ac 100644
--- a/apct-tests/perftests/core/src/android/os/SharedPreferencesTest.java
+++ b/apct-tests/perftests/core/src/android/os/SharedPreferencesTest.java
@@ -20,9 +20,10 @@
import android.content.SharedPreferences;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
import org.junit.Test;
diff --git a/apct-tests/perftests/core/src/android/os/StrictModeTest.java b/apct-tests/perftests/core/src/android/os/StrictModeTest.java
index d973c20..60678e9 100644
--- a/apct-tests/perftests/core/src/android/os/StrictModeTest.java
+++ b/apct-tests/perftests/core/src/android/os/StrictModeTest.java
@@ -23,17 +23,21 @@
import android.net.Uri;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
import com.google.common.util.concurrent.SettableFuture;
-import java.io.File;
-import java.io.IOException;
-import java.util.concurrent.ExecutionException;
+
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.io.File;
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+
@RunWith(AndroidJUnit4.class)
@LargeTest
public class StrictModeTest {
diff --git a/apct-tests/perftests/core/src/android/os/TracePerfTest.java b/apct-tests/perftests/core/src/android/os/TracePerfTest.java
index 8e5cfaa..0d64c39 100644
--- a/apct-tests/perftests/core/src/android/os/TracePerfTest.java
+++ b/apct-tests/perftests/core/src/android/os/TracePerfTest.java
@@ -20,7 +20,8 @@
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
import android.perftests.utils.ShellHelper;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.runner.AndroidJUnit4;
import org.junit.AfterClass;
import org.junit.Assert;
diff --git a/apct-tests/perftests/core/src/android/perftests/SystemPerfTest.java b/apct-tests/perftests/core/src/android/perftests/SystemPerfTest.java
index 95a7144..4f0d108 100644
--- a/apct-tests/perftests/core/src/android/perftests/SystemPerfTest.java
+++ b/apct-tests/perftests/core/src/android/perftests/SystemPerfTest.java
@@ -18,8 +18,9 @@
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
import dalvik.annotation.optimization.FastNative;
diff --git a/apct-tests/perftests/core/src/android/text/BoringLayoutCreateDrawPerfTest.java b/apct-tests/perftests/core/src/android/text/BoringLayoutCreateDrawPerfTest.java
index 9245c1b..c8121c5 100644
--- a/apct-tests/perftests/core/src/android/text/BoringLayoutCreateDrawPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/BoringLayoutCreateDrawPerfTest.java
@@ -22,9 +22,10 @@
import android.graphics.RenderNode;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.support.test.filters.LargeTest;
import android.text.NonEditableTextGenerator.TextType;
+import androidx.test.filters.LargeTest;
+
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/apct-tests/perftests/core/src/android/text/BoringLayoutIsBoringPerfTest.java b/apct-tests/perftests/core/src/android/text/BoringLayoutIsBoringPerfTest.java
index 194a88c..ad1327c 100644
--- a/apct-tests/perftests/core/src/android/text/BoringLayoutIsBoringPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/BoringLayoutIsBoringPerfTest.java
@@ -18,9 +18,10 @@
import android.graphics.Canvas;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.support.test.filters.LargeTest;
import android.text.NonEditableTextGenerator.TextType;
+import androidx.test.filters.LargeTest;
+
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/apct-tests/perftests/core/src/android/text/CanvasDrawTextTest.java b/apct-tests/perftests/core/src/android/text/CanvasDrawTextTest.java
index ad9fb5f..bb6b691 100644
--- a/apct-tests/perftests/core/src/android/text/CanvasDrawTextTest.java
+++ b/apct-tests/perftests/core/src/android/text/CanvasDrawTextTest.java
@@ -19,8 +19,9 @@
import android.graphics.RenderNode;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/text/DynamicLayoutPerfTest.java b/apct-tests/perftests/core/src/android/text/DynamicLayoutPerfTest.java
index b4c7f54..5be99d9 100644
--- a/apct-tests/perftests/core/src/android/text/DynamicLayoutPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/DynamicLayoutPerfTest.java
@@ -24,11 +24,12 @@
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
import android.perftests.utils.StubActivity;
-import android.support.test.filters.LargeTest;
-import android.support.test.rule.ActivityTestRule;
import android.text.style.ReplacementSpan;
import android.util.ArraySet;
+import androidx.test.filters.LargeTest;
+import androidx.test.rule.ActivityTestRule;
+
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/apct-tests/perftests/core/src/android/text/PaintMeasureDrawPerfTest.java b/apct-tests/perftests/core/src/android/text/PaintMeasureDrawPerfTest.java
index a7972f5..bbe75b7 100644
--- a/apct-tests/perftests/core/src/android/text/PaintMeasureDrawPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/PaintMeasureDrawPerfTest.java
@@ -21,7 +21,8 @@
import android.graphics.RenderNode;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.support.test.filters.LargeTest;
+
+import androidx.test.filters.LargeTest;
import org.junit.Rule;
import org.junit.Test;
diff --git a/apct-tests/perftests/core/src/android/text/PrecomputedTextMemoryUsageTest.java b/apct-tests/perftests/core/src/android/text/PrecomputedTextMemoryUsageTest.java
index 00a6267..4ae2b93 100644
--- a/apct-tests/perftests/core/src/android/text/PrecomputedTextMemoryUsageTest.java
+++ b/apct-tests/perftests/core/src/android/text/PrecomputedTextMemoryUsageTest.java
@@ -18,9 +18,10 @@
import android.app.Activity;
import android.os.Bundle;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
diff --git a/apct-tests/perftests/core/src/android/text/PrecomputedTextPerfTest.java b/apct-tests/perftests/core/src/android/text/PrecomputedTextPerfTest.java
index 33b1a47..3be9114 100644
--- a/apct-tests/perftests/core/src/android/text/PrecomputedTextPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/PrecomputedTextPerfTest.java
@@ -18,8 +18,9 @@
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/text/StaticLayoutCreateDrawPerfTest.java b/apct-tests/perftests/core/src/android/text/StaticLayoutCreateDrawPerfTest.java
index b40dd6b..6c92229 100644
--- a/apct-tests/perftests/core/src/android/text/StaticLayoutCreateDrawPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/StaticLayoutCreateDrawPerfTest.java
@@ -22,9 +22,10 @@
import android.graphics.RenderNode;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.support.test.filters.LargeTest;
import android.text.NonEditableTextGenerator.TextType;
+import androidx.test.filters.LargeTest;
+
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/apct-tests/perftests/core/src/android/text/StaticLayoutGetOffsetForHorizontalPerfTest.java b/apct-tests/perftests/core/src/android/text/StaticLayoutGetOffsetForHorizontalPerfTest.java
index 2768a7d..47e04ef 100644
--- a/apct-tests/perftests/core/src/android/text/StaticLayoutGetOffsetForHorizontalPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/StaticLayoutGetOffsetForHorizontalPerfTest.java
@@ -19,8 +19,8 @@
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/text/StaticLayoutMultithreadPerfTest.java b/apct-tests/perftests/core/src/android/text/StaticLayoutMultithreadPerfTest.java
index 60c6d89..0b79834 100644
--- a/apct-tests/perftests/core/src/android/text/StaticLayoutMultithreadPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/StaticLayoutMultithreadPerfTest.java
@@ -19,17 +19,16 @@
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.util.Random;
import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
@LargeTest
@RunWith(AndroidJUnit4.class)
diff --git a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
index f60cbee6..bd7522d 100644
--- a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
@@ -21,8 +21,9 @@
import android.graphics.RenderNode;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/text/TextViewSetTextMeasurePerfTest.java b/apct-tests/perftests/core/src/android/text/TextViewSetTextMeasurePerfTest.java
index 25cc707..10bfa42 100644
--- a/apct-tests/perftests/core/src/android/text/TextViewSetTextMeasurePerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/TextViewSetTextMeasurePerfTest.java
@@ -23,11 +23,12 @@
import android.graphics.RenderNode;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
import android.text.NonEditableTextGenerator.TextType;
import android.widget.TextView;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/apct-tests/perftests/core/src/android/textclassifier/TextClassifierPerfTest.java b/apct-tests/perftests/core/src/android/textclassifier/TextClassifierPerfTest.java
index a482c4a..a7a81f2 100644
--- a/apct-tests/perftests/core/src/android/textclassifier/TextClassifierPerfTest.java
+++ b/apct-tests/perftests/core/src/android/textclassifier/TextClassifierPerfTest.java
@@ -18,13 +18,14 @@
import android.content.Context;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
import android.view.textclassifier.ConversationActions;
import android.view.textclassifier.TextClassificationManager;
import android.view.textclassifier.TextClassifier;
import android.view.textclassifier.TextLanguage;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
diff --git a/apct-tests/perftests/core/src/android/util/ArraySetPerfTest.java b/apct-tests/perftests/core/src/android/util/ArraySetPerfTest.java
index 0c1f289..b24bf42 100644
--- a/apct-tests/perftests/core/src/android/util/ArraySetPerfTest.java
+++ b/apct-tests/perftests/core/src/android/util/ArraySetPerfTest.java
@@ -18,8 +18,9 @@
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
import org.junit.Test;
diff --git a/apct-tests/perftests/core/src/android/util/perftests/LogPerfTest.java b/apct-tests/perftests/core/src/android/util/perftests/LogPerfTest.java
index 07cd33f..26cec95 100644
--- a/apct-tests/perftests/core/src/android/util/perftests/LogPerfTest.java
+++ b/apct-tests/perftests/core/src/android/util/perftests/LogPerfTest.java
@@ -18,11 +18,11 @@
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
-
import android.util.Log;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/apct-tests/perftests/core/src/android/view/ViewPerfTest.java b/apct-tests/perftests/core/src/android/view/ViewPerfTest.java
index 990be24..a1f8608 100644
--- a/apct-tests/perftests/core/src/android/view/ViewPerfTest.java
+++ b/apct-tests/perftests/core/src/android/view/ViewPerfTest.java
@@ -17,13 +17,13 @@
package android.view;
import android.content.Context;
-import android.content.res.Resources;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
import android.widget.FrameLayout;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+
import com.android.perftests.core.R;
import org.junit.Rule;
diff --git a/apct-tests/perftests/core/src/android/view/ViewShowHidePerfTest.java b/apct-tests/perftests/core/src/android/view/ViewShowHidePerfTest.java
index dc4d4bd..b34001d 100644
--- a/apct-tests/perftests/core/src/android/view/ViewShowHidePerfTest.java
+++ b/apct-tests/perftests/core/src/android/view/ViewShowHidePerfTest.java
@@ -24,14 +24,15 @@
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
import android.perftests.utils.StubActivity;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.rule.ActivityTestRule;
import android.view.View.MeasureSpec;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.rule.ActivityTestRule;
+
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/apct-tests/perftests/core/src/android/widget/EditTextBackspacePerfTest.java b/apct-tests/perftests/core/src/android/widget/EditTextBackspacePerfTest.java
index d219d3a..b3ea62a 100644
--- a/apct-tests/perftests/core/src/android/widget/EditTextBackspacePerfTest.java
+++ b/apct-tests/perftests/core/src/android/widget/EditTextBackspacePerfTest.java
@@ -19,14 +19,15 @@
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
import android.perftests.utils.StubActivity;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.rule.ActivityTestRule;
import android.text.Selection;
import android.view.KeyEvent;
import android.view.View.MeasureSpec;
import android.view.ViewGroup;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.rule.ActivityTestRule;
+
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
diff --git a/apct-tests/perftests/core/src/android/widget/EditTextCursorMovementPerfTest.java b/apct-tests/perftests/core/src/android/widget/EditTextCursorMovementPerfTest.java
index b6cf7d3..aa47d5b 100644
--- a/apct-tests/perftests/core/src/android/widget/EditTextCursorMovementPerfTest.java
+++ b/apct-tests/perftests/core/src/android/widget/EditTextCursorMovementPerfTest.java
@@ -19,14 +19,15 @@
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
import android.perftests.utils.StubActivity;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.rule.ActivityTestRule;
import android.text.Selection;
import android.view.KeyEvent;
import android.view.View.MeasureSpec;
import android.view.ViewGroup;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.rule.ActivityTestRule;
+
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
diff --git a/apct-tests/perftests/core/src/android/widget/EditTextLongTextPerfTest.java b/apct-tests/perftests/core/src/android/widget/EditTextLongTextPerfTest.java
index ce0c357..e50016c 100644
--- a/apct-tests/perftests/core/src/android/widget/EditTextLongTextPerfTest.java
+++ b/apct-tests/perftests/core/src/android/widget/EditTextLongTextPerfTest.java
@@ -16,10 +16,16 @@
package android.widget;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Locale;
-import java.util.Random;
+import android.app.Activity;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.perftests.utils.StubActivity;
+import android.view.KeyEvent;
+import android.view.View.MeasureSpec;
+import android.view.ViewGroup;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.rule.ActivityTestRule;
import org.junit.Rule;
import org.junit.Test;
@@ -27,22 +33,9 @@
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
-import android.app.Activity;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.util.Log;
-import android.view.KeyEvent;
-import android.view.RenderNodeAnimator;
-import android.view.ViewGroup;
-import android.view.View.MeasureSpec;
-
-import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.PerfStatusReporter;
-import android.perftests.utils.StubActivity;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.InstrumentationRegistry;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Random;
@LargeTest
@RunWith(Parameterized.class)
diff --git a/apct-tests/perftests/core/src/android/widget/LayoutPerfTest.java b/apct-tests/perftests/core/src/android/widget/LayoutPerfTest.java
index d570ef3..644095b 100644
--- a/apct-tests/perftests/core/src/android/widget/LayoutPerfTest.java
+++ b/apct-tests/perftests/core/src/android/widget/LayoutPerfTest.java
@@ -16,17 +16,26 @@
package android.widget;
+import static android.perftests.utils.LayoutUtils.gatherViewTree;
+import static android.perftests.utils.LayoutUtils.requestLayoutForAllNodes;
+import static android.view.View.MeasureSpec.AT_MOST;
+import static android.view.View.MeasureSpec.EXACTLY;
+import static android.view.View.MeasureSpec.UNSPECIFIED;
+
+import static org.junit.Assert.assertTrue;
+
import android.app.Activity;
import android.os.Looper;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
import android.perftests.utils.StubActivity;
-import android.support.test.annotation.UiThreadTest;
-import android.support.test.filters.LargeTest;
-import android.support.test.rule.ActivityTestRule;
import android.view.View;
import android.view.ViewGroup;
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.filters.LargeTest;
+import androidx.test.rule.ActivityTestRule;
+
import com.android.perftests.core.R;
import org.junit.Rule;
@@ -38,13 +47,6 @@
import java.util.Collection;
import java.util.List;
-import static android.perftests.utils.LayoutUtils.gatherViewTree;
-import static android.perftests.utils.LayoutUtils.requestLayoutForAllNodes;
-import static android.view.View.MeasureSpec.AT_MOST;
-import static android.view.View.MeasureSpec.EXACTLY;
-import static android.view.View.MeasureSpec.UNSPECIFIED;
-import static org.junit.Assert.assertTrue;
-
@LargeTest
@RunWith(Parameterized.class)
public class LayoutPerfTest {
diff --git a/apct-tests/perftests/core/src/android/widget/TextViewAutoSizeLayoutPerfTest.java b/apct-tests/perftests/core/src/android/widget/TextViewAutoSizeLayoutPerfTest.java
index c310166..bed173b 100644
--- a/apct-tests/perftests/core/src/android/widget/TextViewAutoSizeLayoutPerfTest.java
+++ b/apct-tests/perftests/core/src/android/widget/TextViewAutoSizeLayoutPerfTest.java
@@ -16,33 +16,27 @@
package android.widget;
+import static org.junit.Assert.assertTrue;
+
import android.app.Activity;
import android.os.Looper;
-import android.os.Bundle;
-import android.perftests.utils.PerfStatusReporter;
-import android.util.Log;
-import android.view.View;
-
import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
import android.perftests.utils.StubActivity;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.InstrumentationRegistry;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.rule.ActivityTestRule;
import com.android.perftests.core.R;
-import java.util.Locale;
-import java.util.Collection;
-import java.util.Arrays;
-
-import org.junit.Test;
import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
-import org.junit.runner.RunWith;
-import static org.junit.Assert.assertTrue;
+import java.util.Arrays;
+import java.util.Collection;
@LargeTest
@RunWith(Parameterized.class)
diff --git a/apct-tests/perftests/core/src/android/widget/TextViewFontFamilyLayoutPerfTest.java b/apct-tests/perftests/core/src/android/widget/TextViewFontFamilyLayoutPerfTest.java
index 4b6da6b..1f00838 100644
--- a/apct-tests/perftests/core/src/android/widget/TextViewFontFamilyLayoutPerfTest.java
+++ b/apct-tests/perftests/core/src/android/widget/TextViewFontFamilyLayoutPerfTest.java
@@ -19,23 +19,21 @@
import android.content.Context;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
import android.view.LayoutInflater;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+
import com.android.perftests.core.R;
-import java.util.Collection;
-import java.util.Arrays;
-
-import org.junit.Test;
import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
-import org.junit.runner.RunWith;
-import static org.junit.Assert.assertTrue;
+import java.util.Arrays;
+import java.util.Collection;
@LargeTest
@RunWith(Parameterized.class)
diff --git a/apct-tests/perftests/core/src/android/widget/TextViewOnMeasurePerfTest.java b/apct-tests/perftests/core/src/android/widget/TextViewOnMeasurePerfTest.java
index a14dd25..88acbba 100644
--- a/apct-tests/perftests/core/src/android/widget/TextViewOnMeasurePerfTest.java
+++ b/apct-tests/perftests/core/src/android/widget/TextViewOnMeasurePerfTest.java
@@ -25,24 +25,20 @@
import android.graphics.Typeface;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.style.TextAppearanceSpan;
-import android.view.LayoutInflater;
-import com.android.perftests.core.R;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
-import java.util.Random;
-import java.util.Locale;
-
-import org.junit.Test;
import org.junit.Rule;
+import org.junit.Test;
import org.junit.runner.RunWith;
-import static org.junit.Assert.assertTrue;
+import java.util.Locale;
+import java.util.Random;
@LargeTest
@RunWith(AndroidJUnit4.class)
diff --git a/apct-tests/perftests/core/src/android/widget/TextViewPrecomputedTextPerfTest.java b/apct-tests/perftests/core/src/android/widget/TextViewPrecomputedTextPerfTest.java
index bd91112..0bc9ee4 100644
--- a/apct-tests/perftests/core/src/android/widget/TextViewPrecomputedTextPerfTest.java
+++ b/apct-tests/perftests/core/src/android/widget/TextViewPrecomputedTextPerfTest.java
@@ -24,9 +24,6 @@
import android.graphics.RenderNode;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
import android.text.BoringLayout;
import android.text.Layout;
import android.text.PrecomputedText;
@@ -34,6 +31,10 @@
import android.text.TextPerfUtils;
import android.view.View.MeasureSpec;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
diff --git a/apct-tests/perftests/core/src/android/widget/TextViewSetTextLocalePerfTest.java b/apct-tests/perftests/core/src/android/widget/TextViewSetTextLocalePerfTest.java
index e95676b..00bd8db 100644
--- a/apct-tests/perftests/core/src/android/widget/TextViewSetTextLocalePerfTest.java
+++ b/apct-tests/perftests/core/src/android/widget/TextViewSetTextLocalePerfTest.java
@@ -19,8 +19,9 @@
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
import android.perftests.utils.StubActivity;
-import android.support.test.filters.LargeTest;
-import android.support.test.rule.ActivityTestRule;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.rule.ActivityTestRule;
import org.junit.Rule;
import org.junit.Test;
diff --git a/api/current.txt b/api/current.txt
index 16652bf..72dd139 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -8532,42 +8532,44 @@
field public static final java.lang.String VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY = "android.bluetooth.headset.intent.category.companyid";
}
- public final class BluetoothHealth implements android.bluetooth.BluetoothProfile {
- method public boolean connectChannelToSource(android.bluetooth.BluetoothDevice, android.bluetooth.BluetoothHealthAppConfiguration);
- method public boolean disconnectChannel(android.bluetooth.BluetoothDevice, android.bluetooth.BluetoothHealthAppConfiguration, int);
+ public final deprecated class BluetoothHealth implements android.bluetooth.BluetoothProfile {
+ ctor public BluetoothHealth();
+ method public deprecated boolean connectChannelToSource(android.bluetooth.BluetoothDevice, android.bluetooth.BluetoothHealthAppConfiguration);
+ method public deprecated boolean disconnectChannel(android.bluetooth.BluetoothDevice, android.bluetooth.BluetoothHealthAppConfiguration, int);
method public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
method public int getConnectionState(android.bluetooth.BluetoothDevice);
method public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(int[]);
- method public android.os.ParcelFileDescriptor getMainChannelFd(android.bluetooth.BluetoothDevice, android.bluetooth.BluetoothHealthAppConfiguration);
- method public boolean registerSinkAppConfiguration(java.lang.String, int, android.bluetooth.BluetoothHealthCallback);
- method public boolean unregisterAppConfiguration(android.bluetooth.BluetoothHealthAppConfiguration);
- field public static final int APP_CONFIG_REGISTRATION_FAILURE = 1; // 0x1
- field public static final int APP_CONFIG_REGISTRATION_SUCCESS = 0; // 0x0
- field public static final int APP_CONFIG_UNREGISTRATION_FAILURE = 3; // 0x3
- field public static final int APP_CONFIG_UNREGISTRATION_SUCCESS = 2; // 0x2
- field public static final int CHANNEL_TYPE_RELIABLE = 10; // 0xa
- field public static final int CHANNEL_TYPE_STREAMING = 11; // 0xb
- field public static final int SINK_ROLE = 2; // 0x2
- field public static final int SOURCE_ROLE = 1; // 0x1
- field public static final int STATE_CHANNEL_CONNECTED = 2; // 0x2
- field public static final int STATE_CHANNEL_CONNECTING = 1; // 0x1
- field public static final int STATE_CHANNEL_DISCONNECTED = 0; // 0x0
- field public static final int STATE_CHANNEL_DISCONNECTING = 3; // 0x3
+ method public deprecated android.os.ParcelFileDescriptor getMainChannelFd(android.bluetooth.BluetoothDevice, android.bluetooth.BluetoothHealthAppConfiguration);
+ method public deprecated boolean registerSinkAppConfiguration(java.lang.String, int, android.bluetooth.BluetoothHealthCallback);
+ method public deprecated boolean unregisterAppConfiguration(android.bluetooth.BluetoothHealthAppConfiguration);
+ field public static final deprecated int APP_CONFIG_REGISTRATION_FAILURE = 1; // 0x1
+ field public static final deprecated int APP_CONFIG_REGISTRATION_SUCCESS = 0; // 0x0
+ field public static final deprecated int APP_CONFIG_UNREGISTRATION_FAILURE = 3; // 0x3
+ field public static final deprecated int APP_CONFIG_UNREGISTRATION_SUCCESS = 2; // 0x2
+ field public static final deprecated int CHANNEL_TYPE_RELIABLE = 10; // 0xa
+ field public static final deprecated int CHANNEL_TYPE_STREAMING = 11; // 0xb
+ field public static final deprecated int SINK_ROLE = 2; // 0x2
+ field public static final deprecated int SOURCE_ROLE = 1; // 0x1
+ field public static final deprecated int STATE_CHANNEL_CONNECTED = 2; // 0x2
+ field public static final deprecated int STATE_CHANNEL_CONNECTING = 1; // 0x1
+ field public static final deprecated int STATE_CHANNEL_DISCONNECTED = 0; // 0x0
+ field public static final deprecated int STATE_CHANNEL_DISCONNECTING = 3; // 0x3
}
- public final class BluetoothHealthAppConfiguration implements android.os.Parcelable {
+ public final deprecated class BluetoothHealthAppConfiguration implements android.os.Parcelable {
+ ctor public BluetoothHealthAppConfiguration();
method public int describeContents();
- method public int getDataType();
- method public java.lang.String getName();
- method public int getRole();
+ method public deprecated int getDataType();
+ method public deprecated java.lang.String getName();
+ method public deprecated int getRole();
method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothHealthAppConfiguration> CREATOR;
+ field public static final deprecated android.os.Parcelable.Creator<android.bluetooth.BluetoothHealthAppConfiguration> CREATOR;
}
- public abstract class BluetoothHealthCallback {
+ public abstract deprecated class BluetoothHealthCallback {
ctor public BluetoothHealthCallback();
- method public void onHealthAppConfigurationStatusChange(android.bluetooth.BluetoothHealthAppConfiguration, int);
- method public void onHealthChannelStateChange(android.bluetooth.BluetoothHealthAppConfiguration, android.bluetooth.BluetoothDevice, int, int, android.os.ParcelFileDescriptor, int);
+ method public deprecated void onHealthAppConfigurationStatusChange(android.bluetooth.BluetoothHealthAppConfiguration, int);
+ method public deprecated void onHealthChannelStateChange(android.bluetooth.BluetoothHealthAppConfiguration, android.bluetooth.BluetoothDevice, int, int, android.os.ParcelFileDescriptor, int);
}
public final class BluetoothHidDevice implements android.bluetooth.BluetoothProfile {
@@ -8664,7 +8666,7 @@
field public static final int GATT = 7; // 0x7
field public static final int GATT_SERVER = 8; // 0x8
field public static final int HEADSET = 1; // 0x1
- field public static final int HEALTH = 3; // 0x3
+ field public static final deprecated int HEALTH = 3; // 0x3
field public static final int HID_DEVICE = 19; // 0x13
field public static final int SAP = 10; // 0xa
field public static final int STATE_CONNECTED = 2; // 0x2
@@ -25362,12 +25364,15 @@
method public java.lang.Object clearNextDataSources();
method public void clearPendingCommands();
method public void close();
+ method public java.lang.Object deselectTrack(int);
method public java.lang.Object deselectTrack(android.media.DataSourceDesc, int);
method public android.media.AudioAttributes getAudioAttributes();
method public int getAudioSessionId();
+ method public long getBufferedPosition();
method public long getBufferedPosition(android.media.DataSourceDesc);
method public android.media.DataSourceDesc getCurrentDataSource();
method public long getCurrentPosition();
+ method public long getDuration();
method public long getDuration(android.media.DataSourceDesc);
method public float getMaxPlayerVolume();
method public android.os.PersistableBundle getMetrics();
@@ -25375,10 +25380,12 @@
method public float getPlayerVolume();
method public android.media.AudioDeviceInfo getPreferredDevice();
method public android.media.AudioDeviceInfo getRoutedDevice();
+ method public int getSelectedTrack(int);
method public int getSelectedTrack(android.media.DataSourceDesc, int);
method public int getState();
method public android.media.SyncParams getSyncParams();
method public android.media.MediaTimestamp getTimestamp();
+ method public java.util.List<android.media.MediaPlayer2.TrackInfo> getTrackInfo();
method public java.util.List<android.media.MediaPlayer2.TrackInfo> getTrackInfo(android.media.DataSourceDesc);
method public android.media.VideoSize getVideoSize();
method public boolean isLooping();
@@ -25392,6 +25399,7 @@
method public void reset();
method public java.lang.Object seekTo(long);
method public java.lang.Object seekTo(long, int);
+ method public java.lang.Object selectTrack(int);
method public java.lang.Object selectTrack(android.media.DataSourceDesc, int);
method public java.lang.Object setAudioAttributes(android.media.AudioAttributes);
method public java.lang.Object setAudioSessionId(int);
@@ -33701,6 +33709,7 @@
method public static final void flushPendingCommands();
method public static final int getCallingPid();
method public static final int getCallingUid();
+ method public static final int getCallingUidOrThrow();
method public static final android.os.UserHandle getCallingUserHandle();
method public java.lang.String getInterfaceDescriptor();
method public boolean isBinderAlive();
@@ -42653,6 +42662,7 @@
method public static java.lang.String capabilitiesToString(int);
method public android.telecom.PhoneAccountHandle getAccountHandle();
method public int getCallCapabilities();
+ method public int getCallDirection();
method public android.telecom.CallIdentification getCallIdentification();
method public int getCallProperties();
method public java.lang.String getCallerDisplayName();
@@ -42689,6 +42699,9 @@
field public static final int CAPABILITY_SUPPORT_DEFLECT = 16777216; // 0x1000000
field public static final int CAPABILITY_SUPPORT_HOLD = 2; // 0x2
field public static final int CAPABILITY_SWAP_CONFERENCE = 8; // 0x8
+ field public static final int DIRECTION_INCOMING = 0; // 0x0
+ field public static final int DIRECTION_OUTGOING = 1; // 0x1
+ field public static final int DIRECTION_UNKNOWN = -1; // 0xffffffff
field public static final int PROPERTY_CONFERENCE = 1; // 0x1
field public static final int PROPERTY_EMERGENCY_CALLBACK_MODE = 4; // 0x4
field public static final int PROPERTY_ENTERPRISE_CALL = 32; // 0x20
@@ -44609,6 +44622,7 @@
field public static final int DATA_CONNECTING = 1; // 0x1
field public static final int DATA_DISCONNECTED = 0; // 0x0
field public static final int DATA_SUSPENDED = 3; // 0x3
+ field public static final int DATA_UNKNOWN = -1; // 0xffffffff
field public static final java.lang.String EXTRA_CALL_VOICEMAIL_INTENT = "android.telephony.extra.CALL_VOICEMAIL_INTENT";
field public static final java.lang.String EXTRA_CARRIER_ID = "android.telephony.extra.CARRIER_ID";
field public static final java.lang.String EXTRA_CARRIER_NAME = "android.telephony.extra.CARRIER_NAME";
@@ -44838,6 +44852,7 @@
method public int compareTo(android.telephony.emergency.EmergencyNumber);
method public int describeContents();
method public java.lang.String getCountryIso();
+ method public int getEmergencyCallRouting();
method public int getEmergencyNumberSourceBitmask();
method public java.util.List<java.lang.Integer> getEmergencyNumberSources();
method public java.util.List<java.lang.Integer> getEmergencyServiceCategories();
@@ -44848,6 +44863,9 @@
method public boolean isInEmergencyServiceCategories(int);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.telephony.emergency.EmergencyNumber> CREATOR;
+ field public static final int EMERGENCY_CALL_ROUTING_EMERGENCY = 1; // 0x1
+ field public static final int EMERGENCY_CALL_ROUTING_NORMAL = 2; // 0x2
+ field public static final int EMERGENCY_CALL_ROUTING_UNKNOWN = 0; // 0x0
field public static final int EMERGENCY_NUMBER_SOURCE_DATABASE = 16; // 0x10
field public static final int EMERGENCY_NUMBER_SOURCE_DEFAULT = 8; // 0x8
field public static final int EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG = 4; // 0x4
@@ -53117,6 +53135,7 @@
field public static final int CATEGORY_SELECTION = 1; // 0x1
field public static final int CATEGORY_UNDEFINED = 0; // 0x0
field public static final android.os.Parcelable.Creator<android.view.textclassifier.TextClassifierEvent> CREATOR;
+ field public static final int TYPE_ACTIONS_GENERATED = 20; // 0x14
field public static final int TYPE_ACTIONS_SHOWN = 6; // 0x6
field public static final int TYPE_AUTO_SELECTION = 5; // 0x5
field public static final int TYPE_COPY_ACTION = 9; // 0x9
@@ -55431,7 +55450,7 @@
method public int getSourceWidth();
method public int getWidth();
method public float getZoom();
- method public boolean isForcePositionWithinWindowSystemInsetsBounds();
+ method public boolean isClippingEnabled();
method public void setZoom(float);
method public void show(float, float);
method public void show(float, float, float, float);
@@ -55444,10 +55463,10 @@
public static class Magnifier.Builder {
ctor public Magnifier.Builder(android.view.View);
method public android.widget.Magnifier build();
+ method public android.widget.Magnifier.Builder setClippingEnabled(boolean);
method public android.widget.Magnifier.Builder setCornerRadius(float);
method public android.widget.Magnifier.Builder setDefaultSourceToMagnifierOffset(int, int);
method public android.widget.Magnifier.Builder setElevation(float);
- method public android.widget.Magnifier.Builder setForcePositionWithinWindowSystemInsetsBounds(boolean);
method public android.widget.Magnifier.Builder setOverlay(android.graphics.drawable.Drawable);
method public android.widget.Magnifier.Builder setSize(int, int);
method public android.widget.Magnifier.Builder setSourceBounds(int, int, int, int);
diff --git a/api/system-current.txt b/api/system-current.txt
index 9ce25f5..79648a9 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -537,6 +537,13 @@
method public void onVrStateChanged(boolean);
}
+ public final class WallpaperColors implements android.os.Parcelable {
+ ctor public WallpaperColors(android.graphics.Color, android.graphics.Color, android.graphics.Color, int);
+ method public int getColorHints();
+ field public static final int HINT_SUPPORTS_DARK_TEXT = 1; // 0x1
+ field public static final int HINT_SUPPORTS_DARK_THEME = 2; // 0x2
+ }
+
public final class WallpaperInfo implements android.os.Parcelable {
method public boolean supportsAmbientMode();
}
@@ -1074,6 +1081,7 @@
field public static final java.lang.String ACTION_MANAGE_APP_PERMISSIONS = "android.intent.action.MANAGE_APP_PERMISSIONS";
field public static final java.lang.String ACTION_MANAGE_PERMISSIONS = "android.intent.action.MANAGE_PERMISSIONS";
field public static final java.lang.String ACTION_MANAGE_PERMISSION_APPS = "android.intent.action.MANAGE_PERMISSION_APPS";
+ field public static final java.lang.String ACTION_MANAGE_SPECIAL_APP_ACCESSES = "android.intent.action.MANAGE_SPECIAL_APP_ACCESSES";
field public static final java.lang.String ACTION_MASTER_CLEAR_NOTIFICATION = "android.intent.action.MASTER_CLEAR_NOTIFICATION";
field public static final java.lang.String ACTION_PACKAGE_ROLLBACK_EXECUTED = "android.intent.action.PACKAGE_ROLLBACK_EXECUTED";
field public static final java.lang.String ACTION_PRE_BOOT_COMPLETED = "android.intent.action.PRE_BOOT_COMPLETED";
@@ -1515,6 +1523,8 @@
public final class BrightnessConfiguration implements android.os.Parcelable {
method public int describeContents();
+ method public android.hardware.display.BrightnessCorrection getCorrectionByCategory(int);
+ method public android.hardware.display.BrightnessCorrection getCorrectionByPackageName(java.lang.String);
method public android.util.Pair<float[], float[]> getCurve();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.hardware.display.BrightnessConfiguration> CREATOR;
@@ -1522,10 +1532,22 @@
public static class BrightnessConfiguration.Builder {
ctor public BrightnessConfiguration.Builder(float[], float[]);
+ method public android.hardware.display.BrightnessConfiguration.Builder addCorrectionByCategory(int, android.hardware.display.BrightnessCorrection);
+ method public android.hardware.display.BrightnessConfiguration.Builder addCorrectionByPackageName(java.lang.String, android.hardware.display.BrightnessCorrection);
method public android.hardware.display.BrightnessConfiguration build();
+ method public int getMaxCorrectionsByCategory();
+ method public int getMaxCorrectionsByPackageName();
method public android.hardware.display.BrightnessConfiguration.Builder setDescription(java.lang.String);
}
+ public final class BrightnessCorrection implements android.os.Parcelable {
+ method public float apply(float);
+ method public static android.hardware.display.BrightnessCorrection createScaleAndTranslateLog(float, float);
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.hardware.display.BrightnessCorrection> CREATOR;
+ }
+
public final class DisplayManager {
method public java.util.List<android.hardware.display.AmbientBrightnessDayStats> getAmbientBrightnessStats();
method public android.hardware.display.BrightnessConfiguration getBrightnessConfiguration();
@@ -5415,7 +5437,7 @@
method public final void adjustNotifications(java.util.List<android.service.notification.Adjustment>);
method public void onActionClicked(java.lang.String, android.app.Notification.Action, int);
method public final android.os.IBinder onBind(android.content.Intent);
- method public void onNotificationDirectReply(java.lang.String);
+ method public void onNotificationDirectReplied(java.lang.String);
method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification);
method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, android.app.NotificationChannel);
method public void onNotificationExpansionChanged(java.lang.String, boolean, boolean);
@@ -5937,6 +5959,87 @@
field public static final java.lang.String KEY_CARRIER_SETUP_APP_STRING = "carrier_setup_app_string";
}
+ public final class DataFailCause {
+ field public static final int ACTIVATION_REJECT_GGSN = 30; // 0x1e
+ field public static final int ACTIVATION_REJECT_UNSPECIFIED = 31; // 0x1f
+ field public static final int ACTIVE_PDP_CONTEXT_MAX_NUMBER_REACHED = 65; // 0x41
+ field public static final int APN_TYPE_CONFLICT = 112; // 0x70
+ field public static final int AUTH_FAILURE_ON_EMERGENCY_CALL = 122; // 0x7a
+ field public static final int COMPANION_IFACE_IN_USE = 118; // 0x76
+ field public static final int CONDITIONAL_IE_ERROR = 100; // 0x64
+ field public static final int EMERGENCY_IFACE_ONLY = 116; // 0x74
+ field public static final int EMM_ACCESS_BARRED = 115; // 0x73
+ field public static final int EMM_ACCESS_BARRED_INFINITE_RETRY = 121; // 0x79
+ field public static final int ERROR_UNSPECIFIED = 65535; // 0xffff
+ field public static final int ESM_INFO_NOT_RECEIVED = 53; // 0x35
+ field public static final int FEATURE_NOT_SUPP = 40; // 0x28
+ field public static final int FILTER_SEMANTIC_ERROR = 44; // 0x2c
+ field public static final int FILTER_SYTAX_ERROR = 45; // 0x2d
+ field public static final int GPRS_REGISTRATION_FAIL = -2; // 0xfffffffe
+ field public static final int IFACE_AND_POL_FAMILY_MISMATCH = 120; // 0x78
+ field public static final int IFACE_MISMATCH = 117; // 0x75
+ field public static final int INSUFFICIENT_RESOURCES = 26; // 0x1a
+ field public static final int INTERNAL_CALL_PREEMPT_BY_HIGH_PRIO_APN = 114; // 0x72
+ field public static final int INVALID_MANDATORY_INFO = 96; // 0x60
+ field public static final int INVALID_PCSCF_ADDR = 113; // 0x71
+ field public static final int INVALID_TRANSACTION_ID = 81; // 0x51
+ field public static final int IP_ADDRESS_MISMATCH = 119; // 0x77
+ field public static final int LLC_SNDCP = 25; // 0x19
+ field public static final int LOST_CONNECTION = 65540; // 0x10004
+ field public static final int MESSAGE_INCORRECT_SEMANTIC = 95; // 0x5f
+ field public static final int MESSAGE_TYPE_UNSUPPORTED = 97; // 0x61
+ field public static final int MISSING_UNKNOWN_APN = 27; // 0x1b
+ field public static final int MSG_AND_PROTOCOL_STATE_UNCOMPATIBLE = 101; // 0x65
+ field public static final int MSG_TYPE_NONCOMPATIBLE_STATE = 98; // 0x62
+ field public static final int MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED = 55; // 0x37
+ field public static final int NAS_SIGNALLING = 14; // 0xe
+ field public static final int NETWORK_FAILURE = 38; // 0x26
+ field public static final int NONE = 0; // 0x0
+ field public static final int NSAPI_IN_USE = 35; // 0x23
+ field public static final int OEM_DCFAILCAUSE_1 = 4097; // 0x1001
+ field public static final int OEM_DCFAILCAUSE_10 = 4106; // 0x100a
+ field public static final int OEM_DCFAILCAUSE_11 = 4107; // 0x100b
+ field public static final int OEM_DCFAILCAUSE_12 = 4108; // 0x100c
+ field public static final int OEM_DCFAILCAUSE_13 = 4109; // 0x100d
+ field public static final int OEM_DCFAILCAUSE_14 = 4110; // 0x100e
+ field public static final int OEM_DCFAILCAUSE_15 = 4111; // 0x100f
+ field public static final int OEM_DCFAILCAUSE_2 = 4098; // 0x1002
+ field public static final int OEM_DCFAILCAUSE_3 = 4099; // 0x1003
+ field public static final int OEM_DCFAILCAUSE_4 = 4100; // 0x1004
+ field public static final int OEM_DCFAILCAUSE_5 = 4101; // 0x1005
+ field public static final int OEM_DCFAILCAUSE_6 = 4102; // 0x1006
+ field public static final int OEM_DCFAILCAUSE_7 = 4103; // 0x1007
+ field public static final int OEM_DCFAILCAUSE_8 = 4104; // 0x1008
+ field public static final int OEM_DCFAILCAUSE_9 = 4105; // 0x1009
+ field public static final int ONLY_IPV4_ALLOWED = 50; // 0x32
+ field public static final int ONLY_IPV6_ALLOWED = 51; // 0x33
+ field public static final int ONLY_SINGLE_BEARER_ALLOWED = 52; // 0x34
+ field public static final int OPERATOR_BARRED = 8; // 0x8
+ field public static final int PDN_CONN_DOES_NOT_EXIST = 54; // 0x36
+ field public static final int PDP_WITHOUT_ACTIVE_TFT = 46; // 0x2e
+ field public static final int PREF_RADIO_TECH_CHANGED = -4; // 0xfffffffc
+ field public static final int PROTOCOL_ERRORS = 111; // 0x6f
+ field public static final int QOS_NOT_ACCEPTED = 37; // 0x25
+ field public static final int RADIO_NOT_AVAILABLE = 65537; // 0x10001
+ field public static final int RADIO_POWER_OFF = -5; // 0xfffffffb
+ field public static final int REGISTRATION_FAIL = -1; // 0xffffffff
+ field public static final int REGULAR_DEACTIVATION = 36; // 0x24
+ field public static final int SERVICE_OPTION_NOT_SUBSCRIBED = 33; // 0x21
+ field public static final int SERVICE_OPTION_NOT_SUPPORTED = 32; // 0x20
+ field public static final int SERVICE_OPTION_OUT_OF_ORDER = 34; // 0x22
+ field public static final int SIGNAL_LOST = -3; // 0xfffffffd
+ field public static final int TETHERED_CALL_ACTIVE = -6; // 0xfffffffa
+ field public static final int TFT_SEMANTIC_ERROR = 41; // 0x29
+ field public static final int TFT_SYTAX_ERROR = 42; // 0x2a
+ field public static final int UMTS_REACTIVATION_REQ = 39; // 0x27
+ field public static final int UNKNOWN = 65536; // 0x10000
+ field public static final int UNKNOWN_INFO_ELEMENT = 99; // 0x63
+ field public static final int UNKNOWN_PDP_ADDRESS_TYPE = 28; // 0x1c
+ field public static final int UNKNOWN_PDP_CONTEXT = 43; // 0x2b
+ field public static final int UNSUPPORTED_APN_IN_CURRENT_PLMN = 66; // 0x42
+ field public static final int USER_AUTHENTICATION = 29; // 0x1d
+ }
+
public class DisconnectCause {
field public static final int ALREADY_DIALING = 72; // 0x48
field public static final int ANSWERED_ELSEWHERE = 52; // 0x34
@@ -6104,11 +6207,13 @@
public class PhoneStateListener {
method public void onCallDisconnectCauseChanged(int, int);
method public void onPreciseCallStateChanged(android.telephony.PreciseCallState);
+ method public void onPreciseDataConnectionStateChanged(android.telephony.PreciseDataConnectionState);
method public void onRadioPowerStateChanged(int);
method public void onSrvccStateChanged(int);
method public void onVoiceActivationStateChanged(int);
field public static final int LISTEN_CALL_DISCONNECT_CAUSES = 33554432; // 0x2000000
field public static final int LISTEN_PRECISE_CALL_STATE = 2048; // 0x800
+ field public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE = 4096; // 0x1000
field public static final int LISTEN_RADIO_POWER_STATE_CHANGED = 8388608; // 0x800000
field public static final int LISTEN_SRVCC_STATE_CHANGED = 16384; // 0x4000
field public static final int LISTEN_VOICE_ACTIVATION_STATE = 131072; // 0x20000
@@ -6133,6 +6238,16 @@
field public static final int PRECISE_CALL_STATE_WAITING = 6; // 0x6
}
+ public final class PreciseDataConnectionState implements android.os.Parcelable {
+ method public int describeContents();
+ method public java.lang.String getDataConnectionApn();
+ method public int getDataConnectionApnTypeBitMask();
+ method public int getDataConnectionFailCause();
+ method public int getDataConnectionState();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.PreciseDataConnectionState> CREATOR;
+ }
+
public class PreciseDisconnectCause {
field public static final int ACCESS_CLASS_BLOCKED = 260; // 0x104
field public static final int ACCESS_INFORMATION_DISCARDED = 43; // 0x2b
@@ -6335,6 +6450,7 @@
method public deprecated boolean getDataEnabled(int);
method public boolean getEmergencyCallbackMode();
method public java.lang.String getIsimDomain();
+ method public java.lang.String getIsimIst();
method public int getPreferredNetworkTypeBitmap();
method public int getRadioPowerState();
method public int getSimApplicationState();
@@ -6735,6 +6851,7 @@
method public android.os.Bundle getCallExtras();
method public int getCallType();
method public static int getCallTypeFromVideoState(int);
+ method public int getEmergencyCallRouting();
method public int getEmergencyServiceCategories();
method public android.telephony.ims.ImsStreamMediaProfile getMediaProfile();
method public int getRestrictCause();
@@ -6748,6 +6865,7 @@
method public void setCallExtraBoolean(java.lang.String, boolean);
method public void setCallExtraInt(java.lang.String, int);
method public void setCallRestrictCause(int);
+ method public void setEmergencyCallRouting(int);
method public void setEmergencyServiceCategories(int);
method public void updateCallExtras(android.telephony.ims.ImsCallProfile);
method public void updateCallType(android.telephony.ims.ImsCallProfile);
@@ -6877,6 +6995,7 @@
public class ImsMmTelManager {
method public static android.telephony.ims.ImsMmTelManager createForSubscriptionId(android.content.Context, int);
method public int getVoWiFiModeSetting();
+ method public int getVoWiFiRoamingModeSetting();
method public boolean isAdvancedCallingSettingEnabled();
method public boolean isAvailable(int, int);
method public boolean isCapable(int, int);
diff --git a/api/test-current.txt b/api/test-current.txt
index de64350..ae3c1e0 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -394,6 +394,7 @@
package android.content.res {
public final class Configuration implements java.lang.Comparable android.os.Parcelable {
+ field public int assetsSeq;
field public final android.app.WindowConfiguration windowConfiguration;
}
@@ -1236,7 +1237,7 @@
method public final void adjustNotifications(java.util.List<android.service.notification.Adjustment>);
method public void onActionClicked(java.lang.String, android.app.Notification.Action, int);
method public final android.os.IBinder onBind(android.content.Intent);
- method public void onNotificationDirectReply(java.lang.String);
+ method public void onNotificationDirectReplied(java.lang.String);
method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification);
method public android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, android.app.NotificationChannel);
method public void onNotificationExpansionChanged(java.lang.String, boolean, boolean);
diff --git a/cmds/input/src/com/android/commands/input/Input.java b/cmds/input/src/com/android/commands/input/Input.java
index b905e94..0c861cf 100644
--- a/cmds/input/src/com/android/commands/input/Input.java
+++ b/cmds/input/src/com/android/commands/input/Input.java
@@ -44,6 +44,9 @@
private static final String INVALID_DISPLAY_ARGUMENTS =
"Error: Invalid arguments for display ID.";
+ private static final float DEFAULT_PRESSURE = 1.0f;
+ private static final float NO_PRESSURE = 0.0f;
+
private static final Map<String, Integer> SOURCES = new HashMap<String, Integer>() {{
put("keyboard", InputDevice.SOURCE_KEYBOARD);
put("dpad", InputDevice.SOURCE_DPAD);
@@ -76,6 +79,7 @@
COMMANDS.put("draganddrop", new InputDragAndDrop());
COMMANDS.put("press", new InputPress());
COMMANDS.put("roll", new InputRoll());
+ COMMANDS.put("motionevent", new InputMotionEvent());
}
@Override
@@ -304,6 +308,41 @@
}
}
+ class InputMotionEvent implements InputCmd {
+ @Override
+ public void run(int inputSource, int displayId) {
+ inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
+ sendMotionEvent(inputSource, nextArgRequired(), Float.parseFloat(nextArgRequired()),
+ Float.parseFloat(nextArgRequired()), displayId);
+ }
+
+ private void sendMotionEvent(int inputSource, String motionEventType, float x, float y,
+ int displayId) {
+ final int action;
+ final float pressure;
+
+ switch (motionEventType.toUpperCase()) {
+ case "DOWN":
+ action = MotionEvent.ACTION_DOWN;
+ pressure = DEFAULT_PRESSURE;
+ break;
+ case "UP":
+ action = MotionEvent.ACTION_UP;
+ pressure = NO_PRESSURE;
+ break;
+ case "MOVE":
+ action = MotionEvent.ACTION_MOVE;
+ pressure = DEFAULT_PRESSURE;
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown motionevent " + motionEventType);
+ }
+
+ final long now = SystemClock.uptimeMillis();
+ injectMotionEvent(inputSource, action, now, now, x, y, pressure, displayId);
+ }
+ }
+
/**
* Abstract class for command
* use nextArgRequired or nextArg to check next argument if necessary.
@@ -391,5 +430,6 @@
+ " (Default: touchscreen)");
out.println(" press (Default: trackball)");
out.println(" roll <dx> <dy> (Default: trackball)");
+ out.println(" event <DOWN|UP|MOVE> <x> <y> (Default: touchscreen)");
}
}
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 5c53a3a..fa3be26 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -173,6 +173,10 @@
WifiRunningStateChanged wifi_running_state_changed = 114;
AppCompacted app_compacted = 115;
NetworkDnsEventReported network_dns_event_Reported = 116;
+ DocsUIPickerLaunchedFromReported docs_ui_picker_launched_from_reported = 117;
+ DocsUIPickResultReported docs_ui_pick_result_reported = 118;
+ DocsUISearchModeReported docs_ui_search_mode_reported = 119;
+ DocsUISearchTypeReported docs_ui_search_type_reported = 120;
}
// Pulled events will start at field 10000.
@@ -3656,6 +3660,52 @@
}
/**
+ * Logs the package name that launches docsui picker mode.
+ *
+ * Logged from:
+ * package/app/DocumentsUI/src/com/android/documentsui/Metrics.java
+ */
+message DocsUIPickerLaunchedFromReported {
+ optional string package_name = 1;
+}
+
+/**
+ * Logs the search type.
+ *
+ * Logged from:
+ * package/app/DocumentsUI/src/com/android/documentsui/Metrics.java
+ */
+message DocsUISearchTypeReported {
+ optional android.stats.docsui.SearchType search_type = 1;
+}
+
+/**
+ * Logs the search mode.
+ *
+ * Logged from:
+ * package/app/DocumentsUI/src/com/android/documentsui/Metrics.java
+ */
+message DocsUISearchModeReported {
+ optional android.stats.docsui.SearchMode search_mode = 1;
+}
+
+/**
+ * Logs the pick result information.
+ *
+ * Logged from:
+ * package/app/DocumentsUI/src/com/android/documentsui/Metrics.java
+ */
+message DocsUIPickResultReported {
+ optional int32 total_action_count = 1;
+ optional int64 duration_millis = 2;
+ optional int32 file_count= 3;
+ optional bool is_searching = 4;
+ optional android.stats.docsui.Root picked_from = 5;
+ optional android.stats.docsui.MimeType mime_type = 6;
+ optional int32 repeatedly_pick_times = 7;
+}
+
+/**
* Logs when an app's memory is compacted.
*
* Logged from:
diff --git a/config/hiddenapi-greylist.txt b/config/hiddenapi-greylist.txt
index 97dcee6..e0e1b72 100644
--- a/config/hiddenapi-greylist.txt
+++ b/config/hiddenapi-greylist.txt
@@ -56,7 +56,6 @@
Landroid/app/IActivityManager$Stub$Proxy;->getLaunchedFromUid(Landroid/os/IBinder;)I
Landroid/app/IActivityManager$Stub$Proxy;->getProcessLimit()I
Landroid/app/IActivityManager$Stub$Proxy;->getProcessPss([I)[J
-Landroid/app/IActivityManager$Stub$Proxy;->isAppForeground(I)Z
Landroid/app/IActivityManager$Stub$Proxy;->mRemote:Landroid/os/IBinder;
Landroid/app/IActivityManager$Stub$Proxy;->setActivityController(Landroid/app/IActivityController;Z)V
Landroid/app/IActivityManager$Stub$Proxy;->updatePersistentConfiguration(Landroid/content/res/Configuration;)V
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 5b8261e..fdb71bb 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -2158,6 +2158,7 @@
* for helping activities determine the proper time to cancel a notification.
*
* @see #onUserInteraction()
+ * @see android.content.Intent#FLAG_ACTIVITY_NO_USER_ACTION
*/
protected void onUserLeaveHint() {
}
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 0b50916..9d44e58 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -314,4 +314,7 @@
/** Returns mount mode for process running with given pid */
public abstract int getStorageMountMode(int pid, int uid);
+
+ /** Returns true if the given uid is the app in the foreground. */
+ public abstract boolean isAppForeground(int uid);
}
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index fb519b6..347973e 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -421,7 +421,6 @@
void resizeDockedStack(in Rect dockedBounds, in Rect tempDockedTaskBounds,
in Rect tempDockedTaskInsetBounds,
in Rect tempOtherTaskBounds, in Rect tempOtherTaskInsetBounds);
- boolean isAppForeground(int uid);
void removeStack(int stackId);
void makePackageIdle(String packageName, int userId);
int getMemoryTrimLevel();
diff --git a/core/java/android/app/WallpaperColors.java b/core/java/android/app/WallpaperColors.java
index ace814a..38a98d3c8 100644
--- a/core/java/android/app/WallpaperColors.java
+++ b/core/java/android/app/WallpaperColors.java
@@ -18,7 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UnsupportedAppUsage;
+import android.annotation.SystemApi;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -56,6 +56,7 @@
* eg. A launcher may set its text color to black if this flag is specified.
* @hide
*/
+ @SystemApi
public static final int HINT_SUPPORTS_DARK_TEXT = 1 << 0;
/**
@@ -64,6 +65,7 @@
* eg. A launcher may set its drawer color to black if this flag is specified.
* @hide
*/
+ @SystemApi
public static final int HINT_SUPPORTS_DARK_THEME = 1 << 1;
/**
@@ -234,7 +236,7 @@
* @see WallpaperColors#fromDrawable(Drawable)
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
public WallpaperColors(@NonNull Color primaryColor, @Nullable Color secondaryColor,
@Nullable Color tertiaryColor, int colorHints) {
@@ -349,7 +351,7 @@
* @return True if dark text is supported.
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
public int getColorHints() {
return mColorHints;
}
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index 2990b57..e0a15a5 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -19,6 +19,7 @@
import static android.app.ActivityThread.isSystem;
import static android.app.WindowConfigurationProto.ACTIVITY_TYPE;
import static android.app.WindowConfigurationProto.APP_BOUNDS;
+import static android.app.WindowConfigurationProto.BOUNDS;
import static android.app.WindowConfigurationProto.WINDOWING_MODE;
import static android.view.Surface.rotationToString;
@@ -585,6 +586,9 @@
}
protoOutputStream.write(WINDOWING_MODE, mWindowingMode);
protoOutputStream.write(ACTIVITY_TYPE, mActivityType);
+ if (mBounds != null) {
+ mBounds.writeToProto(protoOutputStream, BOUNDS);
+ }
protoOutputStream.end(token);
}
@@ -606,6 +610,10 @@
mAppBounds = new Rect();
mAppBounds.readFromProto(proto, APP_BOUNDS);
break;
+ case (int) BOUNDS:
+ mBounds = new Rect();
+ mBounds.readFromProto(proto, BOUNDS);
+ break;
case (int) WINDOWING_MODE:
mWindowingMode = proto.readInt(WINDOWING_MODE);
break;
diff --git a/core/java/android/app/role/RoleManager.java b/core/java/android/app/role/RoleManager.java
index 2d630a6..d73e73f 100644
--- a/core/java/android/app/role/RoleManager.java
+++ b/core/java/android/app/role/RoleManager.java
@@ -67,6 +67,56 @@
private static final String LOG_TAG = RoleManager.class.getSimpleName();
/**
+ * The name of the proxy calling role.
+ * <p>
+ * A proxy calling app implements the {@link android.telecom.CallRedirectionService} API and
+ * provides a means to re-write the phone number for an outgoing call to place the call through
+ * a proxy calling service.
+ * <p>
+ * A single app may fill this role at any one time.
+ * @hide
+ */
+ public static final String ROLE_PROXY_CALLING_APP = "android.app.role.PROXY_CALLING_APP";
+
+ /**
+ * The name of the call screening and caller id role.
+ * <p>
+ * A call screening and caller id app implements the
+ * {@link android.telecom.CallScreeningService} API.
+ * <p>
+ * A single app may fill this role at any one time.
+ * @hide
+ */
+ public static final String ROLE_CALL_SCREENING_APP = "android.app.role.CALL_SCREENING_APP";
+
+ /**
+ * The name of the call companion app role.
+ * <p>
+ * A call companion app provides no user interface for calls, but will be bound to by Telecom
+ * when there are active calls on the device. Companion apps for wearable devices are an
+ * acceptable use-case. A call companion app implements the
+ * {@link android.telecom.InCallService} API.
+ * <p>
+ * Multiple apps app may fill this role at any one time.
+ * @hide
+ */
+ public static final String ROLE_CALL_COMPANION_APP = "android.app.role.CALL_COMPANION_APP";
+
+ /**
+ * The name of the car mode dialer app role.
+ * <p>
+ * Similar to the {@link #ROLE_DIALER} role, this role determines which app is responsible for
+ * showing the user interface for ongoing calls on the device. This app filling this role is
+ * only used when the device is in car mode (see
+ * {@link android.app.UiModeManager#ACTION_ENTER_CAR_MODE} for more information). An app
+ * filling this role must implement the {@link android.telecom.InCallService} API.
+ * <p>
+ * A single app may fill this role at any one time.
+ * @hide
+ */
+ public static final String ROLE_CAR_MODE_DIALER_APP = "android.app.role.CAR_MODE_DIALER_APP";
+
+ /**
* The name of the dialer role.
*/
public static final String ROLE_DIALER = "android.app.role.DIALER";
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index c740c42..3e9dd28 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -375,12 +375,12 @@
}
/**
- * Sets whether the widget should is being displayed on a light/white background and use an
+ * Sets whether the widget is being displayed on a light/white background and use an
* alternate UI if available.
* @see RemoteViews#setLightBackgroundLayoutId(int)
*/
- public void setOnLightBackground(boolean useDarkTextLayout) {
- mOnLightBackground = useDarkTextLayout;
+ public void setOnLightBackground(boolean onLightBackground) {
+ mOnLightBackground = onLightBackground;
}
/**
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 87b64797..1945b2f 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -2066,8 +2066,7 @@
* Get the current connection state of a profile.
* This function can be used to check whether the local Bluetooth adapter
* is connected to any remote device for a specific profile.
- * Profile can be one of {@link BluetoothProfile#HEALTH}, {@link BluetoothProfile#HEADSET},
- * {@link BluetoothProfile#A2DP}.
+ * Profile can be one of {@link BluetoothProfile#HEADSET}, {@link BluetoothProfile#A2DP}.
*
* <p> Return value can be one of
* {@link BluetoothProfile#STATE_DISCONNECTED},
@@ -2441,16 +2440,15 @@
/**
* Get the profile proxy object associated with the profile.
*
- * <p>Profile can be one of {@link BluetoothProfile#HEALTH}, {@link BluetoothProfile#HEADSET},
- * {@link BluetoothProfile#A2DP}, {@link BluetoothProfile#GATT}, or
- * {@link BluetoothProfile#GATT_SERVER}. Clients must implement
- * {@link BluetoothProfile.ServiceListener} to get notified of
- * the connection status and to get the proxy object.
+ * <p>Profile can be one of {@link BluetoothProfile#HEADSET}, {@link BluetoothProfile#A2DP},
+ * {@link BluetoothProfile#GATT}, or {@link BluetoothProfile#GATT_SERVER}. Clients must
+ * implement {@link BluetoothProfile.ServiceListener} to get notified of the connection status
+ * and to get the proxy object.
*
* @param context Context of the application
* @param listener The service Listener for connection callbacks.
- * @param profile The Bluetooth profile; either {@link BluetoothProfile#HEALTH}, {@link
- * BluetoothProfile#HEADSET}, {@link BluetoothProfile#A2DP}. {@link BluetoothProfile#GATT} or
+ * @param profile The Bluetooth profile; either {@link BluetoothProfile#HEADSET},
+ * {@link BluetoothProfile#A2DP}. {@link BluetoothProfile#GATT} or
* {@link BluetoothProfile#GATT_SERVER}.
* @return true on success, false on error
*/
@@ -2479,8 +2477,8 @@
BluetoothPan pan = new BluetoothPan(context, listener);
return true;
} else if (profile == BluetoothProfile.HEALTH) {
- BluetoothHealth health = new BluetoothHealth(context, listener);
- return true;
+ Log.e(TAG, "getProfileProxy(): BluetoothHealth is deprecated");
+ return false;
} else if (profile == BluetoothProfile.MAP) {
BluetoothMap map = new BluetoothMap(context, listener);
return true;
@@ -2512,8 +2510,7 @@
*
* <p> Clients should call this when they are no longer using
* the proxy obtained from {@link #getProfileProxy}.
- * Profile can be one of {@link BluetoothProfile#HEALTH}, {@link BluetoothProfile#HEADSET} or
- * {@link BluetoothProfile#A2DP}
+ * Profile can be one of {@link BluetoothProfile#HEADSET} or {@link BluetoothProfile#A2DP}
*
* @param profile
* @param proxy Profile proxy object
@@ -2548,10 +2545,6 @@
BluetoothPan pan = (BluetoothPan) proxy;
pan.close();
break;
- case BluetoothProfile.HEALTH:
- BluetoothHealth health = (BluetoothHealth) proxy;
- health.close();
- break;
case BluetoothProfile.GATT:
BluetoothGatt gatt = (BluetoothGatt) proxy;
gatt.close();
diff --git a/core/java/android/bluetooth/BluetoothHealth.java b/core/java/android/bluetooth/BluetoothHealth.java
index 22d41d9..e2e56fd 100644
--- a/core/java/android/bluetooth/BluetoothHealth.java
+++ b/core/java/android/bluetooth/BluetoothHealth.java
@@ -16,15 +16,7 @@
package android.bluetooth;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.os.Binder;
-import android.os.IBinder;
import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
-import android.os.UserHandle;
import android.util.Log;
import java.util.ArrayList;
@@ -54,79 +46,59 @@
* <li> When done, close the health channel by calling {@link #disconnectChannel}
* and unregister the application configuration calling
* {@link #unregisterAppConfiguration}
+ *
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New apps
+ * should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
*/
+@Deprecated
public final class BluetoothHealth implements BluetoothProfile {
private static final String TAG = "BluetoothHealth";
- private static final boolean DBG = true;
- private static final boolean VDBG = false;
-
/**
* Health Profile Source Role - the health device.
+ *
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+ * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
*/
+ @Deprecated
public static final int SOURCE_ROLE = 1 << 0;
/**
* Health Profile Sink Role the device talking to the health device.
+ *
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+ * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
*/
+ @Deprecated
public static final int SINK_ROLE = 1 << 1;
/**
* Health Profile - Channel Type used - Reliable
+ *
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+ * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
*/
+ @Deprecated
public static final int CHANNEL_TYPE_RELIABLE = 10;
/**
* Health Profile - Channel Type used - Streaming
+ *
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+ * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
*/
+ @Deprecated
public static final int CHANNEL_TYPE_STREAMING = 11;
- /**
- * @hide
- */
- public static final int CHANNEL_TYPE_ANY = 12;
-
- /** @hide */
- public static final int HEALTH_OPERATION_SUCCESS = 6000;
- /** @hide */
- public static final int HEALTH_OPERATION_ERROR = 6001;
- /** @hide */
- public static final int HEALTH_OPERATION_INVALID_ARGS = 6002;
- /** @hide */
- public static final int HEALTH_OPERATION_GENERIC_FAILURE = 6003;
- /** @hide */
- public static final int HEALTH_OPERATION_NOT_FOUND = 6004;
- /** @hide */
- public static final int HEALTH_OPERATION_NOT_ALLOWED = 6005;
-
- private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
- new IBluetoothStateChangeCallback.Stub() {
- public void onBluetoothStateChange(boolean up) {
- if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
- if (!up) {
- if (VDBG) Log.d(TAG, "Unbinding service...");
- synchronized (mConnection) {
- try {
- mService = null;
- mContext.unbindService(mConnection);
- } catch (Exception re) {
- Log.e(TAG, "", re);
- }
- }
- } else {
- synchronized (mConnection) {
- try {
- if (mService == null) {
- if (VDBG) Log.d(TAG, "Binding service...");
- doBind();
- }
- } catch (Exception re) {
- Log.e(TAG, "", re);
- }
- }
- }
- }
- };
-
/**
* Register an application configuration that acts as a Health SINK.
@@ -142,53 +114,17 @@
* @param callback A callback to indicate success or failure of the registration and all
* operations done on this application configuration.
* @return If true, callback will be called.
+ *
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+ * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
*/
+ @Deprecated
public boolean registerSinkAppConfiguration(String name, int dataType,
BluetoothHealthCallback callback) {
- if (!isEnabled() || name == null) return false;
-
- if (VDBG) log("registerSinkApplication(" + name + ":" + dataType + ")");
- return registerAppConfiguration(name, dataType, SINK_ROLE,
- CHANNEL_TYPE_ANY, callback);
- }
-
- /**
- * Register an application configuration that acts as a Health SINK or in a Health
- * SOURCE role.This is an asynchronous call and so
- * the callback is used to notify success or failure if the function returns true.
- *
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
- * @param name The friendly name associated with the application or configuration.
- * @param dataType The dataType of the Source role of Health Profile.
- * @param channelType The channel type. Will be one of {@link #CHANNEL_TYPE_RELIABLE} or {@link
- * #CHANNEL_TYPE_STREAMING}
- * @param callback - A callback to indicate success or failure.
- * @return If true, callback will be called.
- * @hide
- */
- public boolean registerAppConfiguration(String name, int dataType, int role,
- int channelType, BluetoothHealthCallback callback) {
- boolean result = false;
- if (!isEnabled() || !checkAppParam(name, role, channelType, callback)) return result;
-
- if (VDBG) log("registerApplication(" + name + ":" + dataType + ")");
- BluetoothHealthCallbackWrapper wrapper = new BluetoothHealthCallbackWrapper(callback);
- BluetoothHealthAppConfiguration config =
- new BluetoothHealthAppConfiguration(name, dataType, role, channelType);
-
- final IBluetoothHealth service = mService;
- if (service != null) {
- try {
- result = service.registerAppConfiguration(config, wrapper);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
- }
- return result;
+ Log.e(TAG, "registerSinkAppConfiguration(): BluetoothHealth is deprecated");
+ return false;
}
/**
@@ -199,22 +135,16 @@
*
* @param config The health app configuration
* @return Success or failure.
+ *
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+ * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
*/
+ @Deprecated
public boolean unregisterAppConfiguration(BluetoothHealthAppConfiguration config) {
- boolean result = false;
- final IBluetoothHealth service = mService;
- if (service != null && isEnabled() && config != null) {
- try {
- result = service.unregisterAppConfiguration(config);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
- }
-
- return result;
+ Log.e(TAG, "unregisterAppConfiguration(): BluetoothHealth is deprecated");
+ return false;
}
/**
@@ -228,49 +158,16 @@
* @param config The application configuration which has been registered using {@link
* #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) }
* @return If true, the callback associated with the application config will be called.
+ *
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+ * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
*/
+ @Deprecated
public boolean connectChannelToSource(BluetoothDevice device,
BluetoothHealthAppConfiguration config) {
- final IBluetoothHealth service = mService;
- if (service != null && isEnabled() && isValidDevice(device) && config != null) {
- try {
- return service.connectChannelToSource(device, config);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
- }
- return false;
- }
-
- /**
- * Connect to a health device which has the {@link #SINK_ROLE}.
- * This is an asynchronous call. If this function returns true, the callback
- * associated with the application configuration will be called.
- *
- * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
- *
- * @param device The remote Bluetooth device.
- * @param config The application configuration which has been registered using {@link
- * #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) }
- * @return If true, the callback associated with the application config will be called.
- * @hide
- */
- public boolean connectChannelToSink(BluetoothDevice device,
- BluetoothHealthAppConfiguration config, int channelType) {
- final IBluetoothHealth service = mService;
- if (service != null && isEnabled() && isValidDevice(device) && config != null) {
- try {
- return service.connectChannelToSink(device, config, channelType);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
- }
+ Log.e(TAG, "connectChannelToSource(): BluetoothHealth is deprecated");
return false;
}
@@ -286,20 +183,16 @@
* #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) }
* @param channelId The channel id associated with the channel
* @return If true, the callback associated with the application config will be called.
+ *
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+ * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
*/
+ @Deprecated
public boolean disconnectChannel(BluetoothDevice device,
BluetoothHealthAppConfiguration config, int channelId) {
- final IBluetoothHealth service = mService;
- if (service != null && isEnabled() && isValidDevice(device) && config != null) {
- try {
- return service.disconnectChannel(device, config, channelId);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
- }
+ Log.e(TAG, "disconnectChannel(): BluetoothHealth is deprecated");
return false;
}
@@ -315,20 +208,16 @@
* @param device The remote Bluetooth health device
* @param config The application configuration
* @return null on failure, ParcelFileDescriptor on success.
+ *
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+ * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
*/
+ @Deprecated
public ParcelFileDescriptor getMainChannelFd(BluetoothDevice device,
BluetoothHealthAppConfiguration config) {
- final IBluetoothHealth service = mService;
- if (service != null && isEnabled() && isValidDevice(device) && config != null) {
- try {
- return service.getMainChannelFd(device, config);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
- }
+ Log.e(TAG, "getMainChannelFd(): BluetoothHealth is deprecated");
return null;
}
@@ -348,17 +237,7 @@
*/
@Override
public int getConnectionState(BluetoothDevice device) {
- final IBluetoothHealth service = mService;
- if (service != null && isEnabled() && isValidDevice(device)) {
- try {
- return service.getHealthDeviceConnectionState(device);
- } catch (RemoteException e) {
- Log.e(TAG, e.toString());
- }
- } else {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
- }
+ Log.e(TAG, "getConnectionState(): BluetoothHealth is deprecated");
return STATE_DISCONNECTED;
}
@@ -378,17 +257,8 @@
*/
@Override
public List<BluetoothDevice> getConnectedDevices() {
- final IBluetoothHealth service = mService;
- if (service != null && isEnabled()) {
- try {
- return service.getConnectedHealthDevices();
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
- }
- }
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ Log.e(TAG, "getConnectedDevices(): BluetoothHealth is deprecated");
+ return new ArrayList<>();
}
/**
@@ -410,163 +280,81 @@
*/
@Override
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
- final IBluetoothHealth service = mService;
- if (service != null && isEnabled()) {
- try {
- return service.getHealthDevicesMatchingConnectionStates(states);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return new ArrayList<BluetoothDevice>();
- }
- }
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return new ArrayList<BluetoothDevice>();
+ Log.e(TAG, "getDevicesMatchingConnectionStates(): BluetoothHealth is deprecated");
+ return new ArrayList<>();
}
- private static class BluetoothHealthCallbackWrapper extends IBluetoothHealthCallback.Stub {
- private BluetoothHealthCallback mCallback;
-
- public BluetoothHealthCallbackWrapper(BluetoothHealthCallback callback) {
- mCallback = callback;
- }
-
- @Override
- public void onHealthAppConfigurationStatusChange(BluetoothHealthAppConfiguration config,
- int status) {
- mCallback.onHealthAppConfigurationStatusChange(config, status);
- }
-
- @Override
- public void onHealthChannelStateChange(BluetoothHealthAppConfiguration config,
- BluetoothDevice device, int prevState, int newState,
- ParcelFileDescriptor fd, int channelId) {
- mCallback.onHealthChannelStateChange(config, device, prevState, newState, fd,
- channelId);
- }
- }
-
- /** Health Channel Connection State - Disconnected */
+ /** Health Channel Connection State - Disconnected
+ *
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+ * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
+ */
+ @Deprecated
public static final int STATE_CHANNEL_DISCONNECTED = 0;
- /** Health Channel Connection State - Connecting */
+ /** Health Channel Connection State - Connecting
+ *
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+ * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
+ */
+ @Deprecated
public static final int STATE_CHANNEL_CONNECTING = 1;
- /** Health Channel Connection State - Connected */
+ /** Health Channel Connection State - Connected
+ *
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+ * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
+ */
+ @Deprecated
public static final int STATE_CHANNEL_CONNECTED = 2;
- /** Health Channel Connection State - Disconnecting */
+ /** Health Channel Connection State - Disconnecting
+ *
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+ * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
+ */
+ @Deprecated
public static final int STATE_CHANNEL_DISCONNECTING = 3;
- /** Health App Configuration registration success */
- public static final int APP_CONFIG_REGISTRATION_SUCCESS = 0;
- /** Health App Configuration registration failure */
- public static final int APP_CONFIG_REGISTRATION_FAILURE = 1;
- /** Health App Configuration un-registration success */
- public static final int APP_CONFIG_UNREGISTRATION_SUCCESS = 2;
- /** Health App Configuration un-registration failure */
- public static final int APP_CONFIG_UNREGISTRATION_FAILURE = 3;
-
- private Context mContext;
- private ServiceListener mServiceListener;
- private volatile IBluetoothHealth mService;
- BluetoothAdapter mAdapter;
-
- /**
- * Create a BluetoothHealth proxy object.
+ /** Health App Configuration registration success
+ *
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+ * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
*/
- /*package*/ BluetoothHealth(Context context, ServiceListener l) {
- mContext = context;
- mServiceListener = l;
- mAdapter = BluetoothAdapter.getDefaultAdapter();
- IBluetoothManager mgr = mAdapter.getBluetoothManager();
- if (mgr != null) {
- try {
- mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
- } catch (RemoteException e) {
- Log.e(TAG, "", e);
- }
- }
-
- doBind();
- }
-
- boolean doBind() {
- Intent intent = new Intent(IBluetoothHealth.class.getName());
- ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
- intent.setComponent(comp);
- if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
- UserHandle.CURRENT_OR_SELF)) {
- Log.e(TAG, "Could not bind to Bluetooth Health Service with " + intent);
- return false;
- }
- return true;
- }
-
- /*package*/ void close() {
- if (VDBG) log("close()");
- IBluetoothManager mgr = mAdapter.getBluetoothManager();
- if (mgr != null) {
- try {
- mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
- } catch (Exception e) {
- Log.e(TAG, "", e);
- }
- }
-
- synchronized (mConnection) {
- if (mService != null) {
- try {
- mService = null;
- mContext.unbindService(mConnection);
- } catch (Exception re) {
- Log.e(TAG, "", re);
- }
- }
- }
- mServiceListener = null;
- }
-
- private final ServiceConnection mConnection = new ServiceConnection() {
- public void onServiceConnected(ComponentName className, IBinder service) {
- if (DBG) Log.d(TAG, "Proxy object connected");
- mService = IBluetoothHealth.Stub.asInterface(Binder.allowBlocking(service));
-
- if (mServiceListener != null) {
- mServiceListener.onServiceConnected(BluetoothProfile.HEALTH, BluetoothHealth.this);
- }
- }
-
- public void onServiceDisconnected(ComponentName className) {
- if (DBG) Log.d(TAG, "Proxy object disconnected");
- mService = null;
- if (mServiceListener != null) {
- mServiceListener.onServiceDisconnected(BluetoothProfile.HEALTH);
- }
- }
- };
-
- private boolean isEnabled() {
- BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
-
- if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) return true;
- log("Bluetooth is Not enabled");
- return false;
- }
-
- private static boolean isValidDevice(BluetoothDevice device) {
- return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
- }
-
- private boolean checkAppParam(String name, int role, int channelType,
- BluetoothHealthCallback callback) {
- if (name == null || (role != SOURCE_ROLE && role != SINK_ROLE)
- || (channelType != CHANNEL_TYPE_RELIABLE && channelType != CHANNEL_TYPE_STREAMING
- && channelType != CHANNEL_TYPE_ANY)
- || callback == null) {
- return false;
- }
- if (role == SOURCE_ROLE && channelType == CHANNEL_TYPE_ANY) return false;
- return true;
- }
-
- private static void log(String msg) {
- Log.d(TAG, msg);
- }
+ @Deprecated
+ public static final int APP_CONFIG_REGISTRATION_SUCCESS = 0;
+ /** Health App Configuration registration failure
+ *
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+ * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
+ */
+ @Deprecated
+ public static final int APP_CONFIG_REGISTRATION_FAILURE = 1;
+ /** Health App Configuration un-registration success
+ *
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+ * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
+ */
+ @Deprecated
+ public static final int APP_CONFIG_UNREGISTRATION_SUCCESS = 2;
+ /** Health App Configuration un-registration failure
+ *
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+ * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
+ */
+ @Deprecated
+ public static final int APP_CONFIG_UNREGISTRATION_FAILURE = 3;
}
diff --git a/core/java/android/bluetooth/BluetoothHealthAppConfiguration.java b/core/java/android/bluetooth/BluetoothHealthAppConfiguration.java
index 7c9db6f..9788bbf 100644
--- a/core/java/android/bluetooth/BluetoothHealthAppConfiguration.java
+++ b/core/java/android/bluetooth/BluetoothHealthAppConfiguration.java
@@ -25,72 +25,14 @@
* the {@link BluetoothHealth} class. This class represents an application configuration
* that the Bluetooth Health third party application will register to communicate with the
* remote Bluetooth health device.
+ *
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+ * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
*/
+@Deprecated
public final class BluetoothHealthAppConfiguration implements Parcelable {
- private final String mName;
- private final int mDataType;
- private final int mRole;
- private final int mChannelType;
-
- /**
- * Constructor to register the SINK role
- *
- * @param name Friendly name associated with the application configuration
- * @param dataType Data Type of the remote Bluetooth Health device
- * @hide
- */
- BluetoothHealthAppConfiguration(String name, int dataType) {
- mName = name;
- mDataType = dataType;
- mRole = BluetoothHealth.SINK_ROLE;
- mChannelType = BluetoothHealth.CHANNEL_TYPE_ANY;
- }
-
- /**
- * Constructor to register the application configuration.
- *
- * @param name Friendly name associated with the application configuration
- * @param dataType Data Type of the remote Bluetooth Health device
- * @param role {@link BluetoothHealth#SOURCE_ROLE} or {@link BluetoothHealth#SINK_ROLE}
- * @hide
- */
- BluetoothHealthAppConfiguration(String name, int dataType, int role, int
- channelType) {
- mName = name;
- mDataType = dataType;
- mRole = role;
- mChannelType = channelType;
- }
-
- @Override
- public boolean equals(Object o) {
- if (o instanceof BluetoothHealthAppConfiguration) {
- BluetoothHealthAppConfiguration config = (BluetoothHealthAppConfiguration) o;
-
- if (mName == null) return false;
-
- return mName.equals(config.getName()) && mDataType == config.getDataType()
- && mRole == config.getRole() && mChannelType == config.getChannelType();
- }
- return false;
- }
-
- @Override
- public int hashCode() {
- int result = 17;
- result = 31 * result + (mName != null ? mName.hashCode() : 0);
- result = 31 * result + mDataType;
- result = 31 * result + mRole;
- result = 31 * result + mChannelType;
- return result;
- }
-
- @Override
- public String toString() {
- return "BluetoothHealthAppConfiguration [mName = " + mName + ",mDataType = " + mDataType
- + ", mRole = " + mRole + ",mChannelType = " + mChannelType + "]";
- }
-
@Override
public int describeContents() {
return 0;
@@ -100,50 +42,59 @@
* Return the data type associated with this application configuration.
*
* @return dataType
+ *
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+ * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
*/
+ @Deprecated
public int getDataType() {
- return mDataType;
+ return 0;
}
/**
* Return the name of the application configuration.
*
* @return String name
+ *
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+ * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
*/
+ @Deprecated
public String getName() {
- return mName;
+ return null;
}
/**
* Return the role associated with this application configuration.
*
* @return One of {@link BluetoothHealth#SOURCE_ROLE} or {@link BluetoothHealth#SINK_ROLE}
+ *
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+ * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
*/
+ @Deprecated
public int getRole() {
- return mRole;
+ return 0;
}
/**
- * Return the channel type associated with this application configuration.
- *
- * @return One of {@link BluetoothHealth#CHANNEL_TYPE_RELIABLE} or {@link
- * BluetoothHealth#CHANNEL_TYPE_STREAMING} or {@link BluetoothHealth#CHANNEL_TYPE_ANY}.
- * @hide
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+ * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
*/
- public int getChannelType() {
- return mChannelType;
- }
-
+ @Deprecated
public static final Parcelable.Creator<BluetoothHealthAppConfiguration> CREATOR =
new Parcelable.Creator<BluetoothHealthAppConfiguration>() {
@Override
public BluetoothHealthAppConfiguration createFromParcel(Parcel in) {
- String name = in.readString();
- int type = in.readInt();
- int role = in.readInt();
- int channelType = in.readInt();
- return new BluetoothHealthAppConfiguration(name, type, role,
- channelType);
+ return new BluetoothHealthAppConfiguration();
}
@Override
@@ -153,10 +104,5 @@
};
@Override
- public void writeToParcel(Parcel out, int flags) {
- out.writeString(mName);
- out.writeInt(mDataType);
- out.writeInt(mRole);
- out.writeInt(mChannelType);
- }
+ public void writeToParcel(Parcel out, int flags) {}
}
diff --git a/core/java/android/bluetooth/BluetoothHealthCallback.java b/core/java/android/bluetooth/BluetoothHealthCallback.java
index 4023485..4769212 100644
--- a/core/java/android/bluetooth/BluetoothHealthCallback.java
+++ b/core/java/android/bluetooth/BluetoothHealthCallback.java
@@ -23,7 +23,13 @@
/**
* This abstract class is used to implement {@link BluetoothHealth} callbacks.
+ *
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+ * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
*/
+@Deprecated
public abstract class BluetoothHealthCallback {
private static final String TAG = "BluetoothHealthCallback";
@@ -38,8 +44,14 @@
* BluetoothHealth#APP_CONFIG_REGISTRATION_FAILURE} or
* {@link BluetoothHealth#APP_CONFIG_UNREGISTRATION_SUCCESS}
* or {@link BluetoothHealth#APP_CONFIG_UNREGISTRATION_FAILURE}
+ *
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+ * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
*/
@BinderThread
+ @Deprecated
public void onHealthAppConfigurationStatusChange(BluetoothHealthAppConfiguration config,
int status) {
Log.d(TAG, "onHealthAppConfigurationStatusChange: " + config + "Status: " + status);
@@ -58,8 +70,14 @@
* @param fd The Parcel File Descriptor when the channel state is connected.
* @param channelId The id associated with the channel. This id will be used in future calls
* like when disconnecting the channel.
+ *
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+ * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()(int)}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
*/
@BinderThread
+ @Deprecated
public void onHealthChannelStateChange(BluetoothHealthAppConfiguration config,
BluetoothDevice device, int prevState, int newState, ParcelFileDescriptor fd,
int channelId) {
diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java
index 3c3a01b..3c87c73 100644
--- a/core/java/android/bluetooth/BluetoothProfile.java
+++ b/core/java/android/bluetooth/BluetoothProfile.java
@@ -72,7 +72,13 @@
/**
* Health Profile
+ *
+ * @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New
+ * apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},
+ * {@link BluetoothAdapter#listenUsingL2capChannel()}, or
+ * {@link BluetoothDevice#createL2capChannel(int)}
*/
+ @Deprecated
int HEALTH = 3;
/**
@@ -269,9 +275,8 @@
* Called to notify the client when the proxy object has been
* connected to the service.
*
- * @param profile - One of {@link #HEALTH}, {@link #HEADSET} or {@link #A2DP}
- * @param proxy - One of {@link BluetoothHealth}, {@link BluetoothHeadset} or {@link
- * BluetoothA2dp}
+ * @param profile - One of {@link #HEADSET} or {@link #A2DP}
+ * @param proxy - One of {@link BluetoothHeadset} or {@link BluetoothA2dp}
*/
public void onServiceConnected(int profile, BluetoothProfile proxy);
@@ -279,7 +284,7 @@
* Called to notify the client that this proxy object has been
* disconnected from the service.
*
- * @param profile - One of {@link #HEALTH}, {@link #HEADSET} or {@link #A2DP}
+ * @param profile - One of {@link #HEADSET} or {@link #A2DP}
*/
public void onServiceDisconnected(int profile);
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index ab60b84..de810e8 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1833,6 +1833,23 @@
"android.intent.action.REVIEW_PERMISSIONS";
/**
+ * Activity action: Launch UI to manage special app accesses.
+ * <p>
+ * Input: Nothing.
+ * </p>
+ * <p>
+ * Output: Nothing.
+ * </p>
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS)
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ @SystemApi
+ public static final String ACTION_MANAGE_SPECIAL_APP_ACCESSES =
+ "android.intent.action.MANAGE_SPECIAL_APP_ACCESSES";
+
+ /**
* Intent extra: A callback for reporting remote result as a bundle.
* <p>
* Type: IRemoteCallback
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 2b266b7..c984651 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1621,7 +1621,7 @@
}
final AttributeSet attrs = parser;
- return parseApkLite(apkPath, parser, attrs, signingDetails);
+ return parseApkLite(apkPath, parser, attrs, signingDetails, flags);
} catch (XmlPullParserException | IOException | RuntimeException e) {
Slog.w(TAG, "Failed to parse " + apkPath, e);
@@ -1708,7 +1708,7 @@
}
private static ApkLite parseApkLite(String codePath, XmlPullParser parser, AttributeSet attrs,
- SigningDetails signingDetails)
+ SigningDetails signingDetails, int flags)
throws IOException, XmlPullParserException, PackageParserException {
final Pair<String, String> packageSplit = parsePackageSplitNames(parser, attrs);
@@ -1716,11 +1716,12 @@
int versionCode = 0;
int versionCodeMajor = 0;
int revisionCode = 0;
+ int targetSdkVersion = 0;
boolean coreApp = false;
boolean debuggable = false;
boolean multiArch = false;
boolean use32bitAbi = false;
- boolean extractNativeLibs = true;
+ Boolean extractNativeLibsProvided = null;
boolean isolatedSplits = false;
boolean isFeatureSplit = false;
boolean isSplitRequired = false;
@@ -1785,7 +1786,8 @@
use32bitAbi = attrs.getAttributeBooleanValue(i, false);
}
if ("extractNativeLibs".equals(attr)) {
- extractNativeLibs = attrs.getAttributeBooleanValue(i, true);
+ extractNativeLibsProvided = Boolean.valueOf(
+ attrs.getAttributeBooleanValue(i, true));
}
if ("preferCodeIntegrity".equals(attr)) {
preferCodeIntegrity = attrs.getAttributeBooleanValue(i, false);
@@ -1803,9 +1805,51 @@
PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
"<uses-split> tag requires 'android:name' attribute");
}
+ } else if (TAG_USES_SDK.equals(parser.getName())) {
+ final String[] errorMsg = new String[1];
+ Pair<Integer, Integer> versions = deriveSdkVersions(new AbstractVersionsAccessor() {
+ @Override public String getMinSdkVersionCode() {
+ return getAttributeAsString("minSdkVersion");
+ }
+
+ @Override public int getMinSdkVersion() {
+ return getAttributeAsInt("minSdkVersion");
+ }
+
+ @Override public String getTargetSdkVersionCode() {
+ return getAttributeAsString("targetSdkVersion");
+ }
+
+ @Override public int getTargetSdkVersion() {
+ return getAttributeAsInt("targetSdkVersion");
+ }
+
+ private String getAttributeAsString(String name) {
+ return attrs.getAttributeValue(ANDROID_RESOURCES, name);
+ }
+
+ private int getAttributeAsInt(String name) {
+ try {
+ return attrs.getAttributeIntValue(ANDROID_RESOURCES, name, -1);
+ } catch (NumberFormatException e) {
+ return -1;
+ }
+ }
+ }, flags, errorMsg);
+
+ if (versions == null) {
+ throw new PackageParserException(
+ PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED, errorMsg[0]);
+ }
+
+ targetSdkVersion = versions.second;
}
}
+ final boolean extractNativeLibsDefault = targetSdkVersion < Build.VERSION_CODES.Q;
+ final boolean extractNativeLibs = (extractNativeLibsProvided != null)
+ ? extractNativeLibsProvided : extractNativeLibsDefault;
+
if (preferCodeIntegrity && extractNativeLibs) {
throw new PackageParserException(
PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
@@ -2215,65 +2259,60 @@
} else if (tagName.equals(TAG_USES_SDK)) {
if (SDK_VERSION > 0) {
- sa = res.obtainAttributes(parser,
- com.android.internal.R.styleable.AndroidManifestUsesSdk);
+ sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesSdk);
+ final TypedArray saFinal = sa;
+ Pair<Integer, Integer> versions = deriveSdkVersions(
+ new AbstractVersionsAccessor() {
+ @Override public String getMinSdkVersionCode() {
+ return getAttributeAsString(
+ R.styleable.AndroidManifestUsesSdk_minSdkVersion);
+ }
- int minVers = 1;
- String minCode = null;
- int targetVers = 0;
- String targetCode = null;
+ @Override public int getMinSdkVersion() {
+ return getAttributeAsInt(
+ R.styleable.AndroidManifestUsesSdk_minSdkVersion);
+ }
- TypedValue val = sa.peekValue(
- com.android.internal.R.styleable.AndroidManifestUsesSdk_minSdkVersion);
- if (val != null) {
- if (val.type == TypedValue.TYPE_STRING && val.string != null) {
- minCode = val.string.toString();
- } else {
- // If it's not a string, it's an integer.
- minVers = val.data;
- }
+ @Override public String getTargetSdkVersionCode() {
+ return getAttributeAsString(
+ R.styleable.AndroidManifestUsesSdk_targetSdkVersion);
+ }
+
+ @Override public int getTargetSdkVersion() {
+ return getAttributeAsInt(
+ R.styleable.AndroidManifestUsesSdk_targetSdkVersion);
+ }
+
+ private String getAttributeAsString(int index) {
+ TypedValue val = saFinal.peekValue(index);
+ if (val != null && val.type == TypedValue.TYPE_STRING
+ && val.string != null) {
+ return val.string.toString();
+ }
+ return null;
+ }
+
+ private int getAttributeAsInt(int index) {
+ TypedValue val = saFinal.peekValue(index);
+ if (val != null && val.type != TypedValue.TYPE_STRING) {
+ // If it's not a string, it's an integer.
+ return val.data;
+ }
+ return -1;
+ }
+ }, flags, outError);
+
+ if (versions == null) {
+ mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;
+ return null;
}
- val = sa.peekValue(
- com.android.internal.R.styleable.AndroidManifestUsesSdk_targetSdkVersion);
- if (val != null) {
- if (val.type == TypedValue.TYPE_STRING && val.string != null) {
- targetCode = val.string.toString();
- if (minCode == null) {
- minCode = targetCode;
- }
- } else {
- // If it's not a string, it's an integer.
- targetVers = val.data;
- }
- } else {
- targetVers = minVers;
- targetCode = minCode;
- }
+ pkg.applicationInfo.minSdkVersion = versions.first;
+ pkg.applicationInfo.targetSdkVersion = versions.second;
sa.recycle();
-
- final int minSdkVersion = PackageParser.computeMinSdkVersion(minVers, minCode,
- SDK_VERSION, SDK_CODENAMES, outError);
- if (minSdkVersion < 0) {
- mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;
- return null;
- }
-
- boolean defaultToCurrentDevBranch = (flags & PARSE_FORCE_SDK) != 0;
- final int targetSdkVersion = PackageParser.computeTargetSdkVersion(targetVers,
- targetCode, SDK_CODENAMES, outError, defaultToCurrentDevBranch);
- if (targetSdkVersion < 0) {
- mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;
- return null;
- }
-
- pkg.applicationInfo.minSdkVersion = minSdkVersion;
- pkg.applicationInfo.targetSdkVersion = targetSdkVersion;
}
-
XmlUtils.skipCurrentTag(parser);
-
} else if (tagName.equals(TAG_SUPPORT_SCREENS)) {
sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestSupportsScreens);
@@ -2675,6 +2714,67 @@
return -1;
}
+ private interface AbstractVersionsAccessor {
+ /** Returns minimum SDK version code string, or null if absent. */
+ String getMinSdkVersionCode();
+
+ /** Returns minimum SDK version code, or -1 if absent. */
+ int getMinSdkVersion();
+
+ /** Returns target SDK version code string, or null if absent. */
+ String getTargetSdkVersionCode();
+
+ /** Returns target SDK version code, or -1 if absent. */
+ int getTargetSdkVersion();
+ }
+
+ private static @Nullable Pair<Integer, Integer> deriveSdkVersions(
+ @NonNull AbstractVersionsAccessor accessor, int flags, String[] outError) {
+ int minVers = 1;
+ String minCode = null;
+ int targetVers = 0;
+ String targetCode = null;
+
+ String code = accessor.getMinSdkVersionCode();
+ int version = accessor.getMinSdkVersion();
+ // Check integer first since code is almost never a null string (e.g. "28").
+ if (version >= 0) {
+ minVers = version;
+ } else if (code != null) {
+ minCode = code;
+ }
+
+ code = accessor.getTargetSdkVersionCode();
+ version = accessor.getTargetSdkVersion();
+ // Check integer first since code is almost never a null string (e.g. "28").
+ if (version >= 0) {
+ targetVers = version;
+ } else if (code != null) {
+ targetCode = code;
+ if (minCode == null) {
+ minCode = targetCode;
+ }
+ } else {
+ targetVers = minVers;
+ targetCode = minCode;
+ }
+
+ final int minSdkVersion = computeMinSdkVersion(minVers, minCode,
+ SDK_VERSION, SDK_CODENAMES, outError);
+ if (minSdkVersion < 0) {
+ return null;
+ }
+
+ boolean defaultToCurrentDevBranch = (flags & PARSE_FORCE_SDK) != 0;
+ final int targetSdkVersion = computeTargetSdkVersion(targetVers,
+ targetCode, SDK_CODENAMES, outError, defaultToCurrentDevBranch);
+ if (targetSdkVersion < 0) {
+ return null;
+ }
+
+ return Pair.create(minSdkVersion, targetSdkVersion);
+ }
+
/**
* Computes the minSdkVersion to use at runtime. If the package is not
* compatible with this platform, populates {@code outError[0]} with an
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 799f8e5..536a1b7 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -794,6 +794,7 @@
* {@link ActivityInfo#CONFIG_ASSETS_PATHS}.
* @hide
*/
+ @TestApi
public int assetsSeq;
/**
diff --git a/core/java/android/hardware/display/BrightnessConfiguration.java b/core/java/android/hardware/display/BrightnessConfiguration.java
index 7e52ca3..be054297 100644
--- a/core/java/android/hardware/display/BrightnessConfiguration.java
+++ b/core/java/android/hardware/display/BrightnessConfiguration.java
@@ -19,26 +19,54 @@
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
+import android.content.pm.ApplicationInfo;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Pair;
import com.android.internal.util.Preconditions;
+import com.android.internal.util.XmlUtils;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
import java.util.Objects;
/** @hide */
@SystemApi
@TestApi
public final class BrightnessConfiguration implements Parcelable {
+ private static final String TAG_BRIGHTNESS_CURVE = "brightness-curve";
+ private static final String TAG_BRIGHTNESS_POINT = "brightness-point";
+ private static final String TAG_BRIGHTNESS_CORRECTIONS = "brightness-corrections";
+ private static final String TAG_BRIGHTNESS_CORRECTION = "brightness-correction";
+ private static final String ATTR_LUX = "lux";
+ private static final String ATTR_NITS = "nits";
+ private static final String ATTR_DESCRIPTION = "description";
+ private static final String ATTR_PACKAGE_NAME = "package-name";
+ private static final String ATTR_CATEGORY = "category";
+
private final float[] mLux;
private final float[] mNits;
+ private final Map<String, BrightnessCorrection> mCorrectionsByPackageName;
+ private final Map<Integer, BrightnessCorrection> mCorrectionsByCategory;
private final String mDescription;
- private BrightnessConfiguration(float[] lux, float[] nits, String description) {
+ private BrightnessConfiguration(float[] lux, float[] nits,
+ Map<String, BrightnessCorrection> correctionsByPackageName,
+ Map<Integer, BrightnessCorrection> correctionsByCategory, String description) {
mLux = lux;
mNits = nits;
+ mCorrectionsByPackageName = correctionsByPackageName;
+ mCorrectionsByCategory = correctionsByCategory;
mDescription = description;
}
@@ -56,6 +84,38 @@
}
/**
+ * Returns a brightness correction by app, or null.
+ *
+ * @param packageName
+ * The app's package name.
+ *
+ * @return The matching brightness correction, or null.
+ *
+ * @hide
+ */
+ @SystemApi
+ @Nullable
+ public BrightnessCorrection getCorrectionByPackageName(String packageName) {
+ return mCorrectionsByPackageName.get(packageName);
+ }
+
+ /**
+ * Returns a brightness correction by app category, or null.
+ *
+ * @param category
+ * The app category.
+ *
+ * @return The matching brightness correction, or null.
+ *
+ * @hide
+ */
+ @SystemApi
+ @Nullable
+ public BrightnessCorrection getCorrectionByCategory(int category) {
+ return mCorrectionsByCategory.get(category);
+ }
+
+ /**
* Returns description string.
* @hide
*/
@@ -67,6 +127,20 @@
public void writeToParcel(Parcel dest, int flags) {
dest.writeFloatArray(mLux);
dest.writeFloatArray(mNits);
+ dest.writeInt(mCorrectionsByPackageName.size());
+ for (Entry<String, BrightnessCorrection> entry : mCorrectionsByPackageName.entrySet()) {
+ final String packageName = entry.getKey();
+ final BrightnessCorrection correction = entry.getValue();
+ dest.writeString(packageName);
+ correction.writeToParcel(dest, flags);
+ }
+ dest.writeInt(mCorrectionsByCategory.size());
+ for (Entry<Integer, BrightnessCorrection> entry : mCorrectionsByCategory.entrySet()) {
+ final int category = entry.getKey();
+ final BrightnessCorrection correction = entry.getValue();
+ dest.writeInt(category);
+ correction.writeToParcel(dest, flags);
+ }
dest.writeString(mDescription);
}
@@ -85,7 +159,14 @@
}
sb.append("(").append(mLux[i]).append(", ").append(mNits[i]).append(")");
}
- sb.append("], '");
+ sb.append("], {");
+ for (Entry<String, BrightnessCorrection> entry : mCorrectionsByPackageName.entrySet()) {
+ sb.append("'" + entry.getKey() + "': " + entry.getValue() + ", ");
+ }
+ for (Entry<Integer, BrightnessCorrection> entry : mCorrectionsByCategory.entrySet()) {
+ sb.append(entry.getKey() + ": " + entry.getValue() + ", ");
+ }
+ sb.append("}, '");
if (mDescription != null) {
sb.append(mDescription);
}
@@ -98,6 +179,8 @@
int result = 1;
result = result * 31 + Arrays.hashCode(mLux);
result = result * 31 + Arrays.hashCode(mNits);
+ result = result * 31 + mCorrectionsByPackageName.hashCode();
+ result = result * 31 + mCorrectionsByCategory.hashCode();
if (mDescription != null) {
result = result * 31 + mDescription.hashCode();
}
@@ -114,6 +197,8 @@
}
final BrightnessConfiguration other = (BrightnessConfiguration) o;
return Arrays.equals(mLux, other.mLux) && Arrays.equals(mNits, other.mNits)
+ && mCorrectionsByPackageName.equals(other.mCorrectionsByPackageName)
+ && mCorrectionsByCategory.equals(other.mCorrectionsByCategory)
&& Objects.equals(mDescription, other.mDescription);
}
@@ -123,7 +208,25 @@
float[] lux = in.createFloatArray();
float[] nits = in.createFloatArray();
Builder builder = new Builder(lux, nits);
- builder.setDescription(in.readString());
+
+ int n = in.readInt();
+ for (int i = 0; i < n; i++) {
+ final String packageName = in.readString();
+ final BrightnessCorrection correction =
+ BrightnessCorrection.CREATOR.createFromParcel(in);
+ builder.addCorrectionByPackageName(packageName, correction);
+ }
+
+ n = in.readInt();
+ for (int i = 0; i < n; i++) {
+ final int category = in.readInt();
+ final BrightnessCorrection correction =
+ BrightnessCorrection.CREATOR.createFromParcel(in);
+ builder.addCorrectionByCategory(category, correction);
+ }
+
+ final String description = in.readString();
+ builder.setDescription(description);
return builder.build();
}
@@ -133,11 +236,146 @@
};
/**
+ * Writes the configuration to an XML serializer.
+ *
+ * @param serializer
+ * The XML serializer.
+ *
+ * @hide
+ */
+ public void saveToXml(XmlSerializer serializer) throws IOException {
+ serializer.startTag(null, TAG_BRIGHTNESS_CURVE);
+ if (mDescription != null) {
+ serializer.attribute(null, ATTR_DESCRIPTION, mDescription);
+ }
+ for (int i = 0; i < mLux.length; i++) {
+ serializer.startTag(null, TAG_BRIGHTNESS_POINT);
+ serializer.attribute(null, ATTR_LUX, Float.toString(mLux[i]));
+ serializer.attribute(null, ATTR_NITS, Float.toString(mNits[i]));
+ serializer.endTag(null, TAG_BRIGHTNESS_POINT);
+ }
+ serializer.endTag(null, TAG_BRIGHTNESS_CURVE);
+ serializer.startTag(null, TAG_BRIGHTNESS_CORRECTIONS);
+ for (Map.Entry<String, BrightnessCorrection> entry :
+ mCorrectionsByPackageName.entrySet()) {
+ final String packageName = entry.getKey();
+ final BrightnessCorrection correction = entry.getValue();
+ serializer.startTag(null, TAG_BRIGHTNESS_CORRECTION);
+ serializer.attribute(null, ATTR_PACKAGE_NAME, packageName);
+ correction.saveToXml(serializer);
+ serializer.endTag(null, TAG_BRIGHTNESS_CORRECTION);
+ }
+ for (Map.Entry<Integer, BrightnessCorrection> entry : mCorrectionsByCategory.entrySet()) {
+ final int category = entry.getKey();
+ final BrightnessCorrection correction = entry.getValue();
+ serializer.startTag(null, TAG_BRIGHTNESS_CORRECTION);
+ serializer.attribute(null, ATTR_CATEGORY, Integer.toString(category));
+ correction.saveToXml(serializer);
+ serializer.endTag(null, TAG_BRIGHTNESS_CORRECTION);
+ }
+ serializer.endTag(null, TAG_BRIGHTNESS_CORRECTIONS);
+ }
+
+ /**
+ * Read a configuration from an XML parser.
+ *
+ * @param parser
+ * The XML parser.
+ *
+ * @throws IOException
+ * The parser failed to read the XML file.
+ * @throws XmlPullParserException
+ * The parser failed to parse the XML file.
+ *
+ * @hide
+ */
+ public static BrightnessConfiguration loadFromXml(XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ String description = null;
+ List<Float> luxList = new ArrayList<>();
+ List<Float> nitsList = new ArrayList<>();
+ Map<String, BrightnessCorrection> correctionsByPackageName = new HashMap<>();
+ Map<Integer, BrightnessCorrection> correctionsByCategory = new HashMap<>();
+ final int configDepth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, configDepth)) {
+ if (TAG_BRIGHTNESS_CURVE.equals(parser.getName())) {
+ description = parser.getAttributeValue(null, ATTR_DESCRIPTION);
+ final int curveDepth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, curveDepth)) {
+ if (!TAG_BRIGHTNESS_POINT.equals(parser.getName())) {
+ continue;
+ }
+ final float lux = loadFloatFromXml(parser, ATTR_LUX);
+ final float nits = loadFloatFromXml(parser, ATTR_NITS);
+ luxList.add(lux);
+ nitsList.add(nits);
+ }
+ }
+ if (TAG_BRIGHTNESS_CORRECTIONS.equals(parser.getName())) {
+ final int correctionsDepth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, correctionsDepth)) {
+ if (!TAG_BRIGHTNESS_CORRECTION.equals(parser.getName())) {
+ continue;
+ }
+ final String packageName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME);
+ final String categoryText = parser.getAttributeValue(null, ATTR_CATEGORY);
+ BrightnessCorrection correction = BrightnessCorrection.loadFromXml(parser);
+ if (packageName != null) {
+ correctionsByPackageName.put(packageName, correction);
+ } else if (categoryText != null) {
+ try {
+ final int category = Integer.parseInt(categoryText);
+ correctionsByCategory.put(category, correction);
+ } catch (NullPointerException | NumberFormatException e) {
+ continue;
+ }
+ }
+ }
+ }
+ }
+ final int n = luxList.size();
+ float[] lux = new float[n];
+ float[] nits = new float[n];
+ for (int i = 0; i < n; i++) {
+ lux[i] = luxList.get(i);
+ nits[i] = nitsList.get(i);
+ }
+ final BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder(lux,
+ nits);
+ builder.setDescription(description);
+ for (Map.Entry<String, BrightnessCorrection> entry : correctionsByPackageName.entrySet()) {
+ final String packageName = entry.getKey();
+ final BrightnessCorrection correction = entry.getValue();
+ builder.addCorrectionByPackageName(packageName, correction);
+ }
+ for (Map.Entry<Integer, BrightnessCorrection> entry : correctionsByCategory.entrySet()) {
+ final int category = entry.getKey();
+ final BrightnessCorrection correction = entry.getValue();
+ builder.addCorrectionByCategory(category, correction);
+ }
+ return builder.build();
+ }
+
+ private static float loadFloatFromXml(XmlPullParser parser, String attribute) {
+ final String string = parser.getAttributeValue(null, attribute);
+ try {
+ return Float.parseFloat(string);
+ } catch (NullPointerException | NumberFormatException e) {
+ return Float.NaN;
+ }
+ }
+
+ /**
* A builder class for {@link BrightnessConfiguration}s.
*/
public static class Builder {
+ private static final int MAX_CORRECTIONS_BY_PACKAGE_NAME = 20;
+ private static final int MAX_CORRECTIONS_BY_CATEGORY = 20;
+
private float[] mCurveLux;
private float[] mCurveNits;
+ private Map<String, BrightnessCorrection> mCorrectionsByPackageName;
+ private Map<Integer, BrightnessCorrection> mCorrectionsByCategory;
private String mDescription;
/**
@@ -169,6 +407,88 @@
checkMonotonic(nits, false /*strictly increasing*/, "nits");
mCurveLux = lux;
mCurveNits = nits;
+ mCorrectionsByPackageName = new HashMap<>();
+ mCorrectionsByCategory = new HashMap<>();
+ }
+
+ /**
+ * Returns the maximum number of corrections by package name allowed.
+ *
+ * @return The maximum number of corrections by package name allowed.
+ *
+ * @hide
+ */
+ @SystemApi
+ public int getMaxCorrectionsByPackageName() {
+ return MAX_CORRECTIONS_BY_PACKAGE_NAME;
+ }
+
+ /**
+ * Returns the maximum number of corrections by category allowed.
+ *
+ * @return The maximum number of corrections by category allowed.
+ *
+ * @hide
+ */
+ @SystemApi
+ public int getMaxCorrectionsByCategory() {
+ return MAX_CORRECTIONS_BY_CATEGORY;
+ }
+
+ /**
+ * Add a brightness correction by app package name.
+ * This correction is applied whenever an app with this package name has the top activity
+ * of the focused stack.
+ *
+ * @param packageName
+ * The app's package name.
+ * @param correction
+ * The brightness correction.
+ *
+ * @return The builder.
+ *
+ * @throws IllegalArgumentExceptions
+ * Maximum number of corrections by package name exceeded (see
+ * {@link #getMaxCorrectionsByPackageName}).
+ *
+ * @hide
+ */
+ @SystemApi
+ public Builder addCorrectionByPackageName(String packageName,
+ BrightnessCorrection correction) {
+ if (mCorrectionsByPackageName.size() >= getMaxCorrectionsByPackageName()) {
+ throw new IllegalArgumentException("Too many corrections by package name");
+ }
+ mCorrectionsByPackageName.put(packageName, correction);
+ return this;
+ }
+
+ /**
+ * Add a brightness correction by app category.
+ * This correction is applied whenever an app with this category has the top activity of
+ * the focused stack, and only if a correction by package name has not been applied.
+ *
+ * @param category
+ * The {@link android.content.pm.ApplicationInfo#category app category}.
+ * @param correction
+ * The brightness correction.
+ *
+ * @return The builder.
+ *
+ * @throws IllegalArgumentException
+ * Maximum number of corrections by category exceeded (see
+ * {@link #getMaxCorrectionsByCategory}).
+ *
+ * @hide
+ */
+ @SystemApi
+ public Builder addCorrectionByCategory(@ApplicationInfo.Category int category,
+ BrightnessCorrection correction) {
+ if (mCorrectionsByCategory.size() >= getMaxCorrectionsByCategory()) {
+ throw new IllegalArgumentException("Too many corrections by category");
+ }
+ mCorrectionsByCategory.put(category, correction);
+ return this;
}
/**
@@ -191,7 +511,8 @@
if (mCurveLux == null || mCurveNits == null) {
throw new IllegalStateException("A curve must be set!");
}
- return new BrightnessConfiguration(mCurveLux, mCurveNits, mDescription);
+ return new BrightnessConfiguration(mCurveLux, mCurveNits, mCorrectionsByPackageName,
+ mCorrectionsByCategory, mDescription);
}
private static void checkMonotonic(float[] vals, boolean strictlyIncreasing, String name) {
diff --git a/core/java/android/hardware/display/BrightnessCorrection.aidl b/core/java/android/hardware/display/BrightnessCorrection.aidl
new file mode 100644
index 0000000..3abe29c
--- /dev/null
+++ b/core/java/android/hardware/display/BrightnessCorrection.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2018 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.hardware.display;
+
+parcelable BrightnessCorrection;
diff --git a/core/java/android/hardware/display/BrightnessCorrection.java b/core/java/android/hardware/display/BrightnessCorrection.java
new file mode 100644
index 0000000..c4e0e3b
--- /dev/null
+++ b/core/java/android/hardware/display/BrightnessCorrection.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2018 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.hardware.display;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.MathUtils;
+
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+
+/**
+ * BrightnessCorrection encapsulates a correction to the brightness, without comitting to the
+ * actual correction scheme.
+ * It is used by the BrightnessConfiguration, which maps context (e.g. the foreground app's package
+ * name and category) to corrections that need to be applied to the brightness within that context.
+ * Corrections are currently done by the app that has the top activity of the focused stack, either
+ * by its package name, or (if its package name is not mapped to any correction) by its category.
+ *
+ * @hide
+ */
+@SystemApi
+public final class BrightnessCorrection implements Parcelable {
+
+ private static final int SCALE_AND_TRANSLATE_LOG = 1;
+
+ private static final String TAG_SCALE_AND_TRANSLATE_LOG = "scale-and-translate-log";
+
+ private BrightnessCorrectionImplementation mImplementation;
+
+ // Parcelable classes must be final, and protected methods are not allowed in APIs, so we can't
+ // make this class abstract and use composition instead of inheritence.
+ private BrightnessCorrection(BrightnessCorrectionImplementation implementation) {
+ mImplementation = implementation;
+ }
+
+ /**
+ * Creates a BrightnessCorrection that given {@code brightness}, corrects it to be
+ * {@code exp(scale * log(brightness) + translate)}.
+ *
+ * @param scale
+ * How much to scale the log brightness.
+ * @param translate
+ * How much to translate the log brightness.
+ *
+ * @return A BrightnessCorrection that given {@code brightness}, corrects it to be
+ * {@code exp(scale * log(brightness) + translate)}.
+ *
+ * @throws IllegalArgumentException
+ * - scale or translate are NaN.
+ */
+ @NonNull
+ public static BrightnessCorrection createScaleAndTranslateLog(float scale, float translate) {
+ BrightnessCorrectionImplementation implementation =
+ new ScaleAndTranslateLog(scale, translate);
+ return new BrightnessCorrection(implementation);
+ }
+
+ /**
+ * Applies the brightness correction to a given brightness.
+ *
+ * @param brightness
+ * The brightness.
+ *
+ * @return The corrected brightness.
+ */
+ public float apply(float brightness) {
+ return mImplementation.apply(brightness);
+ }
+
+ /**
+ * Returns a string representation.
+ *
+ * @return A string representation.
+ */
+ public String toString() {
+ return mImplementation.toString();
+ }
+
+ public static final Creator<BrightnessCorrection> CREATOR =
+ new Creator<BrightnessCorrection>() {
+ public BrightnessCorrection createFromParcel(Parcel in) {
+ final int type = in.readInt();
+ switch (type) {
+ case SCALE_AND_TRANSLATE_LOG:
+ return ScaleAndTranslateLog.readFromParcel(in);
+ }
+ return null;
+ }
+
+ public BrightnessCorrection[] newArray(int size) {
+ return new BrightnessCorrection[size];
+ }
+ };
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ mImplementation.writeToParcel(dest);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Writes the correction to an XML serializer.
+ *
+ * @param serializer
+ * The XML serializer.
+ *
+ * @hide
+ */
+ public void saveToXml(XmlSerializer serializer) throws IOException {
+ mImplementation.saveToXml(serializer);
+ }
+
+ /**
+ * Read a correction from an XML parser.
+ *
+ * @param parser
+ * The XML parser.
+ *
+ * @throws IOException
+ * The parser failed to read the XML file.
+ * @throws XmlPullParserException
+ * The parser failed to parse the XML file.
+ *
+ * @hide
+ */
+ public static BrightnessCorrection loadFromXml(XmlPullParser parser) throws IOException,
+ XmlPullParserException {
+ final int depth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, depth)) {
+ if (TAG_SCALE_AND_TRANSLATE_LOG.equals(parser.getName())) {
+ return ScaleAndTranslateLog.loadFromXml(parser);
+ }
+ }
+ return null;
+ }
+
+ private static float loadFloatFromXml(XmlPullParser parser, String attribute) {
+ final String string = parser.getAttributeValue(null, attribute);
+ try {
+ return Float.parseFloat(string);
+ } catch (NullPointerException | NumberFormatException e) {
+ return Float.NaN;
+ }
+ }
+
+ private interface BrightnessCorrectionImplementation {
+ float apply(float brightness);
+ String toString();
+ void writeToParcel(Parcel dest);
+ void saveToXml(XmlSerializer serializer) throws IOException;
+ // Package-private static methods:
+ // static BrightnessCorrection readFromParcel(Parcel in);
+ // static BrightnessCorrection loadFromXml(XmlPullParser parser) throws IOException,
+ // XmlPullParserException;
+ }
+
+ /**
+ * A BrightnessCorrection that given {@code brightness}, corrects it to be
+ * {@code exp(scale * log(brightness) + translate)}.
+ */
+ private static class ScaleAndTranslateLog implements BrightnessCorrectionImplementation {
+ private static final float MIN_SCALE = 0.5f;
+ private static final float MAX_SCALE = 2.0f;
+ private static final float MIN_TRANSLATE = -0.6f;
+ private static final float MAX_TRANSLATE = 0.7f;
+
+ private static final String ATTR_SCALE = "scale";
+ private static final String ATTR_TRANSLATE = "translate";
+
+ private final float mScale;
+ private final float mTranslate;
+
+ ScaleAndTranslateLog(float scale, float translate) {
+ if (Float.isNaN(scale) || Float.isNaN(translate)) {
+ throw new IllegalArgumentException("scale and translate must be numbers");
+ }
+ mScale = MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE);
+ mTranslate = MathUtils.constrain(translate, MIN_TRANSLATE, MAX_TRANSLATE);
+ }
+
+ @Override
+ public float apply(float brightness) {
+ return MathUtils.exp(mScale * MathUtils.log(brightness) + mTranslate);
+ }
+
+ @Override
+ public String toString() {
+ return "ScaleAndTranslateLog(" + mScale + ", " + mTranslate + ")";
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest) {
+ dest.writeInt(SCALE_AND_TRANSLATE_LOG);
+ dest.writeFloat(mScale);
+ dest.writeFloat(mTranslate);
+ }
+
+ @Override
+ public void saveToXml(XmlSerializer serializer) throws IOException {
+ serializer.startTag(null, TAG_SCALE_AND_TRANSLATE_LOG);
+ serializer.attribute(null, ATTR_SCALE, Float.toString(mScale));
+ serializer.attribute(null, ATTR_TRANSLATE, Float.toString(mTranslate));
+ serializer.endTag(null, TAG_SCALE_AND_TRANSLATE_LOG);
+ }
+
+ static BrightnessCorrection readFromParcel(Parcel in) {
+ float scale = in.readFloat();
+ float translate = in.readFloat();
+ return BrightnessCorrection.createScaleAndTranslateLog(scale, translate);
+ }
+
+ static BrightnessCorrection loadFromXml(XmlPullParser parser) throws IOException,
+ XmlPullParserException {
+ final float scale = loadFloatFromXml(parser, ATTR_SCALE);
+ final float translate = loadFloatFromXml(parser, ATTR_TRANSLATE);
+ return BrightnessCorrection.createScaleAndTranslateLog(scale, translate);
+ }
+ }
+}
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 23e4ec0..436b4a1 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -2485,6 +2485,8 @@
public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10;
/** {@hide} */
public static final int TETHER_ERROR_PROVISION_FAILED = 11;
+ /** {@hide} */
+ public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12;
/**
* Get a more detailed error code after a Tethering or Untethering
diff --git a/core/java/android/net/INetworkStackConnector.aidl b/core/java/android/net/INetworkStackConnector.aidl
index 29f8828..be0dc07 100644
--- a/core/java/android/net/INetworkStackConnector.aidl
+++ b/core/java/android/net/INetworkStackConnector.aidl
@@ -15,7 +15,11 @@
*/
package android.net;
+import android.net.dhcp.DhcpServingParamsParcel;
+import android.net.dhcp.IDhcpServerCallbacks;
+
/** @hide */
oneway interface INetworkStackConnector {
- // TODO: requestDhcpServer(), etc. will go here
+ void makeDhcpServer(in String ifName, in DhcpServingParamsParcel params,
+ in IDhcpServerCallbacks cb);
}
\ No newline at end of file
diff --git a/core/java/android/net/INetworkStackStatusCallback.aidl b/core/java/android/net/INetworkStackStatusCallback.aidl
new file mode 100644
index 0000000..51032d8
--- /dev/null
+++ b/core/java/android/net/INetworkStackStatusCallback.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2018 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;
+
+/** @hide */
+oneway interface INetworkStackStatusCallback {
+ void onStatusAvailable(int statusCode);
+}
\ No newline at end of file
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 3a79206..4a466f3 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -55,6 +55,8 @@
private String mIfaceName;
private ArrayList<LinkAddress> mLinkAddresses = new ArrayList<>();
private ArrayList<InetAddress> mDnses = new ArrayList<>();
+ // PCSCF addresses are addresses of SIP proxies that only exist for the IMS core service.
+ private ArrayList<InetAddress> mPcscfs = new ArrayList<InetAddress>();
private ArrayList<InetAddress> mValidatedPrivateDnses = new ArrayList<>();
private boolean mUsePrivateDns;
private String mPrivateDnsServerName;
@@ -179,6 +181,7 @@
mValidatedPrivateDnses.addAll(source.mValidatedPrivateDnses);
mUsePrivateDns = source.mUsePrivateDns;
mPrivateDnsServerName = source.mPrivateDnsServerName;
+ mPcscfs.addAll(source.mPcscfs);
mDomains = source.mDomains;
mRoutes.addAll(source.mRoutes);
mHttpProxy = (source.mHttpProxy == null) ? null : new ProxyInfo(source.mHttpProxy);
@@ -525,6 +528,60 @@
}
/**
+ * Adds the given {@link InetAddress} to the list of PCSCF servers, if not present.
+ *
+ * @param pcscfServer The {@link InetAddress} to add to the list of PCSCF servers.
+ * @return true if the PCSCF server was added, false otherwise.
+ * @hide
+ */
+ public boolean addPcscfServer(InetAddress pcscfServer) {
+ if (pcscfServer != null && !mPcscfs.contains(pcscfServer)) {
+ mPcscfs.add(pcscfServer);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Removes the given {@link InetAddress} from the list of PCSCF servers.
+ *
+ * @param pcscf Server The {@link InetAddress} to remove from the list of PCSCF servers.
+ * @return true if the PCSCF server was removed, false otherwise.
+ * @hide
+ */
+ public boolean removePcscfServer(InetAddress pcscfServer) {
+ if (pcscfServer != null) {
+ return mPcscfs.remove(pcscfServer);
+ }
+ return false;
+ }
+
+ /**
+ * Replaces the PCSCF servers in this {@code LinkProperties} with
+ * the given {@link Collection} of {@link InetAddress} objects.
+ *
+ * @param addresses The {@link Collection} of PCSCF servers to set in this object.
+ * @hide
+ */
+ public void setPcscfServers(Collection<InetAddress> pcscfServers) {
+ mPcscfs.clear();
+ for (InetAddress pcscfServer: pcscfServers) {
+ addPcscfServer(pcscfServer);
+ }
+ }
+
+ /**
+ * Returns all the {@link InetAddress} for PCSCF servers on this link.
+ *
+ * @return An unmodifiable {@link List} of {@link InetAddress} for PCSCF servers on
+ * this link.
+ * @hide
+ */
+ public List<InetAddress> getPcscfServers() {
+ return Collections.unmodifiableList(mPcscfs);
+ }
+
+ /**
* Sets the DNS domain search path used on this link.
*
* @param domains A {@link String} listing in priority order the comma separated
@@ -767,6 +824,7 @@
mDnses.clear();
mUsePrivateDns = false;
mPrivateDnsServerName = null;
+ mPcscfs.clear();
mDomains = null;
mRoutes.clear();
mHttpProxy = null;
@@ -813,6 +871,12 @@
resultJoiner.add(mPrivateDnsServerName);
}
+ if (!mPcscfs.isEmpty()) {
+ resultJoiner.add("PcscfAddresses: [");
+ resultJoiner.add(TextUtils.join(",", mPcscfs));
+ resultJoiner.add("]");
+ }
+
if (!mValidatedPrivateDnses.isEmpty()) {
final StringJoiner validatedPrivateDnsesJoiner =
new StringJoiner(",", "ValidatedPrivateDnsAddresses: [", "]");
@@ -965,6 +1029,36 @@
}
/**
+ * Returns true if this link has an IPv4 PCSCF server.
+ *
+ * @return {@code true} if there is an IPv4 PCSCF server, {@code false} otherwise.
+ * @hide
+ */
+ public boolean hasIPv4PcscfServer() {
+ for (InetAddress ia : mPcscfs) {
+ if (ia instanceof Inet4Address) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if this link has an IPv6 PCSCF server.
+ *
+ * @return {@code true} if there is an IPv6 PCSCF server, {@code false} otherwise.
+ * @hide
+ */
+ public boolean hasIPv6PcscfServer() {
+ for (InetAddress ia : mPcscfs) {
+ if (ia instanceof Inet6Address) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
* Returns true if this link is provisioned for global IPv4 connectivity.
* This requires an IP address, default route, and DNS server.
*
@@ -1117,6 +1211,19 @@
}
/**
+ * Compares this {@code LinkProperties} PCSCF addresses against the target
+ *
+ * @param target LinkProperties to compare.
+ * @return {@code true} if both are identical, {@code false} otherwise.
+ * @hide
+ */
+ public boolean isIdenticalPcscfs(LinkProperties target) {
+ Collection<InetAddress> targetPcscfs = target.getPcscfServers();
+ return (mPcscfs.size() == targetPcscfs.size()) ?
+ mPcscfs.containsAll(targetPcscfs) : false;
+ }
+
+ /**
* Compares this {@code LinkProperties} Routes against the target
*
* @param target LinkProperties to compare.
@@ -1218,6 +1325,7 @@
&& isIdenticalDnses(target)
&& isIdenticalPrivateDns(target)
&& isIdenticalValidatedPrivateDnses(target)
+ && isIdenticalPcscfs(target)
&& isIdenticalRoutes(target)
&& isIdenticalHttpProxy(target)
&& isIdenticalStackedLinks(target)
@@ -1334,6 +1442,7 @@
+ mMtu * 51
+ ((null == mTcpBufferSizes) ? 0 : mTcpBufferSizes.hashCode())
+ (mUsePrivateDns ? 57 : 0)
+ + mPcscfs.size() * 67
+ ((null == mPrivateDnsServerName) ? 0 : mPrivateDnsServerName.hashCode());
}
@@ -1357,6 +1466,10 @@
}
dest.writeBoolean(mUsePrivateDns);
dest.writeString(mPrivateDnsServerName);
+ dest.writeInt(mPcscfs.size());
+ for (InetAddress d : mPcscfs) {
+ dest.writeByteArray(d.getAddress());
+ }
dest.writeString(mDomains);
dest.writeInt(mMtu);
dest.writeString(mTcpBufferSizes);
@@ -1406,6 +1519,12 @@
}
netProp.setUsePrivateDns(in.readBoolean());
netProp.setPrivateDnsServerName(in.readString());
+ addressCount = in.readInt();
+ for (int i = 0; i < addressCount; i++) {
+ try {
+ netProp.addPcscfServer(InetAddress.getByAddress(in.createByteArray()));
+ } catch (UnknownHostException e) { }
+ }
netProp.setDomains(in.readString());
netProp.setMtu(in.readInt());
netProp.setTcpBufferSizes(in.readString());
diff --git a/core/java/android/net/NetworkStack.java b/core/java/android/net/NetworkStack.java
index 82a4e31..d4a0ec63 100644
--- a/core/java/android/net/NetworkStack.java
+++ b/core/java/android/net/NetworkStack.java
@@ -25,9 +25,12 @@
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
+import android.net.dhcp.DhcpServingParamsParcel;
+import android.net.dhcp.IDhcpServerCallbacks;
import android.os.Binder;
import android.os.IBinder;
import android.os.Process;
+import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.util.Slog;
@@ -58,6 +61,22 @@
public NetworkStack() { }
+ /**
+ * Create a DHCP server according to the specified parameters.
+ *
+ * <p>The server will be returned asynchronously through the provided callbacks.
+ */
+ public void makeDhcpServer(final String ifName, final DhcpServingParamsParcel params,
+ final IDhcpServerCallbacks cb) {
+ requestConnector(connector -> {
+ try {
+ connector.makeDhcpServer(ifName, params, cb);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ });
+ }
+
private class NetworkStackConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
diff --git a/core/java/android/net/dhcp/DhcpServerCallbacks.java b/core/java/android/net/dhcp/DhcpServerCallbacks.java
new file mode 100644
index 0000000..bb56876
--- /dev/null
+++ b/core/java/android/net/dhcp/DhcpServerCallbacks.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2018 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.dhcp;
+
+/**
+ * Convenience wrapper around IDhcpServerCallbacks.Stub that implements getInterfaceVersion().
+ * @hide
+ */
+public abstract class DhcpServerCallbacks extends IDhcpServerCallbacks.Stub {
+ // TODO: add @Override here once the API is versioned
+
+ /**
+ * Get the version of the aidl interface implemented by the callbacks.
+ */
+ public int getInterfaceVersion() {
+ // TODO: return IDhcpServerCallbacks.VERSION;
+ return 0;
+ }
+}
diff --git a/core/java/android/net/dhcp/IDhcpServer.aidl b/core/java/android/net/dhcp/IDhcpServer.aidl
new file mode 100644
index 0000000..559433b
--- /dev/null
+++ b/core/java/android/net/dhcp/IDhcpServer.aidl
@@ -0,0 +1,32 @@
+/**
+ * Copyright (c) 2018, 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 perNmissions and
+ * limitations under the License.
+ */
+
+package android.net.dhcp;
+
+import android.net.INetworkStackStatusCallback;
+import android.net.dhcp.DhcpServingParamsParcel;
+
+/** @hide */
+oneway interface IDhcpServer {
+ const int STATUS_UNKNOWN = 0;
+ const int STATUS_SUCCESS = 1;
+ const int STATUS_INVALID_ARGUMENT = 2;
+ const int STATUS_UNKNOWN_ERROR = 3;
+
+ void start(in INetworkStackStatusCallback cb);
+ void updateParams(in DhcpServingParamsParcel params, in INetworkStackStatusCallback cb);
+ void stop(in INetworkStackStatusCallback cb);
+}
diff --git a/core/java/android/net/dhcp/IDhcpServerCallbacks.aidl b/core/java/android/net/dhcp/IDhcpServerCallbacks.aidl
new file mode 100644
index 0000000..7ab4dcd
--- /dev/null
+++ b/core/java/android/net/dhcp/IDhcpServerCallbacks.aidl
@@ -0,0 +1,24 @@
+/**
+ * Copyright (c) 2018, 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 perNmissions and
+ * limitations under the License.
+ */
+
+package android.net.dhcp;
+
+import android.net.dhcp.IDhcpServer;
+
+/** @hide */
+oneway interface IDhcpServerCallbacks {
+ void onDhcpServerCreated(int statusCode, in IDhcpServer server);
+}
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 9939a3c..1ebb551 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -273,6 +273,30 @@
public static final native int getCallingUid();
/**
+ * Returns {@code true} if the current thread is currently executing an
+ * incoming transaction.
+ *
+ * @hide
+ */
+ @CriticalNative
+ public static final native boolean isHandlingTransaction();
+
+ /**
+ * Return the Linux uid assigned to the process that sent the transaction
+ * currently being processed.
+ *
+ * @throws IllegalStateException if the current thread is not currently
+ * executing an incoming transaction.
+ */
+ public static final int getCallingUidOrThrow() {
+ if (!isHandlingTransaction()) {
+ throw new IllegalStateException(
+ "Thread is not in a binder transcation");
+ }
+ return getCallingUid();
+ }
+
+ /**
* Return the UserHandle assigned to the process that sent you the
* current transaction that is being processed. This is the user
* of the caller. It is distinct from {@link #getCallingUid()} in that a
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 4c0ee6f..b0b8f49 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -353,6 +353,26 @@
}
/**
+ * Attempt to setup ANGLE with a (temporary) default rules file: b/121153494
+ * True: Rules file was loaded.
+ * False: Rules file was *not* loaded.
+ */
+ private boolean setupAngleRulesDebug(String packageName, String paths, String devOptIn) {
+ // b/121153494
+ // Skip APK rules file checking.
+ if (!DEBUG) {
+ Log.v(TAG, "Skipping loading the rules file.");
+ // Fill in some default values for now, so the loader can get an answer when it asks.
+ // Most importantly, we need to indicate which app we are init'ing and what the
+ // developer options for it are so we can turn on ANGLE if needed.
+ setAngleInfo(paths, packageName, devOptIn, null, 0, 0);
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
* Attempt to setup ANGLE with a rules file loaded from the ANGLE APK.
* True: APK rules file was loaded.
* False: APK rules file was *not* loaded.
@@ -430,6 +450,12 @@
return;
}
+ // b/121153494
+ if (setupAngleRulesDebug(packageName, paths, devOptIn)) {
+ // We setup ANGLE with defaults, so we're done here.
+ return;
+ }
+
if (setupAngleRulesApk(anglePkgName, angleInfo, context, packageName, paths, devOptIn)) {
// We setup ANGLE with rules from the APK, so we're done here.
return;
@@ -454,14 +480,19 @@
return;
}
- String applicationPackageName = context.getPackageName();
- String devOptInApplicationName = coreSettings.getString(
- Settings.Global.GUP_DEV_OPT_IN_APPS);
- boolean devOptIn = applicationPackageName.equals(devOptInApplicationName);
- boolean whitelisted = onWhitelist(context, driverPackageName, ai.packageName);
- if (!devOptIn && !whitelisted) {
+ if (getGlobalSettingsString(coreSettings, Settings.Global.GUP_DEV_OPT_OUT_APPS)
+ .contains(ai.packageName)) {
if (DEBUG) {
- Log.w(TAG, applicationPackageName + " is not on the whitelist.");
+ Log.w(TAG, ai.packageName + " opts out from GUP.");
+ }
+ return;
+ }
+
+ if (!getGlobalSettingsString(coreSettings, Settings.Global.GUP_DEV_OPT_IN_APPS)
+ .contains(ai.packageName)
+ && !onWhitelist(context, driverPackageName, ai.packageName)) {
+ if (DEBUG) {
+ Log.w(TAG, ai.packageName + " is not on the whitelist.");
}
return;
}
diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java
index 9801406..68d6d85 100644
--- a/core/java/android/os/Trace.java
+++ b/core/java/android/os/Trace.java
@@ -16,6 +16,8 @@
package android.os;
+import android.annotation.NonNull;
+
import com.android.internal.os.Zygote;
import dalvik.annotation.optimization.FastNative;
@@ -313,7 +315,7 @@
* @param sectionName The name of the code section to appear in the trace. This may be at
* most 127 Unicode code units long.
*/
- public static void beginSection(String sectionName) {
+ public static void beginSection(@NonNull String sectionName) {
if (isTagEnabled(TRACE_TAG_APP)) {
if (sectionName.length() > MAX_SECTION_NAME_LEN) {
throw new IllegalArgumentException("sectionName is too long");
@@ -345,7 +347,7 @@
* @param methodName The method name to appear in the trace.
* @param cookie Unique identifier for distinguishing simultaneous events
*/
- public static void beginAsyncSection(String methodName, int cookie) {
+ public static void beginAsyncSection(@NonNull String methodName, int cookie) {
asyncTraceBegin(TRACE_TAG_APP, methodName, cookie);
}
@@ -357,7 +359,7 @@
* @param methodName The method name to appear in the trace.
* @param cookie Unique identifier for distinguishing simultaneous events
*/
- public static void endAsyncSection(String methodName, int cookie) {
+ public static void endAsyncSection(@NonNull String methodName, int cookie) {
asyncTraceEnd(TRACE_TAG_APP, methodName, cookie);
}
@@ -367,7 +369,7 @@
* @param counterName The counter name to appear in the trace.
* @param counterValue The counter value.
*/
- public static void setCounter(String counterName, long counterValue) {
+ public static void setCounter(@NonNull String counterName, long counterValue) {
if (isTagEnabled(TRACE_TAG_APP)) {
nativeTraceCounter(TRACE_TAG_APP, counterName, counterValue);
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index ec37472..66e1c80 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8882,6 +8882,14 @@
public static final String ADD_USERS_WHEN_LOCKED = "add_users_when_locked";
/**
+ * Whether applying ramping ringer on incoming phone call ringtone.
+ * <p>1 = apply ramping ringer
+ * <p>0 = do not apply ramping ringer
+ * @hide
+ */
+ public static final String APPLY_RAMPING_RINGER = "apply_ramping_ringer";
+
+ /**
* Setting whether the global gesture for enabling accessibility is enabled.
* If this gesture is enabled the user will be able to perfrom it to enable
* the accessibility state without visiting the settings app.
@@ -11992,12 +12000,18 @@
"angle_gl_driver_selection_values";
/**
- * Apps that are selected to use Game Update Package.
+ * List of Apps selected to use Game Update Packages.
* @hide
*/
public static final String GUP_DEV_OPT_IN_APPS = "gup_dev_opt_in_apps";
/**
+ * List of Apps selected not to use Game Update Packages.
+ * @hide
+ */
+ public static final String GUP_DEV_OPT_OUT_APPS = "gup_dev_opt_out_apps";
+
+ /**
* Apps on the black list that are forbidden to useGame Update Package.
* @hide
*/
@@ -13821,6 +13835,7 @@
* enabled (boolean)
* requires_targeting_p (boolean)
* max_squeeze_remeasure_attempts (int)
+ * edit_choices_before_sending (boolean)
* </pre>
* @see com.android.systemui.statusbar.policy.SmartReplyConstants
* @hide
diff --git a/core/java/android/service/dreams/IDreamManager.aidl b/core/java/android/service/dreams/IDreamManager.aidl
index be3f3b3..b84e6c9b 100644
--- a/core/java/android/service/dreams/IDreamManager.aidl
+++ b/core/java/android/service/dreams/IDreamManager.aidl
@@ -33,4 +33,5 @@
void finishSelf(in IBinder token, boolean immediate);
void startDozing(in IBinder token, int screenState, int screenBrightness);
void stopDozing(in IBinder token);
+ void forceAmbientDisplayEnabled(boolean enabled);
}
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index dd0242a..fd2b0fae 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -185,7 +185,7 @@
* Implement this to know when a direct reply is sent from a notification.
* @param key the notification key
*/
- public void onNotificationDirectReply(@NonNull String key) {}
+ public void onNotificationDirectReplied(@NonNull String key) {}
/**
* Implement this to know when a suggested reply is sent.
@@ -407,7 +407,7 @@
SomeArgs args = (SomeArgs) msg.obj;
String key = (String) args.arg1;
args.recycle();
- onNotificationDirectReply(key);
+ onNotificationDirectReplied(key);
break;
}
case MSG_ON_SUGGESTED_REPLY_SENT: {
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 984846e..0d7223d 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -970,7 +970,7 @@
mFinalSystemInsets.set(mDispatchedOverscanInsets);
mFinalStableInsets.set(mDispatchedStableInsets);
WindowInsets insets = new WindowInsets(mFinalSystemInsets,
- null, mFinalStableInsets,
+ mFinalStableInsets,
getResources().getConfiguration().isScreenRound(), false,
mDispatchedDisplayCutout);
if (DEBUG) {
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 3d0c662..24d746e 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -376,9 +376,13 @@
* Set paragraph justification mode. The default value is
* {@link Layout#JUSTIFICATION_MODE_NONE}. If the last line is too short for justification,
* the last line will be displayed with the alignment set by {@link #setAlignment}.
+ * When Justification mode is JUSTIFICATION_MODE_INTER_WORD, wordSpacing on the given
+ * {@link Paint} will be ignored. This behavior also affects Spans which change the
+ * wordSpacing.
*
* @param justificationMode justification mode for the paragraph.
* @return this builder, useful for chaining.
+ * @see Paint#setWordSpacing(float)
*/
@NonNull
public Builder setJustificationMode(@JustificationMode int justificationMode) {
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index 6eb433a..949328f 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -75,8 +75,9 @@
private int mEllipsisEnd;
// Additional width of whitespace for justification. This value is per whitespace, thus
- // the line width will increase by mAddedWidth x (number of stretchable whitespaces).
- private float mAddedWidth;
+ // the line width will increase by mAddedWidthForJustify x (number of stretchable whitespaces).
+ private float mAddedWidthForJustify;
+ private boolean mIsJustifying;
private final TextPaint mWorkPaint = new TextPaint();
private final TextPaint mActivePaint = new TextPaint();
@@ -229,7 +230,8 @@
}
}
mTabs = tabStops;
- mAddedWidth = 0;
+ mAddedWidthForJustify = 0;
+ mIsJustifying = false;
mEllipsisStart = ellipsisStart != ellipsisEnd ? ellipsisStart : 0;
mEllipsisEnd = ellipsisStart != ellipsisEnd ? ellipsisEnd : 0;
@@ -255,7 +257,8 @@
return;
}
final float width = Math.abs(measure(end, false, null));
- mAddedWidth = (justifyWidth - width) / spaces;
+ mAddedWidthForJustify = (justifyWidth - width) / spaces;
+ mIsJustifying = true;
}
/**
@@ -713,7 +716,9 @@
TextPaint wp = mWorkPaint;
wp.set(mPaint);
- wp.setWordSpacing(mAddedWidth);
+ if (mIsJustifying) {
+ wp.setWordSpacing(mAddedWidthForJustify);
+ }
int spanStart = runStart;
int spanLimit;
@@ -849,7 +854,9 @@
FontMetricsInt fmi, boolean needWidth, int offset,
@Nullable ArrayList<DecorationInfo> decorations) {
- wp.setWordSpacing(mAddedWidth);
+ if (mIsJustifying) {
+ wp.setWordSpacing(mAddedWidthForJustify);
+ }
// Get metrics first (even for empty strings or "0" width runs)
if (fmi != null) {
expandMetricsFromPaint(fmi, wp);
diff --git a/core/java/android/util/KeyValueListParser.java b/core/java/android/util/KeyValueListParser.java
index 7eef63e..d051ed8 100644
--- a/core/java/android/util/KeyValueListParser.java
+++ b/core/java/android/util/KeyValueListParser.java
@@ -16,7 +16,9 @@
package android.util;
import android.text.TextUtils;
+import android.util.proto.ProtoOutputStream;
+import java.io.PrintWriter;
import java.time.Duration;
import java.time.format.DateTimeParseException;
@@ -212,4 +214,163 @@
}
return def;
}
+
+ /** Represents an integer config value. */
+ public static class IntValue {
+ private final String mKey;
+ private final int mDefaultValue;
+ private int mValue;
+
+ /** Constructor, initialize with a config key and a default value. */
+ public IntValue(String key, int defaultValue) {
+ mKey = key;
+ mDefaultValue = defaultValue;
+ mValue = mDefaultValue;
+ }
+
+ /** Read a value from {@link KeyValueListParser} */
+ public void parse(KeyValueListParser parser) {
+ mValue = parser.getInt(mKey, mDefaultValue);
+ }
+
+ /** Return the config key. */
+ public String getKey() {
+ return mKey;
+ }
+
+ /** Return the default value. */
+ public int getDefaultValue() {
+ return mDefaultValue;
+ }
+
+ /** Return the actual config value. */
+ public int getValue() {
+ return mValue;
+ }
+
+ /** Overwrites with a value. */
+ public void setValue(int value) {
+ mValue = value;
+ }
+
+ /** Used for dumpsys */
+ public void dump(PrintWriter pw, String prefix) {
+ pw.print(prefix);
+ pw.print(mKey);
+ pw.print("=");
+ pw.print(mValue);
+ pw.println();
+ }
+
+ /** Used for proto dumpsys */
+ public void dumpProto(ProtoOutputStream proto, long tag) {
+ proto.write(tag, mValue);
+ }
+ }
+
+ /** Represents an long config value. */
+ public static class LongValue {
+ private final String mKey;
+ private final long mDefaultValue;
+ private long mValue;
+
+ /** Constructor, initialize with a config key and a default value. */
+ public LongValue(String key, long defaultValue) {
+ mKey = key;
+ mDefaultValue = defaultValue;
+ mValue = mDefaultValue;
+ }
+
+ /** Read a value from {@link KeyValueListParser} */
+ public void parse(KeyValueListParser parser) {
+ mValue = parser.getLong(mKey, mDefaultValue);
+ }
+
+ /** Return the config key. */
+ public String getKey() {
+ return mKey;
+ }
+
+ /** Return the default value. */
+ public long getDefaultValue() {
+ return mDefaultValue;
+ }
+
+ /** Return the actual config value. */
+ public long getValue() {
+ return mValue;
+ }
+
+ /** Overwrites with a value. */
+ public void setValue(long value) {
+ mValue = value;
+ }
+
+ /** Used for dumpsys */
+ public void dump(PrintWriter pw, String prefix) {
+ pw.print(prefix);
+ pw.print(mKey);
+ pw.print("=");
+ pw.print(mValue);
+ pw.println();
+ }
+
+ /** Used for proto dumpsys */
+ public void dumpProto(ProtoOutputStream proto, long tag) {
+ proto.write(tag, mValue);
+ }
+ }
+
+ /** Represents an string config value. */
+ public static class StringValue {
+ private final String mKey;
+ private final String mDefaultValue;
+ private String mValue;
+
+ /** Constructor, initialize with a config key and a default value. */
+ public StringValue(String key, String defaultValue) {
+ mKey = key;
+ mDefaultValue = defaultValue;
+ mValue = mDefaultValue;
+ }
+
+ /** Read a value from {@link KeyValueListParser} */
+ public void parse(KeyValueListParser parser) {
+ mValue = parser.getString(mKey, mDefaultValue);
+ }
+
+ /** Return the config key. */
+ public String getKey() {
+ return mKey;
+ }
+
+ /** Return the default value. */
+ public String getDefaultValue() {
+ return mDefaultValue;
+ }
+
+ /** Return the actual config value. */
+ public String getValue() {
+ return mValue;
+ }
+
+ /** Overwrites with a value. */
+ public void setValue(String value) {
+ mValue = value;
+ }
+
+ /** Used for dumpsys */
+ public void dump(PrintWriter pw, String prefix) {
+ pw.print(prefix);
+ pw.print(mKey);
+ pw.print("=");
+ pw.print(mValue);
+ pw.println();
+ }
+
+ /** Used for proto dumpsys */
+ public void dumpProto(ProtoOutputStream proto, long tag) {
+ proto.write(tag, mValue);
+ }
+ }
}
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index 885b3e9..0931914 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -16,6 +16,8 @@
package android.view;
+import static android.view.WindowInsets.Type.indexOf;
+
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.graphics.Insets;
@@ -114,8 +116,8 @@
public WindowInsets calculateInsets(Rect frame, boolean isScreenRound,
boolean alwaysConsumeNavBar, DisplayCutout cutout,
@Nullable @InsetSide SparseIntArray typeSideMap) {
- Insets systemInsets = Insets.NONE;
- Insets maxInsets = Insets.NONE;
+ Insets[] typeInsetsMap = new Insets[Type.SIZE];
+ Insets[] typeMaxInsetsMap = new Insets[Type.SIZE];
final Rect relativeFrame = new Rect(frame);
final Rect relativeFrameMax = new Rect(frame);
for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) {
@@ -123,32 +125,38 @@
if (source == null) {
continue;
}
- systemInsets = processSource(source, systemInsets, relativeFrame,
- false /* ignoreVisibility */, typeSideMap);
+ processSource(source, relativeFrame, false /* ignoreVisibility */, typeInsetsMap,
+ typeSideMap);
// IME won't be reported in max insets as the size depends on the EditorInfo of the IME
// target.
if (source.getType() != TYPE_IME) {
- maxInsets = processSource(source, maxInsets, relativeFrameMax,
- true /* ignoreVisibility */, null /* typeSideMap */);
+ processSource(source, relativeFrameMax, true /* ignoreVisibility */,
+ typeMaxInsetsMap, null /* typeSideMap */);
}
}
- return new WindowInsets(new Rect(systemInsets), null, new Rect(maxInsets), isScreenRound,
+ return new WindowInsets(typeInsetsMap, typeMaxInsetsMap, isScreenRound,
alwaysConsumeNavBar, cutout);
}
- private Insets processSource(InsetsSource source, Insets insets, Rect relativeFrame,
- boolean ignoreVisibility, @Nullable @InsetSide SparseIntArray typeSideMap) {
- Insets currentInsets = source.calculateInsets(relativeFrame, ignoreVisibility);
- insets = Insets.add(currentInsets, insets);
- relativeFrame.inset(insets);
- if (typeSideMap != null && !Insets.NONE.equals(currentInsets)) {
- @InsetSide int insetSide = getInsetSide(currentInsets);
+ private void processSource(InsetsSource source, Rect relativeFrame, boolean ignoreVisibility,
+ Insets[] typeInsetsMap, @Nullable @InsetSide SparseIntArray typeSideMap) {
+ Insets insets = source.calculateInsets(relativeFrame, ignoreVisibility);
+
+ int index = indexOf(toPublicType(source.getType()));
+ Insets existing = typeInsetsMap[index];
+ if (existing == null) {
+ typeInsetsMap[index] = insets;
+ } else {
+ typeInsetsMap[index] = Insets.max(existing, insets);
+ }
+
+ if (typeSideMap != null && !Insets.NONE.equals(insets)) {
+ @InsetSide int insetSide = getInsetSide(insets);
if (insetSide != INSET_SIDE_UNKNWON) {
- typeSideMap.put(source.getType(), getInsetSide(currentInsets));
+ typeSideMap.put(source.getType(), getInsetSide(insets));
}
}
- return insets;
}
/**
@@ -229,6 +237,21 @@
return result;
}
+ static @InsetType int toPublicType(@InternalInsetType int type) {
+ switch (type) {
+ case TYPE_TOP_BAR:
+ return Type.TOP_BAR;
+ case TYPE_SIDE_BAR_1:
+ case TYPE_SIDE_BAR_2:
+ case TYPE_SIDE_BAR_3:
+ return Type.SIDE_BARS;
+ case TYPE_IME:
+ return Type.IME;
+ default:
+ throw new IllegalArgumentException("Unknown type: " + type);
+ }
+ }
+
public static boolean getDefaultVisibly(@InsetType int type) {
switch (type) {
case TYPE_TOP_BAR:
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 0b6beba..acad5d7 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -158,7 +158,6 @@
private static native void nativeSeverChildren(long transactionObj, long nativeObject);
private static native void nativeSetOverrideScalingMode(long transactionObj, long nativeObject,
int scalingMode);
- private static native void nativeDestroy(long transactionObj, long nativeObject);
private static native IBinder nativeGetHandle(long nativeObject);
private static native boolean nativeGetTransformToDisplayInverse(long nativeObject);
@@ -360,11 +359,18 @@
*/
public static final int WINDOW_TYPE_DONT_SCREENSHOT = 441731;
+ private void assignNativeObject(long nativeObject) {
+ if (mNativeObject != 0) {
+ release();
+ }
+ mNativeObject = nativeObject;
+ }
+
public void copyFrom(SurfaceControl other) {
mName = other.mName;
mWidth = other.mWidth;
mHeight = other.mHeight;
- mNativeObject = nativeCopyFromSurfaceControl(other.mNativeObject);
+ assignNativeObject(nativeCopyFromSurfaceControl(other.mNativeObject));
}
/**
@@ -685,12 +691,11 @@
mWidth = in.readInt();
mHeight = in.readInt();
- release();
+ long object = 0;
if (in.readInt() != 0) {
- mNativeObject = nativeReadFromParcel(in);
- } else {
- mNativeObject = 0;
+ object = nativeReadFromParcel(in);
}
+ assignNativeObject(object);
}
@Override
@@ -1765,30 +1770,6 @@
return this;
}
- /**
- * Same as {@link #destroy()} except this is invoked in a transaction instead of
- * immediately.
- */
- public Transaction destroy(SurfaceControl sc) {
- sc.checkNotReleased();
-
- /**
- * Perhaps it's safer to transfer the close guard to the Transaction
- * but then we have a whole wonky scenario regarding merging, multiple
- * close-guards per transaction etc...the whole scenario is kind of wonky
- * and it seems really we'd like to just be able to call release here
- * but the WindowManager has some code that looks like
- * --- destroyInTransaction(a)
- * --- reparentChildrenInTransaction(a)
- * so we need to ensure the SC remains valid until the transaction
- * is applied.
- */
- sc.mCloseGuard.close();
-
- nativeDestroy(mNativeObject, sc.mNativeObject);
- return this;
- }
-
public Transaction setDisplaySurface(IBinder displayToken, Surface surface) {
if (displayToken == null) {
throw new IllegalArgumentException("displayToken must not be null");
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 9dfbf28..cb2c40e 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -939,6 +939,26 @@
*/
private static boolean sAcceptZeroSizeDragShadow;
+ /**
+ * Prior to Q, {@link #dispatchApplyWindowInsets} had some issues:
+ * <ul>
+ * <li>The modified insets changed by {@link #onApplyWindowInsets} were passed to the
+ * entire view hierarchy in prefix order, including siblings as well as siblings of parents
+ * further down the hierarchy. This violates the basic concepts of the view hierarchy, and
+ * thus, the hierarchical dispatching mechanism was hard to use for apps.</li>
+ *
+ * <li>Dispatch was stopped after the insets were fully consumed. This is somewhat confusing
+ * for developers, but more importantly, by adding more granular information to
+ * {@link WindowInsets} it becomes really cumbersome to define what consumed actually means
+ * </li>
+ * </ul>
+ *
+ * In order to make window inset dispatching work properly, we dispatch window insets
+ * in the view hierarchy in a proper hierarchical manner and don't stop dispatching if the
+ * insets are consumed if this flag is set to {@code false}.
+ */
+ static boolean sBrokenInsetsDispatch;
+
/** @hide */
@IntDef({NOT_FOCUSABLE, FOCUSABLE, FOCUSABLE_AUTO})
@Retention(RetentionPolicy.SOURCE)
@@ -5108,6 +5128,9 @@
sAcceptZeroSizeDragShadow = targetSdkVersion < Build.VERSION_CODES.P;
+ sBrokenInsetsDispatch = !ViewRootImpl.USE_NEW_INSETS
+ || targetSdkVersion < Build.VERSION_CODES.Q;
+
sCompatibilityDone = true;
}
}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 8372032..9d11397 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -7111,6 +7111,14 @@
@Override
public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) {
insets = super.dispatchApplyWindowInsets(insets);
+ if (View.sBrokenInsetsDispatch) {
+ return brokenDispatchApplyWindowInsets(insets);
+ } else {
+ return newDispatchApplyWindowInsets(insets);
+ }
+ }
+
+ private WindowInsets brokenDispatchApplyWindowInsets(WindowInsets insets) {
if (!insets.isConsumed()) {
final int count = getChildCount();
for (int i = 0; i < count; i++) {
@@ -7123,6 +7131,14 @@
return insets;
}
+ private WindowInsets newDispatchApplyWindowInsets(WindowInsets insets) {
+ final int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ getChildAt(i).dispatchApplyWindowInsets(insets);
+ }
+ return insets;
+ }
+
/**
* Returns the animation listener to which layout animation events are
* sent.
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index c0b4283..8e4dc67 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1859,8 +1859,7 @@
mContext.getResources().getConfiguration().isScreenRound(),
mAttachInfo.mAlwaysConsumeNavBar, displayCutout);
} else {
- mLastWindowInsets = new WindowInsets(contentInsets,
- null /* windowDecorInsets */, stableInsets,
+ mLastWindowInsets = new WindowInsets(contentInsets, stableInsets,
mContext.getResources().getConfiguration().isScreenRound(),
mAttachInfo.mAlwaysConsumeNavBar, displayCutout);
}
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index 572d331..b3da727 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -17,18 +17,33 @@
package android.view;
-import android.annotation.NonNull;
+import static android.view.WindowInsets.Type.FIRST;
+import static android.view.WindowInsets.Type.IME;
+import static android.view.WindowInsets.Type.LAST;
+import static android.view.WindowInsets.Type.SIDE_BARS;
+import static android.view.WindowInsets.Type.SIZE;
+import static android.view.WindowInsets.Type.TOP_BAR;
+import static android.view.WindowInsets.Type.all;
+import static android.view.WindowInsets.Type.compatSystemInsets;
+import static android.view.WindowInsets.Type.indexOf;
+
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.graphics.Insets;
import android.graphics.Rect;
+import android.util.SparseArray;
+import android.view.InsetsState.InternalInsetType;
+import android.view.WindowInsets.Type.InsetType;
+import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethod;
import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
import java.util.Objects;
/**
@@ -49,9 +64,9 @@
*/
public final class WindowInsets {
- @NonNull private final Insets mSystemWindowInsets;
- @NonNull private final Insets mWindowDecorInsets;
- @NonNull private final Insets mStableInsets;
+ private final Insets[] mTypeInsetsMap;
+ private final Insets[] mTypeMaxInsetsMap;
+
@Nullable private Rect mTempRect;
private final boolean mIsRound;
@Nullable private final DisplayCutout mDisplayCutout;
@@ -64,7 +79,6 @@
private final boolean mAlwaysConsumeNavBar;
private final boolean mSystemWindowInsetsConsumed;
- private final boolean mWindowDecorInsetsConsumed;
private final boolean mStableInsetsConsumed;
private final boolean mDisplayCutoutConsumed;
@@ -78,7 +92,7 @@
public static final WindowInsets CONSUMED;
static {
- CONSUMED = new WindowInsets((Insets) null, null, null, false, false, null);
+ CONSUMED = new WindowInsets((Rect) null, null, false, false, null);
}
/**
@@ -87,24 +101,38 @@
* A {@code null} inset indicates that the respective inset is consumed.
*
* @hide
+ * @deprecated Use {@link WindowInsets(SparseArray, SparseArray, boolean, boolean, DisplayCutout)}
*/
- public WindowInsets(Rect systemWindowInsets, Rect windowDecorInsets, Rect stableInsets,
+ public WindowInsets(Rect systemWindowInsetsRect, Rect stableInsetsRect,
boolean isRound, boolean alwaysConsumeNavBar, DisplayCutout displayCutout) {
- this(insetsOrNull(systemWindowInsets), insetsOrNull(windowDecorInsets),
- insetsOrNull(stableInsets), isRound, alwaysConsumeNavBar, displayCutout);
+ this(createCompatTypeMap(systemWindowInsetsRect), createCompatTypeMap(stableInsetsRect),
+ isRound, alwaysConsumeNavBar, displayCutout);
}
- private WindowInsets(Insets systemWindowInsets, Insets windowDecorInsets,
- Insets stableInsets, boolean isRound, boolean alwaysConsumeNavBar,
- DisplayCutout displayCutout) {
- mSystemWindowInsetsConsumed = systemWindowInsets == null;
- mSystemWindowInsets = mSystemWindowInsetsConsumed ? Insets.NONE : systemWindowInsets;
+ /**
+ * Construct a new WindowInsets from individual insets.
+ *
+ * {@code typeInsetsMap} and {@code typeMaxInsetsMap} are a map of indexOf(type) -> insets that
+ * contain the information what kind of system bars causes how much insets. The insets in this
+ * map are non-additive; i.e. they have the same origin. In other words: If two system bars
+ * overlap on one side, the insets of the larger bar will also include the insets of the smaller
+ * bar.
+ *
+ * {@code null} type inset map indicates that the respective inset is fully consumed.
+ * @hide
+ */
+ public WindowInsets(@Nullable Insets[] typeInsetsMap,
+ @Nullable Insets[] typeMaxInsetsMap, boolean isRound,
+ boolean alwaysConsumeNavBar, DisplayCutout displayCutout) {
+ mSystemWindowInsetsConsumed = typeInsetsMap == null;
+ mTypeInsetsMap = mSystemWindowInsetsConsumed
+ ? new Insets[SIZE]
+ : typeInsetsMap.clone();
- mWindowDecorInsetsConsumed = windowDecorInsets == null;
- mWindowDecorInsets = mWindowDecorInsetsConsumed ? Insets.NONE : windowDecorInsets;
-
- mStableInsetsConsumed = stableInsets == null;
- mStableInsets = mStableInsetsConsumed ? Insets.NONE : stableInsets;
+ mStableInsetsConsumed = typeMaxInsetsMap == null;
+ mTypeMaxInsetsMap = mStableInsetsConsumed
+ ? new Insets[SIZE]
+ : typeMaxInsetsMap.clone();
mIsRound = isRound;
mAlwaysConsumeNavBar = alwaysConsumeNavBar;
@@ -120,10 +148,7 @@
* @param src Source to copy insets from
*/
public WindowInsets(WindowInsets src) {
- this(src.mSystemWindowInsetsConsumed ? null : src.mSystemWindowInsets,
- src.mWindowDecorInsetsConsumed ? null : src.mWindowDecorInsets,
- src.mStableInsetsConsumed ? null : src.mStableInsets,
- src.mIsRound, src.mAlwaysConsumeNavBar,
+ this(src.mTypeInsetsMap, src.mTypeMaxInsetsMap, src.mIsRound, src.mAlwaysConsumeNavBar,
displayCutoutCopyConstructorArgument(src));
}
@@ -137,10 +162,64 @@
}
}
+ /**
+ * @return The insets that include system bars indicated by {@code typeMask}, taken from
+ * {@code typeInsetMap}.
+ */
+ private static Insets getInsets(Insets[] typeInsetsMap, @InsetType int typeMask) {
+ Insets result = null;
+ for (int i = FIRST; i <= LAST; i = i << 1) {
+ if ((typeMask & i) == 0) {
+ continue;
+ }
+ Insets insets = typeInsetsMap[indexOf(i)];
+ if (insets == null) {
+ continue;
+ }
+ if (result == null) {
+ result = insets;
+ } else {
+ result = Insets.max(result, insets);
+ }
+ }
+ return result == null ? Insets.NONE : result;
+ }
+
+ /**
+ * Sets all entries in {@code typeInsetsMap} that belong to {@code typeMask} to {@code insets},
+ */
+ private static void setInsets(Insets[] typeInsetsMap, @InsetType int typeMask, Insets insets) {
+ for (int i = FIRST; i <= LAST; i = i << 1) {
+ if ((typeMask & i) == 0) {
+ continue;
+ }
+ typeInsetsMap[indexOf(i)] = insets;
+ }
+ }
+
/** @hide */
@UnsupportedAppUsage
public WindowInsets(Rect systemWindowInsets) {
- this(systemWindowInsets, null, null, false, false, null);
+ this(createCompatTypeMap(systemWindowInsets), null, false, false, null);
+ }
+
+ /**
+ * Creates a indexOf(type) -> inset map for which the {@code insets} is just mapped to
+ * {@link InsetType#topBar()} and {@link InsetType#sideBars()}, depending on the location of the
+ * inset.
+ */
+ private static Insets[] createCompatTypeMap(@Nullable Rect insets) {
+ if (insets == null) {
+ return null;
+ }
+ Insets[] typeInsetMap = new Insets[SIZE];
+ assignCompatInsets(typeInsetMap, insets);
+ return typeInsetMap;
+ }
+
+ private static void assignCompatInsets(Insets[] typeInsetMap, Rect insets) {
+ typeInsetMap[indexOf(TOP_BAR)] = Insets.of(0, insets.top, 0, 0);
+ typeInsetMap[indexOf(SIDE_BARS)] = Insets.of(insets.left, 0, insets.right, insets.bottom);
}
/**
@@ -156,8 +235,8 @@
if (mTempRect == null) {
mTempRect = new Rect();
}
- mTempRect.set(mSystemWindowInsets.left, mSystemWindowInsets.top,
- mSystemWindowInsets.right, mSystemWindowInsets.bottom);
+ Insets insets = getSystemWindowInsets();
+ mTempRect.set(insets.left, insets.top, insets.right, insets.bottom);
return mTempRect;
}
@@ -172,7 +251,46 @@
*/
@NonNull
public Insets getSystemWindowInsets() {
- return mSystemWindowInsets;
+ return getInsets(mTypeInsetsMap, compatSystemInsets());
+ }
+
+ /**
+ * Returns the insets of a specific set of windows causing insets, denoted by the
+ * {@code typeMask} bit mask of {@link InsetType}s.
+ *
+ * @param typeMask Bit mask of {@link InsetType}s to query the insets for.
+ * @return The insets.
+ *
+ * @hide pending unhide
+ */
+ public Insets getInsets(@InsetType int typeMask) {
+ return getInsets(mTypeInsetsMap, typeMask);
+ }
+
+ /**
+ * Returns the maximum amount of insets a specific set of windows can cause, denoted by the
+ * {@code typeMask} bit mask of {@link InsetType}s.
+ *
+ * <p>The maximum insets represents the area of a a window that that <b>may</b> be partially
+ * or fully obscured by the system window identified by {@code type}. This value does not
+ * change based on the visibility state of those elements. for example, if the status bar is
+ * normally shown, but temporarily hidden, the maximum inset will still provide the inset
+ * associated with the status bar being shown.</p>
+ *
+ * @param typeMask Bit mask of {@link InsetType}s to query the insets for.
+ * @return The insets.
+ *
+ * @throws IllegalArgumentException If the caller tries to query {@link Type#ime()}. Maximum
+ * insets are not available for this type as the height of the
+ * IME is dynamic depending on the {@link EditorInfo} of the
+ * currently focused view, as well as the UI state of the IME.
+ * @hide pending unhide
+ */
+ public Insets getMaxInsets(@InsetType int typeMask) throws IllegalArgumentException {
+ if ((typeMask & IME) != 0) {
+ throw new IllegalArgumentException("Unable to query the maximum insets for IME");
+ }
+ return getInsets(mTypeMaxInsetsMap, typeMask);
}
/**
@@ -185,7 +303,7 @@
* @return The left system window inset
*/
public int getSystemWindowInsetLeft() {
- return mSystemWindowInsets.left;
+ return getSystemWindowInsets().left;
}
/**
@@ -198,7 +316,7 @@
* @return The top system window inset
*/
public int getSystemWindowInsetTop() {
- return mSystemWindowInsets.top;
+ return getSystemWindowInsets().top;
}
/**
@@ -211,7 +329,7 @@
* @return The right system window inset
*/
public int getSystemWindowInsetRight() {
- return mSystemWindowInsets.right;
+ return getSystemWindowInsets().right;
}
/**
@@ -224,63 +342,7 @@
* @return The bottom system window inset
*/
public int getSystemWindowInsetBottom() {
- return mSystemWindowInsets.bottom;
- }
-
- /**
- * Returns the left window decor inset in pixels.
- *
- * <p>The window decor inset represents the area of the window content area that is
- * partially or fully obscured by decorations within the window provided by the framework.
- * This can include action bars, title bars, toolbars, etc.</p>
- *
- * @return The left window decor inset
- * @hide pending API
- */
- public int getWindowDecorInsetLeft() {
- return mWindowDecorInsets.left;
- }
-
- /**
- * Returns the top window decor inset in pixels.
- *
- * <p>The window decor inset represents the area of the window content area that is
- * partially or fully obscured by decorations within the window provided by the framework.
- * This can include action bars, title bars, toolbars, etc.</p>
- *
- * @return The top window decor inset
- * @hide pending API
- */
- public int getWindowDecorInsetTop() {
- return mWindowDecorInsets.top;
- }
-
- /**
- * Returns the right window decor inset in pixels.
- *
- * <p>The window decor inset represents the area of the window content area that is
- * partially or fully obscured by decorations within the window provided by the framework.
- * This can include action bars, title bars, toolbars, etc.</p>
- *
- * @return The right window decor inset
- * @hide pending API
- */
- public int getWindowDecorInsetRight() {
- return mWindowDecorInsets.right;
- }
-
- /**
- * Returns the bottom window decor inset in pixels.
- *
- * <p>The window decor inset represents the area of the window content area that is
- * partially or fully obscured by decorations within the window provided by the framework.
- * This can include action bars, title bars, toolbars, etc.</p>
- *
- * @return The bottom window decor inset
- * @hide pending API
- */
- public int getWindowDecorInsetBottom() {
- return mWindowDecorInsets.bottom;
+ return getSystemWindowInsets().bottom;
}
/**
@@ -293,23 +355,7 @@
* @return true if any of the system window inset values are nonzero
*/
public boolean hasSystemWindowInsets() {
- return mSystemWindowInsets.left != 0 || mSystemWindowInsets.top != 0 ||
- mSystemWindowInsets.right != 0 || mSystemWindowInsets.bottom != 0;
- }
-
- /**
- * Returns true if this WindowInsets has nonzero window decor insets.
- *
- * <p>The window decor inset represents the area of the window content area that is
- * partially or fully obscured by decorations within the window provided by the framework.
- * This can include action bars, title bars, toolbars, etc.</p>
- *
- * @return true if any of the window decor inset values are nonzero
- * @hide pending API
- */
- public boolean hasWindowDecorInsets() {
- return mWindowDecorInsets.left != 0 || mWindowDecorInsets.top != 0 ||
- mWindowDecorInsets.right != 0 || mWindowDecorInsets.bottom != 0;
+ return !getSystemWindowInsets().equals(Insets.NONE);
}
/**
@@ -318,7 +364,8 @@
* @return true if any inset values are nonzero
*/
public boolean hasInsets() {
- return hasSystemWindowInsets() || hasWindowDecorInsets() || hasStableInsets()
+ return !getInsets(mTypeInsetsMap, all()).equals(Insets.NONE)
+ || !getInsets(mTypeMaxInsetsMap, all()).equals(Insets.NONE)
|| mDisplayCutout != null;
}
@@ -340,9 +387,7 @@
*/
@NonNull
public WindowInsets consumeDisplayCutout() {
- return new WindowInsets(mSystemWindowInsetsConsumed ? null : mSystemWindowInsets,
- mWindowDecorInsetsConsumed ? null : mWindowDecorInsets,
- mStableInsetsConsumed ? null : mStableInsets,
+ return new WindowInsets(mTypeInsetsMap, mTypeMaxInsetsMap,
mIsRound, mAlwaysConsumeNavBar,
null /* displayCutout */);
}
@@ -362,7 +407,7 @@
* @return true if the insets have been fully consumed.
*/
public boolean isConsumed() {
- return mSystemWindowInsetsConsumed && mWindowDecorInsetsConsumed && mStableInsetsConsumed
+ return mSystemWindowInsetsConsumed && mStableInsetsConsumed
&& mDisplayCutoutConsumed;
}
@@ -387,9 +432,7 @@
*/
@NonNull
public WindowInsets consumeSystemWindowInsets() {
- return new WindowInsets(null /* systemWindowInsets */,
- mWindowDecorInsetsConsumed ? null : mWindowDecorInsets,
- mStableInsetsConsumed ? null : mStableInsets,
+ return new WindowInsets(null, mStableInsetsConsumed ? null : mTypeMaxInsetsMap,
mIsRound, mAlwaysConsumeNavBar,
displayCutoutCopyConstructorArgument(this));
}
@@ -449,18 +492,6 @@
}
/**
- * @hide
- */
- @NonNull
- public WindowInsets consumeWindowDecorInsets() {
- return new WindowInsets(mSystemWindowInsetsConsumed ? null : mSystemWindowInsets,
- null /* windowDecorInsets */,
- mStableInsetsConsumed ? null : mStableInsets,
- mIsRound, mAlwaysConsumeNavBar,
- displayCutoutCopyConstructorArgument(this));
- }
-
- /**
* Returns the stable insets in pixels.
*
* <p>The stable inset represents the area of a full-screen window that <b>may</b> be
@@ -473,7 +504,7 @@
*/
@NonNull
public Insets getStableInsets() {
- return mStableInsets;
+ return getInsets(mTypeMaxInsetsMap, compatSystemInsets());
}
/**
@@ -488,7 +519,7 @@
* @return The top stable inset
*/
public int getStableInsetTop() {
- return mStableInsets.top;
+ return getStableInsets().top;
}
/**
@@ -503,7 +534,7 @@
* @return The left stable inset
*/
public int getStableInsetLeft() {
- return mStableInsets.left;
+ return getStableInsets().left;
}
/**
@@ -518,7 +549,7 @@
* @return The right stable inset
*/
public int getStableInsetRight() {
- return mStableInsets.right;
+ return getStableInsets().right;
}
/**
@@ -533,7 +564,7 @@
* @return The bottom stable inset
*/
public int getStableInsetBottom() {
- return mStableInsets.bottom;
+ return getStableInsets().bottom;
}
/**
@@ -548,8 +579,7 @@
* @return true if any of the stable inset values are nonzero
*/
public boolean hasStableInsets() {
- return mStableInsets.top != 0 || mStableInsets.left != 0 || mStableInsets.right != 0
- || mStableInsets.bottom != 0;
+ return !getStableInsets().equals(Insets.NONE);
}
/**
@@ -559,9 +589,7 @@
*/
@NonNull
public WindowInsets consumeStableInsets() {
- return new WindowInsets(mSystemWindowInsetsConsumed ? null : mSystemWindowInsets,
- mWindowDecorInsetsConsumed ? null : mWindowDecorInsets,
- null /* stableInsets */,
+ return new WindowInsets(mSystemWindowInsetsConsumed ? null : mTypeInsetsMap, null,
mIsRound, mAlwaysConsumeNavBar,
displayCutoutCopyConstructorArgument(this));
}
@@ -575,9 +603,8 @@
@Override
public String toString() {
- return "WindowInsets{systemWindowInsets=" + mSystemWindowInsets
- + " windowDecorInsets=" + mWindowDecorInsets
- + " stableInsets=" + mStableInsets
+ return "WindowInsets{systemWindowInsets=" + getSystemWindowInsets()
+ + " stableInsets=" + getStableInsets()
+ (mDisplayCutout != null ? " cutout=" + mDisplayCutout : "")
+ (isRound() ? " round" : "")
+ "}";
@@ -634,16 +661,16 @@
Preconditions.checkArgumentNonnegative(bottom);
return new WindowInsets(
- mSystemWindowInsetsConsumed ? null :
- insetInsets(mSystemWindowInsets, left, top, right, bottom),
- mWindowDecorInsetsConsumed ? null :
- insetInsets(mWindowDecorInsets, left, top, right, bottom),
- mStableInsetsConsumed ? null :
- insetInsets(mStableInsets, left, top, right, bottom),
+ mSystemWindowInsetsConsumed
+ ? null
+ : insetInsets(mTypeInsetsMap, left, top, right, bottom),
+ mStableInsetsConsumed
+ ? null
+ : insetInsets(mTypeMaxInsetsMap, left, top, right, bottom),
mIsRound, mAlwaysConsumeNavBar,
mDisplayCutoutConsumed
- ? null :
- mDisplayCutout == null
+ ? null
+ : mDisplayCutout == null
? DisplayCutout.NO_CUTOUT
: mDisplayCutout.inset(left, top, right, bottom));
}
@@ -653,23 +680,49 @@
if (this == o) return true;
if (o == null || !(o instanceof WindowInsets)) return false;
WindowInsets that = (WindowInsets) o;
+
return mIsRound == that.mIsRound
&& mAlwaysConsumeNavBar == that.mAlwaysConsumeNavBar
&& mSystemWindowInsetsConsumed == that.mSystemWindowInsetsConsumed
- && mWindowDecorInsetsConsumed == that.mWindowDecorInsetsConsumed
&& mStableInsetsConsumed == that.mStableInsetsConsumed
&& mDisplayCutoutConsumed == that.mDisplayCutoutConsumed
- && Objects.equals(mSystemWindowInsets, that.mSystemWindowInsets)
- && Objects.equals(mWindowDecorInsets, that.mWindowDecorInsets)
- && Objects.equals(mStableInsets, that.mStableInsets)
+ && Arrays.equals(mTypeInsetsMap, that.mTypeInsetsMap)
+ && Arrays.equals(mTypeMaxInsetsMap, that.mTypeMaxInsetsMap)
&& Objects.equals(mDisplayCutout, that.mDisplayCutout);
}
@Override
public int hashCode() {
- return Objects.hash(mSystemWindowInsets, mWindowDecorInsets, mStableInsets, mIsRound,
- mDisplayCutout, mAlwaysConsumeNavBar, mSystemWindowInsetsConsumed,
- mWindowDecorInsetsConsumed, mStableInsetsConsumed, mDisplayCutoutConsumed);
+ return Objects.hash(Arrays.hashCode(mTypeInsetsMap), Arrays.hashCode(mTypeMaxInsetsMap),
+ mIsRound, mDisplayCutout, mAlwaysConsumeNavBar, mSystemWindowInsetsConsumed,
+ mStableInsetsConsumed, mDisplayCutoutConsumed);
+ }
+
+
+ /**
+ * Insets every inset in {@code typeInsetsMap} by the specified left, top, right, bottom.
+ *
+ * @return {@code typeInsetsMap} if no inset was modified; a copy of the map with the modified
+ * insets otherwise.
+ */
+ private static Insets[] insetInsets(
+ Insets[] typeInsetsMap, int left, int top, int right, int bottom) {
+ boolean cloned = false;
+ for (int i = 0; i < SIZE; i++) {
+ Insets insets = typeInsetsMap[i];
+ if (insets == null) {
+ continue;
+ }
+ Insets insetInsets = insetInsets(insets, left, top, right, bottom);
+ if (insetInsets != insets) {
+ if (!cloned) {
+ typeInsetsMap = typeInsetsMap.clone();
+ cloned = true;
+ }
+ typeInsetsMap[i] = insetInsets;
+ }
+ }
+ return typeInsetsMap;
}
private static Insets insetInsets(Insets insets, int left, int top, int right, int bottom) {
@@ -683,10 +736,6 @@
return Insets.of(newLeft, newTop, newRight, newBottom);
}
- private static Insets insetsOrNull(Rect insets) {
- return insets != null ? Insets.of(insets) : null;
- }
-
/**
* @return whether system window insets have been consumed.
*/
@@ -699,11 +748,13 @@
*/
public static class Builder {
- private Insets mSystemWindowInsets;
- private Insets mStableInsets;
+ private final Insets[] mTypeInsetsMap;
+ private final Insets[] mTypeMaxInsetsMap;
+ private boolean mSystemInsetsConsumed = true;
+ private boolean mStableInsetsConsumed = true;
+
private DisplayCutout mDisplayCutout;
- private Insets mWindowDecorInsets;
private boolean mIsRound;
private boolean mAlwaysConsumeNavBar;
@@ -711,6 +762,8 @@
* Creates a builder where all insets are initially consumed.
*/
public Builder() {
+ mTypeInsetsMap = new Insets[SIZE];
+ mTypeMaxInsetsMap = new Insets[SIZE];
}
/**
@@ -719,12 +772,11 @@
* @param insets the instance to initialize from.
*/
public Builder(WindowInsets insets) {
- mSystemWindowInsets = insets.mSystemWindowInsetsConsumed ? null
- : insets.mSystemWindowInsets;
- mStableInsets = insets.mStableInsetsConsumed ? null : insets.mStableInsets;
+ mTypeInsetsMap = insets.mTypeInsetsMap.clone();
+ mTypeMaxInsetsMap = insets.mTypeMaxInsetsMap.clone();
+ mSystemInsetsConsumed = insets.mSystemWindowInsetsConsumed;
+ mStableInsetsConsumed = insets.mStableInsetsConsumed;
mDisplayCutout = displayCutoutCopyConstructorArgument(insets);
- mWindowDecorInsets = insets.mWindowDecorInsetsConsumed ? null
- : insets.mWindowDecorInsets;
mIsRound = insets.mIsRound;
mAlwaysConsumeNavBar = insets.mAlwaysConsumeNavBar;
}
@@ -742,7 +794,66 @@
@NonNull
public Builder setSystemWindowInsets(@NonNull Insets systemWindowInsets) {
Preconditions.checkNotNull(systemWindowInsets);
- mSystemWindowInsets = systemWindowInsets;
+ assignCompatInsets(mTypeInsetsMap, systemWindowInsets.toRect());
+ mSystemInsetsConsumed = false;
+ return this;
+ }
+
+ /**
+ * Sets the insets of a specific window type in pixels.
+ *
+ * <p>The insets represents the area of a a window that is partially or fully obscured by
+ * the system windows identified by {@code typeMask}.
+ * </p>
+ *
+ * @see #getInsets(int)
+ *
+ * @param typeMask The bitmask of {@link InsetType} to set the insets for.
+ * @param insets The insets to set.
+ *
+ * @return itself
+ * @hide pending unhide
+ */
+ @NonNull
+ public Builder setInsets(@InsetType int typeMask, @NonNull Insets insets) {
+ Preconditions.checkNotNull(insets);
+ WindowInsets.setInsets(mTypeInsetsMap, typeMask, insets);
+ mSystemInsetsConsumed = false;
+ return this;
+ }
+
+ /**
+ * Sets the maximum amount of insets a specific window type in pixels.
+ *
+ * <p>The maximum insets represents the area of a a window that that <b>may</b> be partially
+ * or fully obscured by the system windows identified by {@code typeMask}. This value does
+ * not change based on the visibility state of those elements. for example, if the status
+ * bar is normally shown, but temporarily hidden, the maximum inset will still provide the
+ * inset associated with the status bar being shown.</p>
+ *
+ * @see #getMaxInsets(int)
+ *
+ * @param typeMask The bitmask of {@link InsetType} to set the insets for.
+ * @param insets The insets to set.
+ *
+ * @return itself
+ *
+ * @throws IllegalArgumentException If {@code typeMask} contains {@link Type#ime()}. Maximum
+ * insets are not available for this type as the height of
+ * the IME is dynamic depending on the {@link EditorInfo}
+ * of the currently focused view, as well as the UI
+ * state of the IME.
+ * @hide pending unhide
+ */
+ @NonNull
+ public Builder setMaxInsets(@InsetType int typeMask, @NonNull Insets insets)
+ throws IllegalArgumentException{
+ if (typeMask == IME) {
+ throw new IllegalArgumentException("Maximum inset not available for IME");
+ }
+ Preconditions.checkNotNull(insets);
+ WindowInsets.setInsets(mTypeMaxInsetsMap, typeMask, insets);
+ mStableInsetsConsumed = false;
return this;
}
@@ -761,7 +872,8 @@
@NonNull
public Builder setStableInsets(@NonNull Insets stableInsets) {
Preconditions.checkNotNull(stableInsets);
- mStableInsets = stableInsets;
+ assignCompatInsets(mTypeInsetsMap, stableInsets.toRect());
+ mStableInsetsConsumed = false;
return this;
}
@@ -780,14 +892,6 @@
/** @hide */
@NonNull
- public Builder setWindowDecorInsets(@NonNull Insets windowDecorInsets) {
- Preconditions.checkNotNull(windowDecorInsets);
- mWindowDecorInsets = windowDecorInsets;
- return this;
- }
-
- /** @hide */
- @NonNull
public Builder setRound(boolean round) {
mIsRound = round;
return this;
@@ -807,8 +911,9 @@
*/
@NonNull
public WindowInsets build() {
- return new WindowInsets(mSystemWindowInsets, mWindowDecorInsets, mStableInsets,
- mIsRound, mAlwaysConsumeNavBar, mDisplayCutout);
+ return new WindowInsets(mSystemInsetsConsumed ? null : mTypeInsetsMap,
+ mStableInsetsConsumed ? null : mTypeMaxInsetsMap, mIsRound,
+ mAlwaysConsumeNavBar, mDisplayCutout);
}
}
@@ -818,10 +923,31 @@
*/
public static final class Type {
- static final int TOP_BAR = 0x1;
+ static final int FIRST = 0x1;
+ static final int TOP_BAR = FIRST;
+
static final int IME = 0x2;
static final int SIDE_BARS = 0x4;
- static final int WINDOW_DECOR = 0x8;
+
+ static final int LAST = 0x8;
+ static final int SIZE = 4;
+ static final int WINDOW_DECOR = LAST;
+
+ static int indexOf(@InsetType int type) {
+ switch (type) {
+ case TOP_BAR:
+ return 0;
+ case IME:
+ return 1;
+ case SIDE_BARS:
+ return 2;
+ case WINDOW_DECOR:
+ return 3;
+ default:
+ throw new IllegalArgumentException("type needs to be >= FIRST and <= LAST,"
+ + " type=" + type);
+ }
+ }
private Type() {
}
@@ -870,6 +996,15 @@
}
/**
+ * @return Inset types representing the list of bars that traditionally were denoted as
+ * system insets.
+ * @hide
+ */
+ static @InsetType int compatSystemInsets() {
+ return TOP_BAR | SIDE_BARS | IME;
+ }
+
+ /**
* @return All inset types combined.
*/
public static @InsetType int all() {
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index 44a381e..8be887b 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -199,6 +199,8 @@
final int flags = 0; // TODO(b/111276913): get proper flags
try {
+ if (mSystemServerInterface == null) return;
+
mSystemServerInterface.startSession(mContext.getUserId(), mApplicationToken,
componentName, mId, flags, new IResultReceiver.Stub() {
@Override
@@ -383,6 +385,8 @@
}
try {
+ if (mSystemServerInterface == null) return;
+
mSystemServerInterface.finishSession(mContext.getUserId(), mId);
} catch (RemoteException e) {
Log.e(TAG, "Error destroying system-service session " + mId + " for "
diff --git a/core/java/android/view/textclassifier/GenerateLinksLogger.java b/core/java/android/view/textclassifier/GenerateLinksLogger.java
index 067513f..3996b27 100644
--- a/core/java/android/view/textclassifier/GenerateLinksLogger.java
+++ b/core/java/android/view/textclassifier/GenerateLinksLogger.java
@@ -39,7 +39,6 @@
public final class GenerateLinksLogger {
private static final String LOG_TAG = "GenerateLinksLogger";
- private static final boolean DEBUG_LOG_ENABLED = false;
private static final String ZERO = "0";
private final MetricsLogger mMetricsLogger;
@@ -128,8 +127,9 @@
}
private static void debugLog(LogMaker log) {
- if (!DEBUG_LOG_ENABLED) return;
-
+ if (!Log.ENABLE_FULL_LOGGING) {
+ return;
+ }
final String callId = Objects.toString(
log.getTaggedData(MetricsEvent.FIELD_LINKIFY_CALL_ID), "");
final String entityType = Objects.toString(
@@ -143,7 +143,7 @@
final int latencyMs = Integer.parseInt(
Objects.toString(log.getTaggedData(MetricsEvent.FIELD_LINKIFY_LATENCY), ZERO));
- Log.d(LOG_TAG,
+ Log.v(LOG_TAG,
String.format(Locale.US, "%s:%s %d links (%d/%d chars) %dms %s", callId, entityType,
numLinks, linkLength, textLength, latencyMs, log.getPackageName()));
}
diff --git a/core/java/android/view/textclassifier/Log.java b/core/java/android/view/textclassifier/Log.java
index ef19ee5..5c60c09 100644
--- a/core/java/android/view/textclassifier/Log.java
+++ b/core/java/android/view/textclassifier/Log.java
@@ -16,10 +16,12 @@
package android.view.textclassifier;
-import android.util.Slog;
-
/**
* Logging for android.view.textclassifier package.
+ * <p>
+ * To enable full log:
+ * 1. adb shell setprop log.tag.androidtc VERBOSE
+ * 2. adb shell stop && adb shell start
*/
final class Log {
@@ -27,24 +29,32 @@
* true: Enables full logging.
* false: Limits logging to debug level.
*/
- private static final boolean ENABLE_FULL_LOGGING = false;
+ static final boolean ENABLE_FULL_LOGGING =
+ android.util.Log.isLoggable(TextClassifier.DEFAULT_LOG_TAG, android.util.Log.VERBOSE);
- private Log() {}
+ private Log() {
+ }
+
+ public static void v(String tag, String msg) {
+ if (ENABLE_FULL_LOGGING) {
+ android.util.Log.v(tag, msg);
+ }
+ }
public static void d(String tag, String msg) {
- Slog.d(tag, msg);
+ android.util.Log.d(tag, msg);
}
public static void w(String tag, String msg) {
- Slog.w(tag, msg);
+ android.util.Log.w(tag, msg);
}
public static void e(String tag, String msg, Throwable tr) {
if (ENABLE_FULL_LOGGING) {
- Slog.e(tag, msg, tr);
+ android.util.Log.e(tag, msg, tr);
} else {
final String trString = (tr != null) ? tr.getClass().getSimpleName() : "??";
- Slog.d(tag, String.format("%s (%s)", msg, trString));
+ android.util.Log.d(tag, String.format("%s (%s)", msg, trString));
}
}
}
diff --git a/core/java/android/view/textclassifier/SelectionSessionLogger.java b/core/java/android/view/textclassifier/SelectionSessionLogger.java
index cdacdd5..48a568a 100644
--- a/core/java/android/view/textclassifier/SelectionSessionLogger.java
+++ b/core/java/android/view/textclassifier/SelectionSessionLogger.java
@@ -39,7 +39,6 @@
public final class SelectionSessionLogger {
private static final String LOG_TAG = "SelectionSessionLogger";
- private static final boolean DEBUG_LOG_ENABLED = false;
static final String CLASSIFIER_ID = "androidtc";
private static final int START_EVENT_DELTA = MetricsEvent.FIELD_SELECTION_SINCE_START;
@@ -195,8 +194,9 @@
}
private static void debugLog(LogMaker log) {
- if (!DEBUG_LOG_ENABLED) return;
-
+ if (!Log.ENABLE_FULL_LOGGING) {
+ return;
+ }
final String widgetType = Objects.toString(log.getTaggedData(WIDGET_TYPE), UNKNOWN);
final String widgetVersion = Objects.toString(log.getTaggedData(WIDGET_VERSION), "");
final String widget = widgetVersion.isEmpty()
@@ -221,7 +221,7 @@
final int eventEnd = Integer.parseInt(
Objects.toString(log.getTaggedData(EVENT_END), ZERO));
- Log.d(LOG_TAG,
+ Log.v(LOG_TAG,
String.format(Locale.US, "%2d: %s/%s/%s, range=%d,%d - smart_range=%d,%d (%s/%s)",
index, type, subType, entity, eventStart, eventEnd, smartStart, smartEnd,
widget, model));
diff --git a/core/java/android/view/textclassifier/TextClassificationSession.java b/core/java/android/view/textclassifier/TextClassificationSession.java
index 4c64198..45668c0 100644
--- a/core/java/android/view/textclassifier/TextClassificationSession.java
+++ b/core/java/android/view/textclassifier/TextClassificationSession.java
@@ -27,7 +27,6 @@
@WorkerThread
final class TextClassificationSession implements TextClassifier {
- /* package */ static final boolean DEBUG_LOG_ENABLED = true;
private static final String LOG_TAG = "TextClassificationSession";
private final TextClassifier mDelegate;
@@ -133,9 +132,7 @@
if (event.getEventType() != SelectionEvent.EVENT_SELECTION_STARTED
&& mStartEvent == null) {
- if (DEBUG_LOG_ENABLED) {
- Log.d(LOG_TAG, "Selection session not yet started. Ignoring event");
- }
+ Log.d(LOG_TAG, "Selection session not yet started. Ignoring event");
return false;
}
diff --git a/core/java/android/view/textclassifier/TextClassifierEvent.java b/core/java/android/view/textclassifier/TextClassifierEvent.java
index f2fea02..b84f6f0 100644
--- a/core/java/android/view/textclassifier/TextClassifierEvent.java
+++ b/core/java/android/view/textclassifier/TextClassifierEvent.java
@@ -116,6 +116,8 @@
public static final int TYPE_SELECTION_RESET = 18;
/** User composed a reply. */
public static final int TYPE_MANUAL_REPLY = 19;
+ /** TextClassifier generated some actions */
+ public static final int TYPE_ACTIONS_GENERATED = 20;
@Category private final int mEventCategory;
@Type private final int mEventType;
diff --git a/core/java/android/view/textclassifier/TextClassifierEventTronLogger.java b/core/java/android/view/textclassifier/TextClassifierEventTronLogger.java
new file mode 100644
index 0000000..439e594
--- /dev/null
+++ b/core/java/android/view/textclassifier/TextClassifierEventTronLogger.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2018 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.view.textclassifier;
+
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_SELECTION_ENTITY_TYPE;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_SELECTION_SESSION_ID;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_SELECTION_WIDGET_TYPE;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_SELECTION_WIDGET_VERSION;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXTCLASSIFIER_MODEL;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_EVENT_TIME;
+
+import android.metrics.LogMaker;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.util.Preconditions;
+
+
+/**
+ * Log {@link TextClassifierEvent} by using Tron, only support language detection and
+ * conversation actions.
+ *
+ * @hide
+ */
+public final class TextClassifierEventTronLogger {
+
+ private static final String TAG = "TCEventTronLogger";
+
+ private final MetricsLogger mMetricsLogger;
+
+ public TextClassifierEventTronLogger() {
+ mMetricsLogger = new MetricsLogger();
+ }
+
+ @VisibleForTesting
+ public TextClassifierEventTronLogger(MetricsLogger metricsLogger) {
+ mMetricsLogger = Preconditions.checkNotNull(metricsLogger);
+ }
+
+ /** Emits a text classifier event to the logs. */
+ public void writeEvent(TextClassifierEvent event) {
+ Preconditions.checkNotNull(event);
+ int category = getCategory(event);
+ if (category == -1) {
+ Log.w(TAG, "Unknown category: " + event.getEventCategory());
+ return;
+ }
+ final LogMaker log = new LogMaker(category)
+ .setType(getLogType(event))
+ .addTaggedData(FIELD_SELECTION_SESSION_ID, event.getResultId())
+ .addTaggedData(FIELD_TEXT_CLASSIFIER_EVENT_TIME, event.getEventTime())
+ .addTaggedData(FIELD_TEXTCLASSIFIER_MODEL,
+ SelectionSessionLogger.SignatureParser.getModelName(event.getResultId()))
+ .addTaggedData(FIELD_SELECTION_ENTITY_TYPE, event.getEntityType());
+ TextClassificationContext eventContext = event.getEventContext();
+ if (eventContext != null) {
+ log.addTaggedData(FIELD_SELECTION_WIDGET_TYPE, eventContext.getWidgetType());
+ log.addTaggedData(FIELD_SELECTION_WIDGET_VERSION, eventContext.getWidgetVersion());
+ log.setPackageName(eventContext.getPackageName());
+ }
+ mMetricsLogger.write(log);
+ debugLog(log);
+ }
+
+ private static int getCategory(TextClassifierEvent event) {
+ switch (event.getEventCategory()) {
+ case TextClassifierEvent.CATEGORY_CONVERSATION_ACTIONS:
+ return MetricsEvent.CONVERSATION_ACTIONS;
+ case TextClassifierEvent.CATEGORY_LANGUAGE_DETECTION:
+ return MetricsEvent.LANGUAGE_DETECTION;
+ }
+ return -1;
+ }
+
+ private static int getLogType(TextClassifierEvent event) {
+ switch (event.getEventType()) {
+ case TextClassifierEvent.TYPE_SMART_ACTION:
+ return MetricsEvent.ACTION_TEXT_SELECTION_SMART_SHARE;
+ case TextClassifierEvent.TYPE_ACTIONS_SHOWN:
+ return MetricsEvent.ACTION_TEXT_CLASSIFIER_ACTIONS_SHOWN;
+ case TextClassifierEvent.TYPE_MANUAL_REPLY:
+ return MetricsEvent.ACTION_TEXT_CLASSIFIER_MANUAL_REPLY;
+ default:
+ return MetricsEvent.VIEW_UNKNOWN;
+ }
+ }
+
+ private String toCategoryName(int category) {
+ switch (category) {
+ case MetricsEvent.CONVERSATION_ACTIONS:
+ return "conversation_actions";
+ case MetricsEvent.LANGUAGE_DETECTION:
+ return "language_detection";
+ }
+ return "unknown";
+ }
+
+ private String toEventName(int logType) {
+ switch (logType) {
+ case MetricsEvent.ACTION_TEXT_SELECTION_SMART_SHARE:
+ return "smart_share";
+ case MetricsEvent.ACTION_TEXT_CLASSIFIER_ACTIONS_SHOWN:
+ return "actions_shown";
+ case MetricsEvent.ACTION_TEXT_CLASSIFIER_MANUAL_REPLY:
+ return "manual_reply";
+ case MetricsEvent.ACTION_TEXT_CLASSIFIER_ACTIONS_GENERATED:
+ return "actions_generated";
+ }
+ return "unknown";
+ }
+
+ private void debugLog(LogMaker log) {
+ if (!Log.ENABLE_FULL_LOGGING) {
+ return;
+ }
+ final String id = String.valueOf(log.getTaggedData(FIELD_SELECTION_SESSION_ID));
+ final String categoryName = toCategoryName(log.getCategory());
+ final String eventName = toEventName(log.getType());
+ final String widgetType = String.valueOf(log.getTaggedData(FIELD_SELECTION_WIDGET_TYPE));
+ final String widgetVersion =
+ String.valueOf(log.getTaggedData(FIELD_SELECTION_WIDGET_VERSION));
+ final String model = String.valueOf(log.getTaggedData(FIELD_TEXTCLASSIFIER_MODEL));
+ final String entityType = String.valueOf(log.getTaggedData(FIELD_SELECTION_ENTITY_TYPE));
+
+ StringBuilder builder = new StringBuilder();
+ builder.append("writeEvent: ");
+ builder.append("id=").append(id);
+ builder.append(", category=").append(categoryName);
+ builder.append(", eventName=").append(eventName);
+ builder.append(", widgetType=").append(widgetType);
+ builder.append(", widgetVersion=").append(widgetVersion);
+ builder.append(", model=").append(model);
+ builder.append(", entityType=").append(entityType);
+
+ Log.v(TAG, builder.toString());
+ }
+}
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index fcd06c3..d5b9eb1 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -115,9 +115,9 @@
@GuardedBy("mLock") // Do not access outside this lock.
private ActionsSuggestionsModel mActionsImpl;
- private final Object mLoggerLock = new Object();
- @GuardedBy("mLoggerLock") // Do not access outside this lock.
- private SelectionSessionLogger mSessionLogger;
+ private final SelectionSessionLogger mSessionLogger = new SelectionSessionLogger();
+ private final TextClassifierEventTronLogger mTextClassifierEventTronLogger =
+ new TextClassifierEventTronLogger();
private final TextClassificationConstants mSettings;
@@ -337,12 +337,7 @@
@Override
public void onSelectionEvent(SelectionEvent event) {
Preconditions.checkNotNull(event);
- synchronized (mLoggerLock) {
- if (mSessionLogger == null) {
- mSessionLogger = new SelectionSessionLogger();
- }
- mSessionLogger.writeEvent(event);
- }
+ mSessionLogger.writeEvent(event);
}
@Override
@@ -350,6 +345,7 @@
if (DEBUG) {
Log.d(DEFAULT_LOG_TAG, "onTextClassifierEvent() called with: event = [" + event + "]");
}
+ mTextClassifierEventTronLogger.writeEvent(event);
}
/** @inheritDoc */
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index 886718d..afe46701 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -104,8 +104,9 @@
private final int mDefaultHorizontalSourceToMagnifierOffset;
// The vertical offset between the source and window coords when #show(float, float) is used.
private final int mDefaultVerticalSourceToMagnifierOffset;
- // Whether the magnifier will be clamped inside the main surface and not overlap system insets.
- private final boolean mForcePositionWithinWindowSystemInsetsBounds;
+ // Whether the area where the magnifier can be positioned will be clipped to the main window
+ // and within system insets.
+ private final boolean mClippingEnabled;
// The behavior of the left bound of the rectangle where the content can be copied from.
private @SourceBound int mLeftContentBound;
// The behavior of the top bound of the rectangle where the content can be copied from.
@@ -165,7 +166,7 @@
params.mOverlay = new ColorDrawable(a.getColor(
R.styleable.Magnifier_magnifierColorOverlay, Color.TRANSPARENT));
a.recycle();
- params.mForcePositionWithinWindowSystemInsetsBounds = true;
+ params.mClippingEnabled = true;
params.mLeftContentBound = SOURCE_BOUND_MAX_VISIBLE;
params.mTopContentBound = SOURCE_BOUND_MAX_IN_SURFACE;
params.mRightContentBound = SOURCE_BOUND_MAX_VISIBLE;
@@ -203,8 +204,7 @@
params.mHorizontalDefaultSourceToMagnifierOffset;
mDefaultVerticalSourceToMagnifierOffset =
params.mVerticalDefaultSourceToMagnifierOffset;
- mForcePositionWithinWindowSystemInsetsBounds =
- params.mForcePositionWithinWindowSystemInsetsBounds;
+ mClippingEnabled = params.mClippingEnabled;
mLeftContentBound = params.mLeftContentBound;
mTopContentBound = params.mTopContentBound;
mRightContentBound = params.mRightContentBound;
@@ -447,7 +447,7 @@
}
/**
- * Returns the overlay to be drawn on the top of the magnifier content, or
+ * Returns the overlay to be drawn on the top of the magnifier, or
* {@code null} if no overlay should be drawn.
* @return the overlay
* @see Magnifier.Builder#setOverlay(Drawable)
@@ -459,13 +459,15 @@
/**
* Returns whether the magnifier position will be adjusted such that the magnifier will be
- * fully within the bounds of the main application window, by also avoiding any overlap with
- * system insets (such as the one corresponding to the status bar).
+ * fully within the bounds of the main application window, by also avoiding any overlap
+ * with system insets (such as the one corresponding to the status bar) i.e. whether the
+ * area where the magnifier can be positioned will be clipped to the main application window
+ * and the system insets.
* @return whether the magnifier position will be adjusted
- * @see Magnifier.Builder#setForcePositionWithinWindowSystemInsetsBounds(boolean)
+ * @see Magnifier.Builder#setClippingEnabled(boolean)
*/
- public boolean isForcePositionWithinWindowSystemInsetsBounds() {
- return mForcePositionWithinWindowSystemInsetsBounds;
+ public boolean isClippingEnabled() {
+ return mClippingEnabled;
}
/**
@@ -711,7 +713,7 @@
* @return the current window coordinates, after they are clamped inside the parent surface
*/
private Point getCurrentClampedWindowCoordinates() {
- if (!mForcePositionWithinWindowSystemInsetsBounds) {
+ if (!mClippingEnabled) {
// No position adjustment should be done, so return the raw coordinates.
return new Point(mWindowCoords);
}
@@ -835,8 +837,8 @@
mContentWidth = width;
mContentHeight = height;
- mOffsetX = (int) (0.1f * width);
- mOffsetY = (int) (0.1f * height);
+ mOffsetX = (int) (1.05f * elevation);
+ mOffsetY = (int) (1.05f * elevation);
// Setup the surface we will use for drawing the content and shadow.
mSurfaceWidth = mContentWidth + 2 * mOffsetX;
mSurfaceHeight = mContentHeight + 2 * mOffsetY;
@@ -1136,7 +1138,7 @@
private @Nullable Drawable mOverlay;
private int mHorizontalDefaultSourceToMagnifierOffset;
private int mVerticalDefaultSourceToMagnifierOffset;
- private boolean mForcePositionWithinWindowSystemInsetsBounds;
+ private boolean mClippingEnabled;
private @SourceBound int mLeftContentBound;
private @SourceBound int mTopContentBound;
private @SourceBound int mRightContentBound;
@@ -1164,7 +1166,7 @@
resources.getDimensionPixelSize(R.dimen.default_magnifier_vertical_offset);
mOverlay = new ColorDrawable(resources.getColor(
R.color.default_magnifier_color_overlay, null));
- mForcePositionWithinWindowSystemInsetsBounds = true;
+ mClippingEnabled = true;
mLeftContentBound = SOURCE_BOUND_MAX_VISIBLE;
mTopContentBound = SOURCE_BOUND_MAX_VISIBLE;
mRightContentBound = SOURCE_BOUND_MAX_VISIBLE;
@@ -1227,11 +1229,11 @@
}
/**
- * Sets an overlay that will be drawn on the top of the magnifier content.
- * In general, the overlay should not be opaque, in order to let the expected magnifier
- * content be partially visible. The default overlay is {@code null} (no overlay).
- * As an example, TextView applies a white {@link ColorDrawable} overlay with
- * 5% alpha, aiming to make the magnifier distinguishable when shown in dark
+ * Sets an overlay that will be drawn on the top of the magnifier.
+ * In general, the overlay should not be opaque, in order to let the magnified
+ * content be partially visible in the magnifier. The default overlay is {@code null}
+ * (no overlay). As an example, TextView applies a white {@link ColorDrawable}
+ * overlay with 5% alpha, aiming to make the magnifier distinguishable when shown in dark
* application regions. To disable the overlay, the parameter should be set
* to {@code null}. If not null, the overlay will be automatically redrawn
* when the drawable is invalidated. To achieve this, the magnifier will set a new
@@ -1265,22 +1267,24 @@
* Defines the behavior of the magnifier when it is requested to position outside the
* surface of the main application window. The default value is {@code true}, which means
* that the position will be adjusted such that the magnifier will be fully within the
- * bounds of the main application window, by also avoiding any overlap with system insets
- * (such as the one corresponding to the status bar). If you require a custom behavior, this
- * flag should be set to {@code false}, meaning that the magnifier will be able to cross the
- * main application surface boundaries (and also overlap the system insets). This should be
- * handled with care, when passing coordinates to {@link #show(float, float)}; note that:
+ * bounds of the main application window, while also avoiding any overlap with system insets
+ * (such as the one corresponding to the status bar). If this flag is set to {@code false},
+ * the area where the magnifier can be positioned will no longer be clipped, so the
+ * magnifier will be able to extend outside the main application window boundaries (and also
+ * overlap the system insets). This can be useful if you require a custom behavior, but it
+ * should be handled with care, when passing coordinates to {@link #show(float, float)};
+ * note that:
* <ul>
* <li>in a multiwindow context, if the magnifier crosses the boundary between the two
* windows, it will not be able to show over the window of the other application</li>
* <li>if the magnifier overlaps the status bar, there is no guarantee about which one
* will be displayed on top. This should be handled with care.</li>
* </ul>
- * @param force whether the magnifier position will be adjusted
+ * @param clip whether the magnifier position will be adjusted
*/
@NonNull
- public Builder setForcePositionWithinWindowSystemInsetsBounds(boolean force) {
- mForcePositionWithinWindowSystemInsetsBounds = force;
+ public Builder setClippingEnabled(boolean clip) {
+ mClippingEnabled = clip;
return this;
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 0ea8579..7f63653 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -3520,7 +3520,7 @@
*/
@android.view.RemotableViewMethod
public void setTextSelectHandle(@DrawableRes int textSelectHandle) {
- Preconditions.checkArgumentPositive(textSelectHandle,
+ Preconditions.checkArgument(textSelectHandle != 0,
"The text select handle should be a valid drawable resource id.");
setTextSelectHandle(mContext.getDrawable(textSelectHandle));
}
@@ -3577,7 +3577,7 @@
*/
@android.view.RemotableViewMethod
public void setTextSelectHandleLeft(@DrawableRes int textSelectHandleLeft) {
- Preconditions.checkArgumentPositive(textSelectHandleLeft,
+ Preconditions.checkArgument(textSelectHandleLeft != 0,
"The text select left handle should be a valid drawable resource id.");
setTextSelectHandleLeft(mContext.getDrawable(textSelectHandleLeft));
}
@@ -3634,7 +3634,7 @@
*/
@android.view.RemotableViewMethod
public void setTextSelectHandleRight(@DrawableRes int textSelectHandleRight) {
- Preconditions.checkArgumentPositive(textSelectHandleRight,
+ Preconditions.checkArgument(textSelectHandleRight != 0,
"The text select right handle should be a valid drawable resource id.");
setTextSelectHandleRight(mContext.getDrawable(textSelectHandleRight));
}
@@ -3667,9 +3667,7 @@
* @see #setTextCursorDrawable(int)
* @attr ref android.R.styleable#TextView_textCursorDrawable
*/
- public void setTextCursorDrawable(@NonNull Drawable textCursorDrawable) {
- Preconditions.checkNotNull(textCursorDrawable,
- "The cursor drawable should not be null.");
+ public void setTextCursorDrawable(@Nullable Drawable textCursorDrawable) {
mCursorDrawable = textCursorDrawable;
mCursorDrawableRes = 0;
if (mEditor != null) {
@@ -3687,8 +3685,6 @@
* @attr ref android.R.styleable#TextView_textCursorDrawable
*/
public void setTextCursorDrawable(@DrawableRes int textCursorDrawable) {
- Preconditions.checkArgumentPositive(textCursorDrawable,
- "The cursor drawable should be a valid drawable resource id.");
setTextCursorDrawable(mContext.getDrawable(textCursorDrawable));
}
diff --git a/core/java/android/widget/TimePickerClockDelegate.java b/core/java/android/widget/TimePickerClockDelegate.java
index 6a3a8f0..dc9a585 100644
--- a/core/java/android/widget/TimePickerClockDelegate.java
+++ b/core/java/android/widget/TimePickerClockDelegate.java
@@ -454,6 +454,7 @@
(RelativeLayout.LayoutParams) mAmPmLayout.getLayoutParams();
if (params.getRule(RelativeLayout.RIGHT_OF) != 0
|| params.getRule(RelativeLayout.LEFT_OF) != 0) {
+ final int margin = (int) (mContext.getResources().getDisplayMetrics().density * 8);
// Horizontal mode, with AM/PM appearing to left/right of hours and minutes.
final boolean isAmPmAtLeft;
if (TextUtils.getLayoutDirectionFromLocale(mLocale) == View.LAYOUT_DIRECTION_LTR) {
@@ -461,10 +462,6 @@
} else {
isAmPmAtLeft = !isAmPmAtStart;
}
- if (mIsAmPmAtLeft == isAmPmAtLeft) {
- // AM/PM is already at the correct location. No change needed.
- return;
- }
if (isAmPmAtLeft) {
params.removeRule(RelativeLayout.RIGHT_OF);
@@ -473,6 +470,14 @@
params.removeRule(RelativeLayout.LEFT_OF);
params.addRule(RelativeLayout.RIGHT_OF, mMinuteView.getId());
}
+
+ if (isAmPmAtStart) {
+ params.setMarginStart(0);
+ params.setMarginEnd(margin);
+ } else {
+ params.setMarginStart(margin);
+ params.setMarginEnd(0);
+ }
mIsAmPmAtLeft = isAmPmAtLeft;
} else if (params.getRule(RelativeLayout.BELOW) != 0
|| params.getRule(RelativeLayout.ABOVE) != 0) {
diff --git a/core/java/com/android/internal/net/NetworkStatsFactory.java b/core/java/com/android/internal/net/NetworkStatsFactory.java
index 0a7cff6..9bacf9b 100644
--- a/core/java/com/android/internal/net/NetworkStatsFactory.java
+++ b/core/java/com/android/internal/net/NetworkStatsFactory.java
@@ -20,6 +20,7 @@
import static android.net.NetworkStats.TAG_ALL;
import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStats.UID_ALL;
+
import static com.android.server.NetworkManagementSocketTagger.kernelToTag;
import android.annotation.Nullable;
@@ -33,10 +34,8 @@
import libcore.io.IoUtils;
-import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
-import java.io.FileReader;
import java.io.IOException;
import java.net.ProtocolException;
import java.util.Arrays;
@@ -127,7 +126,7 @@
}
public NetworkStatsFactory() {
- this(new File("/proc/"), new File("/sys/fs/bpf/traffic_uid_stats_map").exists());
+ this(new File("/proc/"), new File("/sys/fs/bpf/map_netd_app_uid_stats_map").exists());
}
@VisibleForTesting
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index e2c23de..cdaf33f 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -232,7 +232,7 @@
// This is the caption view for the window, containing the caption and window control
// buttons. The visibility of this decor depends on the workspace and the window type.
// If the window type does not require such a view, this member might be null.
- DecorCaptionView mDecorCaptionView;
+ private DecorCaptionView mDecorCaptionView;
private boolean mWindowResizeCallbacksAdded = false;
private Drawable.Callback mLastBackgroundDrawableCb = null;
@@ -977,6 +977,7 @@
@Override
public void onWindowSystemUiVisibilityChanged(int visible) {
updateColorViews(null /* insets */, true /* animate */);
+ updateDecorCaptionStatus(getResources().getConfiguration());
}
@Override
@@ -1855,8 +1856,27 @@
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- final boolean displayWindowDecor =
- newConfig.windowConfiguration.hasWindowDecorCaption();
+ updateDecorCaptionStatus(newConfig);
+
+ updateAvailableWidth();
+ initializeElevation();
+ }
+
+ /**
+ * Determines if the workspace is entirely covered by the window.
+ * @return {@code true} when the window is filling the entire screen/workspace.
+ **/
+ private boolean isFillingScreen(Configuration config) {
+ final boolean isFullscreen = config.windowConfiguration.getWindowingMode()
+ == WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+ return isFullscreen && (0 != ((getWindowSystemUiVisibility() | getSystemUiVisibility())
+ & (View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+ | View.SYSTEM_UI_FLAG_IMMERSIVE | View.SYSTEM_UI_FLAG_LOW_PROFILE)));
+ }
+
+ private void updateDecorCaptionStatus(Configuration config) {
+ final boolean displayWindowDecor = config.windowConfiguration.hasWindowDecorCaption()
+ && !isFillingScreen(config);
if (mDecorCaptionView == null && displayWindowDecor) {
// Configuration now requires a caption.
final LayoutInflater inflater = mWindow.getLayoutInflater();
@@ -1875,9 +1895,6 @@
mDecorCaptionView.onConfigurationChanged(displayWindowDecor);
enableCaption(displayWindowDecor);
}
-
- updateAvailableWidth();
- initializeElevation();
}
void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
diff --git a/core/java/com/android/internal/policy/ScreenDecorationsUtils.java b/core/java/com/android/internal/policy/ScreenDecorationsUtils.java
index 100c6ee..adf7692 100644
--- a/core/java/com/android/internal/policy/ScreenDecorationsUtils.java
+++ b/core/java/com/android/internal/policy/ScreenDecorationsUtils.java
@@ -16,10 +16,10 @@
package com.android.internal.policy;
-import com.android.internal.R;
-
import android.content.res.Resources;
+import com.android.internal.R;
+
/**
* Utility functions for screen decorations used by both window manager and System UI.
*/
@@ -31,15 +31,19 @@
* scaling, this means that we don't have to reload them on config changes.
*/
public static float getWindowCornerRadius(Resources resources) {
+ if (!supportsRoundedCornersOnWindows(resources)) {
+ return 0f;
+ }
+
// Radius that should be used in case top or bottom aren't defined.
float defaultRadius = resources.getDimension(R.dimen.rounded_corner_radius);
float topRadius = resources.getDimension(R.dimen.rounded_corner_radius_top);
- if (topRadius == 0) {
+ if (topRadius == 0f) {
topRadius = defaultRadius;
}
float bottomRadius = resources.getDimension(R.dimen.rounded_corner_radius_bottom);
- if (bottomRadius == 0) {
+ if (bottomRadius == 0f) {
bottomRadius = defaultRadius;
}
@@ -47,4 +51,11 @@
// completely cover the display.
return Math.min(topRadius, bottomRadius);
}
+
+ /**
+ * If live rounded corners are supported on windows.
+ */
+ public static boolean supportsRoundedCornersOnWindows(Resources resources) {
+ return resources.getBoolean(R.bool.config_supportsRoundedCornersOnWindows);
+ }
}
diff --git a/core/java/com/android/internal/widget/DecorCaptionView.java b/core/java/com/android/internal/widget/DecorCaptionView.java
index b419113..21558d3 100644
--- a/core/java/com/android/internal/widget/DecorCaptionView.java
+++ b/core/java/com/android/internal/widget/DecorCaptionView.java
@@ -319,23 +319,12 @@
mOwner.notifyRestrictedCaptionAreaCallback(mMaximize.getLeft(), mMaximize.getTop(),
mClose.getRight(), mClose.getBottom());
}
- /**
- * Determine if the workspace is entirely covered by the window.
- * @return Returns true when the window is filling the entire screen/workspace.
- **/
- private boolean isFillingScreen() {
- return (0 != ((getWindowSystemUiVisibility() | getSystemUiVisibility()) &
- (View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
- View.SYSTEM_UI_FLAG_IMMERSIVE | View.SYSTEM_UI_FLAG_LOW_PROFILE)));
- }
/**
* Updates the visibility of the caption.
**/
private void updateCaptionVisibility() {
- // Don't show the caption if the window has e.g. entered full screen.
- boolean invisible = isFillingScreen() || !mShow;
- mCaption.setVisibility(invisible ? GONE : VISIBLE);
+ mCaption.setVisibility(mShow ? VISIBLE : GONE);
mCaption.setOnTouchListener(this);
}
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 9ae05f4..4b0ab5b 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -231,7 +231,7 @@
}
if (isMutable && isHardware) {
- doThrowIAE(env, "Bitmaps with Config.HARWARE are always immutable");
+ doThrowIAE(env, "Bitmaps with Config.HARDWARE are always immutable");
return nullObjectReturn("Cannot create mutable hardware bitmap");
}
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 3329e20..f201ceb 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -875,6 +875,11 @@
return IPCThreadState::self()->getCallingUid();
}
+static jboolean android_os_Binder_isHandlingTransaction()
+{
+ return IPCThreadState::self()->isServingCall();
+}
+
static jlong android_os_Binder_clearCallingIdentity()
{
return IPCThreadState::self()->clearCallingIdentity();
@@ -960,6 +965,8 @@
// @CriticalNative
{ "getCallingUid", "()I", (void*)android_os_Binder_getCallingUid },
// @CriticalNative
+ { "isHandlingTransaction", "()Z", (void*)android_os_Binder_isHandlingTransaction },
+ // @CriticalNative
{ "clearCallingIdentity", "()J", (void*)android_os_Binder_clearCallingIdentity },
{ "restoreCallingIdentity", "(J)V", (void*)android_os_Binder_restoreCallingIdentity },
// @CriticalNative
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index f292d25..9ce6df1 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -870,14 +870,6 @@
transaction->setOverrideScalingMode(ctrl, scalingMode);
}
-static void nativeDestroyInTransaction(JNIEnv* env, jclass clazz,
- jlong transactionObj,
- jlong nativeObject) {
- auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
- auto ctrl = reinterpret_cast<SurfaceControl*>(nativeObject);
- transaction->destroySurface(ctrl);
-}
-
static jobject nativeGetHandle(JNIEnv* env, jclass clazz, jlong nativeObject) {
auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
return javaObjectForIBinder(env, ctrl->getHandle());
@@ -1048,8 +1040,6 @@
(void*)nativeSeverChildren } ,
{"nativeSetOverrideScalingMode", "(JJI)V",
(void*)nativeSetOverrideScalingMode },
- {"nativeDestroy", "(JJ)V",
- (void*)nativeDestroyInTransaction },
{"nativeGetHandle", "(J)Landroid/os/IBinder;",
(void*)nativeGetHandle },
{"nativeScreenshot", "(Landroid/os/IBinder;Landroid/graphics/Rect;IIZI)Landroid/graphics/GraphicBuffer;",
diff --git a/core/proto/android/app/window_configuration.proto b/core/proto/android/app/window_configuration.proto
index 2d15552..6cc1a40 100644
--- a/core/proto/android/app/window_configuration.proto
+++ b/core/proto/android/app/window_configuration.proto
@@ -29,4 +29,5 @@
optional .android.graphics.RectProto app_bounds = 1;
optional int32 windowing_mode = 2;
optional int32 activity_type = 3;
+ optional .android.graphics.RectProto bounds = 4;
}
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index a914369..a00fde9 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -436,11 +436,12 @@
// Ordered GPU debug layer list for GLES
// i.e. <layer1>:<layer2>:...:<layerN>
optional SettingProto debug_layers_gles = 7;
- // Apps opt in to load graphics driver from Game Update Package
- // instead of native graphcis driver through developer options.
+ // GUP - List of Apps selected to use Game Update Packages
optional SettingProto gup_dev_opt_in_apps = 8;
- // Apps on the black list that are forbidden to useGame Update Package.
- optional SettingProto gup_black_list = 9;
+ // GUP - List of Apps selected not to use Game Update Packages
+ optional SettingProto gup_dev_opt_out_apps = 9;
+ // GUP - List of Apps that are forbidden to use Game Update Packages
+ optional SettingProto gup_black_list = 10;
}
optional Gpu gpu = 59;
@@ -558,6 +559,8 @@
// ringer mode.
optional SettingProto mode_ringer = 75 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto apply_ramping_ringer = 147 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
message MultiSim {
option (android.msg_privacy).dest = DEST_EXPLICIT;
@@ -1017,5 +1020,5 @@
// Please insert fields in alphabetical order and group them into messages
// if possible (to avoid reaching the method limit).
- // Next tag = 147;
+ // Next tag = 148;
}
diff --git a/core/proto/android/stats/docsui/docsui_enums.proto b/core/proto/android/stats/docsui/docsui_enums.proto
index 6cb606a..655b5e3 100644
--- a/core/proto/android/stats/docsui/docsui_enums.proto
+++ b/core/proto/android/stats/docsui/docsui_enums.proto
@@ -33,13 +33,14 @@
MIME_UNKNOWN = 0;
MIME_NONE = 1;
MIME_ANY = 2;
- MIME_AUDIO = 3;
- MIME_IMAGE = 4;
- MIME_MESSAGE = 5;
- MIME_MULTIPART = 6;
- MIME_TEXT = 7;
- MIME_VIDEO = 8;
- MIME_OTHER = 9;
+ MIME_APPLICATION = 3;
+ MIME_AUDIO = 4;
+ MIME_IMAGE = 5;
+ MIME_MESSAGE = 6;
+ MIME_MULTIPART = 7;
+ MIME_TEXT = 8;
+ MIME_VIDEO = 9;
+ MIME_OTHER = 10;
}
enum Root {
@@ -163,6 +164,8 @@
ACTION_EXTRACT_TO = 29;
ACTION_VIEW_IN_APPLICATION = 30;
ACTION_INSPECTOR = 31;
+ ACTION_SEARCH_CHIP = 32;
+ ACTION_SEARCH_HISTORY = 33;
}
enum InvalidScopedAccess {
@@ -172,3 +175,20 @@
SCOPED_DIR_ACCESS_ERROR = 3;
SCOPED_DIR_ACCESS_DEPRECATED = 4;
}
+
+enum SearchType {
+ TYPE_UNKNOWN = 0;
+ TYPE_CHIP_IMAGES = 1;
+ TYPE_CHIP_AUDIOS = 2;
+ TYPE_CHIP_VIDEOS = 3;
+ TYPE_CHIP_DOCS = 4;
+ TYPE_SEARCH_HISTORY = 5;
+ TYPE_SEARCH_STRING = 6;
+}
+
+enum SearchMode {
+ SEARCH_UNKNOWN = 0;
+ SEARCH_KEYWORD = 1;
+ SEARCH_CHIPS = 2;
+ SEARCH_KEYWORD_N_CHIPS = 3;
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 0778304..449a7b3 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2256,7 +2256,7 @@
<!-- @SystemApi @TestApi @hide Allows an application to create/manage/remove stacks -->
<permission android:name="android.permission.MANAGE_ACTIVITY_STACKS"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature" />
<!-- @SystemApi @TestApi @hide Allows an application to embed other activities -->
<permission android:name="android.permission.ACTIVITY_EMBEDDING"
diff --git a/core/res/res/layout/time_picker_header_material.xml b/core/res/res/layout/time_picker_header_material.xml
index adb2b62..30c10b1 100644
--- a/core/res/res/layout/time_picker_header_material.xml
+++ b/core/res/res/layout/time_picker_header_material.xml
@@ -76,16 +76,14 @@
android:layout_height="wrap_content"
android:layout_toRightOf="@+id/minutes"
android:layout_alignBaseline="@+id/minutes"
- android:paddingStart="4dp"
- android:paddingEnd="4dp"
+ android:layout_marginStart="8dp"
+ android:layout_marginEnd="0dp"
android:orientation="vertical"
android:baselineAlignedChildIndex="1">
<RadioButton
android:id="@+id/am_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:paddingLeft="4dp"
- android:paddingRight="4dp"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:layout_marginBottom="-8dp"
@@ -101,8 +99,6 @@
android:id="@+id/pm_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:paddingLeft="4dp"
- android:paddingRight="4dp"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:textAppearance="@style/TextAppearance.Material.TimePicker.AmPmLabel"
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 8735557..140225e 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1404,6 +1404,13 @@
<integer-array name="config_autoBrightnessLevels">
</integer-array>
+ <!-- Timeout (in milliseconds) after which we remove the effects any user interactions might've
+ had on the brightness mapping. This timeout doesn't start until we transition to a
+ non-interactive display policy so that we don't reset while users are using their devices,
+ but also so that we don't erroneously keep the short-term model if the device is dozing
+ but the display is fully on. -->
+ <integer name="config_autoBrightnessShortTermModelTimeout">300000</integer>
+
<!-- Array of output values for LCD backlight corresponding to the lux values
in the config_autoBrightnessLevels array. This array should have size one greater
than the size of the config_autoBrightnessLevels array.
@@ -3641,4 +3648,8 @@
set in AndroidManifest.
{@see android.view.Display#FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS} -->
<string name="config_secondaryHomeComponent" translatable="false">com.android.launcher3/com.android.launcher3.SecondaryDisplayLauncher</string>
+
+ <!-- If device supports corner radius on windows.
+ This should be turned off on low-end devices to improve animation performance. -->
+ <bool name="config_supportsRoundedCornersOnWindows">true</bool>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index c2d97bc..f8c9037 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1964,6 +1964,7 @@
<java-symbol type="integer" name="config_screenBrightnessDark" />
<java-symbol type="integer" name="config_screenBrightnessDim" />
<java-symbol type="integer" name="config_screenBrightnessDoze" />
+ <java-symbol type="integer" name="config_autoBrightnessShortTermModelTimeout" />
<java-symbol type="integer" name="config_shutdownBatteryTemperature" />
<java-symbol type="integer" name="config_undockedHdmiRotation" />
<java-symbol type="integer" name="config_virtualKeyQuietTimeMillis" />
@@ -3514,6 +3515,7 @@
<java-symbol type="dimen" name="rounded_corner_radius" />
<java-symbol type="dimen" name="rounded_corner_radius_top" />
<java-symbol type="dimen" name="rounded_corner_radius_bottom" />
+ <java-symbol type="bool" name="config_supportsRoundedCornersOnWindows" />
<java-symbol type="string" name="config_defaultModuleMetadataProvider" />
diff --git a/core/tests/coretests/src/android/os/BinderTest.java b/core/tests/coretests/src/android/os/BinderTest.java
index 534c5cd..6c9c3c1 100644
--- a/core/tests/coretests/src/android/os/BinderTest.java
+++ b/core/tests/coretests/src/android/os/BinderTest.java
@@ -43,4 +43,13 @@
Binder.restoreCallingWorkSource(token);
assertEquals(UID, Binder.getCallingWorkSourceUid());
}
+
+ @SmallTest
+ public void testGetCallingUidOrThrow() throws Exception {
+ try {
+ Binder.getCallingUidOrThrow();
+ throw new AssertionError("IllegalStateException expected");
+ } catch (IllegalStateException expected) {
+ }
+ }
}
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 68b08f9..f863356 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -114,6 +114,7 @@
Settings.Global.ANOMALY_CONFIG_VERSION,
Settings.Global.APN_DB_UPDATE_CONTENT_URL,
Settings.Global.APN_DB_UPDATE_METADATA_URL,
+ Settings.Global.APPLY_RAMPING_RINGER,
Settings.Global.APP_BINDING_CONSTANTS,
Settings.Global.APP_IDLE_CONSTANTS,
Settings.Global.APP_OPS_CONSTANTS,
@@ -477,6 +478,7 @@
Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_PKGS,
Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES,
Settings.Global.GUP_DEV_OPT_IN_APPS,
+ Settings.Global.GUP_DEV_OPT_OUT_APPS,
Settings.Global.GUP_BLACK_LIST,
Settings.Global.GPU_DEBUG_LAYER_APP,
Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING,
diff --git a/core/tests/coretests/src/android/text/TextLineTest.java b/core/tests/coretests/src/android/text/TextLineTest.java
index 61f976a..2fe882c 100644
--- a/core/tests/coretests/src/android/text/TextLineTest.java
+++ b/core/tests/coretests/src/android/text/TextLineTest.java
@@ -254,6 +254,18 @@
}
@Test
+ public void testMeasure_wordSpacing() {
+ final TextPaint paint = new TextPaint();
+ paint.setTypeface(TYPEFACE);
+ paint.setTextSize(10.0f); // make 1em = 10px
+ paint.setWordSpacing(10.0f);
+
+ TextLine tl = getTextLine("I I", paint);
+ assertMeasurements(tl, 3, false,
+ new float[]{0.0f, 10.0f, 120.0f, 130.0f});
+ }
+
+ @Test
public void testHandleRun_ellipsizedReplacementSpan_isSkipped() {
final Spannable text = new SpannableStringBuilder("This is a... text");
diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java
index 9807f26..95af525 100644
--- a/core/tests/coretests/src/android/view/InsetsStateTest.java
+++ b/core/tests/coretests/src/android/view/InsetsStateTest.java
@@ -29,12 +29,14 @@
import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertNotEquals;
+import android.graphics.Insets;
import android.graphics.Rect;
import android.os.Parcel;
import android.platform.test.annotations.Presubmit;
import android.support.test.filters.FlakyTest;
import android.support.test.runner.AndroidJUnit4;
import android.util.SparseIntArray;
+import android.view.WindowInsets.Type;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -56,9 +58,12 @@
SparseIntArray typeSideMap = new SparseIntArray();
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
DisplayCutout.NO_CUTOUT, typeSideMap);
- assertEquals(new Rect(0, 100, 0, 100), insets.getSystemWindowInsets());
+ assertEquals(Insets.of(0, 100, 0, 100), insets.getSystemWindowInsets());
+ assertEquals(Insets.of(0, 100, 0, 100), insets.getInsets(Type.all()));
assertEquals(INSET_SIDE_TOP, typeSideMap.get(TYPE_TOP_BAR));
assertEquals(INSET_SIDE_BOTTOM, typeSideMap.get(TYPE_IME));
+ assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(Type.topBar()));
+ assertEquals(Insets.of(0, 0, 0, 100), insets.getInsets(Type.ime()));
}
@Test
@@ -70,7 +75,11 @@
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
DisplayCutout.NO_CUTOUT, null);
assertEquals(100, insets.getStableInsetBottom());
- assertEquals(new Rect(0, 0, 0, 200), insets.getSystemWindowInsets());
+ assertEquals(Insets.of(0, 0, 0, 100), insets.getMaxInsets(Type.all()));
+ assertEquals(Insets.of(0, 0, 0, 200), insets.getSystemWindowInsets());
+ assertEquals(Insets.of(0, 0, 0, 200), insets.getInsets(Type.all()));
+ assertEquals(Insets.of(0, 0, 0, 100), insets.getInsets(Type.sideBars()));
+ assertEquals(Insets.of(0, 0, 0, 200), insets.getInsets(Type.ime()));
}
@Test
@@ -81,7 +90,9 @@
mState.getSource(TYPE_NAVIGATION_BAR).setVisible(true);
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
DisplayCutout.NO_CUTOUT, null);
- assertEquals(new Rect(0, 100, 20, 0), insets.getSystemWindowInsets());
+ assertEquals(Insets.of(0, 100, 20, 0), insets.getSystemWindowInsets());
+ assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(Type.topBar()));
+ assertEquals(Insets.of(0, 0, 20, 0), insets.getInsets(Type.sideBars()));
}
@Test
diff --git a/core/tests/coretests/src/android/view/WindowInsetsTest.java b/core/tests/coretests/src/android/view/WindowInsetsTest.java
index 1c2df2c..1513a1a 100644
--- a/core/tests/coretests/src/android/view/WindowInsetsTest.java
+++ b/core/tests/coretests/src/android/view/WindowInsetsTest.java
@@ -16,12 +16,19 @@
package android.view;
+import static android.view.WindowInsets.Type.ime;
+import static android.view.WindowInsets.Type.indexOf;
+import static android.view.WindowInsets.Type.sideBars;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import android.graphics.Insets;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+import android.view.WindowInsets.Builder;
+import android.view.WindowInsets.Type;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -33,13 +40,13 @@
@Test
public void systemWindowInsets_afterConsuming_isConsumed() {
- assertTrue(new WindowInsets(new Rect(1, 2, 3, 4), null, null, false, false, null)
+ assertTrue(new WindowInsets(new Rect(1, 2, 3, 4), null, false, false, null)
.consumeSystemWindowInsets().isConsumed());
}
@Test
public void multiNullConstructor_isConsumed() {
- assertTrue(new WindowInsets(null, null, null, false, false, null).isConsumed());
+ assertTrue(new WindowInsets((Rect) null, null, false, false, null).isConsumed());
}
@Test
@@ -47,4 +54,12 @@
assertTrue(new WindowInsets((Rect) null).isConsumed());
}
+ @Test
+ public void typeMap() {
+ Builder b = new WindowInsets.Builder();
+ b.setInsets(sideBars(), Insets.of(0, 0, 0, 100));
+ b.setInsets(ime(), Insets.of(0, 0, 0, 300));
+ WindowInsets insets = b.build();
+ assertEquals(300, insets.getSystemWindowInsets().bottom);
+ }
}
diff --git a/core/tests/coretests/src/android/view/textclassifier/logging/TextClassifierEventTronLoggerTest.java b/core/tests/coretests/src/android/view/textclassifier/logging/TextClassifierEventTronLoggerTest.java
new file mode 100644
index 0000000..344f79d
--- /dev/null
+++ b/core/tests/coretests/src/android/view/textclassifier/logging/TextClassifierEventTronLoggerTest.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2018 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.view.textclassifier.logging;
+
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_TEXT_SELECTION_SMART_SHARE;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.CONVERSATION_ACTIONS;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_SELECTION_ENTITY_TYPE;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_SELECTION_WIDGET_TYPE;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_TEXT_CLASSIFIER_EVENT_TIME;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.metrics.LogMaker;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.textclassifier.ConversationActions;
+import android.view.textclassifier.TextClassificationContext;
+import android.view.textclassifier.TextClassifierEvent;
+import android.view.textclassifier.TextClassifierEventTronLogger;
+
+import com.android.internal.logging.MetricsLogger;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class TextClassifierEventTronLoggerTest {
+ private static final String WIDGET_TYPE = "notification";
+ private static final String PACKAGE_NAME = "pkg";
+ private static final long EVENT_TIME = System.currentTimeMillis();
+
+
+ @Mock
+ private MetricsLogger mMetricsLogger;
+ private TextClassifierEventTronLogger mTextClassifierEventTronLogger;
+
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mTextClassifierEventTronLogger = new TextClassifierEventTronLogger(mMetricsLogger);
+ }
+
+ @Test
+ public void testWriteEvent() {
+ TextClassificationContext textClassificationContext =
+ new TextClassificationContext.Builder(PACKAGE_NAME, WIDGET_TYPE)
+ .build();
+ TextClassifierEvent textClassifierEvent =
+ new TextClassifierEvent.Builder(
+ TextClassifierEvent.CATEGORY_CONVERSATION_ACTIONS,
+ TextClassifierEvent.TYPE_SMART_ACTION)
+ .setEntityType(ConversationActions.TYPE_CALL_PHONE)
+ .setEventTime(EVENT_TIME)
+ .setEventContext(textClassificationContext)
+ .build();
+
+ mTextClassifierEventTronLogger.writeEvent(textClassifierEvent);
+
+ ArgumentCaptor<LogMaker> captor = ArgumentCaptor.forClass(LogMaker.class);
+ Mockito.verify(mMetricsLogger).write(captor.capture());
+ LogMaker logMaker = captor.getValue();
+ assertThat(logMaker.getCategory()).isEqualTo(
+ CONVERSATION_ACTIONS);
+ assertThat(logMaker.getType()).isEqualTo(
+ ACTION_TEXT_SELECTION_SMART_SHARE);
+ assertThat(logMaker.getTaggedData(FIELD_SELECTION_ENTITY_TYPE))
+ .isEqualTo(ConversationActions.TYPE_CALL_PHONE);
+ assertThat(logMaker.getTaggedData(FIELD_TEXT_CLASSIFIER_EVENT_TIME))
+ .isEqualTo(EVENT_TIME);
+ assertThat(logMaker.getPackageName()).isEqualTo(PACKAGE_NAME);
+ assertThat(logMaker.getTaggedData(FIELD_SELECTION_WIDGET_TYPE))
+ .isEqualTo(WIDGET_TYPE);
+ }
+
+ @Test
+ public void testWriteEvent_unsupportedCategory() {
+ TextClassifierEvent textClassifierEvent =
+ new TextClassifierEvent.Builder(
+ TextClassifierEvent.CATEGORY_SELECTION,
+ TextClassifierEvent.TYPE_SMART_ACTION)
+ .build();
+
+ mTextClassifierEventTronLogger.writeEvent(textClassifierEvent);
+
+ Mockito.verify(mMetricsLogger, Mockito.never()).write(Mockito.any(LogMaker.class));
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/widget/ActionBarOverlayLayoutTest.java b/core/tests/coretests/src/com/android/internal/widget/ActionBarOverlayLayoutTest.java
index 218566e..d782c0c 100644
--- a/core/tests/coretests/src/com/android/internal/widget/ActionBarOverlayLayoutTest.java
+++ b/core/tests/coretests/src/com/android/internal/widget/ActionBarOverlayLayoutTest.java
@@ -167,7 +167,7 @@
}
private WindowInsets insetsWith(Insets content, DisplayCutout cutout) {
- return new WindowInsets(content.toRect(), null, null, false, false, cutout);
+ return new WindowInsets(content.toRect(), null, false, false, cutout);
}
private ViewGroup createViewGroupWithId(int id) {
diff --git a/data/etc/com.android.systemui.xml b/data/etc/com.android.systemui.xml
index b65bc1d..221708b 100644
--- a/data/etc/com.android.systemui.xml
+++ b/data/etc/com.android.systemui.xml
@@ -29,7 +29,6 @@
<permission name="android.permission.DUMP"/>
<permission name="android.permission.GET_APP_OPS_STATS"/>
<permission name="android.permission.INTERACT_ACROSS_USERS"/>
- <permission name="android.permission.MANAGE_ACTIVITY_STACKS"/>
<permission name="android.permission.MANAGE_DEBUGGING"/>
<permission name="android.permission.MANAGE_SENSOR_PRIVACY"/>
<permission name="android.permission.MANAGE_USB"/>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 58b57e5..fbe613d 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -242,6 +242,32 @@
<permission name="android.permission.WRITE_SECURE_SETTINGS"/>
</privapp-permissions>
+ <privapp-permissions package="com.android.mainline.networkstack">
+ <permission name="android.permission.ACCESS_NETWORK_CONDITIONS"/>
+ <permission name="android.permission.CHANGE_BACKGROUND_DATA_SETTING"/>
+ <permission name="android.permission.CONNECTIVITY_INTERNAL"/>
+ <permission name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"/>
+ <permission name="android.permission.CONTROL_VPN"/>
+ <permission name="android.permission.LOCAL_MAC_ADDRESS"/>
+ <permission name="android.permission.MANAGE_IPSEC_TUNNELS"/>
+ <permission name="android.permission.MANAGE_NETWORK_POLICY"/>
+ <permission name="android.permission.MANAGE_SUBSCRIPTION_PLANS"/>
+ <permission name="android.permission.MANAGE_USB"/>
+ <permission name="android.permission.NETWORK_BYPASS_PRIVATE_DNS"/>
+ <permission name="android.permission.NETWORK_SETTINGS"/>
+ <permission name="android.permission.NETWORK_STACK" />
+ <permission name="android.permission.NET_TUNNELING"/>
+ <permission name="android.permission.PACKET_KEEPALIVE_OFFLOAD"/>
+ <permission name="android.permission.PEERS_MAC_ADDRESS"/>
+ <permission name="android.permission.READ_NETWORK_USAGE_HISTORY"/>
+ <permission name="android.permission.READ_PRECISE_PHONE_STATE"/>
+ <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
+ <permission name="android.permission.READ_WIFI_CREDENTIAL"/>
+ <permission name="android.permission.RECEIVE_DATA_ACTIVITY_CHANGE"/>
+ <permission name="android.permission.TETHER_PRIVILEGED"/>
+ <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+ </privapp-permissions>
+
<privapp-permissions package="com.android.server.telecom">
<permission name="android.permission.BIND_CONNECTION_SERVICE"/>
<permission name="android.permission.BIND_INCALL_SERVICE"/>
@@ -289,7 +315,6 @@
<permission name="android.permission.INTERACT_ACROSS_USERS"/>
<permission name="android.permission.LOCAL_MAC_ADDRESS"/>
<permission name="android.permission.MANAGE_ACCESSIBILITY"/>
- <permission name="android.permission.MANAGE_ACTIVITY_STACKS"/>
<permission name="android.permission.MANAGE_DEVICE_ADMINS"/>
<permission name="android.permission.MANAGE_USB"/>
<permission name="android.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS"/>
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index 04bf804..21531ab 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -99,6 +99,46 @@
<font weight="400" style="normal">CarroisGothicSC-Regular.ttf</font>
</family>
+ <family name="arbutus-slab">
+ <font weight="400" style="normal">ArbutusSlab-Regular.ttf</font>
+ </family>
+
+ <family name="arvo">
+ <font weight="400" style="normal">Arvo-Regular.ttf</font>
+ <font weight="400" style="italic">Arvo-Italic.ttf</font>
+ <font weight="700" style="normal">Arvo-Bold.ttf</font>
+ <font weight="700" style="italic">Arvo-BoldItalic.ttf</font>
+ </family>
+ <alias name="arvo-bold" to="arvo" weight="700" />
+
+ <family name="lato">
+ <font weight="400" style="normal">Lato-Regular.ttf</font>
+ <font weight="400" style="italic">Lato-Italic.ttf</font>
+ <font weight="700" style="normal">Lato-Bold.ttf</font>
+ <font weight="700" style="italic">Lato-BoldItalic.ttf</font>
+ </family>
+ <alias name="lato-bold" to="lato" weight="700" />
+
+ <family name="rubik">
+ <font weight="400" style="normal">Rubik-Regular.ttf</font>
+ <font weight="400" style="italic">Rubik-Italic.ttf</font>
+ <font weight="500" style="normal">Rubik-Medium.ttf</font>
+ <font weight="500" style="italic">Rubik-MediumItalic.ttf</font>
+ <font weight="700" style="normal">Rubik-Bold.ttf</font>
+ <font weight="700" style="italic">Rubik-BoldItalic.ttf</font>
+ </family>
+ <alias name="rubik-medium" to="rubik" weight="500" />
+
+ <family name="source-sans-pro">
+ <font weight="400" style="normal">SourceSansPro-Regular.ttf</font>
+ <font weight="400" style="italic">SourceSansPro-Italic.ttf</font>
+ <font weight="600" style="normal">SourceSansPro-SemiBold.ttf</font>
+ <font weight="600" style="italic">SourceSansPro-SemiBoldItalic.ttf</font>
+ <font weight="700" style="normal">SourceSansPro-Bold.ttf</font>
+ <font weight="700" style="italic">SourceSansPro-BoldItalic.ttf</font>
+ </family>
+ <alias name="source-sans-pro-semi-bold" to="source-sans-pro" weight="600" />
+
<!-- fallback fonts -->
<family lang="und-Arab" variant="elegant">
<font weight="400" style="normal">NotoNaskhArabic-Regular.ttf</font>
diff --git a/drm/java/android/drm/DrmManagerClient.java b/drm/java/android/drm/DrmManagerClient.java
index 76eab4a..fcebad3 100644
--- a/drm/java/android/drm/DrmManagerClient.java
+++ b/drm/java/android/drm/DrmManagerClient.java
@@ -831,6 +831,7 @@
* content://media/<table_name>/<row_index> (or)
* file://sdcard/test.mp4
* http://test.com/test.mp4
+ * https://test.com/test.mp4
*
* Here <table_name> shall be "video" or "audio" or "images"
* <row_index> the index of the content in given table
@@ -843,7 +844,7 @@
scheme.equals(ContentResolver.SCHEME_FILE)) {
path = uri.getPath();
- } else if (scheme.equals("http")) {
+ } else if (scheme.equals("http") || scheme.equals("https")) {
path = uri.toString();
} else if (scheme.equals(ContentResolver.SCHEME_CONTENT)) {
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index adab1a9c..022fbdc 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -440,7 +440,8 @@
if (opts == null) return;
if (opts.inBitmap != null && opts.inBitmap.getConfig() == Bitmap.Config.HARDWARE) {
- throw new IllegalArgumentException("Bitmaps with Config.HARWARE are always immutable");
+ throw new IllegalArgumentException(
+ "Bitmaps with Config.HARDWARE are always immutable");
}
if (opts.inMutable && opts.inPreferredConfig == Bitmap.Config.HARDWARE) {
diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h
index b33cfe2..0c515a4 100644
--- a/libs/hwui/Matrix.h
+++ b/libs/hwui/Matrix.h
@@ -81,7 +81,7 @@
explicit Matrix4(const float* v) { load(v); }
- Matrix4(const SkMatrix& v) { // NOLINT, implicit
+ Matrix4(const SkMatrix& v) { // NOLINT(google-explicit-constructor)
load(v);
}
diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h
index d6362ef..24443c8 100644
--- a/libs/hwui/Rect.h
+++ b/libs/hwui/Rect.h
@@ -57,15 +57,15 @@
inline Rect(float width, float height) : left(0.0f), top(0.0f), right(width), bottom(height) {}
- inline Rect(const SkIRect& rect)
- : // NOLINT, implicit
+ inline Rect(const SkIRect& rect) // NOLINT(google-explicit-constructor)
+ :
left(rect.fLeft)
, top(rect.fTop)
, right(rect.fRight)
, bottom(rect.fBottom) {}
- inline Rect(const SkRect& rect)
- : // NOLINT, implicit
+ inline Rect(const SkRect& rect) // NOLINT(google-explicit-constructor)
+ :
left(rect.fLeft)
, top(rect.fTop)
, right(rect.fRight)
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index ab95e69..cc62fdc 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -678,15 +678,15 @@
// Canvas draw operations: Text
// ----------------------------------------------------------------------------
-void SkiaCanvas::drawGlyphs(ReadGlyphFunc glyphFunc, int count, const SkPaint& paint, float x,
+void SkiaCanvas::drawGlyphs(ReadGlyphFunc glyphFunc, int count, const Paint& paint, float x,
float y, float boundsLeft, float boundsTop, float boundsRight,
float boundsBottom, float totalAdvance) {
if (count <= 0 || paint.nothingToDraw()) return;
- SkFont font = SkFont::LEGACY_ExtractFromPaint(paint);
SkPaint paintCopy(paint);
if (mPaintFilter) {
mPaintFilter->filter(&paintCopy);
}
+ SkFont font = SkFont::LEGACY_ExtractFromPaint(paintCopy);
SkASSERT(paintCopy.getTextEncoding() == kGlyphID_SkTextEncoding);
// Stroke with a hairline is drawn on HW with a fill style for compatibility with Android O and
// older.
@@ -708,13 +708,13 @@
}
void SkiaCanvas::drawLayoutOnPath(const minikin::Layout& layout, float hOffset, float vOffset,
- const SkPaint& paint, const SkPath& path, size_t start,
+ const Paint& paint, const SkPath& path, size_t start,
size_t end) {
- SkFont font = SkFont::LEGACY_ExtractFromPaint(paint);
SkPaint paintCopy(paint);
if (mPaintFilter) {
mPaintFilter->filter(&paintCopy);
}
+ SkFont font = SkFont::LEGACY_ExtractFromPaint(paintCopy);
SkASSERT(paintCopy.getTextEncoding() == kGlyphID_SkTextEncoding);
const int N = end - start;
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index 4ab0a59..3fe2bce 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -158,11 +158,11 @@
void reset(SkCanvas* skiaCanvas);
void drawDrawable(SkDrawable* drawable) { mCanvas->drawDrawable(drawable); }
- virtual void drawGlyphs(ReadGlyphFunc glyphFunc, int count, const SkPaint& paint, float x,
+ virtual void drawGlyphs(ReadGlyphFunc glyphFunc, int count, const Paint& paint, float x,
float y, float boundsLeft, float boundsTop, float boundsRight,
float boundsBottom, float totalAdvance) override;
virtual void drawLayoutOnPath(const minikin::Layout& layout, float hOffset, float vOffset,
- const SkPaint& paint, const SkPath& path, size_t start,
+ const Paint& paint, const SkPath& path, size_t start,
size_t end) override;
/** This class acts as a copy on write SkPaint.
diff --git a/libs/hwui/debug/GlesErrorCheckWrapper.h b/libs/hwui/debug/GlesErrorCheckWrapper.h
index ee5cc1f..791400b 100644
--- a/libs/hwui/debug/GlesErrorCheckWrapper.h
+++ b/libs/hwui/debug/GlesErrorCheckWrapper.h
@@ -24,7 +24,7 @@
class GlesErrorCheckWrapper : public GlesDriver {
public:
- GlesErrorCheckWrapper(GlesDriver& base) : mBase(base) {}
+ explicit GlesErrorCheckWrapper(GlesDriver& base) : mBase(base) {}
#define GL_ENTRY(ret, api, ...) virtual ret api##_(__VA_ARGS__) override;
#include "gles_decls.in"
diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp
index a09da6b..277148e 100644
--- a/libs/hwui/hwui/Canvas.cpp
+++ b/libs/hwui/hwui/Canvas.cpp
@@ -38,7 +38,7 @@
canvas->drawRect(left, top, right, bottom, paint);
}
-void Canvas::drawTextDecorations(float x, float y, float length, const SkPaint& paint) {
+void Canvas::drawTextDecorations(float x, float y, float length, const Paint& paint) {
uint32_t flags;
PaintFilter* paintFilter = getPaintFilter();
if (paintFilter) {
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index 4c5365d..11e8579 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -303,18 +303,18 @@
static int GetApiLevel() { return sApiLevel; }
protected:
- void drawTextDecorations(float x, float y, float length, const SkPaint& paint);
+ void drawTextDecorations(float x, float y, float length, const Paint& paint);
/**
* glyphFunc: valid only for the duration of the call and should not be cached.
* drawText: count is of glyphs
* totalAdvance: used to define width of text decorations (underlines, strikethroughs).
*/
- virtual void drawGlyphs(ReadGlyphFunc glyphFunc, int count, const SkPaint& paint, float x,
+ virtual void drawGlyphs(ReadGlyphFunc glyphFunc, int count, const Paint& paint, float x,
float y, float boundsLeft, float boundsTop, float boundsRight,
float boundsBottom, float totalAdvance) = 0;
virtual void drawLayoutOnPath(const minikin::Layout& layout, float hOffset, float vOffset,
- const SkPaint& paint, const SkPath& path, size_t start,
+ const Paint& paint, const SkPath& path, size_t start,
size_t end) = 0;
static int sApiLevel;
diff --git a/libs/hwui/hwui/Paint.h b/libs/hwui/hwui/Paint.h
index c1a3b6d..92ffda9 100644
--- a/libs/hwui/hwui/Paint.h
+++ b/libs/hwui/hwui/Paint.h
@@ -46,7 +46,7 @@
Paint();
Paint(const Paint& paint);
- Paint(const SkPaint& paint); // NOLINT(implicit)
+ Paint(const SkPaint& paint); // NOLINT(google-explicit-constructor)
~Paint();
Paint& operator=(const Paint& other);
diff --git a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
index a5faae7..c3563db 100644
--- a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
@@ -36,8 +36,6 @@
namespace uirenderer {
namespace skiapipeline {
-static std::mutex sLock{};
-static ThreadBase* sGLDrawThread = nullptr;
static renderthread::EglManager sEglManager;
// ScopedDrawRequest makes sure a GL thread is started and EGL context is initialized on it.
@@ -47,32 +45,20 @@
private:
void beginDraw() {
- std::lock_guard _lock{sLock};
-
- if (!sGLDrawThread) {
- sGLDrawThread = new ThreadBase{};
- }
-
- if (!sGLDrawThread->isRunning()) {
- sGLDrawThread->start("GLFunctorThread");
- }
-
if (!sEglManager.hasEglContext()) {
- sGLDrawThread->queue().runSync([]() { sEglManager.initialize(); });
+ sEglManager.initialize();
}
}
};
void VkInteropFunctorDrawable::vkInvokeFunctor(Functor* functor) {
ScopedDrawRequest _drawRequest{};
- sGLDrawThread->queue().runSync([&]() {
- EGLDisplay display = sEglManager.eglDisplay();
- DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext;
- if (display != EGL_NO_DISPLAY) {
- mode = DrawGlInfo::kModeProcess;
- }
- (*functor)(mode, nullptr);
- });
+ EGLDisplay display = sEglManager.eglDisplay();
+ DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext;
+ if (display != EGL_NO_DISPLAY) {
+ mode = DrawGlInfo::kModeProcess;
+ }
+ (*functor)(mode, nullptr);
}
#define FENCE_TIMEOUT 2000000000
@@ -113,7 +99,7 @@
// TODO: draw command has completed.
// TODO: A simple but inefficient way is to flush and issue a QueueWaitIdle call. See
// TODO: GrVkGpu::destroyResources() for example.
- bool success = sGLDrawThread->queue().runSync([&]() -> bool {
+ {
ATRACE_FORMAT("WebViewDraw_%dx%d", mFBInfo.width(), mFBInfo.height());
EGLDisplay display = sEglManager.eglDisplay();
LOG_ALWAYS_FATAL_IF(display == EGL_NO_DISPLAY, "Failed to get EGL_DEFAULT_DISPLAY! err=%s",
@@ -125,7 +111,7 @@
if (autoImage.image == EGL_NO_IMAGE_KHR) {
ALOGW("Could not create EGL image, err =%s",
uirenderer::renderthread::EglManager::eglErrorString());
- return false;
+ return;
}
AutoSkiaGlTexture glTexture;
@@ -156,7 +142,7 @@
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
ALOGE("Failed framebuffer check for created target buffer: %s",
GLUtils::getGLFramebufferError());
- return false;
+ return;
}
glDisable(GL_STENCIL_TEST);
@@ -183,11 +169,6 @@
LOG_ALWAYS_FATAL_IF(waitStatus != EGL_CONDITION_SATISFIED_KHR,
"Failed to wait for the fence %#x", eglGetError());
eglDestroySyncKHR(display, glDrawFinishedFence);
- return true;
- });
-
- if (!success) {
- return;
}
SkPaint paint;
@@ -208,15 +189,14 @@
if (auto lp = std::get_if<LegacyFunctor>(&mAnyFunctor)) {
if (lp->listener) {
ScopedDrawRequest _drawRequest{};
- sGLDrawThread->queue().runSync(
- [&]() { lp->listener->onGlFunctorReleased(lp->functor); });
+ lp->listener->onGlFunctorReleased(lp->functor);
}
}
}
void VkInteropFunctorDrawable::syncFunctor(const WebViewSyncData& data) const {
ScopedDrawRequest _drawRequest{};
- sGLDrawThread->queue().runSync([&]() { FunctorDrawable::syncFunctor(data); });
+ FunctorDrawable::syncFunctor(data);
}
} // namespace skiapipeline
diff --git a/libs/hwui/service/GraphicsStatsService.cpp b/libs/hwui/service/GraphicsStatsService.cpp
index 29e4256..8a16b20 100644
--- a/libs/hwui/service/GraphicsStatsService.cpp
+++ b/libs/hwui/service/GraphicsStatsService.cpp
@@ -48,7 +48,7 @@
class FileDescriptor {
public:
- FileDescriptor(int fd) : mFd(fd) {}
+ explicit FileDescriptor(int fd) : mFd(fd) {}
~FileDescriptor() {
if (mFd != -1) {
close(mFd);
@@ -56,7 +56,7 @@
}
}
bool valid() { return mFd != -1; }
- operator int() { return mFd; }
+ operator int() { return mFd; } // NOLINT(google-explicit-constructor)
private:
int mFd;
@@ -64,7 +64,7 @@
class FileOutputStreamLite : public io::ZeroCopyOutputStream {
public:
- FileOutputStreamLite(int fd) : mCopyAdapter(fd), mImpl(&mCopyAdapter) {}
+ explicit FileOutputStreamLite(int fd) : mCopyAdapter(fd), mImpl(&mCopyAdapter) {}
virtual ~FileOutputStreamLite() {}
int GetErrno() { return mCopyAdapter.mErrno; }
@@ -82,7 +82,7 @@
int mFd;
int mErrno = 0;
- FDAdapter(int fd) : mFd(fd) {}
+ explicit FDAdapter(int fd) : mFd(fd) {}
virtual ~FDAdapter() {}
virtual bool Write(const void* buffer, int size) override {
diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp
index 7aa9b82..16a27598 100644
--- a/libs/hwui/tests/common/TestUtils.cpp
+++ b/libs/hwui/tests/common/TestUtils.cpp
@@ -82,7 +82,7 @@
float y) {
auto utf16 = asciiToUtf16(text);
uint32_t length = strlen(text);
- SkPaint glyphPaint(paint);
+ Paint glyphPaint(paint);
glyphPaint.setTextEncoding(kGlyphID_SkTextEncoding);
canvas->drawText(utf16.get(), length, // text buffer
0, length, // draw range
@@ -93,7 +93,7 @@
void TestUtils::drawUtf8ToCanvas(Canvas* canvas, const char* text, const SkPaint& paint,
const SkPath& path) {
auto utf16 = asciiToUtf16(text);
- SkPaint glyphPaint(paint);
+ Paint glyphPaint(paint);
glyphPaint.setTextEncoding(kGlyphID_SkTextEncoding);
canvas->drawTextOnPath(utf16.get(), strlen(text), minikin::Bidi::LTR, path, 0, 0, glyphPaint,
nullptr);
diff --git a/libs/hwui/utils/LinearAllocator.h b/libs/hwui/utils/LinearAllocator.h
index b401fcf..9c4a1be 100644
--- a/libs/hwui/utils/LinearAllocator.h
+++ b/libs/hwui/utils/LinearAllocator.h
@@ -168,7 +168,7 @@
};
// enable allocators to be constructed from other templated types
template <class U>
- LinearStdAllocator(const LinearStdAllocator<U>& other) // NOLINT(implicit)
+ LinearStdAllocator(const LinearStdAllocator<U>& other) // NOLINT(google-explicit-constructor)
: linearAllocator(other.linearAllocator) {}
T* allocate(size_t num, const void* = 0) {
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index 0a90f85..b9b1cdc 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -89,10 +89,6 @@
mLocked.animationPending = false;
- mLocked.displayWidth = -1;
- mLocked.displayHeight = -1;
- mLocked.displayOrientation = DISPLAY_ORIENTATION_0;
-
mLocked.presentation = PRESENTATION_POINTER;
mLocked.presentationChanged = false;
@@ -110,15 +106,6 @@
mLocked.lastFrameUpdatedTime = 0;
mLocked.buttonState = 0;
-
- mPolicy->loadPointerIcon(&mLocked.pointerIcon);
-
- loadResources();
-
- if (mLocked.pointerIcon.isValid()) {
- mLocked.pointerIconChanged = true;
- updatePointerLocked();
- }
}
PointerController::~PointerController() {
@@ -144,23 +131,15 @@
bool PointerController::getBoundsLocked(float* outMinX, float* outMinY,
float* outMaxX, float* outMaxY) const {
- if (mLocked.displayWidth <= 0 || mLocked.displayHeight <= 0) {
+
+ if (!mLocked.viewport.isValid()) {
return false;
}
- *outMinX = 0;
- *outMinY = 0;
- switch (mLocked.displayOrientation) {
- case DISPLAY_ORIENTATION_90:
- case DISPLAY_ORIENTATION_270:
- *outMaxX = mLocked.displayHeight - 1;
- *outMaxY = mLocked.displayWidth - 1;
- break;
- default:
- *outMaxX = mLocked.displayWidth - 1;
- *outMaxY = mLocked.displayHeight - 1;
- break;
- }
+ *outMinX = mLocked.viewport.logicalLeft;
+ *outMinY = mLocked.viewport.logicalTop;
+ *outMaxX = mLocked.viewport.logicalRight - 1;
+ *outMaxY = mLocked.viewport.logicalBottom - 1;
return true;
}
@@ -231,6 +210,12 @@
*outY = mLocked.pointerY;
}
+int32_t PointerController::getDisplayId() const {
+ AutoMutex _l(mLock);
+
+ return mLocked.viewport.displayId;
+}
+
void PointerController::fade(Transition transition) {
AutoMutex _l(mLock);
@@ -355,48 +340,56 @@
void PointerController::reloadPointerResources() {
AutoMutex _l(mLock);
- loadResources();
-
- if (mLocked.presentation == PRESENTATION_POINTER) {
- mLocked.additionalMouseResources.clear();
- mLocked.animationResources.clear();
- mPolicy->loadPointerIcon(&mLocked.pointerIcon);
- mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
- &mLocked.animationResources);
- }
-
- mLocked.presentationChanged = true;
+ loadResourcesLocked();
updatePointerLocked();
}
-void PointerController::setDisplayViewport(int32_t width, int32_t height, int32_t orientation) {
- AutoMutex _l(mLock);
+/**
+ * The viewport values for deviceHeight and deviceWidth have already been adjusted for rotation,
+ * so here we are getting the dimensions in the original, unrotated orientation (orientation 0).
+ */
+static void getNonRotatedSize(const DisplayViewport& viewport, int32_t& width, int32_t& height) {
+ width = viewport.deviceWidth;
+ height = viewport.deviceHeight;
- // Adjust to use the display's unrotated coordinate frame.
- if (orientation == DISPLAY_ORIENTATION_90
- || orientation == DISPLAY_ORIENTATION_270) {
- int32_t temp = height;
- height = width;
- width = temp;
+ if (viewport.orientation == DISPLAY_ORIENTATION_90
+ || viewport.orientation == DISPLAY_ORIENTATION_270) {
+ std::swap(width, height);
+ }
+}
+
+void PointerController::setDisplayViewport(const DisplayViewport& viewport) {
+ AutoMutex _l(mLock);
+ if (viewport == mLocked.viewport) {
+ return;
}
- if (mLocked.displayWidth != width || mLocked.displayHeight != height) {
- mLocked.displayWidth = width;
- mLocked.displayHeight = height;
+ const DisplayViewport oldViewport = mLocked.viewport;
+ mLocked.viewport = viewport;
+
+ int32_t oldDisplayWidth, oldDisplayHeight;
+ getNonRotatedSize(oldViewport, oldDisplayWidth, oldDisplayHeight);
+ int32_t newDisplayWidth, newDisplayHeight;
+ getNonRotatedSize(viewport, newDisplayWidth, newDisplayHeight);
+
+ // Reset cursor position to center if size or display changed.
+ if (oldViewport.displayId != viewport.displayId
+ || oldDisplayWidth != newDisplayWidth
+ || oldDisplayHeight != newDisplayHeight) {
float minX, minY, maxX, maxY;
if (getBoundsLocked(&minX, &minY, &maxX, &maxY)) {
mLocked.pointerX = (minX + maxX) * 0.5f;
mLocked.pointerY = (minY + maxY) * 0.5f;
+ // Reload icon resources for density may be changed.
+ loadResourcesLocked();
} else {
mLocked.pointerX = 0;
mLocked.pointerY = 0;
}
fadeOutAndReleaseAllSpotsLocked();
- }
-
- if (mLocked.displayOrientation != orientation) {
+ } else if (oldViewport.orientation != viewport.orientation) {
// Apply offsets to convert from the pixel top-left corner position to the pixel center.
// This creates an invariant frame of reference that we can easily rotate when
// taking into account that the pointer may be located at fractional pixel offsets.
@@ -405,37 +398,37 @@
float temp;
// Undo the previous rotation.
- switch (mLocked.displayOrientation) {
+ switch (oldViewport.orientation) {
case DISPLAY_ORIENTATION_90:
temp = x;
- x = mLocked.displayWidth - y;
+ x = oldViewport.deviceHeight - y;
y = temp;
break;
case DISPLAY_ORIENTATION_180:
- x = mLocked.displayWidth - x;
- y = mLocked.displayHeight - y;
+ x = oldViewport.deviceWidth - x;
+ y = oldViewport.deviceHeight - y;
break;
case DISPLAY_ORIENTATION_270:
temp = x;
x = y;
- y = mLocked.displayHeight - temp;
+ y = oldViewport.deviceWidth - temp;
break;
}
// Perform the new rotation.
- switch (orientation) {
+ switch (viewport.orientation) {
case DISPLAY_ORIENTATION_90:
temp = x;
x = y;
- y = mLocked.displayWidth - temp;
+ y = viewport.deviceHeight - temp;
break;
case DISPLAY_ORIENTATION_180:
- x = mLocked.displayWidth - x;
- y = mLocked.displayHeight - y;
+ x = viewport.deviceWidth - x;
+ y = viewport.deviceHeight - y;
break;
case DISPLAY_ORIENTATION_270:
temp = x;
- x = mLocked.displayHeight - y;
+ x = viewport.deviceWidth - y;
y = temp;
break;
}
@@ -444,7 +437,6 @@
// and save the results.
mLocked.pointerX = x - 0.5f;
mLocked.pointerY = y - 0.5f;
- mLocked.displayOrientation = orientation;
}
updatePointerLocked();
@@ -614,11 +606,16 @@
mLooper->removeMessages(mHandler, MSG_INACTIVITY_TIMEOUT);
}
-void PointerController::updatePointerLocked() {
+void PointerController::updatePointerLocked() REQUIRES(mLock) {
+ if (!mLocked.viewport.isValid()) {
+ return;
+ }
+
mSpriteController->openTransaction();
mLocked.pointerSprite->setLayer(Sprite::BASE_LAYER_POINTER);
mLocked.pointerSprite->setPosition(mLocked.pointerX, mLocked.pointerY);
+ mLocked.pointerSprite->setDisplayId(mLocked.viewport.displayId);
if (mLocked.pointerAlpha > 0) {
mLocked.pointerSprite->setAlpha(mLocked.pointerAlpha);
@@ -729,8 +726,18 @@
}
}
-void PointerController::loadResources() {
+void PointerController::loadResourcesLocked() REQUIRES(mLock) {
mPolicy->loadPointerResources(&mResources);
+
+ if (mLocked.presentation == PRESENTATION_POINTER) {
+ mLocked.additionalMouseResources.clear();
+ mLocked.animationResources.clear();
+ mPolicy->loadPointerIcon(&mLocked.pointerIcon);
+ mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
+ &mLocked.animationResources);
+ }
+
+ mLocked.pointerIconChanged = true;
}
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index 7f4e5a5..a32cc42 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -23,6 +23,7 @@
#include <vector>
#include <ui/DisplayInfo.h>
+#include <input/DisplayViewport.h>
#include <input/Input.h>
#include <PointerControllerInterface.h>
#include <utils/BitSet.h>
@@ -96,6 +97,7 @@
virtual int32_t getButtonState() const;
virtual void setPosition(float x, float y);
virtual void getPosition(float* outX, float* outY) const;
+ virtual int32_t getDisplayId() const;
virtual void fade(Transition transition);
virtual void unfade(Transition transition);
@@ -106,7 +108,7 @@
void updatePointerIcon(int32_t iconId);
void setCustomPointerIcon(const SpriteIcon& icon);
- void setDisplayViewport(int32_t width, int32_t height, int32_t orientation);
+ void setDisplayViewport(const DisplayViewport& viewport);
void setInactivityTimeout(InactivityTimeout inactivityTimeout);
void reloadPointerResources();
@@ -156,9 +158,7 @@
size_t animationFrameIndex;
nsecs_t lastFrameUpdatedTime;
- int32_t displayWidth;
- int32_t displayHeight;
- int32_t displayOrientation;
+ DisplayViewport viewport;
InactivityTimeout inactivityTimeout;
@@ -182,7 +182,7 @@
Vector<Spot*> spots;
Vector<sp<Sprite> > recycledSprites;
- } mLocked;
+ } mLocked GUARDED_BY(mLock);
bool getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const;
void setPositionLocked(float x, float y);
@@ -207,7 +207,7 @@
void fadeOutAndReleaseSpotLocked(Spot* spot);
void fadeOutAndReleaseAllSpotsLocked();
- void loadResources();
+ void loadResourcesLocked();
};
} // namespace android
diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp
index eb2bc98..c1868d3 100644
--- a/libs/input/SpriteController.cpp
+++ b/libs/input/SpriteController.cpp
@@ -144,13 +144,16 @@
}
}
- // Resize sprites if needed.
+ // Resize and/or reparent sprites if needed.
SurfaceComposerClient::Transaction t;
bool needApplyTransaction = false;
for (size_t i = 0; i < numSprites; i++) {
SpriteUpdate& update = updates.editItemAt(i);
+ if (update.state.surfaceControl == nullptr) {
+ continue;
+ }
- if (update.state.surfaceControl != NULL && update.state.wantSurfaceVisible()) {
+ if (update.state.wantSurfaceVisible()) {
int32_t desiredWidth = update.state.icon.bitmap.width();
int32_t desiredHeight = update.state.icon.bitmap.height();
if (update.state.surfaceWidth < desiredWidth
@@ -170,6 +173,12 @@
}
}
}
+
+ // If surface is a new one, we have to set right layer stack.
+ if (update.surfaceChanged || update.state.dirty & DIRTY_DISPLAY_ID) {
+ t.setLayerStack(update.state.surfaceControl, update.state.displayId);
+ needApplyTransaction = true;
+ }
}
if (needApplyTransaction) {
t.apply();
@@ -236,7 +245,7 @@
if (update.state.surfaceControl != NULL && (becomingVisible || becomingHidden
|| (wantSurfaceVisibleAndDrawn && (update.state.dirty & (DIRTY_ALPHA
| DIRTY_POSITION | DIRTY_TRANSFORMATION_MATRIX | DIRTY_LAYER
- | DIRTY_VISIBILITY | DIRTY_HOTSPOT))))) {
+ | DIRTY_VISIBILITY | DIRTY_HOTSPOT | DIRTY_DISPLAY_ID))))) {
needApplyTransaction = true;
if (wantSurfaceVisibleAndDrawn
@@ -445,6 +454,15 @@
}
}
+void SpriteController::SpriteImpl::setDisplayId(int32_t displayId) {
+ AutoMutex _l(mController->mLock);
+
+ if (mLocked.state.displayId != displayId) {
+ mLocked.state.displayId = displayId;
+ invalidateLocked(DIRTY_DISPLAY_ID);
+ }
+}
+
void SpriteController::SpriteImpl::invalidateLocked(uint32_t dirty) {
bool wasDirty = mLocked.state.dirty;
mLocked.state.dirty |= dirty;
diff --git a/libs/input/SpriteController.h b/libs/input/SpriteController.h
index 31e43e9..5b216f5 100644
--- a/libs/input/SpriteController.h
+++ b/libs/input/SpriteController.h
@@ -125,6 +125,9 @@
/* Sets the sprite transformation matrix. */
virtual void setTransformationMatrix(const SpriteTransformationMatrix& matrix) = 0;
+
+ /* Sets the id of the display where the sprite should be shown. */
+ virtual void setDisplayId(int32_t displayId) = 0;
};
/*
@@ -170,6 +173,7 @@
DIRTY_LAYER = 1 << 4,
DIRTY_VISIBILITY = 1 << 5,
DIRTY_HOTSPOT = 1 << 6,
+ DIRTY_DISPLAY_ID = 1 << 7,
};
/* Describes the state of a sprite.
@@ -180,7 +184,7 @@
struct SpriteState {
inline SpriteState() :
dirty(0), visible(false),
- positionX(0), positionY(0), layer(0), alpha(1.0f),
+ positionX(0), positionY(0), layer(0), alpha(1.0f), displayId(ADISPLAY_ID_DEFAULT),
surfaceWidth(0), surfaceHeight(0), surfaceDrawn(false), surfaceVisible(false) {
}
@@ -193,6 +197,7 @@
int32_t layer;
float alpha;
SpriteTransformationMatrix transformationMatrix;
+ int32_t displayId;
sp<SurfaceControl> surfaceControl;
int32_t surfaceWidth;
@@ -225,6 +230,7 @@
virtual void setLayer(int32_t layer);
virtual void setAlpha(float alpha);
virtual void setTransformationMatrix(const SpriteTransformationMatrix& matrix);
+ virtual void setDisplayId(int32_t displayId);
inline const SpriteState& getStateLocked() const {
return mLocked.state;
diff --git a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
index b531325..866634e 100644
--- a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
+++ b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
@@ -16,9 +16,6 @@
package com.android.internal.location;
-import java.io.UnsupportedEncodingException;
-import java.util.concurrent.TimeUnit;
-
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -26,20 +23,23 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.location.LocationManager;
import android.location.INetInitiatedListener;
+import android.location.LocationManager;
+import android.os.RemoteException;
import android.os.SystemClock;
-import android.telephony.TelephonyManager;
+import android.os.UserHandle;
import android.telephony.PhoneNumberUtils;
import android.telephony.PhoneStateListener;
-import android.os.RemoteException;
-import android.os.UserHandle;
+import android.telephony.TelephonyManager;
import android.util.Log;
-import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.R;
+import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.telephony.GsmAlphabet;
+import java.io.UnsupportedEncodingException;
+import java.util.concurrent.TimeUnit;
+
/**
* A GPS Network-initiated Handler class used by LocationManager.
*
@@ -92,9 +92,6 @@
public static final int GPS_ENC_SUPL_UCS2 = 3;
public static final int GPS_ENC_UNKNOWN = -1;
- // Limit on SUPL NI emergency mode time extension after emergency sessions ends
- private static final int MAX_EMERGENCY_MODE_EXTENSION_SECONDS = 300; // 5 minute maximum
-
private final Context mContext;
private final TelephonyManager mTelephonyManager;
private final PhoneStateListener mPhoneStateListener;
@@ -252,19 +249,9 @@
}
public void setEmergencyExtensionSeconds(int emergencyExtensionSeconds) {
- if (emergencyExtensionSeconds > MAX_EMERGENCY_MODE_EXTENSION_SECONDS) {
- Log.w(TAG, "emergencyExtensionSeconds " + emergencyExtensionSeconds
- + " too high, reset to " + MAX_EMERGENCY_MODE_EXTENSION_SECONDS);
- emergencyExtensionSeconds = MAX_EMERGENCY_MODE_EXTENSION_SECONDS;
- } else if (emergencyExtensionSeconds < 0) {
- Log.w(TAG, "emergencyExtensionSeconds " + emergencyExtensionSeconds
- + " is negative, reset to zero.");
- emergencyExtensionSeconds = 0;
- }
mEmergencyExtensionMillis = TimeUnit.SECONDS.toMillis(emergencyExtensionSeconds);
}
-
// Handles NI events from HAL
public void handleNiNotification(GpsNiNotification notif) {
if (DEBUG) Log.d(TAG, "in handleNiNotification () :"
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index cef6614..df96994 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -537,6 +537,19 @@
public native long getCurrentPosition();
/**
+ * Gets the duration of the current data source.
+ * Same as {@link #getDuration(DataSourceDesc)} with
+ * {@code dsd = getCurrentDataSource()}.
+ *
+ * @return the duration in milliseconds, if no duration is available
+ * (for example, if streaming live content), -1 is returned.
+ * @throws NullPointerException if current data source is null
+ */
+ public long getDuration() {
+ return getDuration(getCurrentDataSource());
+ }
+
+ /**
* Gets the duration of the dsd.
*
* @param dsd the descriptor of data source of which you want to get duration
@@ -559,6 +572,18 @@
private native long native_getDuration(long srcId);
/**
+ * Gets the buffered media source position of current data source.
+ * Same as {@link #getBufferedPosition(DataSourceDesc)} with
+ * {@code dsd = getCurrentDataSource()}.
+ *
+ * @return the current buffered media source position in milliseconds
+ * @throws NullPointerException if current data source is null
+ */
+ public long getBufferedPosition() {
+ return getBufferedPosition(getCurrentDataSource());
+ }
+
+ /**
* Gets the buffered media source position of given dsd.
* For example a buffering update of 8000 milliseconds when 5000 milliseconds of the content
* has already been played indicates that the next 3000 milliseconds of the
@@ -1309,11 +1334,11 @@
return addTask(new Task(CALL_COMPLETED_SET_WAKE_LOCK, false) {
@Override
void process() {
- boolean washeld = false;
+ boolean wasHeld = false;
if (mWakeLock != null) {
if (mWakeLock.isHeld()) {
- washeld = true;
+ wasHeld = true;
mWakeLock.release();
}
}
@@ -1321,7 +1346,7 @@
mWakeLock = wakeLock;
if (mWakeLock != null) {
mWakeLock.setReferenceCounted(false);
- if (washeld) {
+ if (wasHeld) {
mWakeLock.acquire();
}
}
@@ -2015,6 +2040,21 @@
};
/**
+ * Returns a List of track information of current data source.
+ * Same as {@link #getTrackInfo(DataSourceDesc)} with
+ * {@code dsd = getCurrentDataSource()}.
+ *
+ * @return List of track info. The total number of tracks is the array length.
+ * Must be called again if an external timed text source has been added after
+ * addTimedTextSource method is called.
+ * @throws IllegalStateException if it is called in an invalid state.
+ * @throws NullPointerException if current data source is null
+ */
+ public @NonNull List<TrackInfo> getTrackInfo() {
+ return getTrackInfo(getCurrentDataSource());
+ }
+
+ /**
* Returns a List of track information.
*
* @param dsd the descriptor of data source of which you want to get track info
@@ -2024,7 +2064,6 @@
* @throws IllegalStateException if it is called in an invalid state.
* @throws NullPointerException if dsd is null
*/
-
public @NonNull List<TrackInfo> getTrackInfo(@NonNull DataSourceDesc dsd) {
if (dsd == null) {
throw new NullPointerException("non-null dsd is expected");
@@ -2060,9 +2099,34 @@
}
/**
- * Returns the index of the audio, video, or subtitle track currently selected for playback,
+ * Returns the index of the audio, video, or subtitle track currently selected for playback.
* The return value is an index into the array returned by {@link #getTrackInfo}, and can
- * be used in calls to {@link #selectTrack} or {@link #deselectTrack}.
+ * be used in calls to {@link #selectTrack(int)} or {@link #deselectTrack(int)}.
+ * Same as {@link #getSelectedTrack(DataSourceDesc, int)} with
+ * {@code dsd = getCurrentDataSource()}.
+ *
+ * @param trackType should be one of {@link TrackInfo#MEDIA_TRACK_TYPE_VIDEO},
+ * {@link TrackInfo#MEDIA_TRACK_TYPE_AUDIO}, or
+ * {@link TrackInfo#MEDIA_TRACK_TYPE_SUBTITLE}
+ * @return index of the audio, video, or subtitle track currently selected for playback;
+ * a negative integer is returned when there is no selected track for {@code trackType} or
+ * when {@code trackType} is not one of audio, video, or subtitle.
+ * @throws IllegalStateException if called after {@link #close()}
+ * @throws NullPointerException if current data source is null
+ *
+ * @see #getTrackInfo()
+ * @see #selectTrack(int)
+ * @see #deselectTrack(int)
+ */
+ public int getSelectedTrack(int trackType) {
+ return getSelectedTrack(getCurrentDataSource(), trackType);
+ }
+
+ /**
+ * Returns the index of the audio, video, or subtitle track currently selected for playback.
+ * The return value is an index into the array returned by {@link #getTrackInfo}, and can
+ * be used in calls to {@link #selectTrack(DataSourceDesc, int)} or
+ * {@link #deselectTrack(DataSourceDesc, int)}.
*
* @param dsd the descriptor of data source of which you want to get selected track
* @param trackType should be one of {@link TrackInfo#MEDIA_TRACK_TYPE_VIDEO},
@@ -2074,9 +2138,9 @@
* @throws IllegalStateException if called after {@link #close()}
* @throws NullPointerException if dsd is null
*
- * @see #getTrackInfo
- * @see #selectTrack
- * @see #deselectTrack
+ * @see #getTrackInfo(DataSourceDesc)
+ * @see #selectTrack(DataSourceDesc, int)
+ * @see #deselectTrack(DataSourceDesc, int)
*/
public int getSelectedTrack(@NonNull DataSourceDesc dsd, int trackType) {
if (dsd == null) {
@@ -2100,6 +2164,23 @@
}
/**
+ * Selects a track of current data source.
+ * Same as {@link #selectTrack(DataSourceDesc, int)} with
+ * {@code dsd = getCurrentDataSource()}.
+ *
+ * @param index the index of the track to be selected. The valid range of the index
+ * is 0..total number of track - 1. The total number of tracks as well as the type of
+ * each individual track can be found by calling {@link #getTrackInfo()} method.
+ * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
+ *
+ * @see MediaPlayer2#getTrackInfo()
+ */
+ // This is an asynchronous call.
+ public Object selectTrack(int index) {
+ return selectTrack(getCurrentDataSource(), index);
+ }
+
+ /**
* Selects a track.
* <p>
* If a MediaPlayer2 is in invalid state, it throws an IllegalStateException exception.
@@ -2123,10 +2204,10 @@
* @param dsd the descriptor of data source of which you want to select track
* @param index the index of the track to be selected. The valid range of the index
* is 0..total number of track - 1. The total number of tracks as well as the type of
- * each individual track can be found by calling {@link #getTrackInfo} method.
+ * each individual track can be found by calling {@link #getTrackInfo(DataSourceDesc)} method.
* @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*
- * @see MediaPlayer2#getTrackInfo
+ * @see MediaPlayer2#getTrackInfo(DataSourceDesc)
*/
// This is an asynchronous call.
public Object selectTrack(@NonNull DataSourceDesc dsd, int index) {
@@ -2139,6 +2220,23 @@
}
/**
+ * Deselect a track of current data source.
+ * Same as {@link #deselectTrack(DataSourceDesc, int)} with
+ * {@code dsd = getCurrentDataSource()}.
+ *
+ * @param index the index of the track to be deselected. The valid range of the index
+ * is 0..total number of tracks - 1. The total number of tracks as well as the type of
+ * each individual track can be found by calling {@link #getTrackInfo()} method.
+ * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
+ *
+ * @see MediaPlayer2#getTrackInfo()
+ */
+ // This is an asynchronous call.
+ public Object deselectTrack(int index) {
+ return deselectTrack(getCurrentDataSource(), index);
+ }
+
+ /**
* Deselect a track.
* <p>
* Currently, the track must be a timed text track and no audio or video tracks can be
@@ -2151,7 +2249,7 @@
* each individual track can be found by calling {@link #getTrackInfo} method.
* @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*
- * @see MediaPlayer2#getTrackInfo
+ * @see MediaPlayer2#getTrackInfo(DataSourceDesc)
*/
// This is an asynchronous call.
public Object deselectTrack(@NonNull DataSourceDesc dsd, int index) {
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index eeb1461..25b1df2 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -702,6 +702,12 @@
* is no guarantee that the recorder will have stopped by the time the
* listener is notified.
*
+ * <p>When using MPEG-4 container ({@link #setOutputFormat(int)} with
+ * {@link OutputFormat#MPEG_4}), it is recommended to set maximum duration that fits the use
+ * case. Setting a larger than required duration may result in a larger than needed output file
+ * because of space reserved for MOOV box expecting large movie data in this recording session.
+ * Unused space of MOOV box is turned into FREE box in the output file.</p>
+ *
* @param max_duration_ms the maximum duration in ms (if zero or negative, disables the duration limit)
*
*/
@@ -717,6 +723,12 @@
* is no guarantee that the recorder will have stopped by the time the
* listener is notified.
*
+ * <p>When using MPEG-4 container ({@link #setOutputFormat(int)} with
+ * {@link OutputFormat#MPEG_4}), it is recommended to set maximum filesize that fits the use
+ * case. Setting a larger than required filesize may result in a larger than needed output file
+ * because of space reserved for MOOV box expecting large movie data in this recording session.
+ * Unused space of MOOV box is turned into FREE box in the output file.</p>
+ *
* @param max_filesize_bytes the maximum filesize in bytes (if zero or negative, disables the limit)
*
*/
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index fda0fc4..d6b6339 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -102,7 +102,6 @@
"libhidlbase",
"libhidlmemory",
- "libpowermanager", // Used by JWakeLock. Will be replace with public SDK API.
"libmediametrics", // Used by MediaMetrics. Will be replaced with stable C API.
"libbinder", // Used by JWakeLock and MediaMetrics.
diff --git a/media/jni/android_media_MediaPlayer2.cpp b/media/jni/android_media_MediaPlayer2.cpp
index 7e6a8ab..9b4e730 100644
--- a/media/jni/android_media_MediaPlayer2.cpp
+++ b/media/jni/android_media_MediaPlayer2.cpp
@@ -86,7 +86,8 @@
// ----------------------------------------------------------------------------
struct fields_t {
- jfieldID context;
+ jfieldID context; // passed from Java to native, used for creating JWakeLock
+ jfieldID nativeContext; // mNativeContext in MediaPlayer2.java
jfieldID surface_texture;
jmethodID post_event;
@@ -225,21 +226,21 @@
static sp<MediaPlayer2> getMediaPlayer(JNIEnv* env, jobject thiz)
{
Mutex::Autolock l(sLock);
- MediaPlayer2* const p = (MediaPlayer2*)env->GetLongField(thiz, fields.context);
+ MediaPlayer2* const p = (MediaPlayer2*)env->GetLongField(thiz, fields.nativeContext);
return sp<MediaPlayer2>(p);
}
static sp<MediaPlayer2> setMediaPlayer(JNIEnv* env, jobject thiz, const sp<MediaPlayer2>& player)
{
Mutex::Autolock l(sLock);
- sp<MediaPlayer2> old = (MediaPlayer2*)env->GetLongField(thiz, fields.context);
+ sp<MediaPlayer2> old = (MediaPlayer2*)env->GetLongField(thiz, fields.nativeContext);
if (player.get()) {
player->incStrong((void*)setMediaPlayer);
}
if (old != 0) {
old->decStrong((void*)setMediaPlayer);
}
- env->SetLongField(thiz, fields.context, (jlong)player.get());
+ env->SetLongField(thiz, fields.nativeContext, (jlong)player.get());
return old;
}
@@ -955,11 +956,16 @@
return;
}
- fields.context = env->GetFieldID(clazz, "mNativeContext", "J");
+ fields.context = env->GetFieldID(clazz, "mContext", "Landroid/content/Context;");
if (fields.context == NULL) {
return;
}
+ fields.nativeContext = env->GetFieldID(clazz, "mNativeContext", "J");
+ if (fields.nativeContext == NULL) {
+ return;
+ }
+
fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative",
"(Ljava/lang/Object;JIII[B)V");
if (fields.post_event == NULL) {
@@ -1013,7 +1019,8 @@
jint sessionId, jobject weak_this)
{
ALOGV("native_setup");
- sp<MediaPlayer2> mp = MediaPlayer2::Create(sessionId);
+ jobject context = env->GetObjectField(thiz, fields.context);
+ sp<MediaPlayer2> mp = MediaPlayer2::Create(sessionId, context);
if (mp == NULL) {
jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
return;
diff --git a/native/android/net.c b/native/android/net.c
index 4cac371..a8104fc 100644
--- a/native/android/net.c
+++ b/native/android/net.c
@@ -84,26 +84,28 @@
return android_getaddrinfofornet(node, service, hints, netid, 0, res);
}
-int android_res_nquery(net_handle_t network, const char *dname, int ns_class, int ns_type) {
+int android_res_nquery(net_handle_t network, const char *dname,
+ int ns_class, int ns_type, enum ResNsendFlags flags) {
unsigned netid;
if (!getnetidfromhandle(network, &netid)) {
return -ENONET;
}
- return resNetworkQuery(netid, dname, ns_class, ns_type);
+ return resNetworkQuery(netid, dname, ns_class, ns_type, flags);
}
int android_res_nresult(int fd, int *rcode, uint8_t *answer, size_t anslen) {
return resNetworkResult(fd, rcode, answer, anslen);
}
-int android_res_nsend(net_handle_t network, const uint8_t *msg, size_t msglen) {
+int android_res_nsend(net_handle_t network, const uint8_t *msg, size_t msglen,
+ enum ResNsendFlags flags) {
unsigned netid;
if (!getnetidfromhandle(network, &netid)) {
return -ENONET;
}
- return resNetworkSend(netid, msg, msglen);
+ return resNetworkSend(netid, msg, msglen, flags);
}
void android_res_cancel(int nsend_fd) {
diff --git a/native/webview/plat_support/draw_functor.cpp b/native/webview/plat_support/draw_functor.cpp
index 820bac5..6c1ceab 100644
--- a/native/webview/plat_support/draw_functor.cpp
+++ b/native/webview/plat_support/draw_functor.cpp
@@ -82,13 +82,9 @@
.onDestroyed = &onDestroyed,
};
if (!callbacks_initialized) {
- switch (uirenderer::WebViewFunctor_queryPlatformRenderMode()) {
- case uirenderer::RenderMode::OpenGL_ES:
- webview_functor_callbacks.gles.draw = &draw_gl;
- break;
- case uirenderer::RenderMode::Vulkan:
- break;
- }
+ // Under uirenderer::RenderMode::Vulkan, whether gles or vk union should
+ // be populated should match whether the vk-gl interop is used.
+ webview_functor_callbacks.gles.draw = &draw_gl;
callbacks_initialized = true;
}
SupportData* support = new SupportData{
diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
index af52c00..fab1bcc 100644
--- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java
+++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
@@ -366,9 +366,9 @@
}
@Override
- public void onNotificationDirectReply(@NonNull String key) {
- if (DEBUG) Log.i(TAG, "onNotificationDirectReply " + key);
- mSmartActionsHelper.onNotificationDirectReply(key);
+ public void onNotificationDirectReplied(@NonNull String key) {
+ if (DEBUG) Log.i(TAG, "onNotificationDirectReplied " + key);
+ mSmartActionsHelper.onNotificationDirectReplied(key);
}
@Override
diff --git a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
index f2db2da..a9d8f62 100644
--- a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
+++ b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
@@ -165,7 +165,7 @@
}
}
- void onNotificationDirectReply(@NonNull String key) {
+ void onNotificationDirectReplied(@NonNull String key) {
if (mTextClassifier == null) {
return;
}
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionHelperTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionHelperTest.java
index da382a0..7d74788 100644
--- a/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionHelperTest.java
+++ b/packages/ExtServices/tests/src/android/ext/services/notification/SmartActionHelperTest.java
@@ -241,7 +241,7 @@
when(mNotificationEntry.getNotification()).thenReturn(notification);
mSmartActionsHelper.suggestReplies(mNotificationEntry);
- mSmartActionsHelper.onNotificationDirectReply(NOTIFICATION_KEY);
+ mSmartActionsHelper.onNotificationDirectReplied(NOTIFICATION_KEY);
ArgumentCaptor<TextClassifierEvent> argumentCaptor =
ArgumentCaptor.forClass(TextClassifierEvent.class);
diff --git a/packages/NetworkStack/Android.bp b/packages/NetworkStack/Android.bp
index 55bb517..4688848 100644
--- a/packages/NetworkStack/Android.bp
+++ b/packages/NetworkStack/Android.bp
@@ -22,6 +22,10 @@
srcs: [
"src/**/*.java",
],
+ static_libs: [
+ "dhcp-packet-lib",
+ "frameworks-net-shared-utils",
+ ]
}
// Updatable network stack packaged as an application
diff --git a/packages/NetworkStack/AndroidManifest.xml b/packages/NetworkStack/AndroidManifest.xml
index d1c5cb6..8516d94 100644
--- a/packages/NetworkStack/AndroidManifest.xml
+++ b/packages/NetworkStack/AndroidManifest.xml
@@ -17,7 +17,7 @@
*/
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.google.android.networkstack"
+ package="com.android.mainline.networkstack"
android:sharedUserId="android.uid.networkstack">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
diff --git a/services/net/java/android/net/dhcp/DhcpLease.java b/packages/NetworkStack/src/android/net/dhcp/DhcpLease.java
similarity index 100%
rename from services/net/java/android/net/dhcp/DhcpLease.java
rename to packages/NetworkStack/src/android/net/dhcp/DhcpLease.java
diff --git a/services/net/java/android/net/dhcp/DhcpLeaseRepository.java b/packages/NetworkStack/src/android/net/dhcp/DhcpLeaseRepository.java
similarity index 99%
rename from services/net/java/android/net/dhcp/DhcpLeaseRepository.java
rename to packages/NetworkStack/src/android/net/dhcp/DhcpLeaseRepository.java
index b3d0512..0d298de 100644
--- a/services/net/java/android/net/dhcp/DhcpLeaseRepository.java
+++ b/packages/NetworkStack/src/android/net/dhcp/DhcpLeaseRepository.java
@@ -21,7 +21,8 @@
import static android.net.NetworkUtils.prefixLengthToV4NetmaskIntHTH;
import static android.net.dhcp.DhcpLease.EXPIRATION_NEVER;
import static android.net.dhcp.DhcpLease.inet4AddrToString;
-import static android.net.util.NetworkConstants.IPV4_ADDR_BITS;
+
+import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_BITS;
import static java.lang.Math.min;
diff --git a/services/net/java/android/net/dhcp/DhcpPacketListener.java b/packages/NetworkStack/src/android/net/dhcp/DhcpPacketListener.java
similarity index 100%
rename from services/net/java/android/net/dhcp/DhcpPacketListener.java
rename to packages/NetworkStack/src/android/net/dhcp/DhcpPacketListener.java
diff --git a/services/net/java/android/net/dhcp/DhcpServer.java b/packages/NetworkStack/src/android/net/dhcp/DhcpServer.java
similarity index 85%
rename from services/net/java/android/net/dhcp/DhcpServer.java
rename to packages/NetworkStack/src/android/net/dhcp/DhcpServer.java
index 641bba2..14e2936 100644
--- a/services/net/java/android/net/dhcp/DhcpServer.java
+++ b/packages/NetworkStack/src/android/net/dhcp/DhcpServer.java
@@ -23,7 +23,8 @@
import static android.net.dhcp.DhcpPacket.DHCP_HOST_NAME;
import static android.net.dhcp.DhcpPacket.DHCP_SERVER;
import static android.net.dhcp.DhcpPacket.ENCAP_BOOTP;
-import static android.net.dhcp.DhcpPacket.INFINITE_LEASE;
+import static android.net.dhcp.IDhcpServer.STATUS_INVALID_ARGUMENT;
+import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
import static android.system.OsConstants.AF_INET;
import static android.system.OsConstants.IPPROTO_UDP;
import static android.system.OsConstants.SOCK_DGRAM;
@@ -32,21 +33,28 @@
import static android.system.OsConstants.SO_BROADCAST;
import static android.system.OsConstants.SO_REUSEADDR;
+import static com.android.server.util.NetworkStackConstants.INFINITE_LEASE;
+import static com.android.server.util.PermissionUtil.checkNetworkStackCallingPermission;
+
import static java.lang.Integer.toUnsignedLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.net.INetworkStackStatusCallback;
import android.net.MacAddress;
import android.net.NetworkUtils;
import android.net.TrafficStats;
import android.net.util.SharedLog;
import android.os.Handler;
+import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
+import android.os.RemoteException;
import android.os.SystemClock;
import android.system.ErrnoException;
import android.system.Os;
import android.text.TextUtils;
+import android.util.Pair;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.HexDump;
@@ -70,7 +78,7 @@
* on the looper asynchronously.
* @hide
*/
-public class DhcpServer {
+public class DhcpServer extends IDhcpServer.Stub {
private static final String REPO_TAG = "Repository";
// Lease time to transmit to client instead of a negative time in case a lease expired before
@@ -82,7 +90,7 @@
private static final int CMD_UPDATE_PARAMS = 3;
@NonNull
- private final ServerHandler mHandler;
+ private final HandlerThread mHandlerThread;
@NonNull
private final String mIfName;
@NonNull
@@ -93,10 +101,14 @@
private final Dependencies mDeps;
@NonNull
private final Clock mClock;
- @NonNull
- private final DhcpPacketListener mPacketListener;
@Nullable
+ private volatile ServerHandler mHandler;
+
+ // Accessed only on the handler thread
+ @Nullable
+ private DhcpPacketListener mPacketListener;
+ @Nullable
private FileDescriptor mSocket;
@NonNull
private DhcpServingParams mServingParams;
@@ -156,6 +168,12 @@
*/
void addArpEntry(@NonNull Inet4Address ipv4Addr, @NonNull MacAddress ethAddr,
@NonNull String ifname, @NonNull FileDescriptor fd) throws IOException;
+
+ /**
+ * Verify that the caller is allowed to call public methods on DhcpServer.
+ * @throws SecurityException The caller is not allowed to call public methods on DhcpServer.
+ */
+ void checkCaller() throws SecurityException;
}
private class DependenciesImpl implements Dependencies {
@@ -189,6 +207,11 @@
@NonNull String ifname, @NonNull FileDescriptor fd) throws IOException {
NetworkUtils.addArpEntry(ipv4Addr, ethAddr, ifname, fd);
}
+
+ @Override
+ public void checkCaller() {
+ checkNetworkStackCallingPermission();
+ }
}
private static class MalformedPacketException extends Exception {
@@ -197,41 +220,62 @@
}
}
- public DhcpServer(@NonNull Looper looper, @NonNull String ifName,
+ public DhcpServer(@NonNull String ifName,
@NonNull DhcpServingParams params, @NonNull SharedLog log) {
- this(looper, ifName, params, log, null);
+ this(new HandlerThread(DhcpServer.class.getSimpleName() + "." + ifName),
+ ifName, params, log, null);
}
@VisibleForTesting
- DhcpServer(@NonNull Looper looper, @NonNull String ifName,
+ DhcpServer(@NonNull HandlerThread handlerThread, @NonNull String ifName,
@NonNull DhcpServingParams params, @NonNull SharedLog log,
@Nullable Dependencies deps) {
if (deps == null) {
deps = new DependenciesImpl();
}
- mHandler = new ServerHandler(looper);
+ mHandlerThread = handlerThread;
mIfName = ifName;
mServingParams = params;
mLog = log;
mDeps = deps;
mClock = deps.makeClock();
- mPacketListener = deps.makePacketListener();
mLeaseRepo = deps.makeLeaseRepository(mServingParams, mLog, mClock);
}
/**
* Start listening for and responding to packets.
+ *
+ * <p>It is not legal to call this method more than once; in particular the server cannot be
+ * restarted after being stopped.
*/
- public void start() {
- mHandler.sendEmptyMessage(CMD_START_DHCP_SERVER);
+ @Override
+ public void start(@Nullable INetworkStackStatusCallback cb) {
+ mDeps.checkCaller();
+ mHandlerThread.start();
+ mHandler = new ServerHandler(mHandlerThread.getLooper());
+ sendMessage(CMD_START_DHCP_SERVER, cb);
}
/**
* Update serving parameters. All subsequently received requests will be handled with the new
* parameters, and current leases that are incompatible with the new parameters are dropped.
*/
- public void updateParams(@NonNull DhcpServingParams params) {
- sendMessage(CMD_UPDATE_PARAMS, params);
+ @Override
+ public void updateParams(@Nullable DhcpServingParamsParcel params,
+ @Nullable INetworkStackStatusCallback cb) throws RemoteException {
+ mDeps.checkCaller();
+ final DhcpServingParams parsedParams;
+ try {
+ // throws InvalidParameterException with null params
+ parsedParams = DhcpServingParams.fromParcelableObject(params);
+ } catch (DhcpServingParams.InvalidParameterException e) {
+ mLog.e("Invalid parameters sent to DhcpServer", e);
+ if (cb != null) {
+ cb.onStatusAvailable(STATUS_INVALID_ARGUMENT);
+ }
+ return;
+ }
+ sendMessage(CMD_UPDATE_PARAMS, new Pair<>(parsedParams, cb));
}
/**
@@ -240,11 +284,17 @@
* <p>As the server is stopped asynchronously, some packets may still be processed shortly after
* calling this method.
*/
- public void stop() {
- mHandler.sendEmptyMessage(CMD_STOP_DHCP_SERVER);
+ @Override
+ public void stop(@Nullable INetworkStackStatusCallback cb) {
+ mDeps.checkCaller();
+ sendMessage(CMD_STOP_DHCP_SERVER, cb);
}
private void sendMessage(int what, @Nullable Object obj) {
+ if (mHandler == null) {
+ mLog.e("Attempting to send a command to stopped DhcpServer: " + what);
+ return;
+ }
mHandler.sendMessage(mHandler.obtainMessage(what, obj));
}
@@ -255,23 +305,42 @@
@Override
public void handleMessage(@NonNull Message msg) {
+ final INetworkStackStatusCallback cb;
switch (msg.what) {
case CMD_UPDATE_PARAMS:
- final DhcpServingParams params = (DhcpServingParams) msg.obj;
+ final Pair<DhcpServingParams, INetworkStackStatusCallback> pair =
+ (Pair<DhcpServingParams, INetworkStackStatusCallback>) msg.obj;
+ final DhcpServingParams params = pair.first;
mServingParams = params;
mLeaseRepo.updateParams(
DhcpServingParams.makeIpPrefix(mServingParams.serverAddr),
params.excludedAddrs,
params.dhcpLeaseTimeSecs);
+
+ cb = pair.second;
break;
case CMD_START_DHCP_SERVER:
- // This is a no-op if the listener is already started
+ mPacketListener = mDeps.makePacketListener();
mPacketListener.start();
+ cb = (INetworkStackStatusCallback) msg.obj;
break;
case CMD_STOP_DHCP_SERVER:
- // This is a no-op if the listener was not started
- mPacketListener.stop();
+ if (mPacketListener != null) {
+ mPacketListener.stop();
+ mPacketListener = null;
+ }
+ mHandlerThread.quitSafely();
+ cb = (INetworkStackStatusCallback) msg.obj;
break;
+ default:
+ return;
+ }
+ if (cb != null) {
+ try {
+ cb.onStatusAvailable(STATUS_SUCCESS);
+ } catch (RemoteException e) {
+ mLog.e("Could not send status back to caller", e);
+ }
}
}
}
@@ -572,7 +641,7 @@
return mSocket;
} catch (IOException | ErrnoException e) {
mLog.e("Error creating UDP socket", e);
- DhcpServer.this.stop();
+ DhcpServer.this.stop(null);
return null;
} finally {
TrafficStats.setThreadStatsTag(oldTag);
diff --git a/services/net/java/android/net/dhcp/DhcpServingParams.java b/packages/NetworkStack/src/android/net/dhcp/DhcpServingParams.java
similarity index 95%
rename from services/net/java/android/net/dhcp/DhcpServingParams.java
rename to packages/NetworkStack/src/android/net/dhcp/DhcpServingParams.java
index 2780814a..f38888a 100644
--- a/services/net/java/android/net/dhcp/DhcpServingParams.java
+++ b/packages/NetworkStack/src/android/net/dhcp/DhcpServingParams.java
@@ -18,9 +18,10 @@
import static android.net.NetworkUtils.getPrefixMaskAsInet4Address;
import static android.net.NetworkUtils.intToInet4AddressHTH;
-import static android.net.dhcp.DhcpPacket.INFINITE_LEASE;
-import static android.net.util.NetworkConstants.IPV4_MAX_MTU;
-import static android.net.util.NetworkConstants.IPV4_MIN_MTU;
+
+import static com.android.server.util.NetworkStackConstants.INFINITE_LEASE;
+import static com.android.server.util.NetworkStackConstants.IPV4_MAX_MTU;
+import static com.android.server.util.NetworkStackConstants.IPV4_MIN_MTU;
import static java.lang.Integer.toUnsignedLong;
@@ -107,9 +108,13 @@
/**
* Create parameters from a stable AIDL-compatible parcel.
+ * @throws InvalidParameterException The parameters parcelable is null or invalid.
*/
- public static DhcpServingParams fromParcelableObject(@NonNull DhcpServingParamsParcel parcel)
+ public static DhcpServingParams fromParcelableObject(@Nullable DhcpServingParamsParcel parcel)
throws InvalidParameterException {
+ if (parcel == null) {
+ throw new InvalidParameterException("Null serving parameters");
+ }
final LinkAddress serverAddr = new LinkAddress(
intToInet4AddressHTH(parcel.serverAddr),
parcel.serverAddrPrefixLength);
diff --git a/packages/NetworkStack/src/android/net/util/SharedLog.java b/packages/NetworkStack/src/android/net/util/SharedLog.java
new file mode 100644
index 0000000..74bc147
--- /dev/null
+++ b/packages/NetworkStack/src/android/net/util/SharedLog.java
@@ -0,0 +1,197 @@
+/*
+ * 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.annotation.NonNull;
+import android.annotation.Nullable;
+import android.text.TextUtils;
+import android.util.LocalLog;
+import android.util.Log;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.StringJoiner;
+
+
+/**
+ * Class to centralize logging functionality for tethering.
+ *
+ * All access to class methods other than dump() must be on the same thread.
+ *
+ * @hide
+ */
+public class SharedLog {
+ private static final int DEFAULT_MAX_RECORDS = 500;
+ private static final String COMPONENT_DELIMITER = ".";
+
+ private enum Category {
+ NONE,
+ ERROR,
+ MARK,
+ WARN,
+ };
+
+ private final LocalLog mLocalLog;
+ // The tag to use for output to the system log. This is not output to the
+ // LocalLog because that would be redundant.
+ private final String mTag;
+ // The component (or subcomponent) of a system that is sharing this log.
+ // This can grow in depth if components call forSubComponent() to obtain
+ // their SharedLog instance. The tag is not included in the component for
+ // brevity.
+ private final String mComponent;
+
+ public SharedLog(String tag) {
+ this(DEFAULT_MAX_RECORDS, tag);
+ }
+
+ public SharedLog(int maxRecords, String tag) {
+ this(new LocalLog(maxRecords), tag, tag);
+ }
+
+ private SharedLog(LocalLog localLog, String tag, String component) {
+ mLocalLog = localLog;
+ mTag = tag;
+ mComponent = component;
+ }
+
+ /**
+ * Create a SharedLog based on this log with an additional component prefix on each logged line.
+ */
+ public SharedLog forSubComponent(String component) {
+ if (!isRootLogInstance()) {
+ component = mComponent + COMPONENT_DELIMITER + component;
+ }
+ return new SharedLog(mLocalLog, mTag, component);
+ }
+
+ /**
+ * Dump the contents of this log.
+ *
+ * <p>This method may be called on any thread.
+ */
+ public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+ mLocalLog.readOnlyLocalLog().dump(fd, writer, args);
+ }
+
+ //////
+ // Methods that both log an entry and emit it to the system log.
+ //////
+
+ /**
+ * Log an error due to an exception. This does not include the exception stacktrace.
+ *
+ * <p>The log entry will be also added to the system log.
+ * @see #e(String, Throwable)
+ */
+ public void e(Exception e) {
+ Log.e(mTag, record(Category.ERROR, e.toString()));
+ }
+
+ /**
+ * Log an error message.
+ *
+ * <p>The log entry will be also added to the system log.
+ */
+ public void e(String msg) {
+ Log.e(mTag, record(Category.ERROR, msg));
+ }
+
+ /**
+ * Log an error due to an exception, with the exception stacktrace if provided.
+ *
+ * <p>The error and exception message appear in the shared log, but the stacktrace is only
+ * logged in general log output (logcat). The log entry will be also added to the system log.
+ */
+ public void e(@NonNull String msg, @Nullable Throwable exception) {
+ if (exception == null) {
+ e(msg);
+ return;
+ }
+ Log.e(mTag, record(Category.ERROR, msg + ": " + exception.getMessage()), exception);
+ }
+
+ /**
+ * Log an informational message.
+ *
+ * <p>The log entry will be also added to the system log.
+ */
+ public void i(String msg) {
+ Log.i(mTag, record(Category.NONE, msg));
+ }
+
+ /**
+ * Log a warning message.
+ *
+ * <p>The log entry will be also added to the system log.
+ */
+ public void w(String msg) {
+ Log.w(mTag, record(Category.WARN, msg));
+ }
+
+ //////
+ // Methods that only log an entry (and do NOT emit to the system log).
+ //////
+
+ /**
+ * Log a general message to be only included in the in-memory log.
+ *
+ * <p>The log entry will *not* be added to the system log.
+ */
+ public void log(String msg) {
+ record(Category.NONE, msg);
+ }
+
+ /**
+ * Log a general, formatted message to be only included in the in-memory log.
+ *
+ * <p>The log entry will *not* be added to the system log.
+ * @see String#format(String, Object...)
+ */
+ public void logf(String fmt, Object... args) {
+ log(String.format(fmt, args));
+ }
+
+ /**
+ * Log a message with MARK level.
+ *
+ * <p>The log entry will *not* be added to the system log.
+ */
+ public void mark(String msg) {
+ record(Category.MARK, msg);
+ }
+
+ private String record(Category category, String msg) {
+ final String entry = logLine(category, msg);
+ mLocalLog.log(entry);
+ return entry;
+ }
+
+ private String logLine(Category category, String msg) {
+ final StringJoiner sj = new StringJoiner(" ");
+ if (!isRootLogInstance()) sj.add("[" + mComponent + "]");
+ if (category != Category.NONE) sj.add(category.toString());
+ return sj.add(msg).toString();
+ }
+
+ // Check whether this SharedLog instance is nominally the top level in
+ // a potential hierarchy of shared logs (the root of a tree),
+ // or is a subcomponent within the hierarchy.
+ private boolean isRootLogInstance() {
+ return TextUtils.isEmpty(mComponent) || mComponent.equals(mTag);
+ }
+}
diff --git a/packages/NetworkStack/src/com/android/server/NetworkStackService.java b/packages/NetworkStack/src/com/android/server/NetworkStackService.java
index 5afaf58..7fea1e0 100644
--- a/packages/NetworkStack/src/com/android/server/NetworkStackService.java
+++ b/packages/NetworkStack/src/com/android/server/NetworkStackService.java
@@ -16,15 +16,24 @@
package com.android.server;
-import static android.os.Binder.getCallingUid;
+import static android.net.dhcp.IDhcpServer.STATUS_INVALID_ARGUMENT;
+import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
+import static android.net.dhcp.IDhcpServer.STATUS_UNKNOWN_ERROR;
+
+import static com.android.server.util.PermissionUtil.checkNetworkStackCallingPermission;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Service;
import android.content.Intent;
import android.net.INetworkStackConnector;
+import android.net.dhcp.DhcpServer;
+import android.net.dhcp.DhcpServingParams;
+import android.net.dhcp.DhcpServingParamsParcel;
+import android.net.dhcp.IDhcpServerCallbacks;
+import android.net.util.SharedLog;
import android.os.IBinder;
-import android.os.Process;
+import android.os.RemoteException;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -54,21 +63,37 @@
}
private static class NetworkStackConnector extends INetworkStackConnector.Stub {
- // TODO: makeDhcpServer(), etc. will go here.
+ @NonNull
+ private final SharedLog mLog = new SharedLog(TAG);
+
+ @Override
+ public void makeDhcpServer(@NonNull String ifName, @NonNull DhcpServingParamsParcel params,
+ @NonNull IDhcpServerCallbacks cb) throws RemoteException {
+ checkNetworkStackCallingPermission();
+ final DhcpServer server;
+ try {
+ server = new DhcpServer(
+ ifName,
+ DhcpServingParams.fromParcelableObject(params),
+ mLog.forSubComponent(ifName + ".DHCP"));
+ } catch (DhcpServingParams.InvalidParameterException e) {
+ mLog.e("Invalid DhcpServingParams", e);
+ cb.onDhcpServerCreated(STATUS_INVALID_ARGUMENT, null);
+ return;
+ } catch (Exception e) {
+ mLog.e("Unknown error starting DhcpServer", e);
+ cb.onDhcpServerCreated(STATUS_UNKNOWN_ERROR, null);
+ return;
+ }
+ cb.onDhcpServerCreated(STATUS_SUCCESS, server);
+ }
@Override
protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter fout,
@Nullable String[] args) {
- checkCaller();
+ checkNetworkStackCallingPermission();
fout.println("NetworkStack logs:");
- // TODO: dump logs here
- }
- }
-
- private static void checkCaller() {
- // TODO: check that the calling PID is the system server.
- if (getCallingUid() != Process.SYSTEM_UID && getCallingUid() != Process.ROOT_UID) {
- throw new SecurityException("Invalid caller: " + getCallingUid());
+ mLog.dump(fd, fout, args);
}
}
}
diff --git a/packages/NetworkStack/src/com/android/server/util/NetworkStackConstants.java b/packages/NetworkStack/src/com/android/server/util/NetworkStackConstants.java
new file mode 100644
index 0000000..bb5900c
--- /dev/null
+++ b/packages/NetworkStack/src/com/android/server/util/NetworkStackConstants.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.util;
+
+/**
+ * Network constants used by the network stack.
+ */
+public final class NetworkStackConstants {
+
+ /**
+ * IPv4 constants.
+ *
+ * See also:
+ * - https://tools.ietf.org/html/rfc791
+ */
+ public static final int IPV4_ADDR_BITS = 32;
+ public static final int IPV4_MIN_MTU = 68;
+ public static final int IPV4_MAX_MTU = 65_535;
+
+ /**
+ * DHCP constants.
+ *
+ * See also:
+ * - https://tools.ietf.org/html/rfc2131
+ */
+ public static final int INFINITE_LEASE = 0xffffffff;
+
+ private NetworkStackConstants() {
+ throw new UnsupportedOperationException("This class is not to be instantiated");
+ }
+}
diff --git a/packages/NetworkStack/src/com/android/server/util/PermissionUtil.java b/packages/NetworkStack/src/com/android/server/util/PermissionUtil.java
new file mode 100644
index 0000000..733f873
--- /dev/null
+++ b/packages/NetworkStack/src/com/android/server/util/PermissionUtil.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.util;
+
+import static android.os.Binder.getCallingUid;
+
+import android.os.Process;
+
+/**
+ * Utility class to check calling permissions on the network stack.
+ */
+public final class PermissionUtil {
+
+ /**
+ * Check that the caller is allowed to communicate with the network stack.
+ * @throws SecurityException The caller is not allowed to communicate with the network stack.
+ */
+ public static void checkNetworkStackCallingPermission() {
+ // TODO: check that the calling PID is the system server.
+ if (getCallingUid() != Process.SYSTEM_UID && getCallingUid() != Process.ROOT_UID) {
+ throw new SecurityException("Invalid caller: " + getCallingUid());
+ }
+ }
+
+ private PermissionUtil() {
+ throw new UnsupportedOperationException("This class is not to be instantiated");
+ }
+}
diff --git a/packages/NetworkStack/tests/Android.bp b/packages/NetworkStack/tests/Android.bp
new file mode 100644
index 0000000..bd7ff2a
--- /dev/null
+++ b/packages/NetworkStack/tests/Android.bp
@@ -0,0 +1,35 @@
+//
+// Copyright (C) 2018 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.
+//
+
+android_test {
+ name: "NetworkStackTests",
+ srcs: ["src/**/*.java"],
+ static_libs: [
+ "android-support-test",
+ "mockito-target-extended-minus-junit4",
+ "NetworkStackLib",
+ "testables",
+ ],
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ ],
+ jni_libs: [
+ // For mockito extended
+ "libdexmakerjvmtiagent",
+ "libstaticjvmtiagent",
+ ]
+}
\ No newline at end of file
diff --git a/packages/NetworkStack/tests/AndroidManifest.xml b/packages/NetworkStack/tests/AndroidManifest.xml
new file mode 100644
index 0000000..8b8474f
--- /dev/null
+++ b/packages/NetworkStack/tests/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.server.networkstack.tests">
+ <application android:debuggable="true">
+ <uses-library android:name="android.test.runner" />
+ </application>
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.server.networkstack.tests"
+ android:label="Networking service tests">
+ </instrumentation>
+</manifest>
\ No newline at end of file
diff --git a/packages/NetworkStack/tests/AndroidTest.xml b/packages/NetworkStack/tests/AndroidTest.xml
new file mode 100644
index 0000000..6b08b57
--- /dev/null
+++ b/packages/NetworkStack/tests/AndroidTest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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.
+-->
+<configuration description="Runs Tests for NetworkStack">
+ <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="test-file-name" value="NetworkStackTests.apk" />
+ </target_preparer>
+
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="framework-base-presubmit" />
+ <option name="test-tag" value="NetworkStackTests" />
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.server.networkstack.tests" />
+ <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+</configuration>
\ No newline at end of file
diff --git a/tests/net/java/android/net/dhcp/DhcpLeaseRepositoryTest.java b/packages/NetworkStack/tests/src/android/net/dhcp/DhcpLeaseRepositoryTest.java
similarity index 99%
rename from tests/net/java/android/net/dhcp/DhcpLeaseRepositoryTest.java
rename to packages/NetworkStack/tests/src/android/net/dhcp/DhcpLeaseRepositoryTest.java
index ba0448c..51d50d9 100644
--- a/tests/net/java/android/net/dhcp/DhcpLeaseRepositoryTest.java
+++ b/packages/NetworkStack/tests/src/android/net/dhcp/DhcpLeaseRepositoryTest.java
@@ -16,6 +16,7 @@
package android.net.dhcp;
+import static android.net.InetAddresses.parseNumericAddress;
import static android.net.dhcp.DhcpLease.HOSTNAME_NONE;
import static android.net.dhcp.DhcpLeaseRepository.CLIENTID_UNSPEC;
import static android.net.dhcp.DhcpLeaseRepository.INETADDR_UNSPEC;
@@ -29,7 +30,6 @@
import static org.mockito.Mockito.when;
import static java.lang.String.format;
-import static java.net.InetAddress.parseNumericAddress;
import android.annotation.NonNull;
import android.annotation.Nullable;
diff --git a/tests/net/java/android/net/dhcp/DhcpServerTest.java b/packages/NetworkStack/tests/src/android/net/dhcp/DhcpServerTest.java
similarity index 89%
rename from tests/net/java/android/net/dhcp/DhcpServerTest.java
rename to packages/NetworkStack/tests/src/android/net/dhcp/DhcpServerTest.java
index ab9bd84..d4c1e2e 100644
--- a/tests/net/java/android/net/dhcp/DhcpServerTest.java
+++ b/packages/NetworkStack/tests/src/android/net/dhcp/DhcpServerTest.java
@@ -16,11 +16,13 @@
package android.net.dhcp;
+import static android.net.InetAddresses.parseNumericAddress;
import static android.net.dhcp.DhcpPacket.DHCP_CLIENT;
import static android.net.dhcp.DhcpPacket.DHCP_HOST_NAME;
import static android.net.dhcp.DhcpPacket.ENCAP_BOOTP;
import static android.net.dhcp.DhcpPacket.INADDR_ANY;
import static android.net.dhcp.DhcpPacket.INADDR_BROADCAST;
+import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
@@ -33,14 +35,14 @@
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import static java.net.InetAddress.parseNumericAddress;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.net.INetworkStackStatusCallback;
import android.net.LinkAddress;
import android.net.MacAddress;
import android.net.dhcp.DhcpLeaseRepository.InvalidAddressException;
@@ -48,9 +50,11 @@
import android.net.dhcp.DhcpServer.Clock;
import android.net.dhcp.DhcpServer.Dependencies;
import android.net.util.SharedLog;
-import android.os.test.TestLooper;
+import android.os.HandlerThread;
import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
import org.junit.After;
import org.junit.Before;
@@ -67,10 +71,10 @@
import java.util.HashSet;
import java.util.Set;
-@RunWith(AndroidJUnit4.class)
+@RunWith(AndroidTestingRunner.class)
@SmallTest
+@RunWithLooper
public class DhcpServerTest {
- private static final String PROP_DEXMAKER_SHARE_CLASSLOADER = "dexmaker.share_classloader";
private static final String TEST_IFACE = "testiface";
private static final Inet4Address TEST_SERVER_ADDR = parseAddr("192.168.0.2");
@@ -113,18 +117,25 @@
private ArgumentCaptor<Inet4Address> mResponseDstAddrCaptor;
@NonNull
- private TestLooper mLooper;
+ private HandlerThread mHandlerThread;
+ @NonNull
+ private TestableLooper mLooper;
@NonNull
private DhcpServer mServer;
@Nullable
private String mPrevShareClassloaderProp;
+ private final INetworkStackStatusCallback mAssertSuccessCallback =
+ new INetworkStackStatusCallback.Stub() {
+ @Override
+ public void onStatusAvailable(int statusCode) {
+ assertEquals(STATUS_SUCCESS, statusCode);
+ }
+ };
+
@Before
public void setUp() throws Exception {
- // Allow mocking package-private classes
- mPrevShareClassloaderProp = System.getProperty(PROP_DEXMAKER_SHARE_CLASSLOADER);
- System.setProperty(PROP_DEXMAKER_SHARE_CLASSLOADER, "true");
MockitoAnnotations.initMocks(this);
when(mDeps.makeLeaseRepository(any(), any(), any())).thenReturn(mRepository);
@@ -143,20 +154,22 @@
.setExcludedAddrs(TEST_EXCLUDED_ADDRS)
.build();
- mLooper = new TestLooper();
- mServer = new DhcpServer(mLooper.getLooper(), TEST_IFACE, servingParams,
+ mLooper = TestableLooper.get(this);
+ mHandlerThread = spy(new HandlerThread("TestDhcpServer"));
+ when(mHandlerThread.getLooper()).thenReturn(mLooper.getLooper());
+ mServer = new DhcpServer(mHandlerThread, TEST_IFACE, servingParams,
new SharedLog(DhcpServerTest.class.getSimpleName()), mDeps);
- mServer.start();
- mLooper.dispatchAll();
+ mServer.start(mAssertSuccessCallback);
+ mLooper.processAllMessages();
}
@After
- public void tearDown() {
- // Calling stop() several times is not an issue
- mServer.stop();
- System.setProperty(PROP_DEXMAKER_SHARE_CLASSLOADER,
- (mPrevShareClassloaderProp == null ? "" : mPrevShareClassloaderProp));
+ public void tearDown() throws Exception {
+ mServer.stop(mAssertSuccessCallback);
+ mLooper.processMessages(1);
+ verify(mPacketListener, times(1)).stop();
+ verify(mHandlerThread, times(1)).quitSafely();
}
@Test
@@ -165,13 +178,6 @@
}
@Test
- public void testStop() throws Exception {
- mServer.stop();
- mLooper.dispatchAll();
- verify(mPacketListener, times(1)).stop();
- }
-
- @Test
public void testDiscover() throws Exception {
// TODO: refactor packet construction to eliminate unnecessary/confusing/duplicate fields
when(mRepository.getOffer(isNull() /* clientId */, eq(TEST_CLIENT_MAC),
diff --git a/tests/net/java/android/net/dhcp/DhcpServingParamsTest.java b/packages/NetworkStack/tests/src/android/net/dhcp/DhcpServingParamsTest.java
similarity index 96%
rename from tests/net/java/android/net/dhcp/DhcpServingParamsTest.java
rename to packages/NetworkStack/tests/src/android/net/dhcp/DhcpServingParamsTest.java
index 2ab2246..3ca0564 100644
--- a/tests/net/java/android/net/dhcp/DhcpServingParamsTest.java
+++ b/packages/NetworkStack/tests/src/android/net/dhcp/DhcpServingParamsTest.java
@@ -16,6 +16,7 @@
package android.net.dhcp;
+import static android.net.InetAddresses.parseNumericAddress;
import static android.net.NetworkUtils.inet4AddressToIntHTH;
import static android.net.dhcp.DhcpServingParams.MTU_UNSET;
@@ -23,8 +24,6 @@
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
-import static java.net.InetAddress.parseNumericAddress;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.net.LinkAddress;
@@ -195,6 +194,11 @@
assertEquals(7, numFields);
}
+ @Test(expected = InvalidParameterException.class)
+ public void testFromParcelableObject_NullArgument() throws InvalidParameterException {
+ DhcpServingParams.fromParcelableObject(null);
+ }
+
private static int[] toIntArray(Collection<Inet4Address> addrs) {
return addrs.stream().mapToInt(NetworkUtils::inet4AddressToIntHTH).toArray();
}
diff --git a/packages/NetworkStack/tests/src/com/android/server/util/SharedLogTest.java b/packages/NetworkStack/tests/src/com/android/server/util/SharedLogTest.java
new file mode 100644
index 0000000..07ad3123
--- /dev/null
+++ b/packages/NetworkStack/tests/src/com/android/server/util/SharedLogTest.java
@@ -0,0 +1,96 @@
+/*
+ * 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 com.android.server.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.net.util.SharedLog;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintWriter;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class SharedLogTest {
+ private static final String TIMESTAMP_PATTERN = "\\d{2}:\\d{2}:\\d{2}";
+ private static final String TIMESTAMP = "HH:MM:SS";
+
+ @Test
+ public void testBasicOperation() {
+ final SharedLog logTop = new SharedLog("top");
+ logTop.mark("first post!");
+
+ final SharedLog logLevel2a = logTop.forSubComponent("twoA");
+ final SharedLog logLevel2b = logTop.forSubComponent("twoB");
+ logLevel2b.e("2b or not 2b");
+ logLevel2b.e("No exception", null);
+ logLevel2b.e("Wait, here's one", new Exception("Test"));
+ logLevel2a.w("second post?");
+
+ final SharedLog logLevel3 = logLevel2a.forSubComponent("three");
+ logTop.log("still logging");
+ logLevel3.log("3 >> 2");
+ logLevel2a.mark("ok: last post");
+
+ final String[] expected = {
+ " - MARK first post!",
+ " - [twoB] ERROR 2b or not 2b",
+ " - [twoB] ERROR No exception",
+ // No stacktrace in shared log, only in logcat
+ " - [twoB] ERROR Wait, here's one: Test",
+ " - [twoA] WARN second post?",
+ " - still logging",
+ " - [twoA.three] 3 >> 2",
+ " - [twoA] MARK ok: last post",
+ };
+ // Verify the logs are all there and in the correct order.
+ verifyLogLines(expected, logTop);
+
+ // In fact, because they all share the same underlying LocalLog,
+ // every subcomponent SharedLog's dump() is identical.
+ verifyLogLines(expected, logLevel2a);
+ verifyLogLines(expected, logLevel2b);
+ verifyLogLines(expected, logLevel3);
+ }
+
+ private static void verifyLogLines(String[] expected, SharedLog log) {
+ final ByteArrayOutputStream ostream = new ByteArrayOutputStream();
+ final PrintWriter pw = new PrintWriter(ostream, true);
+ log.dump(null, pw, null);
+
+ final String dumpOutput = ostream.toString();
+ assertTrue(dumpOutput != null);
+ assertTrue(!"".equals(dumpOutput));
+
+ final String[] lines = dumpOutput.split("\n");
+ assertEquals(expected.length, lines.length);
+
+ for (int i = 0; i < expected.length; i++) {
+ String got = lines[i];
+ String want = expected[i];
+ assertTrue(String.format("'%s' did not contain '%s'", got, want), got.endsWith(want));
+ assertTrue(String.format("'%s' did not contain a %s timestamp", got, TIMESTAMP),
+ got.replaceFirst(TIMESTAMP_PATTERN, TIMESTAMP).contains(TIMESTAMP));
+ }
+ }
+}
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 7dcc3ac..03c6205 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1137,9 +1137,6 @@
<!-- The notice header of Third-party licenses. not translatable -->
<string name="notice_header" translatable="false"></string>
- <!-- UI debug setting: opt in to use updated graphics driver? [CHAR LIMIT=100] -->
- <string name="gup_dev_opt_in_app_summary">Opt in app to use Game Update Package in developement</string>
-
<!-- Name of the phone device [CHAR LIMIT=NONE] -->
<string name="media_transfer_phone_device_name">Phone speaker</string>
</resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index 6abe76a..595aeb3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -1,5 +1,7 @@
package com.android.settingslib;
+import static android.telephony.ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN;
+
import android.annotation.ColorInt;
import android.content.Context;
import android.content.Intent;
@@ -429,12 +431,14 @@
// and do not support voice service, and on these SIM cards, we
// want to show signal bars for data service as well as the "no
// service" or "emergency calls only" text that indicates that voice
- // is not available.
+ // is not available. Note that we ignore the IWLAN service state
+ // because that state indicates the use of VoWIFI and not cell service
int state = serviceState.getState();
int dataState = serviceState.getDataRegState();
if (state == ServiceState.STATE_OUT_OF_SERVICE
|| state == ServiceState.STATE_EMERGENCY_ONLY) {
- if (dataState == ServiceState.STATE_IN_SERVICE) {
+ if (dataState == ServiceState.STATE_IN_SERVICE
+ && serviceState.getDataNetworkType() != RIL_RADIO_TECHNOLOGY_IWLAN) {
return ServiceState.STATE_IN_SERVICE;
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
index 594d767..86f0438 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
@@ -247,6 +247,15 @@
}
@Test
+ public void isInService_voiceOutOfServiceDataInServiceOnIwLan_returnFalse() {
+ when(mServiceState.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
+ when(mServiceState.getDataNetworkType())
+ .thenReturn(ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN);
+ when(mServiceState.getDataRegState()).thenReturn(ServiceState.STATE_IN_SERVICE);
+ assertThat(Utils.isInService(mServiceState)).isFalse();
+ }
+
+ @Test
public void isInService_voiceOutOfServiceDataOutOfService_returnFalse() {
when(mServiceState.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
when(mServiceState.getDataRegState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 526efcb..419273e 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -708,6 +708,9 @@
Settings.Global.GUP_DEV_OPT_IN_APPS,
GlobalSettingsProto.Gpu.GUP_DEV_OPT_IN_APPS);
dumpSetting(s, p,
+ Settings.Global.GUP_DEV_OPT_OUT_APPS,
+ GlobalSettingsProto.Gpu.GUP_DEV_OPT_OUT_APPS);
+ dumpSetting(s, p,
Settings.Global.GUP_BLACK_LIST,
GlobalSettingsProto.Gpu.GUP_BLACK_LIST);
p.end(gpuToken);
diff --git a/packages/SystemUI/res/drawable/bubble_expanded_header_bg.xml b/packages/SystemUI/res/drawable/bubble_expanded_header_bg.xml
new file mode 100644
index 0000000..26bf981
--- /dev/null
+++ b/packages/SystemUI/res/drawable/bubble_expanded_header_bg.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2018 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
+ -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item>
+ <shape android:shape="rectangle">
+ <solid android:color="?android:attr/colorBackgroundFloating"/>
+ <corners
+ android:topLeftRadius="@dimen/corner_size"
+ android:topRightRadius="@dimen/corner_size"/>
+ </shape>
+ </item>
+ <item android:gravity="bottom">
+ <shape>
+ <size android:height="1dp"/>
+ <solid android:color="?android:attr/textColorSecondary" />
+ </shape>
+ </item>
+</layer-list>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/battery_percentage_view.xml b/packages/SystemUI/res/layout/battery_percentage_view.xml
index e52aa14..b9b1bb1 100644
--- a/packages/SystemUI/res/layout/battery_percentage_view.xml
+++ b/packages/SystemUI/res/layout/battery_percentage_view.xml
@@ -22,7 +22,7 @@
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:singleLine="true"
- android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock"
+ android:textAppearance="@style/TextAppearance.StatusBar.Clock"
android:textColor="?android:attr/textColorPrimary"
android:gravity="center_vertical|start"
android:paddingStart="@dimen/battery_level_padding_start"
diff --git a/packages/SystemUI/res/layout/bubble_expanded_view.xml b/packages/SystemUI/res/layout/bubble_expanded_view.xml
index b2307e7..1aeb52c 100644
--- a/packages/SystemUI/res/layout/bubble_expanded_view.xml
+++ b/packages/SystemUI/res/layout/bubble_expanded_view.xml
@@ -20,11 +20,23 @@
android:layout_width="match_parent"
android:id="@+id/bubble_expanded_view">
- <!-- TODO: header -->
-
<View
android:id="@+id/pointer_view"
android:layout_width="@dimen/bubble_pointer_width"
android:layout_height="@dimen/bubble_pointer_height"
/>
+
+ <TextView
+ android:id="@+id/bubble_content_header"
+ android:background="@drawable/bubble_expanded_header_bg"
+ android:textAppearance="@*android:style/TextAppearance.Material.Title"
+ android:textSize="18sp"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/bubble_expanded_header_height"
+ android:gravity="start|center_vertical"
+ android:singleLine="true"
+ android:paddingLeft="@dimen/bubble_expanded_header_horizontal_padding"
+ android:paddingRight="@dimen/bubble_expanded_header_horizontal_padding"
+ />
+
</com.android.systemui.bubbles.BubbleExpandedViewContainer>
diff --git a/packages/SystemUI/res/layout/quick_settings_footer_dialog.xml b/packages/SystemUI/res/layout/quick_settings_footer_dialog.xml
index 307b538..5bcc1b3 100644
--- a/packages/SystemUI/res/layout/quick_settings_footer_dialog.xml
+++ b/packages/SystemUI/res/layout/quick_settings_footer_dialog.xml
@@ -39,7 +39,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/monitoring_title_device_owned"
- style="@android:style/TextAppearance.Material.Title"
+ style="@style/TextAppearance.DeviceManagementDialog.Title"
android:textColor="?android:attr/textColorPrimary"
android:paddingBottom="@dimen/qs_footer_dialog_subtitle_padding"
/>
@@ -64,7 +64,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/monitoring_subtitle_ca_certificate"
- style="@android:style/TextAppearance.Material.Title"
+ style="@style/TextAppearance.DeviceManagementDialog.Title"
android:textColor="?android:attr/textColorPrimary"
android:paddingBottom="@dimen/qs_footer_dialog_subtitle_padding"
/>
@@ -89,7 +89,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/monitoring_subtitle_network_logging"
- style="@android:style/TextAppearance.Material.Title"
+ style="@style/TextAppearance.DeviceManagementDialog.Title"
android:textColor="?android:attr/textColorPrimary"
android:paddingBottom="@dimen/qs_footer_dialog_subtitle_padding"
/>
@@ -114,7 +114,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/monitoring_subtitle_vpn"
- style="@android:style/TextAppearance.Material.Title"
+ style="@style/TextAppearance.DeviceManagementDialog.Title"
android:textColor="?android:attr/textColorPrimary"
android:paddingBottom="@dimen/qs_footer_dialog_subtitle_padding"
/>
diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
index 4e0cbe0..ed18dc7 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -24,14 +24,13 @@
android:layout_gravity="@integer/notification_panel_layout_gravity"
android:background="@android:color/transparent"
android:baselineAligned="false"
- android:clickable="true"
+ android:clickable="false"
android:clipChildren="false"
android:clipToPadding="false"
android:paddingTop="0dp"
android:paddingEnd="0dp"
android:paddingStart="0dp"
- android:elevation="4dp"
- android:importantForAccessibility="no" >
+ android:elevation="4dp" >
<include layout="@layout/quick_status_bar_header_system_icons" />
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 889db66..7cc5524 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -449,6 +449,11 @@
better (narrower) line-break for a double-line smart reply button. -->
<integer name="config_smart_replies_in_notifications_max_squeeze_remeasure_attempts">3</integer>
+ <!-- Smart replies in notifications: Whether by default tapping on a choice should let the user
+ edit the input before it is sent to the app. Developers can override this via
+ RemoteInput.Builder.setEditChoicesBeforeSending. -->
+ <bool name="config_smart_replies_in_notifications_edit_choices_before_sending">false</bool>
+
<!-- Screenshot editing default activity. Must handle ACTION_EDIT image/png intents.
Blank sends the user to the Chooser first.
This name is in the ComponentName flattened format (package/class) -->
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 06df0e7..10e5f74 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -997,4 +997,8 @@
<dimen name="bubble_pointer_width">6dp</dimen>
<!-- Extra padding around the dismiss target for bubbles -->
<dimen name="bubble_dismiss_slop">16dp</dimen>
+ <!-- Height of the header within the expanded view. -->
+ <dimen name="bubble_expanded_header_height">48dp</dimen>
+ <!-- Left and right padding applied to the header. -->
+ <dimen name="bubble_expanded_header_horizontal_padding">24dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 8a5a69b..22a0b36 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -125,7 +125,7 @@
<style name="TextAppearance.StatusBar.Clock" parent="@*android:style/TextAppearance.StatusBar.Icon">
<item name="android:textSize">@dimen/status_bar_clock_size</item>
- <item name="android:fontFamily">@*android:string/config_bodyFontFamilyMedium</item>
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
<item name="android:textColor">@color/status_bar_clock_color</item>
</style>
@@ -265,6 +265,10 @@
<item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
</style>
+ <style name="TextAppearance.DeviceManagementDialog.Title" parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle">
+ <item name="android:gravity">center</item>
+ </style>
+
<style name="BaseBrightnessDialogContainer" parent="@style/Theme.SystemUI">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index f3bdbae..078947c 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -76,4 +76,10 @@
*/
float getWindowCornerRadius() = 10;
+ /**
+ * If device supports live rounded corners on windows.
+ * This might be turned off for performance reasons
+ */
+ boolean supportsRoundedCornersOnWindows() = 11;
+
}
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
index 96f216e..96b62ac 100644
--- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
@@ -54,7 +54,7 @@
}
@Override
- public void onEntryUpdated(NotificationEntry entry) {
+ public void onPostEntryUpdated(NotificationEntry entry) {
updateNotification(entry.notification, entry.importance);
}
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpItem.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpItem.java
index 9f363f6..7e5b426 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpItem.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpItem.java
@@ -25,12 +25,20 @@
private int mUid;
private String mPackageName;
private long mTimeStarted;
+ private String mState;
public AppOpItem(int code, int uid, String packageName, long timeStarted) {
this.mCode = code;
this.mUid = uid;
this.mPackageName = packageName;
this.mTimeStarted = timeStarted;
+ mState = new StringBuilder()
+ .append("AppOpItem(")
+ .append("Op code=").append(code).append(", ")
+ .append("UID=").append(uid).append(", ")
+ .append("Package name=").append(packageName)
+ .append(")")
+ .toString();
}
public int getCode() {
@@ -48,4 +56,9 @@
public long getTimeStarted() {
return mTimeStarted;
}
+
+ @Override
+ public String toString() {
+ return mState;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index e3bcb37..c013df3 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -29,7 +29,10 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.Dumpable;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
@@ -47,7 +50,7 @@
@Singleton
public class AppOpsControllerImpl implements AppOpsController,
AppOpsManager.OnOpActiveChangedListener,
- AppOpsManager.OnOpNotedListener {
+ AppOpsManager.OnOpNotedListener, Dumpable {
private static final long NOTED_OP_TIME_DELAY_MS = 5000;
private static final String TAG = "AppOpsControllerImpl";
@@ -271,6 +274,22 @@
}
}
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("AppOpsController state:");
+ pw.println(" Active Items:");
+ for (int i = 0; i < mActiveItems.size(); i++) {
+ final AppOpItem item = mActiveItems.get(i);
+ pw.print(" "); pw.println(item.toString());
+ }
+ pw.println(" Noted Items:");
+ for (int i = 0; i < mNotedItems.size(); i++) {
+ final AppOpItem item = mNotedItems.get(i);
+ pw.print(" "); pw.println(item.toString());
+ }
+
+ }
+
protected final class H extends Handler {
H(Looper looper) {
super(looper);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 7577609..9f3ff78 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -23,15 +23,20 @@
import static com.android.systemui.bubbles.BubbleMovementHelper.EDGE_OVERLAP;
import android.app.Notification;
+import android.app.PendingIntent;
import android.content.Context;
+import android.content.pm.ActivityInfo;
import android.graphics.Point;
import android.graphics.Rect;
import android.provider.Settings;
import android.service.notification.StatusBarNotification;
+import android.util.Log;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.FrameLayout;
+import androidx.annotation.Nullable;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Dependency;
import com.android.systemui.R;
@@ -68,6 +73,8 @@
private static final String ENABLE_AUTO_BUBBLE_MESSAGES = "experiment_autobubble_messaging";
private static final String ENABLE_AUTO_BUBBLE_ONGOING = "experiment_autobubble_ongoing";
private static final String ENABLE_AUTO_BUBBLE_ALL = "experiment_autobubble_all";
+ private static final String ENABLE_BUBBLE_ACTIVITY_VIEW = "experiment_bubble_activity_view";
+ private static final String ENABLE_BUBBLE_CONTENT_INTENT = "experiment_bubble_content_intent";
private final Context mContext;
private final NotificationEntryManager mNotificationEntryManager;
@@ -189,6 +196,9 @@
// It's new
BubbleView bubble = new BubbleView(mContext);
bubble.setNotif(notif);
+ if (shouldUseActivityView(mContext)) {
+ bubble.setAppOverlayIntent(getAppOverlayIntent(notif));
+ }
mBubbles.put(bubble.getKey(), bubble);
boolean setPosition = mStackView != null && mStackView.getVisibility() != VISIBLE;
@@ -216,6 +226,21 @@
}
}
+ @Nullable
+ private PendingIntent getAppOverlayIntent(NotificationEntry notif) {
+ Notification notification = notif.notification.getNotification();
+ if (canLaunchInActivityView(notification.getAppOverlayIntent())) {
+ return notification.getAppOverlayIntent();
+ } else if (shouldUseContentIntent(mContext)
+ && canLaunchInActivityView(notification.contentIntent)) {
+ Log.d(TAG, "[addBubble " + notif.key
+ + "]: No appOverlayIntent, using contentIntent.");
+ return notification.contentIntent;
+ }
+ Log.d(TAG, "[addBubble " + notif.key + "]: No supported intent for ActivityView.");
+ return null;
+ }
+
/**
* Removes the bubble associated with the {@param uri}.
*/
@@ -223,6 +248,7 @@
BubbleView bv = mBubbles.get(key);
if (mStackView != null && bv != null) {
mStackView.removeBubble(bv);
+ bv.destroyActivityView(mStackView);
bv.getEntry().setBubbleDismissed(true);
}
@@ -282,9 +308,10 @@
}
}
}
- for (BubbleView view : viewsToRemove) {
- mBubbles.remove(view.getKey());
- mStackView.removeBubble(view);
+ for (BubbleView bubbleView : viewsToRemove) {
+ mBubbles.remove(bubbleView.getKey());
+ mStackView.removeBubble(bubbleView);
+ bubbleView.destroyActivityView(mStackView);
}
if (mStackView != null) {
mStackView.setVisibility(visible ? VISIBLE : INVISIBLE);
@@ -306,6 +333,17 @@
return mTempRect;
}
+ private boolean canLaunchInActivityView(PendingIntent intent) {
+ if (intent == null) {
+ return false;
+ }
+ ActivityInfo info =
+ intent.getIntent().resolveActivityInfo(mContext.getPackageManager(), 0);
+ return info != null
+ && ActivityInfo.isResizeableMode(info.resizeMode)
+ && (info.flags & ActivityInfo.FLAG_ALLOW_EMBEDDED) != 0;
+ }
+
@VisibleForTesting
BubbleStackView getStackView() {
return mStackView;
@@ -378,4 +416,14 @@
return Settings.Secure.getInt(context.getContentResolver(),
ENABLE_AUTO_BUBBLE_ALL, 0) != 0;
}
+
+ private static boolean shouldUseActivityView(Context context) {
+ return Settings.Secure.getInt(context.getContentResolver(),
+ ENABLE_BUBBLE_ACTIVITY_VIEW, 0) != 0;
+ }
+
+ private static boolean shouldUseContentIntent(Context context) {
+ return Settings.Secure.getInt(context.getContentResolver(),
+ ENABLE_BUBBLE_CONTENT_INTENT, 0) != 0;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedViewContainer.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedViewContainer.java
index e28d96b..badefe1 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedViewContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedViewContainer.java
@@ -24,6 +24,7 @@
import android.util.AttributeSet;
import android.view.View;
import android.widget.LinearLayout;
+import android.widget.TextView;
import com.android.systemui.R;
import com.android.systemui.recents.TriangleShape;
@@ -35,6 +36,8 @@
// The triangle pointing to the expanded view
private View mPointerView;
+ // The view displayed between the pointer and the expanded view
+ private TextView mHeaderView;
// The view that is being displayed for the expanded state
private View mExpandedView;
@@ -68,6 +71,7 @@
TriangleShape.create(width, height, true /* pointUp */));
triangleDrawable.setTint(Color.WHITE); // TODO: dark mode
mPointerView.setBackground(triangleDrawable);
+ mHeaderView = findViewById(R.id.bubble_content_header);
}
/**
@@ -80,9 +84,19 @@
}
/**
+ * Set the text displayed within the header.
+ */
+ public void setHeaderText(CharSequence text) {
+ mHeaderView.setText(text);
+ }
+
+ /**
* Set the view to display for the expanded state. Passing null will clear the view.
*/
public void setExpandedView(View view) {
+ if (mExpandedView == view) {
+ return;
+ }
if (mExpandedView != null) {
removeView(mExpandedView);
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index d69e7b3..3280a33 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -21,10 +21,13 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
+import android.app.ActivityView;
+import android.app.PendingIntent;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.RectF;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
@@ -50,6 +53,7 @@
*/
public class BubbleStackView extends FrameLayout implements BubbleTouchHandler.FloatingView {
+ private static final String TAG = "BubbleStackView";
private Point mDisplaySize;
private FrameLayout mBubbleContainer;
@@ -59,6 +63,7 @@
private int mBubblePadding;
private boolean mIsExpanded;
+ private int mExpandedBubbleHeight;
private BubbleView mExpandedBubble;
private Point mCollapsedPosition;
private BubbleTouchHandler mTouchHandler;
@@ -106,6 +111,7 @@
mBubbleSize = res.getDimensionPixelSize(R.dimen.bubble_size);
mBubblePadding = res.getDimensionPixelSize(R.dimen.bubble_padding);
+ mExpandedBubbleHeight = res.getDimensionPixelSize(R.dimen.bubble_expanded_default_height);
mDisplaySize = new Point();
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
wm.getDefaultDisplay().getSize(mDisplaySize);
@@ -389,27 +395,63 @@
}
private void updateExpandedBubble() {
- if (mExpandedBubble != null) {
+ if (mExpandedBubble == null) {
+ return;
+ }
+
+ if (mExpandedBubble.hasAppOverlayIntent()) {
+ ActivityView expandedView = mExpandedBubble.getActivityView();
+ expandedView.setLayoutParams(new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, mExpandedBubbleHeight));
+
+ final PendingIntent intent = mExpandedBubble.getAppOverlayIntent();
+ mExpandedViewContainer.setHeaderText(intent.getIntent().getComponent().toShortString());
+ mExpandedViewContainer.setExpandedView(expandedView);
+ expandedView.setCallback(new ActivityView.StateCallback() {
+ @Override
+ public void onActivityViewReady(ActivityView view) {
+ Log.d(TAG, "onActivityViewReady("
+ + mExpandedBubble.getEntry().key + "): " + view);
+ view.startActivity(intent);
+ }
+
+ @Override
+ public void onActivityViewDestroyed(ActivityView view) {
+ NotificationEntry entry = mExpandedBubble.getEntry();
+ Log.d(TAG, "onActivityViewDestroyed(key="
+ + ((entry != null) ? entry.key : "(none)") + "): " + view);
+ }
+ });
+ } else {
ExpandableNotificationRow row = mExpandedBubble.getRowView();
- if (!row.equals(mExpandedViewContainer.getChildAt(0))) {
+ if (!row.equals(mExpandedViewContainer.getExpandedView())) {
// Different expanded view than what we have
mExpandedViewContainer.setExpandedView(null);
}
- int pointerPosition = mExpandedBubble.getPosition().x
- + (mExpandedBubble.getWidth() / 2);
- mExpandedViewContainer.setPointerPosition(pointerPosition);
mExpandedViewContainer.setExpandedView(row);
+ mExpandedViewContainer.setHeaderText(null);
}
+ int pointerPosition = mExpandedBubble.getPosition().x
+ + (mExpandedBubble.getWidth() / 2);
+ mExpandedViewContainer.setPointerPosition(pointerPosition);
}
private void applyCurrentState() {
+ Log.d(TAG, "applyCurrentState: mIsExpanded=" + mIsExpanded);
+
mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE);
if (!mIsExpanded) {
mExpandedViewContainer.setExpandedView(null);
} else {
mExpandedViewContainer.setTranslationY(mBubbleContainer.getHeight());
- ExpandableNotificationRow row = mExpandedBubble.getRowView();
- applyRowState(row);
+ View expandedView = mExpandedViewContainer.getExpandedView();
+ if (expandedView instanceof ActivityView) {
+ if (expandedView.isAttachedToWindow()) {
+ ((ActivityView) expandedView).onLocationChanged();
+ }
+ } else {
+ applyRowState(mExpandedBubble.getRowView());
+ }
}
int bubbsCount = mBubbleContainer.getChildCount();
for (int i = 0; i < bubbsCount; i++) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
index 88030ee..96b2dba 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
@@ -33,7 +33,7 @@
* Handles interpreting touches on a {@link BubbleStackView}. This includes expanding, collapsing,
* dismissing, and flings.
*/
-public class BubbleTouchHandler implements View.OnTouchListener {
+class BubbleTouchHandler implements View.OnTouchListener {
private BubbleController mController = Dependency.get(BubbleController.class);
private PipDismissViewController mDismissViewController;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
index 3307992..c1bbb93 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
@@ -16,7 +16,9 @@
package com.android.systemui.bubbles;
+import android.app.ActivityView;
import android.app.Notification;
+import android.app.PendingIntent;
import android.content.Context;
import android.graphics.Color;
import android.graphics.Point;
@@ -25,7 +27,9 @@
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.OvalShape;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.View;
+import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
@@ -37,7 +41,7 @@
/**
* A floating object on the screen that has a collapsed and expanded state.
*/
-public class BubbleView extends LinearLayout implements BubbleTouchHandler.FloatingView {
+class BubbleView extends LinearLayout implements BubbleTouchHandler.FloatingView {
private static final String TAG = "BubbleView";
private Context mContext;
@@ -46,6 +50,8 @@
private NotificationEntry mEntry;
private int mBubbleSize;
private int mIconSize;
+ private PendingIntent mAppOverlayIntent;
+ private ActivityView mActivityView;
public BubbleView(Context context) {
this(context, null);
@@ -117,12 +123,51 @@
}
/**
- * @return the view to display when the bubble is expanded.
+ * @return the view to display notification content when the bubble is expanded.
*/
public ExpandableNotificationRow getRowView() {
return mEntry.getRow();
}
+ /**
+ * @return a view used to display app overlay content when expanded.
+ */
+ public ActivityView getActivityView() {
+ if (mActivityView == null) {
+ mActivityView = new ActivityView(mContext);
+ Log.d(TAG, "[getActivityView] created: " + mActivityView);
+ }
+ return mActivityView;
+ }
+
+ /**
+ * Removes and releases an ActivityView if one was previously created for this bubble.
+ */
+ public void destroyActivityView(ViewGroup tmpParent) {
+ if (mActivityView == null) {
+ return;
+ }
+ // HACK: Only release if initialized. There's no way to know if the ActivityView has
+ // been initialized. Calling release() if it hasn't been initialized will crash.
+
+ if (!mActivityView.isAttachedToWindow()) {
+ // HACK: release() will crash if the view is not attached.
+
+ mActivityView.setVisibility(View.GONE);
+ tmpParent.addView(mActivityView, new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT));
+ }
+ try {
+ mActivityView.release();
+ } catch (IllegalStateException ex) {
+ Log.e(TAG, "ActivityView either already released, or not yet initialized.", ex);
+ }
+
+ ((ViewGroup) mActivityView.getParent()).removeView(mActivityView);
+ mActivityView = null;
+ }
+
@Override
public void setPosition(int x, int y) {
setTranslationX(x);
@@ -162,4 +207,20 @@
lp.height = mBubbleSize;
v.setLayoutParams(lp);
}
+
+ /**
+ * @return whether an ActivityView should be used to display the content of this Bubble
+ */
+ public boolean hasAppOverlayIntent() {
+ return mAppOverlayIntent != null;
+ }
+
+ public PendingIntent getAppOverlayIntent() {
+ return mAppOverlayIntent;
+
+ }
+
+ public void setAppOverlayIntent(PendingIntent intent) {
+ mAppOverlayIntent = intent;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 323cf1f..f14495b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -1843,6 +1843,13 @@
*/
private void handleHide() {
Trace.beginSection("KeyguardViewMediator#handleHide");
+
+ // It's possible that the device was unlocked in a dream state. It's time to wake up.
+ if (mAodShowing) {
+ PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ pm.wakeUp(SystemClock.uptimeMillis(), "com.android.systemui:BOUNCER_DOZING");
+ }
+
synchronized (KeyguardViewMediator.this) {
if (DEBUG) Log.d(TAG, "handleHide");
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipAppOpsListener.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipAppOpsListener.java
index 1e0d4d0..b09d6e1 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipAppOpsListener.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipAppOpsListener.java
@@ -36,8 +36,7 @@
private Handler mHandler;
private IActivityManager mActivityManager;
private AppOpsManager mAppOpsManager;
-
- private PipMotionHelper mMotionHelper;
+ private Callback mCallback;
private AppOpsManager.OnOpChangedListener mAppOpsChangedListener = new OnOpChangedListener() {
@Override
@@ -52,7 +51,7 @@
if (appInfo.packageName.equals(topPipActivityInfo.first.getPackageName()) &&
mAppOpsManager.checkOpNoThrow(OP_PICTURE_IN_PICTURE, appInfo.uid,
packageName) != MODE_ALLOWED) {
- mHandler.post(() -> mMotionHelper.dismissPip());
+ mHandler.post(() -> mCallback.dismissPip());
}
}
} catch (NameNotFoundException e) {
@@ -63,12 +62,12 @@
};
public PipAppOpsListener(Context context, IActivityManager activityManager,
- PipMotionHelper motionHelper) {
+ Callback callback) {
mContext = context;
mHandler = new Handler(mContext.getMainLooper());
mActivityManager = activityManager;
mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
- mMotionHelper = motionHelper;
+ mCallback = callback;
}
public void onActivityPinned(String packageName) {
@@ -89,4 +88,10 @@
private void unregisterAppOpsListener() {
mAppOpsManager.stopWatchingMode(mAppOpsChangedListener);
}
-}
\ No newline at end of file
+
+ /** Callback for PipAppOpsListener to request changes to the PIP window. */
+ public interface Callback {
+ /** Dismisses the PIP window. */
+ void dismissPip();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
index 3858356..82aa473 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
@@ -55,7 +55,7 @@
/**
* A helper to animate and manipulate the PiP.
*/
-public class PipMotionHelper implements Handler.Callback {
+public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Callback {
private static final String TAG = "PipMotionHelper";
private static final boolean DEBUG = false;
@@ -172,7 +172,8 @@
/**
* Dismisses the pinned stack.
*/
- void dismissPip() {
+ @Override
+ public void dismissPip() {
if (DEBUG) {
Log.d(TAG, "dismissPip: callers=\n" + Debug.getCallers(5, " "));
}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
index 01ee5ca..4388200 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt
@@ -52,7 +52,8 @@
@Suppress("DEPRECATION")
override fun onClick(dialog: DialogInterface?, which: Int) {
- Dependency.get(ActivityStarter::class.java).startActivity(intent, false)
+ Dependency.get(ActivityStarter::class.java)
+ .postStartActivityDismissingKeyguard(intent, 0)
}
})
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
index e63f88a..e030e40 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
@@ -284,17 +284,10 @@
public void updateEverything() {
post(() -> {
updateVisibilities();
- updateClickabilities();
setClickable(false);
});
}
- private void updateClickabilities() {
- mMultiUserSwitch.setClickable(mMultiUserSwitch.getVisibility() == View.VISIBLE);
- mEdit.setClickable(mEdit.getVisibility() == View.VISIBLE);
- mSettingsButton.setClickable(mSettingsButton.getVisibility() == View.VISIBLE);
- }
-
private void updateVisibilities() {
mSettingsContainer.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
mSettingsContainer.findViewById(R.id.tuner_icon).setVisibility(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 7224599..40c6039 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -426,6 +426,7 @@
if (mExpanded == expanded) return;
mExpanded = expanded;
mHeaderQsPanel.setExpanded(expanded);
+ updateEverything();
}
/**
@@ -697,6 +698,10 @@
.start();
}
+ public void updateEverything() {
+ post(() -> setClickable(false));
+ }
+
public void setQSPanel(final QSPanel qsPanel) {
mQsPanel = qsPanel;
setupHost(qsPanel.getHost());
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 81757d0..c474faf 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -101,6 +101,7 @@
private float mBackButtonAlpha;
private MotionEvent mStatusBarGestureDownEvent;
private float mWindowCornerRadius;
+ private boolean mSupportsRoundedCornersOnWindows;
private ISystemUiProxy mSysUiProxy = new ISystemUiProxy.Stub() {
@@ -244,6 +245,18 @@
}
}
+ public boolean supportsRoundedCornersOnWindows() {
+ if (!verifyCaller("supportsRoundedCornersOnWindows")) {
+ return false;
+ }
+ long token = Binder.clearCallingIdentity();
+ try {
+ return mSupportsRoundedCornersOnWindows;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
private boolean verifyCaller(String reason) {
final int callerId = Binder.getCallingUserHandle().getIdentifier();
if (callerId != mCurrentBoundedUserId) {
@@ -353,6 +366,8 @@
mInteractionFlags = Prefs.getInt(mContext, Prefs.Key.QUICK_STEP_INTERACTION_FLAGS,
getDefaultInteractionFlags());
mWindowCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(mContext.getResources());
+ mSupportsRoundedCornersOnWindows = ScreenDecorationsUtils
+ .supportsRoundedCornersOnWindows(mContext.getResources());
// Listen for the package update changes.
if (mDeviceProvisionedController.getCurrentUser() == UserHandle.USER_SYSTEM) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java b/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java
index 758fb7a..22d1d5b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java
@@ -18,12 +18,14 @@
import android.animation.Animator;
import android.content.Context;
+import android.util.Log;
import android.view.ViewPropertyAnimator;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
import com.android.systemui.Interpolators;
import com.android.systemui.statusbar.notification.NotificationUtils;
+import com.android.systemui.statusbar.phone.StatusBar;
/**
* Utility class to calculate general fling animation when the finger is released.
@@ -196,9 +198,16 @@
if (startGradient != mCachedStartGradient
|| velocityFactor != mCachedVelocityFactor) {
float speedup = mSpeedUpFactor * (1.0f - velocityFactor);
- mInterpolator = new PathInterpolator(speedup,
- speedup * startGradient,
- mLinearOutSlowInX2, mY2);
+ float x1 = speedup;
+ float y1 = speedup * startGradient;
+ float x2 = mLinearOutSlowInX2;
+ float y2 = mY2;
+ try {
+ mInterpolator = new PathInterpolator(x1, y1, x2, y2);
+ } catch (IllegalArgumentException e) {
+ throw new IllegalArgumentException("Illegal path with "
+ + "x1=" + x1 + " y1=" + y1 + " x2=" + x2 + " y2=" + y2, e);
+ }
mCachedStartGradient = startGradient;
mCachedVelocityFactor = velocityFactor;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
index c24698d..60d8cf4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
@@ -77,7 +77,7 @@
}
@Override
- public void onEntryUpdated(NotificationEntry entry) {
+ public void onPostEntryUpdated(NotificationEntry entry) {
updateAlertState(entry);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
index c640760..eea4490 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
@@ -40,9 +40,15 @@
}
/**
- * Called when a notification was updated.
+ * Called when a notification is updated, before any filtering of notifications have occurred.
*/
- default void onEntryUpdated(NotificationEntry entry) {
+ default void onPreEntryUpdated(NotificationEntry entry) {
+ }
+
+ /**
+ * Called when a notification was updated, after any filtering of notifications have occurred.
+ */
+ default void onPostEntryUpdated(NotificationEntry entry) {
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index a4e3f33..5b08cb9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -18,7 +18,6 @@
import android.annotation.Nullable;
import android.app.Notification;
import android.content.Context;
-import android.os.Handler;
import android.os.UserHandle;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
@@ -39,7 +38,6 @@
import com.android.systemui.statusbar.notification.collection.NotificationData;
import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.row.NotificationInflater;
import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
@@ -52,7 +50,6 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
-import java.util.concurrent.TimeUnit;
/**
* NotificationEntryManager is responsible for the adding, removing, and updating of notifications.
@@ -67,13 +64,9 @@
private static final String TAG = "NotificationEntryMgr";
protected static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- public static final long RECENTLY_ALERTED_THRESHOLD_MS = TimeUnit.SECONDS.toMillis(30);
-
protected final Context mContext;
protected final HashMap<String, NotificationEntry> mPendingNotifications = new HashMap<>();
- private final NotificationGutsManager mGutsManager =
- Dependency.get(NotificationGutsManager.class);
private final DeviceProvisionedController mDeviceProvisionedController =
Dependency.get(DeviceProvisionedController.class);
private final ForegroundServiceController mForegroundServiceController =
@@ -83,9 +76,6 @@
private NotificationRemoteInputManager mRemoteInputManager;
private NotificationRowBinder mNotificationRowBinder;
- private final Handler mDeferredNotificationViewUpdateHandler;
- private Runnable mUpdateNotificationViewsCallback;
-
private NotificationPresenter mPresenter;
private NotificationListenerService.RankingMap mLatestRankingMap;
protected NotificationData mNotificationData;
@@ -124,7 +114,6 @@
public NotificationEntryManager(Context context) {
mContext = context;
mNotificationData = new NotificationData();
- mDeferredNotificationViewUpdateHandler = new Handler();
}
/** Adds a {@link NotificationEntryListener}. */
@@ -159,7 +148,6 @@
NotificationListContainer listContainer,
HeadsUpManager headsUpManager) {
mPresenter = presenter;
- mUpdateNotificationViewsCallback = mPresenter::updateNotificationViews;
mNotificationData.setHeadsUpManager(headsUpManager);
mListContainer = listContainer;
@@ -232,15 +220,6 @@
}
}
- private void maybeScheduleUpdateNotificationViews(NotificationEntry entry) {
- long audibleAlertTimeout = RECENTLY_ALERTED_THRESHOLD_MS
- - (System.currentTimeMillis() - entry.lastAudiblyAlertedMs);
- if (audibleAlertTimeout > 0) {
- mDeferredNotificationViewUpdateHandler.postDelayed(
- mUpdateNotificationViewsCallback, audibleAlertTimeout);
- }
- }
-
@Override
public void onAsyncInflationFinished(NotificationEntry entry,
@InflationFlag int inflatedFlags) {
@@ -259,7 +238,6 @@
for (NotificationEntryListener listener : mNotificationEntryListeners) {
listener.onNotificationAdded(entry);
}
- maybeScheduleUpdateNotificationViews(entry);
} else {
for (NotificationEntryListener listener : mNotificationEntryListeners) {
listener.onEntryReinflated(entry);
@@ -370,19 +348,6 @@
}
}
- public void updateNotificationsOnDensityOrFontScaleChanged() {
- ArrayList<NotificationEntry> userNotifications =
- mNotificationData.getNotificationsForCurrentUser();
- for (int i = 0; i < userNotifications.size(); i++) {
- NotificationEntry entry = userNotifications.get(i);
- entry.onDensityOrFontScaleChanged();
- boolean exposedGuts = entry.areGutsExposed();
- if (exposedGuts) {
- mGutsManager.onDensityOrFontScaleChanged(entry);
- }
- }
- }
-
private void addNotificationInternal(StatusBarNotification notification,
NotificationListenerService.RankingMap rankingMap) throws InflationException {
String key = notification.getKey();
@@ -463,6 +428,10 @@
getRowBinder().inflateViews(entry, () -> performRemoveNotification(notification),
mNotificationData.get(entry.key) != null);
+ for (NotificationEntryListener listener : mNotificationEntryListeners) {
+ listener.onPreEntryUpdated(entry);
+ }
+
updateNotifications();
if (DEBUG) {
@@ -473,10 +442,8 @@
}
for (NotificationEntryListener listener : mNotificationEntryListeners) {
- listener.onEntryUpdated(entry);
+ listener.onPostEntryUpdated(entry);
}
-
- maybeScheduleUpdateNotificationViews(entry);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 9e0dd84..95bd1ce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -86,7 +86,6 @@
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -106,6 +105,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.TimeUnit;
import java.util.function.BooleanSupplier;
import java.util.function.Consumer;
@@ -122,6 +122,7 @@
private static final int MENU_VIEW_INDEX = 0;
private static final String TAG = "ExpandableNotifRow";
public static final float DEFAULT_HEADER_VISIBLE_AMOUNT = 1.0f;
+ private static final long RECENTLY_ALERTED_THRESHOLD_MS = TimeUnit.SECONDS.toMillis(30);
private boolean mUpdateBackgroundOnUpdate;
/**
@@ -1693,17 +1694,31 @@
/** Sets the last time the notification being displayed audibly alerted the user. */
public void setLastAudiblyAlertedMs(long lastAudiblyAlertedMs) {
if (NotificationUtils.useNewInterruptionModel(mContext)) {
- boolean recentlyAudiblyAlerted = System.currentTimeMillis() - lastAudiblyAlertedMs
- < NotificationEntryManager.RECENTLY_ALERTED_THRESHOLD_MS;
- if (mIsSummaryWithChildren && mChildrenContainer.getHeaderView() != null) {
- mChildrenContainer.getHeaderView().setRecentlyAudiblyAlerted(
- recentlyAudiblyAlerted);
+ long timeSinceAlertedAudibly = System.currentTimeMillis() - lastAudiblyAlertedMs;
+ boolean alertedRecently =
+ timeSinceAlertedAudibly < RECENTLY_ALERTED_THRESHOLD_MS;
+
+ applyAudiblyAlertedRecently(alertedRecently);
+
+ removeCallbacks(mExpireRecentlyAlertedFlag);
+ if (alertedRecently) {
+ long timeUntilNoLongerRecent =
+ RECENTLY_ALERTED_THRESHOLD_MS - timeSinceAlertedAudibly;
+ postDelayed(mExpireRecentlyAlertedFlag, timeUntilNoLongerRecent);
}
- mPrivateLayout.setRecentlyAudiblyAlerted(recentlyAudiblyAlerted);
- mPublicLayout.setRecentlyAudiblyAlerted(recentlyAudiblyAlerted);
}
}
+ private final Runnable mExpireRecentlyAlertedFlag = () -> applyAudiblyAlertedRecently(false);
+
+ private void applyAudiblyAlertedRecently(boolean audiblyAlertedRecently) {
+ if (mIsSummaryWithChildren && mChildrenContainer.getHeaderView() != null) {
+ mChildrenContainer.getHeaderView().setRecentlyAudiblyAlerted(audiblyAlertedRecently);
+ }
+ mPrivateLayout.setRecentlyAudiblyAlerted(audiblyAlertedRecently);
+ mPublicLayout.setRecentlyAudiblyAlerted(audiblyAlertedRecently);
+ }
+
public View.OnClickListener getAppOpsOnClickListener() {
return mOnAppOpsClickListener;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index e4b00dd..80db6c1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -521,7 +521,7 @@
mEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
@Override
- public void onEntryUpdated(NotificationEntry entry) {
+ public void onPostEntryUpdated(NotificationEntry entry) {
if (!entry.notification.isClearable()) {
// The user may have performed a dismiss action on the notification, since it's
// not clearable we should snap it back.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index d873b0c..75adf50 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -956,7 +956,7 @@
handled = true;
}
handled |= super.onTouchEvent(event);
- return mDozing ? handled : true;
+ return !mDozing || mPulsing || handled;
}
private boolean handleQsTouch(MotionEvent event) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index e25c829..4bece48 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -364,7 +364,7 @@
mExpansionFraction = fraction;
final boolean keyguardOrUnlocked = mState == ScrimState.UNLOCKED
- || mState == ScrimState.KEYGUARD;
+ || mState == ScrimState.KEYGUARD || mState == ScrimState.PULSING;
if (!keyguardOrUnlocked || !mExpansionAffectsAlpha) {
return;
}
@@ -409,7 +409,7 @@
behindFraction = (float) Math.pow(behindFraction, 0.8f);
mCurrentBehindAlpha = behindFraction * GRADIENT_SCRIM_ALPHA_BUSY;
mCurrentInFrontAlpha = 0;
- } else if (mState == ScrimState.KEYGUARD) {
+ } else if (mState == ScrimState.KEYGUARD || mState == ScrimState.PULSING) {
// Either darken of make the scrim transparent when you
// pull down the shade
float interpolatedFract = getInterpolatedFraction();
@@ -562,8 +562,8 @@
if (alpha == 0f) {
scrim.setClickable(false);
} else {
- // Eat touch events (unless dozing or pulsing).
- scrim.setClickable(mState != ScrimState.AOD && mState != ScrimState.PULSING);
+ // Eat touch events (unless dozing).
+ scrim.setClickable(mState != ScrimState.AOD);
}
updateScrim(scrim, alpha);
}
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 6d78f72..6f877ba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -3573,10 +3573,7 @@
mVisualStabilityManager.setScreenOn(false);
updateVisibleToUser();
- // We need to disable touch events because these might
- // collapse the panel after we expanded it, and thus we would end up with a blank
- // Keyguard.
- mNotificationPanel.setTouchAndAnimationDisabled(true);
+ updateNotificationPanelTouchState();
mStatusBarWindow.cancelCurrentTouch();
if (mLaunchCameraOnFinishedGoingToSleep) {
mLaunchCameraOnFinishedGoingToSleep = false;
@@ -3599,13 +3596,22 @@
mDeviceInteractive = true;
mAmbientPulseManager.releaseAllImmediately();
mVisualStabilityManager.setScreenOn(true);
- mNotificationPanel.setTouchAndAnimationDisabled(false);
+ updateNotificationPanelTouchState();
updateVisibleToUser();
updateIsKeyguard();
mDozeServiceHost.stopDozing();
}
};
+ /**
+ * We need to disable touch events because these might
+ * collapse the panel after we expanded it, and thus we would end up with a blank
+ * Keyguard.
+ */
+ private void updateNotificationPanelTouchState() {
+ mNotificationPanel.setTouchAndAnimationDisabled(!mDeviceInteractive && !mPulsing);
+ }
+
final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() {
@Override
public void onScreenTurningOn() {
@@ -3871,17 +3877,15 @@
@Override
public void onPulseStarted() {
callback.onPulseStarted();
- if (mAmbientPulseManager.hasNotifications()) {
- // Only pulse the stack scroller if there's actually something to show.
- // Otherwise just show the always-on screen.
- setPulsing(true);
- }
+ updateNotificationPanelTouchState();
+ setPulsing(true);
}
@Override
public void onPulseFinished() {
mPulsing = false;
callback.onPulseFinished();
+ updateNotificationPanelTouchState();
setPulsing(false);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 0f8970f..bb23608 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -187,7 +187,7 @@
mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
} else if (bouncerNeedsScrimming()) {
mBouncer.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE);
- } else if (mShowing && !mDozing) {
+ } else if (mShowing) {
if (!isWakeAndUnlocking() && !mStatusBar.isInLaunchTransition()) {
mBouncer.setExpansion(expansion);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index 16c8e62..df7f53b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -104,6 +104,8 @@
Dependency.get(NotificationMediaManager.class);
private final VisualStabilityManager mVisualStabilityManager =
Dependency.get(VisualStabilityManager.class);
+ private final NotificationGutsManager mGutsManager =
+ Dependency.get(NotificationGutsManager.class);
protected AmbientPulseManager mAmbientPulseManager = Dependency.get(AmbientPulseManager.class);
private final NotificationPanelView mNotificationPanel;
@@ -189,7 +191,7 @@
}
@Override
- public void onEntryUpdated(NotificationEntry entry) {
+ public void onPostEntryUpdated(NotificationEntry entry) {
mShadeController.updateAreThereNotifications();
}
@@ -206,14 +208,12 @@
}
};
- NotificationGutsManager gutsManager = Dependency.get(NotificationGutsManager.class);
-
mViewHierarchyManager.setUpWithPresenter(this, notifListContainer);
mEntryManager.setUpWithPresenter(this, notifListContainer, mHeadsUpManager);
mEntryManager.addNotificationEntryListener(notificationEntryListener);
mEntryManager.addNotificationLifetimeExtender(mHeadsUpManager);
mEntryManager.addNotificationLifetimeExtender(mAmbientPulseManager);
- mEntryManager.addNotificationLifetimeExtender(gutsManager);
+ mEntryManager.addNotificationLifetimeExtender(mGutsManager);
mEntryManager.addNotificationLifetimeExtenders(
remoteInputManager.getLifetimeExtenders());
mNotificationRowBinder.setUpWithPresenter(this, notifListContainer, mHeadsUpManager,
@@ -223,7 +223,7 @@
mLockscreenUserManager.setUpWithPresenter(this);
mMediaManager.setUpWithPresenter(this);
mVisualStabilityManager.setUpWithPresenter(this);
- gutsManager.setUpWithPresenter(this,
+ mGutsManager.setUpWithPresenter(this,
notifListContainer, mCheckSaveListener, mOnSettingsClickListener);
// ForegroundServiceControllerListener adds its listener in its constructor
// but we need to request it here in order for it to be instantiated.
@@ -242,7 +242,7 @@
MessagingMessage.dropCache();
MessagingGroup.dropCache();
if (!KeyguardUpdateMonitor.getInstance(mContext).isSwitchingUser()) {
- mEntryManager.updateNotificationsOnDensityOrFontScaleChanged();
+ updateNotificationsOnDensityOrFontScaleChanged();
} else {
mReinflateNotificationsOnUserSwitched = true;
}
@@ -269,6 +269,19 @@
}
}
+ private void updateNotificationsOnDensityOrFontScaleChanged() {
+ ArrayList<NotificationEntry> userNotifications =
+ mEntryManager.getNotificationData().getNotificationsForCurrentUser();
+ for (int i = 0; i < userNotifications.size(); i++) {
+ NotificationEntry entry = userNotifications.get(i);
+ entry.onDensityOrFontScaleChanged();
+ boolean exposedGuts = entry.areGutsExposed();
+ if (exposedGuts) {
+ mGutsManager.onDensityOrFontScaleChanged(entry);
+ }
+ }
+ }
+
@Override
public boolean isCollapsing() {
return mNotificationPanel.isCollapsing()
@@ -367,7 +380,7 @@
if (MULTIUSER_DEBUG) mNotificationPanelDebugText.setText("USER " + newUserId);
mCommandQueue.animateCollapsePanels();
if (mReinflateNotificationsOnUserSwitched) {
- mEntryManager.updateNotificationsOnDensityOrFontScaleChanged();
+ updateNotificationsOnDensityOrFontScaleChanged();
mReinflateNotificationsOnUserSwitched = false;
}
if (mDispatchUiModeChangeOnUserSwitched) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index 53e461d..8b25c34 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -339,7 +339,7 @@
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
NotificationStackScrollLayout stackScrollLayout = getStackScrollLayout();
- if (mService.isDozing() && !stackScrollLayout.hasPulsingNotifications()) {
+ if (mService.isDozing() && !mService.isPulsing()) {
// Capture all touch events in always-on.
return true;
}
@@ -347,8 +347,7 @@
if (mNotificationPanel.isFullyExpanded()
&& stackScrollLayout.getVisibility() == View.VISIBLE
&& mStatusBarStateController.getState() == StatusBarState.KEYGUARD
- && !mService.isBouncerShowing()
- && !mService.isDozing()) {
+ && !mService.isBouncerShowing()) {
intercept = mDragDownHelper.onInterceptTouchEvent(ev);
}
if (!intercept) {
@@ -369,7 +368,7 @@
boolean handled = false;
if (mService.isDozing()) {
mDoubleTapHelper.onTouchEvent(ev);
- handled = true;
+ handled = !mService.isPulsing();
}
if ((mStatusBarStateController.getState() == StatusBarState.KEYGUARD && !handled)
|| mDragDownHelper.isDraggingDown()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java
index 6193159..0c63e29 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java
@@ -18,6 +18,7 @@
import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
+import android.app.RemoteInput;
import android.content.Context;
import android.content.res.Resources;
import android.database.ContentObserver;
@@ -42,14 +43,18 @@
private static final String KEY_REQUIRES_TARGETING_P = "requires_targeting_p";
private static final String KEY_MAX_SQUEEZE_REMEASURE_ATTEMPTS =
"max_squeeze_remeasure_attempts";
+ private static final String KEY_EDIT_CHOICES_BEFORE_SENDING =
+ "edit_choices_before_sending";
private final boolean mDefaultEnabled;
private final boolean mDefaultRequiresP;
private final int mDefaultMaxSqueezeRemeasureAttempts;
+ private final boolean mDefaultEditChoicesBeforeSending;
private boolean mEnabled;
private boolean mRequiresTargetingP;
private int mMaxSqueezeRemeasureAttempts;
+ private boolean mEditChoicesBeforeSending;
private final Context mContext;
private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -66,6 +71,8 @@
R.bool.config_smart_replies_in_notifications_requires_targeting_p);
mDefaultMaxSqueezeRemeasureAttempts = resources.getInteger(
R.integer.config_smart_replies_in_notifications_max_squeeze_remeasure_attempts);
+ mDefaultEditChoicesBeforeSending = resources.getBoolean(
+ R.bool.config_smart_replies_in_notifications_edit_choices_before_sending);
mContext.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(Settings.Global.SMART_REPLIES_IN_NOTIFICATIONS_FLAGS),
@@ -90,6 +97,8 @@
mRequiresTargetingP = mParser.getBoolean(KEY_REQUIRES_TARGETING_P, mDefaultRequiresP);
mMaxSqueezeRemeasureAttempts = mParser.getInt(
KEY_MAX_SQUEEZE_REMEASURE_ATTEMPTS, mDefaultMaxSqueezeRemeasureAttempts);
+ mEditChoicesBeforeSending = mParser.getBoolean(
+ KEY_EDIT_CHOICES_BEFORE_SENDING, mDefaultEditChoicesBeforeSending);
}
}
@@ -113,4 +122,24 @@
public int getMaxSqueezeRemeasureAttempts() {
return mMaxSqueezeRemeasureAttempts;
}
+
+ /**
+ * Returns whether by tapping on a choice should let the user edit the input before it
+ * is sent to the app.
+ *
+ * @param remoteInputEditChoicesBeforeSending The value from
+ * {@link RemoteInput#getEditChoicesBeforeSending()}
+ */
+ public boolean getEffectiveEditChoicesBeforeSending(
+ @RemoteInput.EditChoicesBeforeSending int remoteInputEditChoicesBeforeSending) {
+ switch (remoteInputEditChoicesBeforeSending) {
+ case RemoteInput.EDIT_CHOICES_BEFORE_SENDING_DISABLED:
+ return false;
+ case RemoteInput.EDIT_CHOICES_BEFORE_SENDING_ENABLED:
+ return true;
+ case RemoteInput.EDIT_CHOICES_BEFORE_SENDING_AUTO:
+ default:
+ return mEditChoicesBeforeSending;
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
index 5e5fc42..a76cf16 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
@@ -242,9 +242,8 @@
b.setText(choice);
OnDismissAction action = () -> {
- // TODO(b/111437455): Also for EDIT_CHOICES_BEFORE_SENDING_AUTO, depending on flags.
- if (smartReplies.remoteInput.getEditChoicesBeforeSending()
- == RemoteInput.EDIT_CHOICES_BEFORE_SENDING_ENABLED) {
+ if (mConstants.getEffectiveEditChoicesBeforeSending(
+ smartReplies.remoteInput.getEditChoicesBeforeSending())) {
entry.remoteInputText = choice;
mRemoteInputManager.activateRemoteInput(b,
new RemoteInput[] { smartReplies.remoteInput }, smartReplies.remoteInput,
@@ -428,9 +427,9 @@
markButtonsWithPendingSqueezeStatusAs(
LayoutParams.SQUEEZE_STATUS_FAILED, coveredSuggestions);
- // The current button doesn't fit, so there's no point in measuring further
- // buttons.
- break;
+ // The current button doesn't fit, keep on adding lower-priority buttons in case
+ // any of those fit.
+ continue;
}
// The current button fits, so mark all squeezed buttons as "successfully squeezed"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
index 9400d6d..31e8071 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
@@ -404,6 +404,6 @@
private void entryUpdated(StatusBarNotification notification, int importance) {
NotificationEntry entry = new NotificationEntry(notification);
entry.importance = importance;
- mEntryListener.onEntryUpdated(entry);
+ mEntryListener.onPostEntryUpdated(entry);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index 4d22536..d937f93 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -26,6 +26,7 @@
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
@@ -65,6 +66,7 @@
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.SmartReplyController;
import com.android.systemui.statusbar.StatusBarIconView;
+import com.android.systemui.statusbar.notification.collection.NotificationData;
import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -83,6 +85,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -137,6 +140,10 @@
mCountDownLatch = new CountDownLatch(1);
}
+ public void setNotificationData(NotificationData data) {
+ mNotificationData = data;
+ }
+
@Override
public void onAsyncInflationFinished(NotificationEntry entry,
@NotificationInflater.InflationFlag int inflatedFlags) {
@@ -292,14 +299,43 @@
verify(mEntryListener, never()).onInflationError(any(), any());
+ verify(mEntryListener).onPreEntryUpdated(mEntry);
verify(mPresenter).updateNotificationViews();
- verify(mEntryListener).onEntryUpdated(mEntry);
+ verify(mEntryListener).onPostEntryUpdated(mEntry);
+
assertNotNull(mEntry.getRow());
assertEquals(mEntry.userSentiment,
NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE);
}
@Test
+ public void testUpdateNotification_prePostEntryOrder() throws Exception {
+ com.android.systemui.util.Assert.isNotMainThread();
+ TestableLooper.get(this).processAllMessages();
+
+ NotificationData notifData = mock(NotificationData.class);
+ when(notifData.get(mEntry.key)).thenReturn(mEntry);
+
+ mEntryManager.setNotificationData(notifData);
+
+ mEntryManager.updateNotification(mSbn, mRankingMap);
+ TestableLooper.get(this).processMessages(1);
+ // Wait for content update.
+ assertTrue(mEntryManager.getCountDownLatch().await(10, TimeUnit.SECONDS));
+
+ verify(mEntryListener, never()).onInflationError(any(), any());
+
+ // Ensure that update callbacks happen in correct order
+ InOrder order = inOrder(mEntryListener, notifData, mPresenter, mEntryListener);
+ order.verify(mEntryListener).onPreEntryUpdated(mEntry);
+ order.verify(notifData).filterAndSort();
+ order.verify(mPresenter).updateNotificationViews();
+ order.verify(mEntryListener).onPostEntryUpdated(mEntry);
+
+ assertNotNull(mEntry.getRow());
+ }
+
+ @Test
public void testRemoveNotification() throws Exception {
com.android.systemui.util.Assert.isNotMainThread();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 146c5d6..cc5f50a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -54,7 +54,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.util.Arrays;
+import java.util.Collections;
import java.util.HashSet;
import java.util.function.Consumer;
@@ -578,7 +578,7 @@
@Test
public void testEatsTouchEvent() {
HashSet<ScrimState> eatsTouches =
- new HashSet<>(Arrays.asList(ScrimState.AOD, ScrimState.PULSING));
+ new HashSet<>(Collections.singletonList(ScrimState.AOD));
for (ScrimState state : ScrimState.values()) {
if (state == ScrimState.UNINITIALIZED) {
continue;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java
index 2266b47..37a56a3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java
@@ -20,6 +20,7 @@
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
+import android.app.RemoteInput;
import android.os.Handler;
import android.os.Looper;
import android.provider.Settings;
@@ -51,6 +52,8 @@
resources.addOverride(R.bool.config_smart_replies_in_notifications_enabled, true);
resources.addOverride(
R.integer.config_smart_replies_in_notifications_max_squeeze_remeasure_attempts, 7);
+ resources.addOverride(
+ R.bool.config_smart_replies_in_notifications_edit_choices_before_sending, false);
mConstants = new SmartReplyConstants(Handler.createAsync(Looper.myLooper()), mContext);
}
@@ -104,6 +107,51 @@
assertEquals(5, mConstants.getMaxSqueezeRemeasureAttempts());
}
+ @Test
+ public void testGetEffectiveEditChoicesBeforeSendingWithNoConfig() {
+ overrideSetting("enabled=true");
+ triggerConstantsOnChange();
+ assertFalse(
+ mConstants.getEffectiveEditChoicesBeforeSending(
+ RemoteInput.EDIT_CHOICES_BEFORE_SENDING_AUTO));
+ assertTrue(
+ mConstants.getEffectiveEditChoicesBeforeSending(
+ RemoteInput.EDIT_CHOICES_BEFORE_SENDING_ENABLED));
+ assertFalse(
+ mConstants.getEffectiveEditChoicesBeforeSending(
+ RemoteInput.EDIT_CHOICES_BEFORE_SENDING_DISABLED));
+ }
+
+ @Test
+ public void testGetEffectiveEditChoicesBeforeSendingWithEnabledConfig() {
+ overrideSetting("enabled=true,edit_choices_before_sending=true");
+ triggerConstantsOnChange();
+ assertTrue(
+ mConstants.getEffectiveEditChoicesBeforeSending(
+ RemoteInput.EDIT_CHOICES_BEFORE_SENDING_AUTO));
+ assertTrue(
+ mConstants.getEffectiveEditChoicesBeforeSending(
+ RemoteInput.EDIT_CHOICES_BEFORE_SENDING_ENABLED));
+ assertFalse(
+ mConstants.getEffectiveEditChoicesBeforeSending(
+ RemoteInput.EDIT_CHOICES_BEFORE_SENDING_DISABLED));
+ }
+
+ @Test
+ public void testGetEffectiveEditChoicesBeforeSendingWithDisabledConfig() {
+ overrideSetting("enabled=true,edit_choices_before_sending=false");
+ triggerConstantsOnChange();
+ assertFalse(
+ mConstants.getEffectiveEditChoicesBeforeSending(
+ RemoteInput.EDIT_CHOICES_BEFORE_SENDING_AUTO));
+ assertTrue(
+ mConstants.getEffectiveEditChoicesBeforeSending(
+ RemoteInput.EDIT_CHOICES_BEFORE_SENDING_ENABLED));
+ assertFalse(
+ mConstants.getEffectiveEditChoicesBeforeSending(
+ RemoteInput.EDIT_CHOICES_BEFORE_SENDING_DISABLED));
+ }
+
private void overrideSetting(String flags) {
Settings.Global.putString(mContext.getContentResolver(),
Settings.Global.SMART_REPLIES_IN_NOTIFICATIONS_FLAGS, flags);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
index fdc8ef1..1066bc1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
@@ -855,4 +855,92 @@
assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(2));
assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(3));
}
+
+ /**
+ * Test to ensure that we try to add all possible actions - if we find one action that's too
+ * long we just skip that one rather than quitting altogether.
+ */
+ @Test
+ public void testMeasure_skipTooLongActions() {
+ String[] choices = new String[] {};
+ String[] actions = new String[] {
+ "a1", "a2", "this action is soooooooo long it's ridiculous", "a4"};
+
+ // All actions should be displayed as DOUBLE-line smart action buttons.
+ ViewGroup expectedView = buildExpectedView(new String[] {}, 1 /* lineCount */,
+ createActions(new String[] {"a1", "a2", "a4"}));
+ expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+ setSmartRepliesAndActions(choices, actions);
+ mView.measure(
+ MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
+ MeasureSpec.UNSPECIFIED);
+
+ assertEqualMeasures(expectedView, mView);
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
+ assertReplyButtonHidden(mView.getChildAt(2));
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(3));
+ }
+
+ /**
+ * Test to ensure that we try to add all possible replies - if we find one reply that's too
+ * long we just skip that one rather than quitting altogether.
+ */
+ @Test
+ public void testMeasure_skipTooLongReplies() {
+ String[] choices = new String[] {
+ "r1", "r2", "this reply is soooooooo long it's ridiculous", "r4"};
+ String[] actions = new String[] {};
+
+ // All replies should be displayed as single-line smart reply buttons.
+ ViewGroup expectedView = buildExpectedView(new String[] {"r1", "r2", "r4"},
+ 1 /* lineCount */, Collections.emptyList());
+ expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+ setSmartRepliesAndActions(choices, actions);
+ mView.measure(
+ MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
+ MeasureSpec.UNSPECIFIED);
+
+ assertEqualMeasures(expectedView, mView);
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(0), mView.getChildAt(0));
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(1), mView.getChildAt(1));
+ assertReplyButtonHidden(mView.getChildAt(2));
+ assertReplyButtonShownWithEqualMeasures(expectedView.getChildAt(2), mView.getChildAt(3));
+ }
+
+ /**
+ * Test to ensure that we try to add all possible replies and actions - if we find a reply or
+ * action that's too long we just skip that one rather than quitting altogether.
+ */
+ @Test
+ public void testMeasure_skipTooLongRepliesAndActions() {
+ String[] choices = new String[] {
+ "r1", "r2", "this reply is soooooooo long it's ridiculous", "r4"};
+ String[] actions = new String[] {
+ "a1", "ThisActionIsSooooooooLongItsRidiculousIPromise"};
+
+ // All replies should be displayed as single-line smart reply buttons.
+ ViewGroup expectedView = buildExpectedView(new String[] {"r1", "r2", "r4"},
+ 1 /* lineCount */, createActions(new String[] {"a1"}));
+ expectedView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
+ setSmartRepliesAndActions(choices, actions);
+ mView.measure(
+ MeasureSpec.makeMeasureSpec(expectedView.getMeasuredWidth(), MeasureSpec.AT_MOST),
+ MeasureSpec.UNSPECIFIED);
+
+ assertEqualMeasures(expectedView, mView);
+ assertReplyButtonShownWithEqualMeasures(
+ expectedView.getChildAt(0), mView.getChildAt(0)); // r1
+ assertReplyButtonShownWithEqualMeasures(
+ expectedView.getChildAt(1), mView.getChildAt(1)); // r2
+ assertReplyButtonHidden(mView.getChildAt(2)); // long reply
+ assertReplyButtonShownWithEqualMeasures(
+ expectedView.getChildAt(2), mView.getChildAt(3)); // r4
+ assertReplyButtonShownWithEqualMeasures(
+ expectedView.getChildAt(3), mView.getChildAt(4)); // a1
+ assertReplyButtonHidden(mView.getChildAt(5)); // long action
+ }
}
diff --git a/packages/overlays/FontArbutusSourceOverlay/res/values/config.xml b/packages/overlays/FontArbutusSourceOverlay/res/values/config.xml
index 165aab2..a6aa64c 100644
--- a/packages/overlays/FontArbutusSourceOverlay/res/values/config.xml
+++ b/packages/overlays/FontArbutusSourceOverlay/res/values/config.xml
@@ -19,7 +19,7 @@
<!-- Name of a font family to use for body text. -->
<string name="config_bodyFontFamily" translatable="false">source-sans-pro</string>
<!-- Name of a font family to use for medium body text. -->
- <string name="config_bodyFontFamilyMedium" translatable="false">source-sans-pro-medium</string>
+ <string name="config_bodyFontFamilyMedium" translatable="false">source-sans-pro-semi-bold</string>
<!-- Name of a font family to use for headlines. If empty, falls back to platform default -->
<string name="config_headlineFontFamily" translatable="false">arbutus-slab</string>
<!-- Name of the font family used for system surfaces where the font should use medium weight -->
diff --git a/packages/overlays/FontArvoLatoOverlay/res/values/config.xml b/packages/overlays/FontArvoLatoOverlay/res/values/config.xml
index 229c578..4e70d72 100644
--- a/packages/overlays/FontArvoLatoOverlay/res/values/config.xml
+++ b/packages/overlays/FontArvoLatoOverlay/res/values/config.xml
@@ -19,10 +19,10 @@
<!-- Name of a font family to use for body text. -->
<string name="config_bodyFontFamily" translatable="false">lato</string>
<!-- Name of a font family to use for medium body text. -->
- <string name="config_bodyFontFamilyMedium" translatable="false">lato-medium</string>
+ <string name="config_bodyFontFamilyMedium" translatable="false">lato-bold</string>
<!-- Name of a font family to use for headlines. If empty, falls back to platform default -->
<string name="config_headlineFontFamily" translatable="false">arvo</string>
<!-- Name of the font family used for system surfaces where the font should use medium weight -->
- <string name="config_headlineFontFamilyMedium" translatable="false">arvo-medium</string>
+ <string name="config_headlineFontFamilyMedium" translatable="false">arvo-bold</string>
</resources>
diff --git a/packages/services/PacProcessor/jni/com_android_pacprocessor_PacNative.cpp b/packages/services/PacProcessor/jni/com_android_pacprocessor_PacNative.cpp
index a40cf1c..d969c69 100644
--- a/packages/services/PacProcessor/jni/com_android_pacprocessor_PacNative.cpp
+++ b/packages/services/PacProcessor/jni/com_android_pacprocessor_PacNative.cpp
@@ -16,6 +16,9 @@
#define LOG_TAG "PacProcessor"
+#include <stdlib.h>
+#include <string>
+
#include <utils/Log.h>
#include <utils/Mutex.h>
#include "android_runtime/AndroidRuntime.h"
@@ -23,24 +26,24 @@
#include "jni.h"
#include <nativehelper/JNIHelp.h>
-#include "proxy_resolver_v8.h"
+#include "proxy_resolver_v8_wrapper.h"
namespace android {
-net::ProxyResolverV8* proxyResolver = NULL;
+ProxyResolverV8Handle* proxyResolver = NULL;
bool pacSet = false;
-String16 jstringToString16(JNIEnv* env, jstring jstr) {
+std::u16string jstringToString16(JNIEnv* env, jstring jstr) {
const jchar* str = env->GetStringCritical(jstr, 0);
- String16 str16(reinterpret_cast<const char16_t*>(str),
+ std::u16string str16(reinterpret_cast<const char16_t*>(str),
env->GetStringLength(jstr));
env->ReleaseStringCritical(jstr, str);
return str16;
}
-jstring string16ToJstring(JNIEnv* env, String16 string) {
- const char16_t* str = string.string();
- size_t len = string.size();
+jstring string16ToJstring(JNIEnv* env, std::u16string string) {
+ const char16_t* str = string.data();
+ size_t len = string.length();
return env->NewString(reinterpret_cast<const jchar*>(str), len);
}
@@ -48,7 +51,7 @@
static jboolean com_android_pacprocessor_PacNative_createV8ParserNativeLocked(JNIEnv* /* env */,
jobject) {
if (proxyResolver == NULL) {
- proxyResolver = new net::ProxyResolverV8(net::ProxyResolverJSBindings::CreateDefault());
+ proxyResolver = ProxyResolverV8Handle_new();
pacSet = false;
return JNI_FALSE;
}
@@ -58,7 +61,7 @@
static jboolean com_android_pacprocessor_PacNative_destroyV8ParserNativeLocked(JNIEnv* /* env */,
jobject) {
if (proxyResolver != NULL) {
- delete proxyResolver;
+ ProxyResolverV8Handle_delete(proxyResolver);
proxyResolver = NULL;
return JNI_FALSE;
}
@@ -67,14 +70,14 @@
static jboolean com_android_pacprocessor_PacNative_setProxyScriptNativeLocked(JNIEnv* env, jobject,
jstring script) {
- String16 script16 = jstringToString16(env, script);
+ std::u16string script16 = jstringToString16(env, script);
if (proxyResolver == NULL) {
ALOGE("V8 Parser not started when setting PAC script");
return JNI_TRUE;
}
- if (proxyResolver->SetPacScript(script16) != OK) {
+ if (ProxyResolverV8Handle_SetPacScript(proxyResolver, script16.data()) != OK) {
ALOGE("Unable to set PAC script");
return JNI_TRUE;
}
@@ -85,9 +88,8 @@
static jstring com_android_pacprocessor_PacNative_makeProxyRequestNativeLocked(JNIEnv* env, jobject,
jstring url, jstring host) {
- String16 url16 = jstringToString16(env, url);
- String16 host16 = jstringToString16(env, host);
- String16 ret;
+ std::u16string url16 = jstringToString16(env, url);
+ std::u16string host16 = jstringToString16(env, host);
if (proxyResolver == NULL) {
ALOGE("V8 Parser not initialized when running PAC script");
@@ -99,12 +101,14 @@
return NULL;
}
- if (proxyResolver->GetProxyForURL(url16, host16, &ret) != OK) {
- String8 ret8(ret);
- ALOGE("Error Running PAC: %s", ret8.string());
+ std::unique_ptr<char16_t, decltype(&free)> result = std::unique_ptr<char16_t, decltype(&free)>(
+ ProxyResolverV8Handle_GetProxyForURL(proxyResolver, url16.data(), host16.data()), &free);
+ if (result.get() == NULL) {
+ ALOGE("Error Running PAC");
return NULL;
}
+ std::u16string ret(result.get());
jstring jret = string16ToJstring(env, ret);
return jret;
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index 2b399de..665773c 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -6735,6 +6735,34 @@
// OS: Q
SETTINGS_GUP_DASHBOARD = 1613;
+ // CATEGORY: The category for all actions relating to language detection logging.
+ // OS: Q
+ LANGUAGE_DETECTION = 1614;
+
+ // CATEGORY: The category for all actions relating to conversation actions logging.
+ // OS: Q
+ CONVERSATION_ACTIONS = 1615;
+
+ // ACTION: Actions from a text classifier are shown to user.
+ // CATEGORY: CONVERSATION_ACTIONS
+ // OS: Q
+ ACTION_TEXT_CLASSIFIER_ACTIONS_SHOWN = 1616;
+
+ // ACTION: Event time of a text classifier event in unix timestamp.
+ // CATEGORY: CONVERSATION_ACTIONS, LANGUAGE_DETECTION
+ // OS: Q
+ FIELD_TEXT_CLASSIFIER_EVENT_TIME = 1617;
+
+ // ACTION: Users compose their own replies instead of using suggested ones.
+ // CATEGORY: CONVERSATION_ACTIONS
+ // OS: Q
+ ACTION_TEXT_CLASSIFIER_MANUAL_REPLY = 1618;
+
+ // ACTION: Text classifier generates an action.
+ // CATEGORY: CONVERSATION_ACTIONS
+ // OS: Q
+ ACTION_TEXT_CLASSIFIER_ACTIONS_GENERATED = 1619;
+
// ---- End Q Constants, all Q constants go above this line ----
// Add new aosp constants above this line.
diff --git a/services/art-profile b/services/art-profile
index af9d7a9..7e4884b2 100644
--- a/services/art-profile
+++ b/services/art-profile
@@ -6970,6 +6970,7 @@
PLcom/android/server/am/ActivityManagerService$LocalService;->getMemoryStateForProcesses()Ljava/util/List;
PLcom/android/server/am/ActivityManagerService$LocalService;->getTopVisibleActivities()Ljava/util/List;
PLcom/android/server/am/ActivityManagerService$LocalService;->getUidProcessState(I)I
+PLcom/android/server/am/ActivityManagerService$LocalService;->isAppForeground(I)Z
PLcom/android/server/am/ActivityManagerService$LocalService;->isCallerRecents(I)Z
PLcom/android/server/am/ActivityManagerService$LocalService;->isRuntimeRestarted()Z
PLcom/android/server/am/ActivityManagerService$LocalService;->isSystemReady()Z
@@ -7125,7 +7126,6 @@
PLcom/android/server/am/ActivityManagerService;->installEncryptionUnawareProviders(I)V
PLcom/android/server/am/ActivityManagerService;->installSystemProviders()V
PLcom/android/server/am/ActivityManagerService;->isAllowedWhileBooting(Landroid/content/pm/ApplicationInfo;)Z
-PLcom/android/server/am/ActivityManagerService;->isAppForeground(I)Z
PLcom/android/server/am/ActivityManagerService;->isAssistDataAllowedOnCurrentActivity()Z
PLcom/android/server/am/ActivityManagerService;->isGetTasksAllowed(Ljava/lang/String;II)Z
PLcom/android/server/am/ActivityManagerService;->isInLockTaskMode()Z
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 5d44144..6b0adfb 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -39,6 +39,7 @@
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
import android.app.AlarmManager;
import android.app.AppGlobals;
import android.app.IActivityManager;
@@ -103,6 +104,7 @@
import com.android.internal.util.Preconditions;
import com.android.server.AppWidgetBackupBridge;
import com.android.server.EventLogTags;
+import com.android.server.LocalServices;
import com.android.server.backup.fullbackup.FullBackupEntry;
import com.android.server.backup.fullbackup.PerformFullTransportBackupTask;
import com.android.server.backup.internal.BackupHandler;
@@ -247,10 +249,11 @@
private final TransportManager mTransportManager;
private final HandlerThread mUserBackupThread;
- private final Context mContext;
- private final PackageManager mPackageManager;
- private final IPackageManager mPackageManagerBinder;
- private final IActivityManager mActivityManager;
+ private Context mContext;
+ private PackageManager mPackageManager;
+ private IPackageManager mPackageManagerBinder;
+ private IActivityManager mActivityManager;
+ private ActivityManagerInternal mActivityManagerInternal;
private PowerManager mPowerManager;
private final AlarmManager mAlarmManager;
private final IStorageManager mStorageManager;
@@ -460,6 +463,7 @@
mPackageManager = context.getPackageManager();
mPackageManagerBinder = AppGlobals.getPackageManager();
mActivityManager = ActivityManager.getService();
+ mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
@@ -499,26 +503,46 @@
mBackupPasswordManager = new BackupPasswordManager(mContext, mBaseStateDir, mRng);
- // Alarm receivers for scheduled backups & initialization operations
- BroadcastReceiver mRunBackupReceiver = new RunBackupReceiver(this);
+ // Receivers for scheduled backups and transport initialization operations.
+ BroadcastReceiver runBackupReceiver = new RunBackupReceiver(this);
IntentFilter filter = new IntentFilter();
filter.addAction(RUN_BACKUP_ACTION);
- context.registerReceiver(mRunBackupReceiver, filter,
- android.Manifest.permission.BACKUP, null);
+ context.registerReceiverAsUser(
+ runBackupReceiver,
+ UserHandle.of(userId),
+ filter,
+ android.Manifest.permission.BACKUP,
+ /* scheduler */ null);
- BroadcastReceiver mRunInitReceiver = new RunInitializeReceiver(this);
+ BroadcastReceiver runInitReceiver = new RunInitializeReceiver(this);
filter = new IntentFilter();
filter.addAction(RUN_INITIALIZE_ACTION);
- context.registerReceiver(mRunInitReceiver, filter,
- android.Manifest.permission.BACKUP, null);
+ context.registerReceiverAsUser(
+ runInitReceiver,
+ UserHandle.of(userId),
+ filter,
+ android.Manifest.permission.BACKUP,
+ /* scheduler */ null);
Intent backupIntent = new Intent(RUN_BACKUP_ACTION);
backupIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- mRunBackupIntent = PendingIntent.getBroadcast(context, 0, backupIntent, 0);
+ mRunBackupIntent =
+ PendingIntent.getBroadcastAsUser(
+ context,
+ /* requestCode */ 0,
+ backupIntent,
+ /* flags */ 0,
+ UserHandle.of(userId));
Intent initIntent = new Intent(RUN_INITIALIZE_ACTION);
initIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- mRunInitIntent = PendingIntent.getBroadcast(context, 0, initIntent, 0);
+ mRunInitIntent =
+ PendingIntent.getBroadcastAsUser(
+ context,
+ /* requestCode */ 0,
+ initIntent,
+ /* flags */ 0,
+ UserHandle.of(userId));
// Set up the backup-request journaling
mJournalDir = new File(mBaseStateDir, "pending");
@@ -826,19 +850,29 @@
mFullBackupQueue = readFullBackupSchedule();
}
- // Register for broadcasts about package install, etc., so we can
- // update the provider list.
+ // Register for broadcasts about package changes.
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_PACKAGE_ADDED);
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
filter.addDataScheme("package");
- mContext.registerReceiver(mBroadcastReceiver, filter);
+ mContext.registerReceiverAsUser(
+ mBroadcastReceiver,
+ UserHandle.of(mUserId),
+ filter,
+ /* broadcastPermission */ null,
+ /* scheduler */ null);
+
// Register for events related to sdcard installation.
IntentFilter sdFilter = new IntentFilter();
sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
- mContext.registerReceiver(mBroadcastReceiver, sdFilter);
+ mContext.registerReceiverAsUser(
+ mBroadcastReceiver,
+ UserHandle.of(mUserId),
+ sdFilter,
+ /* broadcastPermission */ null,
+ /* scheduler */ null);
}
private ArrayList<FullBackupEntry> readFullBackupSchedule() {
@@ -1103,17 +1137,23 @@
}
}
- // ----- Track installation/removal of packages -----
+ /**
+ * A {@link BroadcastReceiver} tracking changes to packages and sd cards in order to update our
+ * internal bookkeeping.
+ */
private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
- if (MORE_DEBUG) Slog.d(TAG, "Received broadcast " + intent);
+ if (MORE_DEBUG) {
+ Slog.d(TAG, "Received broadcast " + intent);
+ }
String action = intent.getAction();
boolean replacing = false;
boolean added = false;
boolean changed = false;
Bundle extras = intent.getExtras();
- String[] pkgList = null;
+ String[] packageList = null;
+
if (Intent.ACTION_PACKAGE_ADDED.equals(action)
|| Intent.ACTION_PACKAGE_REMOVED.equals(action)
|| Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
@@ -1121,69 +1161,70 @@
if (uri == null) {
return;
}
- final String pkgName = uri.getSchemeSpecificPart();
- if (pkgName != null) {
- pkgList = new String[]{pkgName};
- }
- changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
- // At package-changed we only care about looking at new transport states
+ String packageName = uri.getSchemeSpecificPart();
+ if (packageName != null) {
+ packageList = new String[]{packageName};
+ }
+
+ changed = Intent.ACTION_PACKAGE_CHANGED.equals(action);
if (changed) {
- final String[] components =
+ // Look at new transport states for package changed events.
+ String[] components =
intent.getStringArrayExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
if (MORE_DEBUG) {
- Slog.i(TAG, "Package " + pkgName + " changed; rechecking");
+ Slog.i(TAG, "Package " + packageName + " changed");
for (int i = 0; i < components.length; i++) {
Slog.i(TAG, " * " + components[i]);
}
}
mBackupHandler.post(
- () -> mTransportManager.onPackageChanged(pkgName, components));
- return; // nothing more to do in the PACKAGE_CHANGED case
+ () -> mTransportManager.onPackageChanged(packageName, components));
+ return;
}
added = Intent.ACTION_PACKAGE_ADDED.equals(action);
replacing = extras.getBoolean(Intent.EXTRA_REPLACING, false);
} else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
added = true;
- pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+ packageList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
} else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
added = false;
- pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+ packageList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
}
- if (pkgList == null || pkgList.length == 0) {
+ if (packageList == null || packageList.length == 0) {
return;
}
- final int uid = extras.getInt(Intent.EXTRA_UID);
+ int uid = extras.getInt(Intent.EXTRA_UID);
if (added) {
synchronized (mBackupParticipants) {
if (replacing) {
- // This is the package-replaced case; we just remove the entry
- // under the old uid and fall through to re-add. If an app
- // just added key/value backup participation, this picks it up
- // as a known participant.
- removePackageParticipantsLocked(pkgList, uid);
+ // Remove the entry under the old uid and fall through to re-add. If an app
+ // just opted into key/value backup, add it as a known participant.
+ removePackageParticipantsLocked(packageList, uid);
}
- addPackageParticipantsLocked(pkgList);
+ addPackageParticipantsLocked(packageList);
}
- // If they're full-backup candidates, add them there instead
- final long now = System.currentTimeMillis();
- for (final String packageName : pkgList) {
+
+ long now = System.currentTimeMillis();
+ for (String packageName : packageList) {
try {
- PackageInfo app = mPackageManager.getPackageInfo(packageName, 0);
+ PackageInfo app =
+ mPackageManager.getPackageInfoAsUser(
+ packageName, /* flags */ 0, mUserId);
if (AppBackupUtils.appGetsFullBackup(app)
&& AppBackupUtils.appIsEligibleForBackup(
app.applicationInfo, mPackageManager)) {
enqueueFullBackup(packageName, now);
scheduleNextFullBackupJob(0);
} else {
- // The app might have just transitioned out of full-data into
- // doing key/value backups, or might have just disabled backups
- // entirely. Make sure it is no longer in the full-data queue.
+ // The app might have just transitioned out of full-data into doing
+ // key/value backups, or might have just disabled backups entirely. Make
+ // sure it is no longer in the full-data queue.
synchronized (mQueueLock) {
dequeueFullBackupLocked(packageName);
}
@@ -1192,32 +1233,28 @@
mBackupHandler.post(
() -> mTransportManager.onPackageAdded(packageName));
-
} catch (NameNotFoundException e) {
- // doesn't really exist; ignore it
if (DEBUG) {
Slog.w(TAG, "Can't resolve new app " + packageName);
}
}
}
- // Whenever a package is added or updated we need to update
- // the package metadata bookkeeping.
+ // Whenever a package is added or updated we need to update the package metadata
+ // bookkeeping.
dataChangedImpl(PACKAGE_MANAGER_SENTINEL);
} else {
- if (replacing) {
- // The package is being updated. We'll receive a PACKAGE_ADDED shortly.
- } else {
- // Outright removal. In the full-data case, the app will be dropped
- // from the queue when its (now obsolete) name comes up again for
- // backup.
+ if (!replacing) {
+ // Outright removal. In the full-data case, the app will be dropped from the
+ // queue when its (now obsolete) name comes up again for backup.
synchronized (mBackupParticipants) {
- removePackageParticipantsLocked(pkgList, uid);
+ removePackageParticipantsLocked(packageList, uid);
}
}
- for (final String pkgName : pkgList) {
+
+ for (String packageName : packageList) {
mBackupHandler.post(
- () -> mTransportManager.onPackageRemoved(pkgName));
+ () -> mTransportManager.onPackageRemoved(packageName));
}
}
}
@@ -2074,7 +2111,8 @@
final int privFlags = appInfo.applicationInfo.privateFlags;
headBusy = (privFlags & PRIVATE_FLAG_BACKUP_IN_FOREGROUND) == 0
- && mActivityManager.isAppForeground(appInfo.applicationInfo.uid);
+ && mActivityManagerInternal.isAppForeground(
+ appInfo.applicationInfo.uid);
if (headBusy) {
final long nextEligible = System.currentTimeMillis()
@@ -2097,8 +2135,6 @@
// queue entirely and move on, but if there's nothing else in the queue
// we should bail entirely. headBusy cannot have been set to true yet.
runBackup = (mFullBackupQueue.size() > 1);
- } catch (RemoteException e) {
- // Cannot happen; the Activity Manager is in the same process
}
}
} while (headBusy);
diff --git a/services/backup/java/com/android/server/backup/fullbackup/AppMetadataBackupWriter.java b/services/backup/java/com/android/server/backup/fullbackup/AppMetadataBackupWriter.java
index bace1aa..d13f711 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/AppMetadataBackupWriter.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/AppMetadataBackupWriter.java
@@ -7,6 +7,7 @@
import static com.android.server.backup.UserBackupManagerService.BACKUP_WIDGET_METADATA_TOKEN;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.backup.FullBackup;
import android.app.backup.FullBackupDataOutput;
import android.content.pm.PackageInfo;
@@ -15,7 +16,6 @@
import android.content.pm.SigningInfo;
import android.os.Build;
import android.os.Environment;
-import android.os.UserHandle;
import android.util.Log;
import android.util.StringBuilderPrinter;
@@ -254,12 +254,11 @@
* for 'adb backup'.
*/
// TODO(b/113807190): Investigate and potentially remove.
- public void backupObb(PackageInfo packageInfo) {
+ public void backupObb(@UserIdInt int userId, PackageInfo packageInfo) {
// TODO: migrate this to SharedStorageBackup, since AID_SYSTEM doesn't have access to
// external storage.
- // TODO: http://b/22388012
Environment.UserEnvironment userEnv =
- new Environment.UserEnvironment(UserHandle.USER_SYSTEM);
+ new Environment.UserEnvironment(userId);
File obbDir = userEnv.buildExternalStorageAppObbDirs(packageInfo.packageName)[0];
if (obbDir != null) {
if (MORE_DEBUG) {
diff --git a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
index 03d4e97..7ea1892 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
@@ -54,12 +54,12 @@
*/
public class FullBackupEngine {
private UserBackupManagerService backupManagerService;
- OutputStream mOutput;
- FullBackupPreflight mPreflightHook;
- BackupRestoreTask mTimeoutMonitor;
- IBackupAgent mAgent;
- boolean mIncludeApks;
- PackageInfo mPkg;
+ private OutputStream mOutput;
+ private FullBackupPreflight mPreflightHook;
+ private BackupRestoreTask mTimeoutMonitor;
+ private IBackupAgent mAgent;
+ private boolean mIncludeApks;
+ private PackageInfo mPkg;
private final long mQuota;
private final int mOpToken;
private final int mTransportFlags;
@@ -130,7 +130,7 @@
// TODO(b/113807190): Look into removing, only used for 'adb backup'.
if (writeApk) {
appMetadataBackupWriter.backupApk(mPackage);
- appMetadataBackupWriter.backupObb(mPackage);
+ appMetadataBackupWriter.backupObb(mUserId, mPackage);
}
if (DEBUG) {
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
index 43a80c4..31786d7 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
@@ -66,24 +66,22 @@
*/
public class PerformAdbBackupTask extends FullBackupTask implements BackupRestoreTask {
- private UserBackupManagerService backupManagerService;
- FullBackupEngine mBackupEngine;
- final AtomicBoolean mLatch;
+ private final UserBackupManagerService mUserBackupManagerService;
+ private final AtomicBoolean mLatch;
- ParcelFileDescriptor mOutputFile;
- DeflaterOutputStream mDeflater;
- boolean mIncludeApks;
- boolean mIncludeObbs;
- boolean mIncludeShared;
- boolean mDoWidgets;
- boolean mAllApps;
- boolean mIncludeSystem;
- boolean mCompress;
- boolean mKeyValue;
- ArrayList<String> mPackages;
- PackageInfo mCurrentTarget;
- String mCurrentPassword;
- String mEncryptPassword;
+ private final ParcelFileDescriptor mOutputFile;
+ private final boolean mIncludeApks;
+ private final boolean mIncludeObbs;
+ private final boolean mIncludeShared;
+ private final boolean mDoWidgets;
+ private final boolean mAllApps;
+ private final boolean mIncludeSystem;
+ private final boolean mCompress;
+ private final boolean mKeyValue;
+ private final ArrayList<String> mPackages;
+ private PackageInfo mCurrentTarget;
+ private final String mCurrentPassword;
+ private final String mEncryptPassword;
private final int mCurrentOpToken;
public PerformAdbBackupTask(UserBackupManagerService backupManagerService,
@@ -92,7 +90,7 @@
String curPassword, String encryptPassword, boolean doAllApps, boolean doSystem,
boolean doCompress, boolean doKeyValue, String[] packages, AtomicBoolean latch) {
super(observer);
- this.backupManagerService = backupManagerService;
+ mUserBackupManagerService = backupManagerService;
mCurrentOpToken = backupManagerService.generateRandomIntegerToken();
mLatch = latch;
@@ -104,7 +102,7 @@
mAllApps = doAllApps;
mIncludeSystem = doSystem;
mPackages = (packages == null)
- ? new ArrayList<String>()
+ ? new ArrayList<>()
: new ArrayList<>(Arrays.asList(packages));
mCurrentPassword = curPassword;
// when backing up, if there is a current backup password, we require that
@@ -123,11 +121,11 @@
mKeyValue = doKeyValue;
}
- void addPackagesToSet(TreeMap<String, PackageInfo> set, List<String> pkgNames) {
+ private void addPackagesToSet(TreeMap<String, PackageInfo> set, List<String> pkgNames) {
for (String pkgName : pkgNames) {
if (!set.containsKey(pkgName)) {
try {
- PackageInfo info = backupManagerService.getPackageManager().getPackageInfo(
+ PackageInfo info = mUserBackupManagerService.getPackageManager().getPackageInfo(
pkgName,
PackageManager.GET_SIGNING_CERTIFICATES);
set.put(pkgName, info);
@@ -141,7 +139,7 @@
private OutputStream emitAesBackupHeader(StringBuilder headerbuf,
OutputStream ofstream) throws Exception {
// User key will be used to encrypt the master key.
- byte[] newUserSalt = backupManagerService
+ byte[] newUserSalt = mUserBackupManagerService
.randomBytes(PasswordUtils.PBKDF2_SALT_SIZE);
SecretKey userKey = PasswordUtils
.buildPasswordKey(PBKDF_CURRENT, mEncryptPassword,
@@ -150,8 +148,8 @@
// the master key is random for each backup
byte[] masterPw = new byte[256 / 8];
- backupManagerService.getRng().nextBytes(masterPw);
- byte[] checksumSalt = backupManagerService
+ mUserBackupManagerService.getRng().nextBytes(masterPw);
+ byte[] checksumSalt = mUserBackupManagerService
.randomBytes(PasswordUtils.PBKDF2_SALT_SIZE);
// primary encryption of the datastream with the random key
@@ -232,11 +230,11 @@
TreeMap<String, PackageInfo> packagesToBackup = new TreeMap<>();
FullBackupObbConnection obbConnection = new FullBackupObbConnection(
- backupManagerService);
+ mUserBackupManagerService);
obbConnection.establish(); // we'll want this later
sendStartBackup();
- PackageManager pm = backupManagerService.getPackageManager();
+ PackageManager pm = mUserBackupManagerService.getPackageManager();
// doAllApps supersedes the package set if any
if (mAllApps) {
@@ -245,7 +243,7 @@
for (int i = 0; i < allPackages.size(); i++) {
PackageInfo pkg = allPackages.get(i);
// Exclude system apps if we've been asked to do so
- if (mIncludeSystem == true
+ if (mIncludeSystem
|| ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0)) {
packagesToBackup.put(pkg.packageName, pkg);
}
@@ -316,7 +314,7 @@
boolean encrypting = (mEncryptPassword != null && mEncryptPassword.length() > 0);
// Only allow encrypted backups of encrypted devices
- if (backupManagerService.deviceIsEncrypted() && !encrypting) {
+ if (mUserBackupManagerService.deviceIsEncrypted() && !encrypting) {
Slog.e(TAG, "Unencrypted backup of encrypted device; aborting");
return;
}
@@ -325,7 +323,7 @@
// Verify that the given password matches the currently-active
// backup password, if any
- if (!backupManagerService.backupPasswordMatches(mCurrentPassword)) {
+ if (!mUserBackupManagerService.backupPasswordMatches(mCurrentPassword)) {
if (DEBUG) {
Slog.w(TAG, "Backup password mismatch; aborting");
}
@@ -390,7 +388,7 @@
// Shared storage if requested
if (mIncludeShared) {
try {
- pkg = backupManagerService.getPackageManager().getPackageInfo(
+ pkg = mUserBackupManagerService.getPackageManager().getPackageInfo(
SHARED_BACKUP_AGENT_PACKAGE, 0);
backupQueue.add(pkg);
} catch (NameNotFoundException e) {
@@ -410,9 +408,17 @@
pkg.packageName.equals(
SHARED_BACKUP_AGENT_PACKAGE);
- mBackupEngine = new FullBackupEngine(backupManagerService, out,
- null, pkg, mIncludeApks, this, Long.MAX_VALUE,
- mCurrentOpToken, /*transportFlags=*/ 0);
+ FullBackupEngine mBackupEngine =
+ new FullBackupEngine(
+ mUserBackupManagerService,
+ out,
+ null,
+ pkg,
+ mIncludeApks,
+ this,
+ Long.MAX_VALUE,
+ mCurrentOpToken,
+ /*transportFlags=*/ 0);
sendOnBackupPackage(isSharedStorage ? "Shared storage" : pkg.packageName);
// Don't need to check preflight result as there is no preflight hook.
@@ -437,10 +443,10 @@
}
KeyValueAdbBackupEngine kvBackupEngine =
new KeyValueAdbBackupEngine(out, keyValuePackage,
- backupManagerService,
- backupManagerService.getPackageManager(),
- backupManagerService.getBaseStateDir(),
- backupManagerService.getDataDir());
+ mUserBackupManagerService,
+ mUserBackupManagerService.getPackageManager(),
+ mUserBackupManagerService.getBaseStateDir(),
+ mUserBackupManagerService.getDataDir());
sendOnBackupPackage(keyValuePackage.packageName);
kvBackupEngine.backupOnePackage();
}
@@ -471,7 +477,7 @@
if (DEBUG) {
Slog.d(TAG, "Full backup pass complete.");
}
- backupManagerService.getWakelock().release();
+ mUserBackupManagerService.getWakelock().release();
}
}
@@ -493,8 +499,8 @@
Slog.w(TAG, "adb backup cancel of " + target);
}
if (target != null) {
- backupManagerService.tearDownAgentAndKill(mCurrentTarget.applicationInfo);
+ mUserBackupManagerService.tearDownAgentAndKill(mCurrentTarget.applicationInfo);
}
- backupManagerService.removeOperation(mCurrentOpToken);
+ mUserBackupManagerService.removeOperation(mCurrentOpToken);
}
}
diff --git a/services/backup/java/com/android/server/backup/internal/RunBackupReceiver.java b/services/backup/java/com/android/server/backup/internal/RunBackupReceiver.java
index 3b87724..d37b106 100644
--- a/services/backup/java/com/android/server/backup/internal/RunBackupReceiver.java
+++ b/services/backup/java/com/android/server/backup/internal/RunBackupReceiver.java
@@ -26,62 +26,84 @@
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.os.Handler;
import android.os.Message;
import android.util.Slog;
import com.android.server.backup.UserBackupManagerService;
+/**
+ * A {@link BroadcastReceiver} for the action {@link UserBackupManagerService#RUN_BACKUP_ACTION}
+ * that runs an immediate backup operation if eligible.
+ */
public class RunBackupReceiver extends BroadcastReceiver {
+ private final UserBackupManagerService mUserBackupManagerService;
- private UserBackupManagerService backupManagerService;
-
- public RunBackupReceiver(UserBackupManagerService backupManagerService) {
- this.backupManagerService = backupManagerService;
+ public RunBackupReceiver(UserBackupManagerService userBackupManagerService) {
+ mUserBackupManagerService = userBackupManagerService;
}
+ /**
+ * Run a backup pass if we're eligible. We're eligible if the following conditions are met:
+ *
+ * <ul>
+ * <li>No transports are pending initialization (otherwise we kick off an initialization
+ * operation instead).
+ * <li>Backup is enabled for the user.
+ * <li>The user has completed setup.
+ * <li>No backup operation is currently running for the user.
+ * </ul>
+ */
public void onReceive(Context context, Intent intent) {
- if (RUN_BACKUP_ACTION.equals(intent.getAction())) {
- synchronized (backupManagerService.getQueueLock()) {
- if (backupManagerService.getPendingInits().size() > 0) {
- // If there are pending init operations, we process those
- // and then settle into the usual periodic backup schedule.
- if (MORE_DEBUG) {
- Slog.v(TAG, "Init pending at scheduled backup");
- }
- try {
- backupManagerService.getAlarmManager().cancel(
- backupManagerService.getRunInitIntent());
- backupManagerService.getRunInitIntent().send();
- } catch (PendingIntent.CanceledException ce) {
- Slog.e(TAG, "Run init intent cancelled");
- // can't really do more than bail here
- }
- } else {
- // Don't run backups now if we're disabled or not yet
- // fully set up.
- if (backupManagerService.isEnabled()
- && backupManagerService.isSetupComplete()) {
- if (!backupManagerService.isBackupRunning()) {
- if (DEBUG) {
- Slog.v(TAG, "Running a backup pass");
- }
+ if (!RUN_BACKUP_ACTION.equals(intent.getAction())) {
+ return;
+ }
- // Acquire the wakelock and pass it to the backup thread. it will
- // be released once backup concludes.
- backupManagerService.setBackupRunning(true);
- backupManagerService.getWakelock().acquire();
-
- Message msg = backupManagerService.getBackupHandler().obtainMessage(
- MSG_RUN_BACKUP);
- backupManagerService.getBackupHandler().sendMessage(msg);
- } else {
- Slog.i(TAG, "Backup time but one already running");
- }
- } else {
- Slog.w(TAG, "Backup pass but enabled=" + backupManagerService.isEnabled()
- + " setupComplete=" + backupManagerService.isSetupComplete());
- }
+ synchronized (mUserBackupManagerService.getQueueLock()) {
+ if (mUserBackupManagerService.getPendingInits().size() > 0) {
+ // If there are pending init operations, we process those and then settle into the
+ // usual periodic backup schedule.
+ if (MORE_DEBUG) {
+ Slog.v(TAG, "Init pending at scheduled backup");
}
+ try {
+ PendingIntent runInitIntent = mUserBackupManagerService.getRunInitIntent();
+ mUserBackupManagerService.getAlarmManager().cancel(runInitIntent);
+ runInitIntent.send();
+ } catch (PendingIntent.CanceledException ce) {
+ Slog.w(TAG, "Run init intent cancelled");
+ }
+ } else {
+ // Don't run backups if we're disabled or not yet set up.
+ if (!mUserBackupManagerService.isEnabled()
+ || !mUserBackupManagerService.isSetupComplete()) {
+ Slog.w(
+ TAG,
+ "Backup pass but enabled="
+ + mUserBackupManagerService.isEnabled()
+ + " setupComplete="
+ + mUserBackupManagerService.isSetupComplete());
+ return;
+ }
+
+ // Don't run backups if one is already running.
+ if (mUserBackupManagerService.isBackupRunning()) {
+ Slog.i(TAG, "Backup time but one already running");
+ return;
+ }
+
+ if (DEBUG) {
+ Slog.v(TAG, "Running a backup pass");
+ }
+
+ // Acquire the wakelock and pass it to the backup thread. It will be released once
+ // backup concludes.
+ mUserBackupManagerService.setBackupRunning(true);
+ mUserBackupManagerService.getWakelock().acquire();
+
+ Handler backupHandler = mUserBackupManagerService.getBackupHandler();
+ Message message = backupHandler.obtainMessage(MSG_RUN_BACKUP);
+ backupHandler.sendMessage(message);
}
}
}
diff --git a/services/backup/java/com/android/server/backup/internal/RunInitializeReceiver.java b/services/backup/java/com/android/server/backup/internal/RunInitializeReceiver.java
index 38870cb..97711e3 100644
--- a/services/backup/java/com/android/server/backup/internal/RunInitializeReceiver.java
+++ b/services/backup/java/com/android/server/backup/internal/RunInitializeReceiver.java
@@ -24,41 +24,50 @@
import android.content.Context;
import android.content.Intent;
import android.os.PowerManager;
-import android.util.ArraySet;
import android.util.Slog;
import com.android.server.backup.UserBackupManagerService;
-public class RunInitializeReceiver extends BroadcastReceiver {
- private final UserBackupManagerService mBackupManagerService;
+import java.util.Set;
- public RunInitializeReceiver(UserBackupManagerService backupManagerService) {
- mBackupManagerService = backupManagerService;
+/**
+ * A {@link BroadcastReceiver} for the action {@link UserBackupManagerService#RUN_INITIALIZE_ACTION}
+ * that runs an initialization operation on all pending transports.
+ */
+public class RunInitializeReceiver extends BroadcastReceiver {
+ private final UserBackupManagerService mUserBackupManagerService;
+
+ public RunInitializeReceiver(UserBackupManagerService userBackupManagerService) {
+ mUserBackupManagerService = userBackupManagerService;
}
public void onReceive(Context context, Intent intent) {
- if (RUN_INITIALIZE_ACTION.equals(intent.getAction())) {
- synchronized (mBackupManagerService.getQueueLock()) {
- final ArraySet<String> pendingInits = mBackupManagerService.getPendingInits();
- if (DEBUG) {
- Slog.v(TAG, "Running a device init; " + pendingInits.size() + " pending");
- }
+ if (!RUN_INITIALIZE_ACTION.equals(intent.getAction())) {
+ return;
+ }
- if (pendingInits.size() > 0) {
- final String[] transports =
- pendingInits.toArray(new String[pendingInits.size()]);
+ synchronized (mUserBackupManagerService.getQueueLock()) {
+ Set<String> pendingInits = mUserBackupManagerService.getPendingInits();
+ if (DEBUG) {
+ Slog.v(TAG, "Running a device init; " + pendingInits.size() + " pending");
+ }
- mBackupManagerService.clearPendingInits();
+ if (pendingInits.size() > 0) {
+ String[] transports = pendingInits.toArray(new String[pendingInits.size()]);
- PowerManager.WakeLock wakelock = mBackupManagerService.getWakelock();
- wakelock.acquire();
- OnTaskFinishedListener listener = caller -> wakelock.release();
+ mUserBackupManagerService.clearPendingInits();
- Runnable task =
- new PerformInitializeTask(
- mBackupManagerService, transports, null, listener);
- mBackupManagerService.getBackupHandler().post(task);
- }
+ PowerManager.WakeLock wakelock = mUserBackupManagerService.getWakelock();
+ wakelock.acquire();
+ OnTaskFinishedListener listener = caller -> wakelock.release();
+
+ Runnable task =
+ new PerformInitializeTask(
+ mUserBackupManagerService,
+ transports,
+ /* observer */ null,
+ listener);
+ mUserBackupManagerService.getBackupHandler().post(task);
}
}
}
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 5cea56e..2346cfc 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -64,6 +64,7 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
+import android.os.Message;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
@@ -86,7 +87,6 @@
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
-import com.android.internal.util.Preconditions;
import com.android.server.location.AbstractLocationProvider;
import com.android.server.location.ActivityRecognitionProxy;
import com.android.server.location.GeocoderProxy;
@@ -117,11 +117,6 @@
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.Set;
-import java.util.concurrent.Callable;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Executors;
-import java.util.concurrent.FutureTask;
/**
* The service class that manages LocationProviders and issues location
@@ -176,7 +171,10 @@
private static final LocationRequest DEFAULT_LOCATION_REQUEST = new LocationRequest();
private final Context mContext;
- private AppOpsManager mAppOps;
+ private final AppOpsManager mAppOps;
+
+ // used internally for synchronization
+ private final Object mLock = new Object();
// --- fields below are final after systemRunning() ---
private LocationFudger mLocationFudger;
@@ -188,7 +186,7 @@
private GeocoderProxy mGeocodeProvider;
private GnssStatusListenerHelper mGnssStatusProvider;
private INetInitiatedListener mNetInitiatedListener;
- private final Handler mHandler;
+ private LocationWorkerHandler mLocationHandler;
private PassiveProvider mPassiveProvider; // track passive provider for special cases
private LocationBlacklist mBlacklist;
private GnssMeasurementsProvider mGnssMeasurementsProvider;
@@ -197,6 +195,8 @@
private boolean mLocationControllerExtraPackageEnabled;
private IGpsGeofenceHardware mGpsGeofenceProxy;
+ // --- fields below are protected by mLock ---
+
// Mock (test) providers
private final HashMap<String, MockProvider> mMockProviders =
new HashMap<>();
@@ -205,8 +205,8 @@
private final HashMap<Object, Receiver> mReceivers = new HashMap<>();
// currently installed providers (with mocks replacing real providers)
- private final CopyOnWriteArrayList<LocationProvider> mProviders =
- new CopyOnWriteArrayList<>();
+ private final ArrayList<LocationProvider> mProviders =
+ new ArrayList<>();
// real providers, saved here when mocked out
private final HashMap<String, LocationProvider> mRealProviders =
@@ -232,8 +232,8 @@
// all providers that operate over proxy, for authorizing incoming location and whitelisting
// throttling
- private final CopyOnWriteArrayList<LocationProviderProxy> mProxyProviders =
- new CopyOnWriteArrayList<>();
+ private final ArrayList<LocationProviderProxy> mProxyProviders =
+ new ArrayList<>();
private final ArraySet<String> mBackgroundThrottlePackageWhitelist = new ArraySet<>();
@@ -246,6 +246,9 @@
private int mCurrentUserId = UserHandle.USER_SYSTEM;
private int[] mCurrentUserProfiles = new int[]{UserHandle.USER_SYSTEM};
+ // Maximum age of last location returned to clients with foreground-only location permissions.
+ private long mLastLocationMaxAgeMs;
+
private GnssLocationProvider.GnssSystemInfoProvider mGnssSystemInfoProvider;
private GnssLocationProvider.GnssMetricsProvider mGnssMetricsProvider;
@@ -258,7 +261,7 @@
public LocationManagerService(Context context) {
super();
mContext = context;
- mHandler = BackgroundThread.getHandler();
+ mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
// Let the package manager query which are the default location
// providers as they get certain permissions granted by default.
@@ -268,92 +271,132 @@
userId -> mContext.getResources().getStringArray(
com.android.internal.R.array.config_locationProviderPackageNames));
+ if (D) Log.d(TAG, "Constructed");
+
// most startup is deferred until systemRunning()
}
public void systemRunning() {
- runInternal(this::initialize);
- }
+ synchronized (mLock) {
+ if (D) Log.d(TAG, "systemRunning()");
- private void initialize() {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+ // fetch package manager
+ mPackageManager = mContext.getPackageManager();
- mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
- mPackageManager = mContext.getPackageManager();
- mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
- mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
+ // fetch power manager
+ mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
- // prepare mHandler's dependents
- mLocationFudger = new LocationFudger(mContext, mHandler);
- mBlacklist = new LocationBlacklist(mContext, mHandler);
- mBlacklist.init();
- mGeofenceManager = new GeofenceManager(mContext, mBlacklist);
+ // fetch activity manager
+ mActivityManager
+ = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
- mAppOps.startWatchingMode(
- AppOpsManager.OP_COARSE_LOCATION,
- null,
- AppOpsManager.WATCH_FOREGROUND_CHANGES,
- new AppOpsManager.OnOpChangedInternalListener() {
- public void onOpChanged(int op, String packageName) {
- mHandler.post(() -> onAppOpChanged());
- }
- });
+ // prepare worker thread
+ mLocationHandler = new LocationWorkerHandler(BackgroundThread.get().getLooper());
- mPackageManager.addOnPermissionsChangeListener(
- uid -> runInternal(this::onPermissionsChanged));
+ // prepare mLocationHandler's dependents
+ mLocationFudger = new LocationFudger(mContext, mLocationHandler);
+ mBlacklist = new LocationBlacklist(mContext, mLocationHandler);
+ mBlacklist.init();
+ mGeofenceManager = new GeofenceManager(mContext, mBlacklist);
- mActivityManager.addOnUidImportanceListener(
- (uid, importance) -> mHandler.post(
- () -> onUidImportanceChanged(uid, importance)),
- FOREGROUND_IMPORTANCE_CUTOFF);
+ // Monitor for app ops mode changes.
+ AppOpsManager.OnOpChangedListener callback
+ = new AppOpsManager.OnOpChangedInternalListener() {
+ public void onOpChanged(int op, String packageName) {
+ mLocationHandler.post(() -> {
+ synchronized (mLock) {
+ for (Receiver receiver : mReceivers.values()) {
+ receiver.updateMonitoring(true);
+ }
+ applyAllProviderRequirementsLocked();
+ }
+ });
+ }
+ };
+ mAppOps.startWatchingMode(AppOpsManager.OP_COARSE_LOCATION, null,
+ AppOpsManager.WATCH_FOREGROUND_CHANGES, callback);
- mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
- updateUserProfiles(mCurrentUserId);
+ PackageManager.OnPermissionsChangedListener permissionListener = uid -> {
+ synchronized (mLock) {
+ applyAllProviderRequirementsLocked();
+ }
+ };
+ mPackageManager.addOnPermissionsChangeListener(permissionListener);
- updateBackgroundThrottlingWhitelist();
+ // listen for background/foreground changes
+ ActivityManager.OnUidImportanceListener uidImportanceListener =
+ (uid, importance) -> mLocationHandler.post(
+ () -> onUidImportanceChanged(uid, importance));
+ mActivityManager.addOnUidImportanceListener(uidImportanceListener,
+ FOREGROUND_IMPORTANCE_CUTOFF);
- // prepare providers
- loadProvidersLocked();
- updateProvidersSettings();
- for (LocationProvider provider : mProviders) {
- applyRequirements(provider.getName());
+ mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ updateUserProfiles(mCurrentUserId);
+
+ updateBackgroundThrottlingWhitelistLocked();
+ updateLastLocationMaxAgeLocked();
+
+ // prepare providers
+ loadProvidersLocked();
+ updateProvidersSettingsLocked();
+ for (LocationProvider provider : mProviders) {
+ applyRequirementsLocked(provider.getName());
+ }
}
// listen for settings changes
mContext.getContentResolver().registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.LOCATION_PROVIDERS_ALLOWED), true,
- new ContentObserver(mHandler) {
+ new ContentObserver(mLocationHandler) {
@Override
public void onChange(boolean selfChange) {
- onProviderAllowedChanged();
+ synchronized (mLock) {
+ updateProvidersSettingsLocked();
+ }
}
}, UserHandle.USER_ALL);
mContext.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS),
true,
- new ContentObserver(mHandler) {
+ new ContentObserver(mLocationHandler) {
@Override
public void onChange(boolean selfChange) {
- onBackgroundThrottleIntervalChanged();
+ synchronized (mLock) {
+ for (LocationProvider provider : mProviders) {
+ applyRequirementsLocked(provider.getName());
+ }
+ }
}
}, UserHandle.USER_ALL);
mContext.getContentResolver().registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.LOCATION_LAST_LOCATION_MAX_AGE_MILLIS),
+ true,
+ new ContentObserver(mLocationHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ synchronized (mLock) {
+ updateLastLocationMaxAgeLocked();
+ }
+ }
+ }
+ );
+ mContext.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(
Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST),
true,
- new ContentObserver(mHandler) {
+ new ContentObserver(mLocationHandler) {
@Override
public void onChange(boolean selfChange) {
- onBackgroundThrottleWhitelistChanged();
+ synchronized (mLock) {
+ updateBackgroundThrottlingWhitelistLocked();
+ for (LocationProvider provider : mProviders) {
+ applyRequirementsLocked(provider.getName());
+ }
+ }
}
}, UserHandle.USER_ALL);
- new PackageMonitor() {
- @Override
- public void onPackageDisappeared(String packageName, int reason) {
- LocationManagerService.this.onPackageDisappeared(packageName);
- }
- }.register(mContext, mHandler.getLooper(), true);
+ mPackageMonitor.register(mContext, mLocationHandler.getLooper(), true);
// listen for user change
IntentFilter intentFilter = new IntentFilter();
@@ -372,170 +415,73 @@
updateUserProfiles(mCurrentUserId);
}
}
- }, UserHandle.ALL, intentFilter, null, mHandler);
- }
-
- // will block until completion and propagate exceptions, and thus should be used from binder
- // threads, particuarily binder threads from components that sit above LMS (ie, not location
- // providers).
- private void runFromBinderBlocking(Runnable runnable) throws RemoteException {
- runFromBinderBlocking(Executors.callable(runnable));
- }
-
- // will block until completion and propagate exceptions, and thus should be used from binder
- // threads, particuarily binder threads from components that sit above LMS (ie, not location
- // providers).
- private <T> T runFromBinderBlocking(Callable<T> callable) throws RemoteException {
- FutureTask<T> task = new FutureTask<>(callable);
- long identity = Binder.clearCallingIdentity();
- try {
- runInternal(task);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- try {
- return task.get();
- } catch (ExecutionException e) {
- // binder calls can handle 3 types of exceptions, runtimeexception and error (which can
- // be thrown any time), and remote exception. we transfer all of these exceptions from
- // the execution thread to the binder thread so that they may propagate normally. note
- // that we are loosing some context in doing so (losing the stack trace from the binder
- // thread).
- if (e.getCause() instanceof RemoteException) {
- throw (RemoteException) e.getCause();
- } else if (e.getCause() instanceof RuntimeException) {
- throw (RuntimeException) e.getCause();
- } else if (e.getCause() instanceof Error) {
- throw (Error) e.getCause();
- }
-
- // callers should not throw checked exceptions
- Log.wtf(TAG, "caller threw checked exception", e);
- throw new UnsupportedOperationException(e);
- } catch (InterruptedException e) {
- throw new RemoteException("Binder call interrupted", e, true, true);
- }
- }
-
- // will return immediately and will not propagate exceptions. should be used for non-binder work
- // that needs to be shifted onto the location thread, primarily listeners that do not support
- // running on arbitrary threads.
- private void runInternal(Runnable runnable) {
- // it would be a better use of resources to use locks to manage cross thread access to
- // various pieces on information. however, the history of the location package has mostly
- // shown that this is difficult to maintain in a multi-dev environment, and tends to always
- // lead towards the use of uber-locks and deadlocks. using a single thread to run everything
- // is more understandable for most devs, and seems less likely to result in future bugs
- if (Looper.myLooper() == mHandler.getLooper()) {
- runnable.run();
- } else {
- mHandler.post(runnable);
- }
- }
-
- private void onAppOpChanged() {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
- for (Receiver receiver : mReceivers.values()) {
- receiver.updateMonitoring(true);
- }
- for (LocationProvider p : mProviders) {
- applyRequirements(p.getName());
- }
- }
-
- private void onPermissionsChanged() {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
- for (LocationProvider p : mProviders) {
- applyRequirements(p.getName());
- }
- }
-
- private void onProviderAllowedChanged() {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
- updateProvidersSettings();
- }
-
- private void onPackageDisappeared(String packageName) {
- ArrayList<Receiver> deadReceivers = null;
-
- for (Receiver receiver : mReceivers.values()) {
- if (receiver.mIdentity.mPackageName.equals(packageName)) {
- if (deadReceivers == null) {
- deadReceivers = new ArrayList<>();
- }
- deadReceivers.add(receiver);
- }
- }
-
- // perform removal outside of mReceivers loop
- if (deadReceivers != null) {
- for (Receiver receiver : deadReceivers) {
- removeUpdates(receiver);
- }
- }
+ }, UserHandle.ALL, intentFilter, null, mLocationHandler);
}
private void onUidImportanceChanged(int uid, int importance) {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
boolean foreground = isImportanceForeground(importance);
HashSet<String> affectedProviders = new HashSet<>(mRecordsByProvider.size());
- for (Entry<String, ArrayList<UpdateRecord>> entry
- : mRecordsByProvider.entrySet()) {
- String provider = entry.getKey();
- for (UpdateRecord record : entry.getValue()) {
- if (record.mReceiver.mIdentity.mUid == uid
- && record.mIsForegroundUid != foreground) {
+ synchronized (mLock) {
+ for (Entry<String, ArrayList<UpdateRecord>> entry
+ : mRecordsByProvider.entrySet()) {
+ String provider = entry.getKey();
+ for (UpdateRecord record : entry.getValue()) {
+ if (record.mReceiver.mIdentity.mUid == uid
+ && record.mIsForegroundUid != foreground) {
+ if (D) {
+ Log.d(TAG, "request from uid " + uid + " is now "
+ + (foreground ? "foreground" : "background)"));
+ }
+ record.updateForeground(foreground);
+
+ if (!isThrottlingExemptLocked(record.mReceiver.mIdentity)) {
+ affectedProviders.add(provider);
+ }
+ }
+ }
+ }
+ for (String provider : affectedProviders) {
+ applyRequirementsLocked(provider);
+ }
+
+ for (Entry<IBinder, Identity> entry : mGnssMeasurementsListeners.entrySet()) {
+ Identity callerIdentity = entry.getValue();
+ if (callerIdentity.mUid == uid) {
if (D) {
- Log.d(TAG, "request from uid " + uid + " is now "
+ Log.d(TAG, "gnss measurements listener from uid " + uid
+ + " is now " + (foreground ? "foreground" : "background)"));
+ }
+ if (foreground || isThrottlingExemptLocked(entry.getValue())) {
+ mGnssMeasurementsProvider.addListener(
+ IGnssMeasurementsListener.Stub.asInterface(entry.getKey()),
+ callerIdentity.mUid, callerIdentity.mPackageName);
+ } else {
+ mGnssMeasurementsProvider.removeListener(
+ IGnssMeasurementsListener.Stub.asInterface(entry.getKey()));
+ }
+ }
+ }
+
+ for (Entry<IBinder, Identity> entry : mGnssNavigationMessageListeners.entrySet()) {
+ Identity callerIdentity = entry.getValue();
+ if (callerIdentity.mUid == uid) {
+ if (D) {
+ Log.d(TAG, "gnss navigation message listener from uid "
+ + uid + " is now "
+ (foreground ? "foreground" : "background)"));
}
- record.updateForeground(foreground);
-
- if (!isThrottlingExemptLocked(record.mReceiver.mIdentity)) {
- affectedProviders.add(provider);
+ if (foreground || isThrottlingExemptLocked(entry.getValue())) {
+ mGnssNavigationMessageProvider.addListener(
+ IGnssNavigationMessageListener.Stub.asInterface(entry.getKey()),
+ callerIdentity.mUid, callerIdentity.mPackageName);
+ } else {
+ mGnssNavigationMessageProvider.removeListener(
+ IGnssNavigationMessageListener.Stub.asInterface(entry.getKey()));
}
}
}
- }
- for (String provider : affectedProviders) {
- applyRequirements(provider);
- }
- for (Entry<IBinder, Identity> entry : mGnssMeasurementsListeners.entrySet()) {
- Identity callerIdentity = entry.getValue();
- if (callerIdentity.mUid == uid) {
- if (D) {
- Log.d(TAG, "gnss measurements listener from uid " + uid
- + " is now " + (foreground ? "foreground" : "background)"));
- }
- if (foreground || isThrottlingExemptLocked(entry.getValue())) {
- mGnssMeasurementsProvider.addListener(
- IGnssMeasurementsListener.Stub.asInterface(entry.getKey()),
- callerIdentity.mUid, callerIdentity.mPackageName);
- } else {
- mGnssMeasurementsProvider.removeListener(
- IGnssMeasurementsListener.Stub.asInterface(entry.getKey()));
- }
- }
- }
-
- for (Entry<IBinder, Identity> entry : mGnssNavigationMessageListeners.entrySet()) {
- Identity callerIdentity = entry.getValue();
- if (callerIdentity.mUid == uid) {
- if (D) {
- Log.d(TAG, "gnss navigation message listener from uid "
- + uid + " is now "
- + (foreground ? "foreground" : "background)"));
- }
- if (foreground || isThrottlingExemptLocked(entry.getValue())) {
- mGnssNavigationMessageProvider.addListener(
- IGnssNavigationMessageListener.Stub.asInterface(entry.getKey()),
- callerIdentity.mUid, callerIdentity.mPackageName);
- } else {
- mGnssNavigationMessageProvider.removeListener(
- IGnssNavigationMessageListener.Stub.asInterface(entry.getKey()));
- }
- }
+ // TODO(b/120449926): The GNSS status listeners should be handled similar to the above.
}
}
@@ -543,33 +489,31 @@
return importance <= FOREGROUND_IMPORTANCE_CUTOFF;
}
- private void onBackgroundThrottleIntervalChanged() {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
- for (LocationProvider provider : mProviders) {
- applyRequirements(provider.getName());
- }
- }
-
- private void onBackgroundThrottleWhitelistChanged() {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
- updateBackgroundThrottlingWhitelist();
- for (LocationProvider provider : mProviders) {
- applyRequirements(provider.getName());
- }
- }
-
+ /**
+ * Makes a list of userids that are related to the current user. This is
+ * relevant when using managed profiles. Otherwise the list only contains
+ * the current user.
+ *
+ * @param currentUserId the current user, who might have an alter-ego.
+ */
private void updateUserProfiles(int currentUserId) {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
- mCurrentUserProfiles = mUserManager.getProfileIdsWithDisabled(currentUserId);
+ int[] profileIds = mUserManager.getProfileIdsWithDisabled(currentUserId);
+ synchronized (mLock) {
+ mCurrentUserProfiles = profileIds;
+ }
}
+ /**
+ * Checks if the specified userId matches any of the current foreground
+ * users stored in mCurrentUserProfiles.
+ */
private boolean isCurrentProfile(int userId) {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
- return ArrayUtils.contains(mCurrentUserProfiles, userId);
+ synchronized (mLock) {
+ return ArrayUtils.contains(mCurrentUserProfiles, userId);
+ }
}
private void ensureFallbackFusedProviderPresentLocked(String[] pkgs) {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
PackageManager pm = mContext.getPackageManager();
String systemPackageName = mContext.getPackageName();
ArrayList<HashSet<Signature>> sigSets = ServiceWatcher.getSignatureSets(mContext, pkgs);
@@ -640,13 +584,12 @@
}
private void loadProvidersLocked() {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
// create a passive location provider, which is always enabled
LocationProvider passiveProviderManager = new LocationProvider(
LocationManager.PASSIVE_PROVIDER);
PassiveProvider passiveProvider = new PassiveProvider(passiveProviderManager);
- addProvider(passiveProviderManager);
+ addProviderLocked(passiveProviderManager);
mPassiveProvider = passiveProvider;
if (GnssLocationProvider.isSupported()) {
@@ -655,14 +598,14 @@
LocationManager.GPS_PROVIDER);
GnssLocationProvider gnssProvider = new GnssLocationProvider(mContext,
gnssProviderManager,
- mHandler.getLooper());
+ mLocationHandler.getLooper());
mGnssSystemInfoProvider = gnssProvider.getGnssSystemInfoProvider();
mGnssBatchingProvider = gnssProvider.getGnssBatchingProvider();
mGnssMetricsProvider = gnssProvider.getGnssMetricsProvider();
mGnssStatusProvider = gnssProvider.getGnssStatusProvider();
mNetInitiatedListener = gnssProvider.getNetInitiatedListener();
- addProvider(gnssProviderManager);
+ addProviderLocked(gnssProviderManager);
mRealProviders.put(LocationManager.GPS_PROVIDER, gnssProviderManager);
mGnssMeasurementsProvider = gnssProvider.getGnssMeasurementsProvider();
mGnssNavigationMessageProvider = gnssProvider.getGnssNavigationMessageProvider();
@@ -704,7 +647,7 @@
if (networkProvider != null) {
mRealProviders.put(LocationManager.NETWORK_PROVIDER, networkProviderManager);
mProxyProviders.add(networkProvider);
- addProvider(networkProviderManager);
+ addProviderLocked(networkProviderManager);
} else {
Slog.w(TAG, "no network location provider found");
}
@@ -720,7 +663,7 @@
com.android.internal.R.string.config_fusedLocationProviderPackageName,
com.android.internal.R.array.config_locationProviderPackageNames);
if (fusedProvider != null) {
- addProvider(fusedProviderManager);
+ addProviderLocked(fusedProviderManager);
mProxyProviders.add(fusedProvider);
mRealProviders.put(LocationManager.FUSED_PROVIDER, fusedProviderManager);
} else {
@@ -785,22 +728,28 @@
Boolean.parseBoolean(fragments[7]) /* supportsBearing */,
Integer.parseInt(fragments[8]) /* powerRequirement */,
Integer.parseInt(fragments[9]) /* accuracy */);
- addTestProvider(name, properties);
+ addTestProviderLocked(name, properties);
}
}
+ /**
+ * Called when the device's active user changes.
+ *
+ * @param userId the new active user's UserId
+ */
private void switchUser(int userId) {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
if (mCurrentUserId == userId) {
return;
}
mBlacklist.switchUser(userId);
- mHandler.removeMessages(MSG_LOCATION_CHANGED);
- mLastLocation.clear();
- mLastLocationCoarseInterval.clear();
- updateUserProfiles(userId);
- updateProvidersSettings();
- mCurrentUserId = userId;
+ mLocationHandler.removeMessages(MSG_LOCATION_CHANGED);
+ synchronized (mLock) {
+ mLastLocation.clear();
+ mLastLocationCoarseInterval.clear();
+ updateUserProfiles(userId);
+ updateProvidersSettingsLocked();
+ mCurrentUserId = userId;
+ }
}
private static final class Identity {
@@ -859,13 +808,10 @@
}
public void setRequest(ProviderRequest request, WorkSource workSource) {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
mProvider.setRequest(request, workSource);
}
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
-
pw.println(mName + " provider:");
pw.println(" setting=" + mSettingsEnabled);
pw.println(" enabled=" + mEnabled);
@@ -874,38 +820,34 @@
}
public long getStatusUpdateTime() {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
return mProvider.getStatusUpdateTime();
}
public int getStatus(Bundle extras) {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
return mProvider.getStatus(extras);
}
public void sendExtraCommand(String command, Bundle extras) {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
mProvider.sendExtraCommand(command, extras);
}
// called from any thread
@Override
public void onReportLocation(Location location) {
- // no security check necessary because this is coming from an internal-only interface
- runInternal(() -> LocationManagerService.this.handleLocationChanged(location,
+ runOnHandler(() -> LocationManagerService.this.reportLocation(location,
mProvider == mPassiveProvider));
}
// called from any thread
@Override
public void onReportLocation(List<Location> locations) {
- LocationManagerService.this.reportLocationBatch(locations);
+ runOnHandler(() -> LocationManagerService.this.reportLocationBatch(locations));
}
// called from any thread
@Override
public void onSetEnabled(boolean enabled) {
- runInternal(() -> {
+ runOnHandler(() -> {
if (enabled == mEnabled) {
return;
}
@@ -930,15 +872,17 @@
Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
"-" + LocationManager.FUSED_PROVIDER, mCurrentUserId);
- if (!enabled) {
- // If any provider has been disabled, clear all last locations for all
- // providers. This is to be on the safe side in case a provider has location
- // derived from this disabled provider.
- mLastLocation.clear();
- mLastLocationCoarseInterval.clear();
- }
+ synchronized (mLock) {
+ if (!enabled) {
+ // If any provider has been disabled, clear all last locations for all
+ // providers. This is to be on the safe side in case a provider has location
+ // derived from this disabled provider.
+ mLastLocation.clear();
+ mLastLocationCoarseInterval.clear();
+ }
- updateProviderListeners(mName);
+ updateProviderListenersLocked(mName);
+ }
mContext.sendBroadcastAsUser(new Intent(LocationManager.PROVIDERS_CHANGED_ACTION),
UserHandle.ALL);
@@ -947,27 +891,34 @@
@Override
public void onSetProperties(ProviderProperties properties) {
- runInternal(() -> {
- mProperties = properties;
- });
+ runOnHandler(() -> mProperties = properties);
}
private void setSettingsEnabled(boolean enabled) {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
- if (mSettingsEnabled == enabled) {
- return;
- }
+ synchronized (mLock) {
+ if (mSettingsEnabled == enabled) {
+ return;
+ }
- mSettingsEnabled = enabled;
- if (!mSettingsEnabled) {
- // if any provider has been disabled, clear all last locations for all
- // providers. this is to be on the safe side in case a provider has location
- // derived from this disabled provider.
- mLastLocation.clear();
- mLastLocationCoarseInterval.clear();
- updateProviderListeners(mName);
- } else if (mEnabled) {
- updateProviderListeners(mName);
+ mSettingsEnabled = enabled;
+ if (!mSettingsEnabled) {
+ // if any provider has been disabled, clear all last locations for all
+ // providers. this is to be on the safe side in case a provider has location
+ // derived from this disabled provider.
+ mLastLocation.clear();
+ mLastLocationCoarseInterval.clear();
+ updateProviderListenersLocked(mName);
+ } else if (mEnabled) {
+ updateProviderListenersLocked(mName);
+ }
+ }
+ }
+
+ private void runOnHandler(Runnable runnable) {
+ if (Looper.myLooper() == mLocationHandler.getLooper()) {
+ runnable.run();
+ } else {
+ mLocationHandler.post(runnable);
}
}
}
@@ -1071,7 +1022,7 @@
// See if receiver has any enabled update records. Also note if any update records
// are high power (has a high power provider with an interval under a threshold).
for (UpdateRecord updateRecord : mUpdateRecords.values()) {
- if (isAllowedByUserSettingsForUser(updateRecord.mProvider,
+ if (isAllowedByUserSettingsLockedForUser(updateRecord.mProvider,
mCurrentUserId)) {
requestingLocation = true;
LocationManagerService.LocationProvider locationProvider
@@ -1171,7 +1122,7 @@
synchronized (this) {
// synchronize to ensure incrementPendingBroadcastsLocked()
// is called before decrementPendingBroadcasts()
- mPendingIntent.send(mContext, 0, statusChanged, this, mHandler,
+ mPendingIntent.send(mContext, 0, statusChanged, this, mLocationHandler,
getResolutionPermission(mAllowedResolutionLevel),
PendingIntentUtils.createDontSendToRestrictedAppsBundle(null));
// call this after broadcasting so we do not increment
@@ -1207,7 +1158,7 @@
synchronized (this) {
// synchronize to ensure incrementPendingBroadcastsLocked()
// is called before decrementPendingBroadcasts()
- mPendingIntent.send(mContext, 0, locationChanged, this, mHandler,
+ mPendingIntent.send(mContext, 0, locationChanged, this, mLocationHandler,
getResolutionPermission(mAllowedResolutionLevel),
PendingIntentUtils.createDontSendToRestrictedAppsBundle(null));
// call this after broadcasting so we do not increment
@@ -1250,7 +1201,7 @@
synchronized (this) {
// synchronize to ensure incrementPendingBroadcastsLocked()
// is called before decrementPendingBroadcasts()
- mPendingIntent.send(mContext, 0, providerIntent, this, mHandler,
+ mPendingIntent.send(mContext, 0, providerIntent, this, mLocationHandler,
getResolutionPermission(mAllowedResolutionLevel),
PendingIntentUtils.createDontSendToRestrictedAppsBundle(null));
// call this after broadcasting so we do not increment
@@ -1268,12 +1219,12 @@
public void binderDied() {
if (D) Log.d(TAG, "Location listener died");
- runInternal(() -> {
- removeUpdates(this);
- synchronized (this) {
- clearPendingBroadcastsLocked();
- }
- });
+ synchronized (mLock) {
+ removeUpdatesLocked(this);
+ }
+ synchronized (this) {
+ clearPendingBroadcastsLocked();
+ }
}
@Override
@@ -1310,22 +1261,28 @@
}
@Override
- public void locationCallbackFinished(ILocationListener listener) throws RemoteException {
+ public void locationCallbackFinished(ILocationListener listener) {
//Do not use getReceiverLocked here as that will add the ILocationListener to
//the receiver list if it is not found. If it is not found then the
//LocationListener was removed when it had a pending broadcast and should
//not be added back.
- runFromBinderBlocking(() -> {
+ synchronized (mLock) {
IBinder binder = listener.asBinder();
Receiver receiver = mReceivers.get(binder);
if (receiver != null) {
synchronized (receiver) {
+ // so wakelock calls will succeed
+ long identity = Binder.clearCallingIdentity();
receiver.decrementPendingBroadcastsLocked();
+ Binder.restoreCallingIdentity(identity);
}
}
- });
+ }
}
+ /**
+ * Returns the year of the GNSS hardware.
+ */
@Override
public int getGnssYearOfHardware() {
if (mGnssSystemInfoProvider != null) {
@@ -1335,6 +1292,10 @@
}
}
+
+ /**
+ * Returns the model name of the GNSS hardware.
+ */
@Override
@Nullable
public String getGnssHardwareModelName() {
@@ -1345,6 +1306,10 @@
}
}
+ /**
+ * Runs some checks for GNSS (FINE) level permissions, used by several methods which directly
+ * (try to) access GNSS information at this layer.
+ */
private boolean hasGnssPermissions(String packageName) {
int allowedResolutionLevel = getCallerAllowedResolutionLevel();
checkResolutionLevelIsSufficientForProviderUse(
@@ -1364,6 +1329,9 @@
return hasLocationAccess;
}
+ /**
+ * Returns the GNSS batching size, if available.
+ */
@Override
public int getGnssBatchSize(String packageName) {
mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
@@ -1376,6 +1344,10 @@
}
}
+ /**
+ * Adds a callback for GNSS Batching events, if permissions allow, which are transported
+ * to potentially multiple listeners by the BatchedLocationCallbackTransport above this.
+ */
@Override
public boolean addGnssBatchingCallback(IBatchedLocationCallback callback, String packageName) {
mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
@@ -1419,6 +1391,9 @@
}
}
+ /**
+ * Removes callback for GNSS batching
+ */
@Override
public void removeGnssBatchingCallback() {
try {
@@ -1433,6 +1408,10 @@
mGnssBatchingDeathCallback = null;
}
+
+ /**
+ * Starts GNSS batching, if available.
+ */
@Override
public boolean startGnssBatch(long periodNanos, boolean wakeOnFifoFull, String packageName) {
mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
@@ -1453,6 +1432,9 @@
return mGnssBatchingProvider.start(periodNanos, wakeOnFifoFull);
}
+ /**
+ * Flushes a GNSS batch in progress
+ */
@Override
public void flushGnssBatch(String packageName) {
mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
@@ -1472,6 +1454,9 @@
}
}
+ /**
+ * Stops GNSS batching
+ */
@Override
public boolean stopGnssBatch() {
mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
@@ -1490,7 +1475,7 @@
checkCallerIsProvider();
// Currently used only for GNSS locations - update permissions check if changed
- if (isAllowedByUserSettingsForUser(LocationManager.GPS_PROVIDER, mCurrentUserId)) {
+ if (isAllowedByUserSettingsLockedForUser(LocationManager.GPS_PROVIDER, mCurrentUserId)) {
if (mGnssBatchingCallback == null) {
Slog.e(TAG, "reportLocationBatch() called without active Callback");
return;
@@ -1505,28 +1490,35 @@
}
}
- private void addProvider(LocationProvider provider) {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+ private void addProviderLocked(LocationProvider provider) {
mProviders.add(provider);
mProvidersByName.put(provider.getName(), provider);
}
- private void removeProvider(LocationProvider provider) {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+ private void removeProviderLocked(LocationProvider provider) {
mProviders.remove(provider);
mProvidersByName.remove(provider.getName());
}
- private boolean isAllowedByUserSettingsForUser(String provider, int userId) {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+ /**
+ * Returns "true" if access to the specified location provider is allowed by the specified
+ * user's settings. Access to all location providers is forbidden to non-location-provider
+ * processes belonging to background users.
+ *
+ * @param provider the name of the location provider
+ * @param userId the user id to query
+ */
+ private boolean isAllowedByUserSettingsLockedForUser(String provider, int userId) {
if (LocationManager.PASSIVE_PROVIDER.equals(provider)) {
- return isLocationEnabledForUserInternal(userId);
+ return isLocationEnabledForUser(userId);
}
if (LocationManager.FUSED_PROVIDER.equals(provider)) {
- return isLocationEnabledForUserInternal(userId);
+ return isLocationEnabledForUser(userId);
}
- if (mMockProviders.containsKey(provider)) {
- return isLocationEnabledForUserInternal(userId);
+ synchronized (mLock) {
+ if (mMockProviders.containsKey(provider)) {
+ return isLocationEnabledForUser(userId);
+ }
}
long identity = Binder.clearCallingIdentity();
@@ -1541,14 +1533,29 @@
}
}
- private boolean isAllowedByUserSettings(String provider, int uid, int userId) {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+
+ /**
+ * Returns "true" if access to the specified location provider is allowed by the specified
+ * user's settings. Access to all location providers is forbidden to non-location-provider
+ * processes belonging to background users.
+ *
+ * @param provider the name of the location provider
+ * @param uid the requestor's UID
+ * @param userId the user id to query
+ */
+ private boolean isAllowedByUserSettingsLocked(String provider, int uid, int userId) {
if (!isCurrentProfile(UserHandle.getUserId(uid)) && !isUidALocationProvider(uid)) {
return false;
}
- return isAllowedByUserSettingsForUser(provider, userId);
+ return isAllowedByUserSettingsLockedForUser(provider, userId);
}
+ /**
+ * Returns the permission string associated with the specified resolution level.
+ *
+ * @param resolutionLevel the resolution level
+ * @return the permission string
+ */
private String getResolutionPermission(int resolutionLevel) {
switch (resolutionLevel) {
case RESOLUTION_LEVEL_FINE:
@@ -1560,6 +1567,13 @@
}
}
+ /**
+ * Returns the resolution level allowed to the given PID/UID pair.
+ *
+ * @param pid the PID
+ * @param uid the UID
+ * @return resolution level allowed to the pid/uid pair
+ */
private int getAllowedResolutionLevel(int pid, int uid) {
if (mContext.checkPermission(android.Manifest.permission.ACCESS_FINE_LOCATION,
pid, uid) == PERMISSION_GRANTED) {
@@ -1572,16 +1586,32 @@
}
}
+ /**
+ * Returns the resolution level allowed to the caller
+ *
+ * @return resolution level allowed to caller
+ */
private int getCallerAllowedResolutionLevel() {
return getAllowedResolutionLevel(Binder.getCallingPid(), Binder.getCallingUid());
}
+ /**
+ * Throw SecurityException if specified resolution level is insufficient to use geofences.
+ *
+ * @param allowedResolutionLevel resolution level allowed to caller
+ */
private void checkResolutionLevelIsSufficientForGeofenceUse(int allowedResolutionLevel) {
if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
throw new SecurityException("Geofence usage requires ACCESS_FINE_LOCATION permission");
}
}
+ /**
+ * Return the minimum resolution level required to use the specified location provider.
+ *
+ * @param provider the name of the location provider
+ * @return minimum resolution level required for provider
+ */
private int getMinimumResolutionLevelForProviderUse(String provider) {
if (LocationManager.GPS_PROVIDER.equals(provider) ||
LocationManager.PASSIVE_PROVIDER.equals(provider)) {
@@ -1613,6 +1643,13 @@
return RESOLUTION_LEVEL_FINE; // if in doubt, require FINE
}
+ /**
+ * Throw SecurityException if specified resolution level is insufficient to use the named
+ * location provider.
+ *
+ * @param allowedResolutionLevel resolution level allowed to caller
+ * @param providerName the name of the location provider
+ */
private void checkResolutionLevelIsSufficientForProviderUse(int allowedResolutionLevel,
String providerName) {
int requiredResolutionLevel = getMinimumResolutionLevelForProviderUse(providerName);
@@ -1631,6 +1668,20 @@
}
}
+ /**
+ * Throw SecurityException if WorkSource use is not allowed (i.e. can't blame other packages
+ * for battery).
+ */
+ private void checkDeviceStatsAllowed() {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.UPDATE_DEVICE_STATS, null);
+ }
+
+ private void checkUpdateAppOpsAllowed() {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.UPDATE_APP_OPS_STATS, null);
+ }
+
public static int resolutionLevelToOp(int allowedResolutionLevel) {
if (allowedResolutionLevel != RESOLUTION_LEVEL_NONE) {
if (allowedResolutionLevel == RESOLUTION_LEVEL_COARSE) {
@@ -1688,15 +1739,19 @@
*/
@Override
public List<String> getAllProviders() {
- ArrayList<String> providers = new ArrayList<>(mProviders.size());
- for (LocationProvider provider : mProviders) {
- String name = provider.getName();
- if (LocationManager.FUSED_PROVIDER.equals(name)) {
- continue;
+ ArrayList<String> out;
+ synchronized (mLock) {
+ out = new ArrayList<>(mProviders.size());
+ for (LocationProvider provider : mProviders) {
+ String name = provider.getName();
+ if (LocationManager.FUSED_PROVIDER.equals(name)) {
+ continue;
+ }
+ out.add(name);
}
- providers.add(name);
}
- return providers;
+ if (D) Log.d(TAG, "getAllProviders()=" + out);
+ return out;
}
/**
@@ -1705,33 +1760,39 @@
* Can return passive provider, but never returns fused provider.
*/
@Override
- public List<String> getProviders(Criteria criteria, boolean enabledOnly)
- throws RemoteException {
+ public List<String> getProviders(Criteria criteria, boolean enabledOnly) {
int allowedResolutionLevel = getCallerAllowedResolutionLevel();
+ ArrayList<String> out;
int uid = Binder.getCallingUid();
- return runFromBinderBlocking(() -> {
- ArrayList<String> providers = new ArrayList<>(mProviders.size());
- for (LocationProvider provider : mProviders) {
- String name = provider.getName();
- if (LocationManager.FUSED_PROVIDER.equals(name)) {
- continue;
+ long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ out = new ArrayList<>(mProviders.size());
+ for (LocationProvider provider : mProviders) {
+ String name = provider.getName();
+ if (LocationManager.FUSED_PROVIDER.equals(name)) {
+ continue;
+ }
+ if (allowedResolutionLevel >= getMinimumResolutionLevelForProviderUse(name)) {
+ if (enabledOnly
+ && !isAllowedByUserSettingsLocked(name, uid, mCurrentUserId)) {
+ continue;
+ }
+ if (criteria != null
+ && !android.location.LocationProvider.propertiesMeetCriteria(
+ name, provider.getProperties(), criteria)) {
+ continue;
+ }
+ out.add(name);
+ }
}
- if (allowedResolutionLevel < getMinimumResolutionLevelForProviderUse(name)) {
- continue;
- }
- if (enabledOnly
- && !isAllowedByUserSettings(name, uid, mCurrentUserId)) {
- continue;
- }
- if (criteria != null
- && !android.location.LocationProvider.propertiesMeetCriteria(
- name, provider.getProperties(), criteria)) {
- continue;
- }
- providers.add(name);
}
- return providers;
- });
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+
+ if (D) Log.d(TAG, "getProviders()=" + out);
+ return out;
}
/**
@@ -1742,51 +1803,59 @@
* some simplified logic.
*/
@Override
- public String getBestProvider(Criteria criteria, boolean enabledOnly) throws RemoteException {
+ public String getBestProvider(Criteria criteria, boolean enabledOnly) {
+ String result;
+
List<String> providers = getProviders(criteria, enabledOnly);
- if (providers.isEmpty()) {
- providers = getProviders(null, enabledOnly);
- }
-
if (!providers.isEmpty()) {
- if (providers.contains(LocationManager.GPS_PROVIDER)) {
- return LocationManager.GPS_PROVIDER;
- } else if (providers.contains(LocationManager.NETWORK_PROVIDER)) {
- return LocationManager.NETWORK_PROVIDER;
- } else {
- return providers.get(0);
- }
+ result = pickBest(providers);
+ if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + result);
+ return result;
+ }
+ providers = getProviders(null, enabledOnly);
+ if (!providers.isEmpty()) {
+ result = pickBest(providers);
+ if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + result);
+ return result;
}
+ if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + null);
return null;
}
- @Override
- public boolean providerMeetsCriteria(String provider, Criteria criteria)
- throws RemoteException {
- return runFromBinderBlocking(() -> {
- LocationProvider p = mProvidersByName.get(provider);
- if (p == null) {
- throw new IllegalArgumentException("provider=" + provider);
- }
-
- return android.location.LocationProvider.propertiesMeetCriteria(
- p.getName(), p.getProperties(), criteria);
- });
+ private String pickBest(List<String> providers) {
+ if (providers.contains(LocationManager.GPS_PROVIDER)) {
+ return LocationManager.GPS_PROVIDER;
+ } else if (providers.contains(LocationManager.NETWORK_PROVIDER)) {
+ return LocationManager.NETWORK_PROVIDER;
+ } else {
+ return providers.get(0);
+ }
}
- private void updateProvidersSettings() {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+ @Override
+ public boolean providerMeetsCriteria(String provider, Criteria criteria) {
+ LocationProvider p = mProvidersByName.get(provider);
+ if (p == null) {
+ throw new IllegalArgumentException("provider=" + provider);
+ }
+
+ boolean result = android.location.LocationProvider.propertiesMeetCriteria(
+ p.getName(), p.getProperties(), criteria);
+ if (D) Log.d(TAG, "providerMeetsCriteria(" + provider + ", " + criteria + ")=" + result);
+ return result;
+ }
+
+ private void updateProvidersSettingsLocked() {
for (LocationProvider p : mProviders) {
- p.setSettingsEnabled(isAllowedByUserSettingsForUser(p.getName(), mCurrentUserId));
+ p.setSettingsEnabled(isAllowedByUserSettingsLockedForUser(p.getName(), mCurrentUserId));
}
mContext.sendBroadcastAsUser(new Intent(LocationManager.MODE_CHANGED_ACTION),
UserHandle.ALL);
}
- private void updateProviderListeners(String provider) {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+ private void updateProviderListenersLocked(String provider) {
LocationProvider p = mProvidersByName.get(provider);
if (p == null) return;
@@ -1811,15 +1880,14 @@
if (deadReceivers != null) {
for (int i = deadReceivers.size() - 1; i >= 0; i--) {
- removeUpdates(deadReceivers.get(i));
+ removeUpdatesLocked(deadReceivers.get(i));
}
}
- applyRequirements(provider);
+ applyRequirementsLocked(provider);
}
- private void applyRequirements(String provider) {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+ private void applyRequirementsLocked(String provider) {
LocationProvider p = mProvidersByName.get(provider);
if (p == null) return;
@@ -1925,13 +1993,14 @@
}
@Override
- public String[] getBackgroundThrottlingWhitelist() throws RemoteException {
- return runFromBinderBlocking(
- () -> mBackgroundThrottlePackageWhitelist.toArray(new String[0]));
+ public String[] getBackgroundThrottlingWhitelist() {
+ synchronized (mLock) {
+ return mBackgroundThrottlePackageWhitelist.toArray(
+ new String[0]);
+ }
}
- private void updateBackgroundThrottlingWhitelist() {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+ private void updateBackgroundThrottlingWhitelistLocked() {
String setting = Settings.Global.getString(
mContext.getContentResolver(),
Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST);
@@ -1946,8 +2015,15 @@
Arrays.asList(setting.split(",")));
}
+ private void updateLastLocationMaxAgeLocked() {
+ mLastLocationMaxAgeMs =
+ Settings.Global.getLong(
+ mContext.getContentResolver(),
+ Settings.Global.LOCATION_LAST_LOCATION_MAX_AGE_MILLIS,
+ DEFAULT_LAST_LOCATION_MAX_AGE_MS);
+ }
+
private boolean isThrottlingExemptLocked(Identity identity) {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
if (identity.mUid == Process.SYSTEM_UID) {
return true;
}
@@ -2029,7 +2105,7 @@
// and also remove the Receiver if it has no more update records
if (receiverRecords.size() == 0) {
- removeUpdates(mReceiver);
+ removeUpdatesLocked(mReceiver);
}
}
@@ -2043,9 +2119,8 @@
}
}
- private Receiver getReceiver(ILocationListener listener, int pid, int uid,
+ private Receiver getReceiverLocked(ILocationListener listener, int pid, int uid,
String packageName, WorkSource workSource, boolean hideFromAppOps) {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
IBinder binder = listener.asBinder();
Receiver receiver = mReceivers.get(binder);
if (receiver == null) {
@@ -2062,9 +2137,8 @@
return receiver;
}
- private Receiver getReceiver(PendingIntent intent, int pid, int uid, String packageName,
+ private Receiver getReceiverLocked(PendingIntent intent, int pid, int uid, String packageName,
WorkSource workSource, boolean hideFromAppOps) {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
Receiver receiver = mReceivers.get(intent);
if (receiver == null) {
receiver = new Receiver(null, intent, pid, uid, packageName, workSource,
@@ -2128,9 +2202,29 @@
throw new SecurityException("invalid package name: " + packageName);
}
+ private void checkPendingIntent(PendingIntent intent) {
+ if (intent == null) {
+ throw new IllegalArgumentException("invalid pending intent: " + null);
+ }
+ }
+
+ private Receiver checkListenerOrIntentLocked(ILocationListener listener, PendingIntent intent,
+ int pid, int uid, String packageName, WorkSource workSource, boolean hideFromAppOps) {
+ if (intent == null && listener == null) {
+ throw new IllegalArgumentException("need either listener or intent");
+ } else if (intent != null && listener != null) {
+ throw new IllegalArgumentException("cannot register both listener and intent");
+ } else if (intent != null) {
+ checkPendingIntent(intent);
+ return getReceiverLocked(intent, pid, uid, packageName, workSource, hideFromAppOps);
+ } else {
+ return getReceiverLocked(listener, pid, uid, packageName, workSource, hideFromAppOps);
+ }
+ }
+
@Override
public void requestLocationUpdates(LocationRequest request, ILocationListener listener,
- PendingIntent intent, String packageName) throws RemoteException {
+ PendingIntent intent, String packageName) {
if (request == null) request = DEFAULT_LOCATION_REQUEST;
checkPackageName(packageName);
int allowedResolutionLevel = getCallerAllowedResolutionLevel();
@@ -2138,13 +2232,11 @@
request.getProvider());
WorkSource workSource = request.getWorkSource();
if (workSource != null && !workSource.isEmpty()) {
- mContext.enforceCallingOrSelfPermission(
- Manifest.permission.UPDATE_DEVICE_STATS, null);
+ checkDeviceStatsAllowed();
}
boolean hideFromAppOps = request.getHideFromAppOps();
if (hideFromAppOps) {
- mContext.enforceCallingOrSelfPermission(
- Manifest.permission.UPDATE_APP_OPS_STATS, null);
+ checkUpdateAppOpsAllowed();
}
boolean callerHasLocationHardwarePermission =
mContext.checkCallingPermission(android.Manifest.permission.LOCATION_HARDWARE)
@@ -2154,33 +2246,25 @@
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
+ // providers may use public location API's, need to clear identity
+ long identity = Binder.clearCallingIdentity();
+ try {
+ // We don't check for MODE_IGNORED here; we will do that when we go to deliver
+ // a location.
+ checkLocationAccess(pid, uid, packageName, allowedResolutionLevel);
- // We don't check for MODE_IGNORED here; we will do that when we go to deliver
- // a location.
- checkLocationAccess(pid, uid, packageName, allowedResolutionLevel);
-
- if (intent == null && listener == null) {
- throw new IllegalArgumentException("need either listener or intent");
- } else if (intent != null && listener != null) {
- throw new IllegalArgumentException("cannot register both listener and intent");
- }
-
- runFromBinderBlocking(() -> {
- Receiver receiver;
- if (intent != null) {
- receiver = getReceiver(intent, pid, uid, packageName, workSource,
- hideFromAppOps);
- } else {
- receiver = getReceiver(listener, pid, uid, packageName, workSource,
- hideFromAppOps);
+ synchronized (mLock) {
+ Receiver recevier = checkListenerOrIntentLocked(listener, intent, pid, uid,
+ packageName, workSource, hideFromAppOps);
+ requestLocationUpdatesLocked(sanitizedRequest, recevier, uid, packageName);
}
- requestLocationUpdates(sanitizedRequest, receiver, uid, packageName);
- });
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
- private void requestLocationUpdates(LocationRequest request, Receiver receiver,
+ private void requestLocationUpdatesLocked(LocationRequest request, Receiver receiver,
int uid, String packageName) {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
// Figure out the provider. Either its explicitly request (legacy use cases), or
// use the fused provider
if (request == null) request = DEFAULT_LOCATION_REQUEST;
@@ -2209,7 +2293,7 @@
}
if (provider.isEnabled()) {
- applyRequirements(name);
+ applyRequirementsLocked(name);
} else {
// Notify the listener that updates are currently disabled
receiver.callProviderEnabledLocked(name, false);
@@ -2221,31 +2305,27 @@
@Override
public void removeUpdates(ILocationListener listener, PendingIntent intent,
- String packageName) throws RemoteException {
+ String packageName) {
checkPackageName(packageName);
- int pid = Binder.getCallingPid();
- int uid = Binder.getCallingUid();
+ final int pid = Binder.getCallingPid();
+ final int uid = Binder.getCallingUid();
- if (intent == null && listener == null) {
- throw new IllegalArgumentException("need either listener or intent");
- } else if (intent != null && listener != null) {
- throw new IllegalArgumentException("cannot register both listener and intent");
- }
+ synchronized (mLock) {
+ Receiver receiver = checkListenerOrIntentLocked(listener, intent, pid, uid,
+ packageName, null, false);
- runFromBinderBlocking(() -> {
- Receiver receiver;
- if (intent != null) {
- receiver = getReceiver(intent, pid, uid, packageName, null, false);
- } else {
- receiver = getReceiver(listener, pid, uid, packageName, null, false);
+ // providers may use public location API's, need to clear identity
+ long identity = Binder.clearCallingIdentity();
+ try {
+ removeUpdatesLocked(receiver);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
- removeUpdates(receiver);
- });
+ }
}
- private void removeUpdates(Receiver receiver) {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+ private void removeUpdatesLocked(Receiver receiver) {
if (D) Log.i(TAG, "remove " + Integer.toHexString(System.identityHashCode(receiver)));
if (mReceivers.remove(receiver.mKey) != null && receiver.isListener()) {
@@ -2272,14 +2352,20 @@
// update provider
for (String provider : providers) {
- applyRequirements(provider);
+ applyRequirementsLocked(provider);
+ }
+ }
+
+ private void applyAllProviderRequirementsLocked() {
+ for (LocationProvider p : mProviders) {
+ applyRequirementsLocked(p.getName());
}
}
@Override
- public Location getLastLocation(LocationRequest r, String packageName) throws RemoteException {
- if (D) Log.d(TAG, "getLastLocation: " + r);
- LocationRequest request = r != null ? r : DEFAULT_LOCATION_REQUEST;
+ public Location getLastLocation(LocationRequest request, String packageName) {
+ if (D) Log.d(TAG, "getLastLocation: " + request);
+ if (request == null) request = DEFAULT_LOCATION_REQUEST;
int allowedResolutionLevel = getCallerAllowedResolutionLevel();
checkPackageName(packageName);
checkResolutionLevelIsSufficientForProviderUse(allowedResolutionLevel,
@@ -2305,60 +2391,68 @@
}
return null;
}
+
+ synchronized (mLock) {
+ // Figure out the provider. Either its explicitly request (deprecated API's),
+ // or use the fused provider
+ String name = request.getProvider();
+ if (name == null) name = LocationManager.FUSED_PROVIDER;
+ LocationProvider provider = mProvidersByName.get(name);
+ if (provider == null) return null;
+
+ if (!isAllowedByUserSettingsLocked(name, uid, mCurrentUserId)) return null;
+
+ Location location;
+ if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
+ // Make sure that an app with coarse permissions can't get frequent location
+ // updates by calling LocationManager.getLastKnownLocation repeatedly.
+ location = mLastLocationCoarseInterval.get(name);
+ } else {
+ location = mLastLocation.get(name);
+ }
+ if (location == null) {
+ return null;
+ }
+
+ // Don't return stale location to apps with foreground-only location permission.
+ String op = resolutionLevelToOpStr(allowedResolutionLevel);
+ long locationAgeMs = SystemClock.elapsedRealtime() -
+ location.getElapsedRealtimeNanos() / NANOS_PER_MILLI;
+ if ((locationAgeMs > mLastLocationMaxAgeMs)
+ && (mAppOps.unsafeCheckOp(op, uid, packageName)
+ == AppOpsManager.MODE_FOREGROUND)) {
+ return null;
+ }
+
+ if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
+ Location noGPSLocation = location.getExtraLocation(
+ Location.EXTRA_NO_GPS_LOCATION);
+ if (noGPSLocation != null) {
+ return new Location(mLocationFudger.getOrCreate(noGPSLocation));
+ }
+ } else {
+ return new Location(location);
+ }
+ }
+ return null;
} finally {
Binder.restoreCallingIdentity(identity);
}
-
- return runFromBinderBlocking(() -> {
- // Figure out the provider. Either its explicitly request (deprecated API's),
- // or use the fused provider
- String name = request.getProvider();
- if (name == null) name = LocationManager.FUSED_PROVIDER;
- LocationProvider provider = mProvidersByName.get(name);
- if (provider == null) return null;
-
- if (!isAllowedByUserSettings(name, uid, mCurrentUserId)) return null;
-
- Location location;
- if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
- // Make sure that an app with coarse permissions can't get frequent location
- // updates by calling LocationManager.getLastKnownLocation repeatedly.
- location = mLastLocationCoarseInterval.get(name);
- } else {
- location = mLastLocation.get(name);
- }
- if (location == null) {
- return null;
- }
-
- // Don't return stale location to apps with foreground-only location permission.
- String op = resolutionLevelToOpStr(allowedResolutionLevel);
- long locationAgeMs = SystemClock.elapsedRealtime()
- - location.getElapsedRealtimeNanos() / NANOS_PER_MILLI;
- if ((locationAgeMs > Settings.Global.getLong(
- mContext.getContentResolver(),
- Settings.Global.LOCATION_LAST_LOCATION_MAX_AGE_MILLIS,
- DEFAULT_LAST_LOCATION_MAX_AGE_MS))
- && (mAppOps.unsafeCheckOp(op, uid, packageName)
- == AppOpsManager.MODE_FOREGROUND)) {
- return null;
- }
-
- if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
- Location noGPSLocation = location.getExtraLocation(
- Location.EXTRA_NO_GPS_LOCATION);
- if (noGPSLocation != null) {
- return new Location(mLocationFudger.getOrCreate(noGPSLocation));
- }
- } else {
- return new Location(location);
- }
- return null;
- });
}
+ /**
+ * Provides an interface to inject and set the last location if location is not available
+ * currently.
+ *
+ * This helps in cases where the product (Cars for example) has saved the last known location
+ * before powering off. This interface lets the client inject the saved location while the GPS
+ * chipset is getting its first fix, there by improving user experience.
+ *
+ * @param location - Location object to inject
+ * @return true if update was successful, false if not
+ */
@Override
- public boolean injectLocation(Location location) throws RemoteException {
+ public boolean injectLocation(Location location) {
mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
"Location Hardware permission not granted to inject location");
mContext.enforceCallingPermission(android.Manifest.permission.ACCESS_FINE_LOCATION,
@@ -2370,31 +2464,29 @@
}
return false;
}
-
- return runFromBinderBlocking(() -> {
- LocationProvider p = null;
- String provider = location.getProvider();
- if (provider != null) {
- p = mProvidersByName.get(provider);
+ LocationProvider p = null;
+ String provider = location.getProvider();
+ if (provider != null) {
+ p = mProvidersByName.get(provider);
+ }
+ if (p == null) {
+ if (D) {
+ Log.d(TAG, "injectLocation(): unknown provider");
}
- if (p == null) {
+ return false;
+ }
+ synchronized (mLock) {
+ if (!isAllowedByUserSettingsLockedForUser(provider, mCurrentUserId)) {
if (D) {
- Log.d(TAG, "injectLocation(): unknown provider");
- }
- return false;
- }
- if (!isAllowedByUserSettingsForUser(provider, mCurrentUserId)) {
- if (D) {
- Log.d(TAG, "Location disabled in Settings for current user:"
- + mCurrentUserId);
+ Log.d(TAG, "Location disabled in Settings for current user:" + mCurrentUserId);
}
return false;
} else {
// NOTE: If last location is already available, location is not injected. If
- // provider's normal source (like a GPS chipset) have already provided an output
+ // provider's normal source (like a GPS chipset) have already provided an output,
// there is no need to inject this location.
if (mLastLocation.get(provider) == null) {
- updateLastLocation(location, provider);
+ updateLastLocationLocked(location, provider);
} else {
if (D) {
Log.d(TAG, "injectLocation(): Location exists. Not updating");
@@ -2402,8 +2494,8 @@
return false;
}
}
- return true;
- });
+ }
+ return true;
}
@Override
@@ -2412,9 +2504,7 @@
if (request == null) request = DEFAULT_LOCATION_REQUEST;
int allowedResolutionLevel = getCallerAllowedResolutionLevel();
checkResolutionLevelIsSufficientForGeofenceUse(allowedResolutionLevel);
- if (intent == null) {
- throw new IllegalArgumentException("invalid pending intent: " + null);
- }
+ checkPendingIntent(intent);
checkPackageName(packageName);
checkResolutionLevelIsSufficientForProviderUse(allowedResolutionLevel,
request.getProvider());
@@ -2425,10 +2515,7 @@
LocationRequest sanitizedRequest = createSanitizedRequest(request, allowedResolutionLevel,
callerHasLocationHardwarePermission);
- if (D) {
- Log.d(TAG, "requestGeofence: " + sanitizedRequest + " " + geofence + " "
- + intent);
- }
+ if (D) Log.d(TAG, "requestGeofence: " + sanitizedRequest + " " + geofence + " " + intent);
// geo-fence manager uses the public location API, need to clear identity
int uid = Binder.getCallingUid();
@@ -2449,9 +2536,7 @@
@Override
public void removeGeofence(Geofence geofence, PendingIntent intent, String packageName) {
- if (intent == null) {
- throw new IllegalArgumentException("invalid pending intent: " + null);
- }
+ checkPendingIntent(intent);
checkPackageName(packageName);
if (D) Log.d(TAG, "removeGeofence: " + geofence + " " + intent);
@@ -2483,14 +2568,14 @@
@Override
public boolean addGnssMeasurementsListener(
- IGnssMeasurementsListener listener, String packageName) throws RemoteException {
+ IGnssMeasurementsListener listener, String packageName) {
if (!hasGnssPermissions(packageName) || mGnssMeasurementsProvider == null) {
return false;
}
- Identity callerIdentity =
- new Identity(Binder.getCallingUid(), Binder.getCallingPid(), packageName);
- return runFromBinderBlocking(() -> {
+ synchronized (mLock) {
+ Identity callerIdentity
+ = new Identity(Binder.getCallingUid(), Binder.getCallingPid(), packageName);
// TODO(b/120481270): Register for client death notification and update map.
mGnssMeasurementsListeners.put(listener.asBinder(), callerIdentity);
long identity = Binder.clearCallingIdentity();
@@ -2506,7 +2591,7 @@
}
return true;
- });
+ }
}
@Override
@@ -2534,30 +2619,28 @@
}
@Override
- public void removeGnssMeasurementsListener(IGnssMeasurementsListener listener)
- throws RemoteException {
+ public void removeGnssMeasurementsListener(IGnssMeasurementsListener listener) {
if (mGnssMeasurementsProvider == null) {
return;
}
- runFromBinderBlocking(() -> {
+ synchronized (mLock) {
mGnssMeasurementsListeners.remove(listener.asBinder());
mGnssMeasurementsProvider.removeListener(listener);
- });
+ }
}
@Override
public boolean addGnssNavigationMessageListener(
IGnssNavigationMessageListener listener,
- String packageName) throws RemoteException {
+ String packageName) {
if (!hasGnssPermissions(packageName) || mGnssNavigationMessageProvider == null) {
return false;
}
- Identity callerIdentity =
- new Identity(Binder.getCallingUid(), Binder.getCallingPid(), packageName);
-
- return runFromBinderBlocking(() -> {
+ synchronized (mLock) {
+ Identity callerIdentity
+ = new Identity(Binder.getCallingUid(), Binder.getCallingPid(), packageName);
// TODO(b/120481270): Register for client death notification and update map.
mGnssNavigationMessageListeners.put(listener.asBinder(), callerIdentity);
long identity = Binder.clearCallingIdentity();
@@ -2573,23 +2656,21 @@
}
return true;
- });
- }
-
- @Override
- public void removeGnssNavigationMessageListener(IGnssNavigationMessageListener listener)
- throws RemoteException {
- if (mGnssNavigationMessageProvider != null) {
- runFromBinderBlocking(() -> {
- mGnssNavigationMessageListeners.remove(listener.asBinder());
- mGnssNavigationMessageProvider.removeListener(listener);
- });
}
}
@Override
- public boolean sendExtraCommand(String provider, String command, Bundle extras)
- throws RemoteException {
+ public void removeGnssNavigationMessageListener(IGnssNavigationMessageListener listener) {
+ if (mGnssNavigationMessageProvider != null) {
+ synchronized (mLock) {
+ mGnssNavigationMessageListeners.remove(listener.asBinder());
+ mGnssNavigationMessageProvider.removeListener(listener);
+ }
+ }
+ }
+
+ @Override
+ public boolean sendExtraCommand(String provider, String command, Bundle extras) {
if (provider == null) {
// throw NullPointerException to remain compatible with previous implementation
throw new NullPointerException();
@@ -2603,13 +2684,13 @@
throw new SecurityException("Requires ACCESS_LOCATION_EXTRA_COMMANDS permission");
}
- return runFromBinderBlocking(() -> {
+ synchronized (mLock) {
LocationProvider p = mProvidersByName.get(provider);
if (p == null) return false;
p.sendExtraCommand(command, extras);
return true;
- });
+ }
}
@Override
@@ -2626,181 +2707,251 @@
}
}
+ /**
+ * @return null if the provider does not exist
+ * @throws SecurityException if the provider is not allowed to be
+ * accessed by the caller
+ */
@Override
- public ProviderProperties getProviderProperties(String provider) throws RemoteException {
+ public ProviderProperties getProviderProperties(String provider) {
checkResolutionLevelIsSufficientForProviderUse(getCallerAllowedResolutionLevel(),
provider);
- return runFromBinderBlocking(() -> {
- LocationProvider p = mProvidersByName.get(provider);
- if (p == null) return null;
- return p.getProperties();
- });
+ LocationProvider p;
+ synchronized (mLock) {
+ p = mProvidersByName.get(provider);
+ }
+
+ if (p == null) return null;
+ return p.getProperties();
}
+ /**
+ * @return null if the provider does not exist
+ * @throws SecurityException if the provider is not allowed to be
+ * accessed by the caller
+ */
@Override
- public String getNetworkProviderPackage() throws RemoteException {
- return runFromBinderBlocking(() -> {
- LocationProvider p = mProvidersByName.get(LocationManager.NETWORK_PROVIDER);
+ public String getNetworkProviderPackage() {
+ LocationProvider p;
+ synchronized (mLock) {
+ p = mProvidersByName.get(LocationManager.NETWORK_PROVIDER);
+ }
- if (p == null) {
- return null;
- }
- if (p.mProvider instanceof LocationProviderProxy) {
- return ((LocationProviderProxy) p.mProvider).getConnectedPackageName();
- }
+ if (p == null) {
return null;
- });
+ }
+ if (p.mProvider instanceof LocationProviderProxy) {
+ return ((LocationProviderProxy) p.mProvider).getConnectedPackageName();
+ }
+ return null;
}
@Override
- public void setLocationControllerExtraPackage(String packageName) throws RemoteException {
+ public void setLocationControllerExtraPackage(String packageName) {
mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE,
Manifest.permission.LOCATION_HARDWARE + " permission required");
-
- runFromBinderBlocking(() -> mLocationControllerExtraPackage = packageName);
+ synchronized (mLock) {
+ mLocationControllerExtraPackage = packageName;
+ }
}
@Override
- public String getLocationControllerExtraPackage() throws RemoteException {
- return runFromBinderBlocking(() -> mLocationControllerExtraPackage);
+ public String getLocationControllerExtraPackage() {
+ synchronized (mLock) {
+ return mLocationControllerExtraPackage;
+ }
}
@Override
- public void setLocationControllerExtraPackageEnabled(boolean enabled) throws RemoteException {
+ public void setLocationControllerExtraPackageEnabled(boolean enabled) {
mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE,
Manifest.permission.LOCATION_HARDWARE + " permission required");
- runFromBinderBlocking(() -> mLocationControllerExtraPackageEnabled = enabled);
+ synchronized (mLock) {
+ mLocationControllerExtraPackageEnabled = enabled;
+ }
}
@Override
- public boolean isLocationControllerExtraPackageEnabled() throws RemoteException {
- return runFromBinderBlocking(() -> mLocationControllerExtraPackageEnabled
- && (mLocationControllerExtraPackage != null));
+ public boolean isLocationControllerExtraPackageEnabled() {
+ synchronized (mLock) {
+ return mLocationControllerExtraPackageEnabled
+ && (mLocationControllerExtraPackage != null);
+ }
}
+ /**
+ * Returns the current location enabled/disabled status for a user
+ *
+ * @param userId the id of the user
+ * @return true if location is enabled
+ */
@Override
- public boolean isLocationEnabledForUser(int userId) throws RemoteException {
+ public boolean isLocationEnabledForUser(int userId) {
// Check INTERACT_ACROSS_USERS permission if userId is not current user id.
- if (UserHandle.getCallingUserId() != userId) {
- mContext.enforceCallingOrSelfPermission(
- Manifest.permission.INTERACT_ACROSS_USERS,
- "Requires INTERACT_ACROSS_USERS permission");
- }
+ checkInteractAcrossUsersPermission(userId);
- return runFromBinderBlocking(() -> isLocationEnabledForUserInternal(userId));
+ long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ final String allowedProviders = Settings.Secure.getStringForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+ userId);
+ if (allowedProviders == null) {
+ return false;
+ }
+ final List<String> providerList = Arrays.asList(allowedProviders.split(","));
+ for (String provider : mRealProviders.keySet()) {
+ if (provider.equals(LocationManager.PASSIVE_PROVIDER)
+ || provider.equals(LocationManager.FUSED_PROVIDER)) {
+ continue;
+ }
+ if (providerList.contains(provider)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
- private boolean isLocationEnabledForUserInternal(int userId) {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
-
- final String allowedProviders = Settings.Secure.getStringForUser(
- mContext.getContentResolver(),
- Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
- userId);
- if (allowedProviders == null) {
- return false;
- }
- final List<String> providerList = Arrays.asList(allowedProviders.split(","));
-
- for (String provider : mRealProviders.keySet()) {
- if (provider.equals(LocationManager.PASSIVE_PROVIDER)
- || provider.equals(LocationManager.FUSED_PROVIDER)) {
- continue;
- }
- if (providerList.contains(provider)) {
- return true;
- }
- }
- return false;
- }
-
+ /**
+ * Enable or disable location for a user
+ *
+ * @param enabled true to enable location, false to disable location
+ * @param userId the id of the user
+ */
@Override
- public void setLocationEnabledForUser(boolean enabled, int userId) throws RemoteException {
+ public void setLocationEnabledForUser(boolean enabled, int userId) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.WRITE_SECURE_SETTINGS,
"Requires WRITE_SECURE_SETTINGS permission");
// Check INTERACT_ACROSS_USERS permission if userId is not current user id.
- if (UserHandle.getCallingUserId() != userId) {
- mContext.enforceCallingOrSelfPermission(
- Manifest.permission.INTERACT_ACROSS_USERS,
- "Requires INTERACT_ACROSS_USERS permission");
- }
+ checkInteractAcrossUsersPermission(userId);
- runFromBinderBlocking(() -> {
- final Set<String> allRealProviders = mRealProviders.keySet();
- // Update all providers on device plus gps and network provider when disabling
- // location
- Set<String> allProvidersSet = new ArraySet<>(allRealProviders.size() + 2);
- allProvidersSet.addAll(allRealProviders);
- // When disabling location, disable gps and network provider that could have been
- // enabled by location mode api.
- if (!enabled) {
- allProvidersSet.add(LocationManager.GPS_PROVIDER);
- allProvidersSet.add(LocationManager.NETWORK_PROVIDER);
- }
- if (allProvidersSet.isEmpty()) {
- return;
- }
- // to ensure thread safety, we write the provider name with a '+' or '-'
- // and let the SettingsProvider handle it rather than reading and modifying
- // the list of enabled providers.
- final String prefix = enabled ? "+" : "-";
- StringBuilder locationProvidersAllowed = new StringBuilder();
- for (String provider : allProvidersSet) {
- if (provider.equals(LocationManager.PASSIVE_PROVIDER)
- || provider.equals(LocationManager.FUSED_PROVIDER)) {
- continue;
+ long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ final Set<String> allRealProviders = mRealProviders.keySet();
+ // Update all providers on device plus gps and network provider when disabling
+ // location
+ Set<String> allProvidersSet = new ArraySet<>(allRealProviders.size() + 2);
+ allProvidersSet.addAll(allRealProviders);
+ // When disabling location, disable gps and network provider that could have been
+ // enabled by location mode api.
+ if (!enabled) {
+ allProvidersSet.add(LocationManager.GPS_PROVIDER);
+ allProvidersSet.add(LocationManager.NETWORK_PROVIDER);
}
- locationProvidersAllowed.append(prefix);
- locationProvidersAllowed.append(provider);
- locationProvidersAllowed.append(",");
+ if (allProvidersSet.isEmpty()) {
+ return;
+ }
+ // to ensure thread safety, we write the provider name with a '+' or '-'
+ // and let the SettingsProvider handle it rather than reading and modifying
+ // the list of enabled providers.
+ final String prefix = enabled ? "+" : "-";
+ StringBuilder locationProvidersAllowed = new StringBuilder();
+ for (String provider : allProvidersSet) {
+ if (provider.equals(LocationManager.PASSIVE_PROVIDER)
+ || provider.equals(LocationManager.FUSED_PROVIDER)) {
+ continue;
+ }
+ locationProvidersAllowed.append(prefix);
+ locationProvidersAllowed.append(provider);
+ locationProvidersAllowed.append(",");
+ }
+ // Remove the trailing comma
+ locationProvidersAllowed.setLength(locationProvidersAllowed.length() - 1);
+ Settings.Secure.putStringForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+ locationProvidersAllowed.toString(),
+ userId);
}
- // Remove the trailing comma
- locationProvidersAllowed.setLength(locationProvidersAllowed.length() - 1);
- Settings.Secure.putStringForUser(
- mContext.getContentResolver(),
- Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
- locationProvidersAllowed.toString(),
- userId);
- });
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
+ /**
+ * Returns the current enabled/disabled status of a location provider and user
+ *
+ * @param providerName name of the provider
+ * @param userId the id of the user
+ * @return true if the provider exists and is enabled
+ */
@Override
- public boolean isProviderEnabledForUser(String providerName, int userId)
- throws RemoteException {
+ public boolean isProviderEnabledForUser(String providerName, int userId) {
// Check INTERACT_ACROSS_USERS permission if userId is not current user id.
- if (UserHandle.getCallingUserId() != userId) {
- mContext.enforceCallingOrSelfPermission(
- Manifest.permission.INTERACT_ACROSS_USERS,
- "Requires INTERACT_ACROSS_USERS permission");
+ checkInteractAcrossUsersPermission(userId);
+
+ if (!isLocationEnabledForUser(userId)) {
+ return false;
}
// Fused provider is accessed indirectly via criteria rather than the provider-based APIs,
// so we discourage its use
if (LocationManager.FUSED_PROVIDER.equals(providerName)) return false;
- if (!isLocationEnabledForUser(userId)) {
- return false;
- }
-
- return runFromBinderBlocking(() -> {
- LocationProvider provider = mProvidersByName.get(providerName);
+ long identity = Binder.clearCallingIdentity();
+ try {
+ LocationProvider provider;
+ synchronized (mLock) {
+ provider = mProvidersByName.get(providerName);
+ }
return provider != null && provider.isEnabled();
- });
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
}
+ /**
+ * Enable or disable a single location provider.
+ *
+ * @param provider name of the provider
+ * @param enabled true to enable the provider. False to disable the provider
+ * @param userId the id of the user to set
+ * @return true if the value was set, false on errors
+ */
@Override
public boolean setProviderEnabledForUser(String provider, boolean enabled, int userId) {
return false;
}
+ /**
+ * Method for checking INTERACT_ACROSS_USERS permission if specified user id is not the same as
+ * current user id
+ *
+ * @param userId the user id to get or set value
+ */
+ private void checkInteractAcrossUsersPermission(int userId) {
+ int uid = Binder.getCallingUid();
+ if (UserHandle.getUserId(uid) != userId) {
+ if (ActivityManager.checkComponentPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS, uid, -1, true)
+ != PERMISSION_GRANTED) {
+ throw new SecurityException("Requires INTERACT_ACROSS_USERS permission");
+ }
+ }
+ }
+
+ /**
+ * Returns "true" if the UID belongs to a bound location provider.
+ *
+ * @param uid the uid
+ * @return true if uid belongs to a bound location provider
+ */
private boolean isUidALocationProvider(int uid) {
if (uid == Process.SYSTEM_UID) {
return true;
}
-
+ if (mGeocodeProvider != null) {
+ if (doesUidHavePackage(uid, mGeocodeProvider.getConnectedPackageName())) return true;
+ }
for (LocationProviderProxy proxy : mProxyProviders) {
if (doesUidHavePackage(uid, proxy.getConnectedPackageName())) return true;
}
@@ -2819,6 +2970,7 @@
// providers installed oustide the system image. So
// also allow providers with a UID matching the
// currently bound package name
+
if (isUidALocationProvider(Binder.getCallingUid())) {
return;
}
@@ -2827,6 +2979,9 @@
"or UID of a currently bound location provider");
}
+ /**
+ * Returns true if the given package belongs to the given uid.
+ */
private boolean doesUidHavePackage(int uid, String packageName) {
if (packageName == null) {
return false;
@@ -2843,12 +2998,22 @@
return false;
}
- // TODO: will be removed in future
@Override
public void reportLocation(Location location, boolean passive) {
- throw new IllegalStateException("operation unsupported");
+ checkCallerIsProvider();
+
+ if (!location.isComplete()) {
+ Log.w(TAG, "Dropping incomplete location: " + location);
+ return;
+ }
+
+ mLocationHandler.removeMessages(MSG_LOCATION_CHANGED, location);
+ Message m = Message.obtain(mLocationHandler, MSG_LOCATION_CHANGED, location);
+ m.arg1 = (passive ? 1 : 0);
+ mLocationHandler.sendMessageAtFrontOfQueue(m);
}
+
private static boolean shouldBroadcastSafe(
Location loc, Location lastLoc, UpdateRecord record, long now) {
// Always broadcast the first update
@@ -2881,33 +3046,14 @@
return record.mRealRequest.getExpireAt() >= now;
}
- private void handleLocationChanged(Location location, boolean passive) {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
-
- // create a working copy of the incoming Location so that the service can modify it without
- // disturbing the caller's copy
- Location myLocation = new Location(location);
- String pr = myLocation.getProvider();
-
- // set "isFromMockProvider" bit if location came from a mock provider. we do not clear this
- // bit if location did not come from a mock provider because passive/fused providers can
- // forward locations from mock providers, and should not grant them legitimacy in doing so.
- if (!myLocation.isFromMockProvider() && isMockProvider(pr)) {
- myLocation.setIsFromMockProvider(true);
- }
-
- if (!passive) {
- // notify passive provider of the new location
- mPassiveProvider.updateLocation(myLocation);
- }
-
- if (D) Log.d(TAG, "incoming location: " + myLocation);
+ private void handleLocationChangedLocked(Location location, boolean passive) {
+ if (D) Log.d(TAG, "incoming location: " + location);
long now = SystemClock.elapsedRealtime();
- String provider = (passive ? LocationManager.PASSIVE_PROVIDER : myLocation.getProvider());
+ String provider = (passive ? LocationManager.PASSIVE_PROVIDER : location.getProvider());
// Skip if the provider is unknown.
LocationProvider p = mProvidersByName.get(provider);
if (p == null) return;
- updateLastLocation(myLocation, provider);
+ updateLastLocationLocked(location, provider);
// mLastLocation should have been updated from the updateLastLocationLocked call above.
Location lastLocation = mLastLocation.get(provider);
if (lastLocation == null) {
@@ -2918,13 +3064,13 @@
// Update last known coarse interval location if enough time has passed.
Location lastLocationCoarseInterval = mLastLocationCoarseInterval.get(provider);
if (lastLocationCoarseInterval == null) {
- lastLocationCoarseInterval = new Location(myLocation);
+ lastLocationCoarseInterval = new Location(location);
mLastLocationCoarseInterval.put(provider, lastLocationCoarseInterval);
}
- long timeDiffNanos = myLocation.getElapsedRealtimeNanos()
+ long timeDiffNanos = location.getElapsedRealtimeNanos()
- lastLocationCoarseInterval.getElapsedRealtimeNanos();
if (timeDiffNanos > LocationFudger.FASTEST_INTERVAL_MS * NANOS_PER_MILLI) {
- lastLocationCoarseInterval.set(myLocation);
+ lastLocationCoarseInterval.set(location);
}
// Don't ever return a coarse location that is more recent than the allowed update
// interval (i.e. don't allow an app to keep registering and unregistering for
@@ -3048,20 +3194,24 @@
// remove dead records and receivers outside the loop
if (deadReceivers != null) {
for (Receiver receiver : deadReceivers) {
- removeUpdates(receiver);
+ removeUpdatesLocked(receiver);
}
}
if (deadUpdateRecords != null) {
for (UpdateRecord r : deadUpdateRecords) {
r.disposeLocked(true);
}
- applyRequirements(provider);
+ applyRequirementsLocked(provider);
}
}
- private void updateLastLocation(Location location, String provider) {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
-
+ /**
+ * Updates last location with the given location
+ *
+ * @param location new location to update
+ * @param provider Location provider to update for
+ */
+ private void updateLastLocationLocked(Location location, String provider) {
Location noGPSLocation = location.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
Location lastNoGPSLocation;
Location lastLocation = mLastLocation.get(provider);
@@ -3079,11 +3229,75 @@
lastLocation.set(location);
}
- private boolean isMockProvider(String provider) {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
- return mMockProviders.containsKey(provider);
+ private class LocationWorkerHandler extends Handler {
+ public LocationWorkerHandler(Looper looper) {
+ super(looper, null, true);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_LOCATION_CHANGED:
+ handleLocationChanged((Location) msg.obj, msg.arg1 == 1);
+ break;
+ }
+ }
}
+ private boolean isMockProvider(String provider) {
+ synchronized (mLock) {
+ return mMockProviders.containsKey(provider);
+ }
+ }
+
+ private void handleLocationChanged(Location location, boolean passive) {
+ // create a working copy of the incoming Location so that the service can modify it without
+ // disturbing the caller's copy
+ Location myLocation = new Location(location);
+ String provider = myLocation.getProvider();
+
+ // set "isFromMockProvider" bit if location came from a mock provider. we do not clear this
+ // bit if location did not come from a mock provider because passive/fused providers can
+ // forward locations from mock providers, and should not grant them legitimacy in doing so.
+ if (!myLocation.isFromMockProvider() && isMockProvider(provider)) {
+ myLocation.setIsFromMockProvider(true);
+ }
+
+ synchronized (mLock) {
+ if (!passive) {
+ // notify passive provider of the new location
+ mPassiveProvider.updateLocation(myLocation);
+ }
+ handleLocationChangedLocked(myLocation, passive);
+ }
+ }
+
+ private final PackageMonitor mPackageMonitor = new PackageMonitor() {
+ @Override
+ public void onPackageDisappeared(String packageName, int reason) {
+ // remove all receivers associated with this package name
+ synchronized (mLock) {
+ ArrayList<Receiver> deadReceivers = null;
+
+ for (Receiver receiver : mReceivers.values()) {
+ if (receiver.mIdentity.mPackageName.equals(packageName)) {
+ if (deadReceivers == null) {
+ deadReceivers = new ArrayList<>();
+ }
+ deadReceivers.add(receiver);
+ }
+ }
+
+ // perform removal outside of mReceivers loop
+ if (deadReceivers != null) {
+ for (Receiver receiver : deadReceivers) {
+ removeUpdatesLocked(receiver);
+ }
+ }
+ }
+ }
+ };
+
// Geocoder
@Override
@@ -3124,8 +3338,7 @@
}
@Override
- public void addTestProvider(String name, ProviderProperties properties, String opPackageName)
- throws RemoteException {
+ public void addTestProvider(String name, ProviderProperties properties, String opPackageName) {
if (!canCallerAccessMockLocation(opPackageName)) {
return;
}
@@ -3134,24 +3347,23 @@
throw new IllegalArgumentException("Cannot mock the passive location provider");
}
- runFromBinderBlocking(() -> {
+ long identity = Binder.clearCallingIdentity();
+ synchronized (mLock) {
// remove the real provider if we are replacing GPS or network provider
if (LocationManager.GPS_PROVIDER.equals(name)
|| LocationManager.NETWORK_PROVIDER.equals(name)
|| LocationManager.FUSED_PROVIDER.equals(name)) {
LocationProvider p = mProvidersByName.get(name);
if (p != null) {
- removeProvider(p);
+ removeProviderLocked(p);
}
}
- addTestProvider(name, properties);
- return null;
- });
+ addTestProviderLocked(name, properties);
+ }
+ Binder.restoreCallingIdentity(identity);
}
- private void addTestProvider(String name, ProviderProperties properties) {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
-
+ private void addTestProviderLocked(String name, ProviderProperties properties) {
if (mProvidersByName.get(name) != null) {
throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
}
@@ -3159,103 +3371,122 @@
LocationProvider provider = new LocationProvider(name);
MockProvider mockProvider = new MockProvider(provider, properties);
- addProvider(provider);
+ addProviderLocked(provider);
mMockProviders.put(name, mockProvider);
mLastLocation.put(name, null);
mLastLocationCoarseInterval.put(name, null);
}
@Override
- public void removeTestProvider(String provider, String opPackageName) throws RemoteException {
+ public void removeTestProvider(String provider, String opPackageName) {
if (!canCallerAccessMockLocation(opPackageName)) {
return;
}
- runFromBinderBlocking(() -> {
+ synchronized (mLock) {
MockProvider mockProvider = mMockProviders.remove(provider);
if (mockProvider == null) {
throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
}
- removeProvider(mProvidersByName.get(provider));
+ long identity = Binder.clearCallingIdentity();
+ try {
+ removeProviderLocked(mProvidersByName.get(provider));
- // reinstate real provider if available
- LocationProvider realProvider = mRealProviders.get(provider);
- if (realProvider != null) {
- addProvider(realProvider);
+ // reinstate real provider if available
+ LocationProvider realProvider = mRealProviders.get(provider);
+ if (realProvider != null) {
+ addProviderLocked(realProvider);
+ }
+ mLastLocation.put(provider, null);
+ mLastLocationCoarseInterval.put(provider, null);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
- mLastLocation.put(provider, null);
- mLastLocationCoarseInterval.put(provider, null);
- });
+ }
}
@Override
- public void setTestProviderLocation(String provider, Location loc, String opPackageName)
- throws RemoteException {
+ public void setTestProviderLocation(String provider, Location loc, String opPackageName) {
if (!canCallerAccessMockLocation(opPackageName)) {
return;
}
- runFromBinderBlocking(() -> {
+ synchronized (mLock) {
MockProvider mockProvider = mMockProviders.get(provider);
if (mockProvider == null) {
throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
}
- // Ensure that the location is marked as being mock. There's some logic to do this
- // in handleLocationChanged(), but it fails if loc has the wrong provider
- // (b/33091107).
+ // Ensure that the location is marked as being mock. There's some logic to do this in
+ // handleLocationChanged(), but it fails if loc has the wrong provider (bug 33091107).
Location mock = new Location(loc);
mock.setIsFromMockProvider(true);
if (!TextUtils.isEmpty(loc.getProvider()) && !provider.equals(loc.getProvider())) {
- // The location has an explicit provider that is different from the mock
- // provider name. The caller may be trying to fool us via bug 33091107.
+ // The location has an explicit provider that is different from the mock provider
+ // name. The caller may be trying to fool us via bug 33091107.
EventLog.writeEvent(0x534e4554, "33091107", Binder.getCallingUid(),
provider + "!=" + loc.getProvider());
}
- mockProvider.setLocation(mock);
- });
+ // clear calling identity so INSTALL_LOCATION_PROVIDER permission is not required
+ long identity = Binder.clearCallingIdentity();
+ try {
+ mockProvider.setLocation(mock);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
}
@Override
- public void setTestProviderEnabled(String provider, boolean enabled, String opPackageName)
- throws RemoteException {
+ public void setTestProviderEnabled(String provider, boolean enabled, String opPackageName) {
if (!canCallerAccessMockLocation(opPackageName)) {
return;
}
- runFromBinderBlocking(() -> {
+ synchronized (mLock) {
MockProvider mockProvider = mMockProviders.get(provider);
if (mockProvider == null) {
throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
}
- mockProvider.setEnabled(enabled);
- });
+ long identity = Binder.clearCallingIdentity();
+ try {
+ mockProvider.setEnabled(enabled);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
}
@Override
public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime,
- String opPackageName) throws RemoteException {
+ String opPackageName) {
if (!canCallerAccessMockLocation(opPackageName)) {
return;
}
- runFromBinderBlocking(() -> {
+ synchronized (mLock) {
MockProvider mockProvider = mMockProviders.get(provider);
if (mockProvider == null) {
throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
}
mockProvider.setStatus(status, extras, updateTime);
- });
+ }
+ }
+
+ private void log(String log) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Slog.d(TAG, log);
+ }
}
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
- Runnable dump = () -> {
+ synchronized (mLock) {
if (args.length > 0 && args[0].equals("--gnssmetrics")) {
if (mGnssMetricsProvider != null) {
pw.append(mGnssMetricsProvider.getGnssMetricsAsProtoString());
@@ -3348,14 +3579,6 @@
if (mGnssBatchingInProgress) {
pw.println(" GNSS batching in progress");
}
- };
-
- FutureTask<Void> task = new FutureTask<>(dump, null);
- mHandler.postAtFrontOfQueue(task);
- try {
- task.get();
- } catch (InterruptedException | ExecutionException e) {
- pw.println("error dumping: " + e);
}
}
}
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index e10e0d6..cbf6d04 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -34,6 +34,7 @@
import android.os.UserHandle;
import android.telephony.CellInfo;
import android.telephony.CellLocation;
+import android.telephony.DataFailCause;
import android.telephony.DisconnectCause;
import android.telephony.LocationAccessPolicy;
import android.telephony.PhoneCapability;
@@ -47,6 +48,7 @@
import android.telephony.SignalStrength;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import android.telephony.data.ApnSetting;
import android.telephony.emergency.EmergencyNumber;
import android.util.LocalLog;
import android.util.StatsLog;
@@ -1366,7 +1368,8 @@
mDataConnectionNetworkType[phoneId] = networkType;
}
mPreciseDataConnectionState = new PreciseDataConnectionState(state, networkType,
- apnType, apn, linkProperties, "");
+ ApnSetting.getApnTypesBitmaskFromString(apnType), apn,
+ linkProperties, DataFailCause.NONE);
for (Record r : mRecords) {
if (r.matchPhoneStateListenerEvent(
PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE)) {
@@ -1384,7 +1387,7 @@
broadcastDataConnectionStateChanged(state, isDataAllowed, apn, apnType, linkProperties,
networkCapabilities, roaming, subId);
broadcastPreciseDataConnectionStateChanged(state, networkType, apnType, apn,
- linkProperties, "");
+ linkProperties, DataFailCause.NONE);
}
public void notifyDataConnectionFailed(String apnType) {
@@ -1403,7 +1406,8 @@
synchronized (mRecords) {
mPreciseDataConnectionState = new PreciseDataConnectionState(
TelephonyManager.DATA_UNKNOWN,TelephonyManager.NETWORK_TYPE_UNKNOWN,
- apnType, "", null, "");
+ ApnSetting.getApnTypesBitmaskFromString(apnType), "", null,
+ DataFailCause.NONE);
for (Record r : mRecords) {
if (r.matchPhoneStateListenerEvent(
PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE)) {
@@ -1418,7 +1422,8 @@
}
broadcastDataConnectionFailed(apnType, subId);
broadcastPreciseDataConnectionStateChanged(TelephonyManager.DATA_UNKNOWN,
- TelephonyManager.NETWORK_TYPE_UNKNOWN, apnType, "", null, "");
+ TelephonyManager.NETWORK_TYPE_UNKNOWN, apnType, "", null,
+ DataFailCause.NONE);
}
public void notifyCellLocation(Bundle cellLocation) {
@@ -1528,14 +1533,15 @@
}
}
- public void notifyPreciseDataConnectionFailed(String apnType, String apn, String failCause) {
+ public void notifyPreciseDataConnectionFailed(String apnType,
+ String apn, @DataFailCause.FailCause int failCause) {
if (!checkNotifyPermission("notifyPreciseDataConnectionFailed()")) {
return;
}
synchronized (mRecords) {
mPreciseDataConnectionState = new PreciseDataConnectionState(
TelephonyManager.DATA_UNKNOWN, TelephonyManager.NETWORK_TYPE_UNKNOWN,
- apnType, apn, null, failCause);
+ ApnSetting.getApnTypesBitmaskFromString(apnType), apn, null, failCause);
for (Record r : mRecords) {
if (r.matchPhoneStateListenerEvent(
PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE)) {
@@ -1928,9 +1934,8 @@
}
private void broadcastPreciseDataConnectionStateChanged(int state, int networkType,
- String apnType, String apn,
- LinkProperties linkProperties,
- String failCause) {
+ String apnType, String apn, LinkProperties linkProperties,
+ @DataFailCause.FailCause int failCause) {
Intent intent = new Intent(TelephonyManager.ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED);
intent.putExtra(PhoneConstants.STATE_KEY, state);
intent.putExtra(PhoneConstants.DATA_NETWORK_TYPE_KEY, networkType);
@@ -1939,7 +1944,7 @@
if (linkProperties != null) {
intent.putExtra(PhoneConstants.DATA_LINK_PROPERTIES_KEY, linkProperties);
}
- if (failCause != null) intent.putExtra(PhoneConstants.DATA_FAILURE_CAUSE_KEY, failCause);
+ intent.putExtra(PhoneConstants.DATA_FAILURE_CAUSE_KEY, failCause);
mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
android.Manifest.permission.READ_PRECISE_PHONE_STATE);
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 9f353a8..faca750 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -149,6 +149,8 @@
native static boolean vibratorSupportsAmplitudeControl();
native static void vibratorSetAmplitude(int amplitude);
native static long vibratorPerformEffect(long effect, long strength);
+ static native boolean vibratorSupportsExternalControl();
+ static native void vibratorSetExternalControl(boolean enabled);
private final IUidObserver mUidObserver = new IUidObserver.Stub() {
@Override public void onUidStateChanged(int uid, int procState, long procStateSeq) {
@@ -532,12 +534,6 @@
return;
}
verifyIncomingUid(uid);
- if (mProcStatesCache.get(uid, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND)
- > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
- Slog.e(TAG, "Ignoring incoming vibration as process with uid = "
- + uid + " is background");
- return;
- }
if (!verifyVibrationEffect(effect)) {
return;
}
@@ -575,6 +571,13 @@
}
Vibration vib = new Vibration(token, effect, usageHint, uid, opPkg, reason);
+ if (mProcStatesCache.get(uid, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND)
+ > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
+ && vib.isHapticFeedback()) {
+ Slog.e(TAG, "Ignoring incoming vibration as process with uid = "
+ + uid + " is background");
+ return;
+ }
linkVibration(vib);
long ident = Binder.clearCallingIdentity();
try {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 62ebc5d..4f21ee8 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -5329,16 +5329,7 @@
}
}
- @Override
- public boolean isAppForeground(int uid) {
- int callerUid = Binder.getCallingUid();
- if (UserHandle.isCore(callerUid) || callerUid == uid) {
- return isAppForegroundInternal(uid);
- }
- return false;
- }
-
- private boolean isAppForegroundInternal(int uid) {
+ private boolean isAppForeground(int uid) {
synchronized (this) {
UidRecord uidRec = mActiveUids.get(uid);
if (uidRec == null || uidRec.idle) {
@@ -19694,6 +19685,11 @@
return pr == null ? Zygote.MOUNT_EXTERNAL_NONE : pr.mountMode;
}
}
+
+ @Override
+ public boolean isAppForeground(int uid) {
+ return ActivityManagerService.this.isAppForeground(uid);
+ }
}
long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {
diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java
index f9a77af..d7cb2bd 100644
--- a/services/core/java/com/android/server/am/CoreSettingsObserver.java
+++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java
@@ -68,6 +68,7 @@
sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_LAYER_APP, String.class);
sGlobalSettingToTypeMap.put(Settings.Global.SMS_ACCESS_RESTRICTION_ENABLED, int.class);
sGlobalSettingToTypeMap.put(Settings.Global.GUP_DEV_OPT_IN_APPS, String.class);
+ sGlobalSettingToTypeMap.put(Settings.Global.GUP_DEV_OPT_OUT_APPS, String.class);
sGlobalSettingToTypeMap.put(Settings.Global.GUP_BLACK_LIST, String.class);
// add other global settings here...
}
diff --git a/services/core/java/com/android/server/am/TEST_MAPPING b/services/core/java/com/android/server/am/TEST_MAPPING
index 48b4145..c84b5c7 100644
--- a/services/core/java/com/android/server/am/TEST_MAPPING
+++ b/services/core/java/com/android/server/am/TEST_MAPPING
@@ -7,7 +7,7 @@
"include-annotation": "android.platform.test.annotations.Presubmit"
},
{
- "exclude-annotation": "android.support.test.filters.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
},
@@ -21,7 +21,7 @@
"include-annotation": "android.platform.test.annotations.Presubmit"
},
{
- "exclude-annotation": "android.support.test.filters.FlakyTest"
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
}
]
},
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 9dfdddb..eb5be77 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -1837,7 +1837,7 @@
final TetherState tetherState = new TetherState(
new IpServer(iface, mLooper, interfaceType, mLog, mNMService, mStatsService,
makeControlCallback(), mConfig.enableLegacyDhcpServer,
- mDeps.getIpServerDependencies()));
+ mDeps.getIpServerDependencies(mContext)));
mTetherStates.put(iface, tetherState);
tetherState.ipServer.start();
}
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
index 7daf71d..a42efe9 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
@@ -60,8 +60,8 @@
/**
* Get dependencies to be used by IpServer.
*/
- public IpServer.Dependencies getIpServerDependencies() {
- return new IpServer.Dependencies();
+ public IpServer.Dependencies getIpServerDependencies(Context context) {
+ return new IpServer.Dependencies(context);
}
/**
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 78b3c15..d57431e 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -17,6 +17,12 @@
package com.android.server.display;
import android.annotation.Nullable;
+import android.app.ActivityManager.StackInfo;
+import android.app.ActivityTaskManager;
+import android.app.IActivityTaskManager;
+import android.app.TaskStackListener;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
@@ -27,6 +33,7 @@
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
+import android.os.RemoteException;
import android.os.SystemClock;
import android.os.Trace;
import android.util.EventLog;
@@ -34,6 +41,7 @@
import android.util.Slog;
import android.util.TimeUtils;
+import com.android.internal.os.BackgroundThread;
import com.android.server.EventLogTags;
import java.io.PrintWriter;
@@ -41,7 +49,6 @@
class AutomaticBrightnessController {
private static final String TAG = "AutomaticBrightnessController";
- private static final boolean DEBUG = false;
private static final boolean DEBUG_PRETEND_LIGHT_SENSOR_ABSENT = false;
// If true, enables the use of the screen auto-brightness adjustment setting.
@@ -56,16 +63,11 @@
// the user is satisfied with the result before storing the sample.
private static final int BRIGHTNESS_ADJUSTMENT_SAMPLE_DEBOUNCE_MILLIS = 10000;
- // Timeout after which we remove the effects any user interactions might've had on the
- // brightness mapping. This timeout doesn't start until we transition to a non-interactive
- // display policy so that we don't reset while users are using their devices, but also so that
- // we don't erroneously keep the short-term model if the device is dozing but the display is
- // fully on.
- private static final int SHORT_TERM_MODEL_TIMEOUT_MILLIS = 30000;
-
private static final int MSG_UPDATE_AMBIENT_LUX = 1;
private static final int MSG_BRIGHTNESS_ADJUSTMENT_SAMPLE = 2;
private static final int MSG_INVALIDATE_SHORT_TERM_MODEL = 3;
+ private static final int MSG_UPDATE_FOREGROUND_APP = 4;
+ private static final int MSG_UPDATE_FOREGROUND_APP_SYNC = 5;
// Length of the ambient light horizon used to calculate the long term estimate of ambient
// light.
@@ -126,6 +128,15 @@
private final HysteresisLevels mAmbientBrightnessThresholds;
private final HysteresisLevels mScreenBrightnessThresholds;
+ private boolean mLoggingEnabled;
+
+ // Timeout after which we remove the effects any user interactions might've had on the
+ // brightness mapping. This timeout doesn't start until we transition to a non-interactive
+ // display policy so that we don't reset while users are using their devices, but also so that
+ // we don't erroneously keep the short-term model if the device is dozing but the display is
+ // fully on.
+ private long mShortTermModelTimeout;
+
// Amount of time to delay auto-brightness after screen on while waiting for
// the light sensor to warm-up in milliseconds.
// May be 0 if no warm-up is required.
@@ -192,13 +203,26 @@
private float mShortTermModelAnchor;
private float SHORT_TERM_MODEL_THRESHOLD_RATIO = 0.6f;
+ // Context-sensitive brightness configurations require keeping track of the foreground app's
+ // package name and category, which is done by registering a TaskStackListener to call back to
+ // us onTaskStackChanged, and then using the ActivityTaskManager to get the foreground app's
+ // package namd and PackageManager to get its category (so might as well cache them).
+ private String mForegroundAppPackageName;
+ private String mPendingForegroundAppPackageName;
+ private @ApplicationInfo.Category int mForegroundAppCategory;
+ private @ApplicationInfo.Category int mPendingForegroundAppCategory;
+ private TaskStackListenerImpl mTaskStackListener;
+ private IActivityTaskManager mActivityTaskManager;
+ private PackageManager mPackageManager;
+
public AutomaticBrightnessController(Callbacks callbacks, Looper looper,
SensorManager sensorManager, BrightnessMappingStrategy mapper,
int lightSensorWarmUpTime, int brightnessMin, int brightnessMax, float dozeScaleFactor,
int lightSensorRate, int initialLightSensorRate, long brighteningLightDebounceConfig,
long darkeningLightDebounceConfig, boolean resetAmbientLuxAfterWarmUpConfig,
HysteresisLevels ambientBrightnessThresholds,
- HysteresisLevels screenBrightnessThresholds) {
+ HysteresisLevels screenBrightnessThresholds, long shortTermModelTimeout,
+ PackageManager packageManager) {
mCallbacks = callbacks;
mSensorManager = sensorManager;
mBrightnessMapper = mapper;
@@ -216,6 +240,7 @@
mWeightingIntercept = AMBIENT_LIGHT_LONG_HORIZON_MILLIS;
mAmbientBrightnessThresholds = ambientBrightnessThresholds;
mScreenBrightnessThresholds = screenBrightnessThresholds;
+ mShortTermModelTimeout = shortTermModelTimeout;
mShortTermModelValid = true;
mShortTermModelAnchor = -1;
@@ -226,6 +251,31 @@
if (!DEBUG_PRETEND_LIGHT_SENSOR_ABSENT) {
mLightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
}
+
+ mActivityTaskManager = ActivityTaskManager.getService();
+ mPackageManager = packageManager;
+ mTaskStackListener = new TaskStackListenerImpl();
+ mForegroundAppPackageName = null;
+ mPendingForegroundAppPackageName = null;
+ mForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
+ mPendingForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
+ }
+
+ /**
+ * Enable/disable logging.
+ *
+ * @param loggingEnabled
+ * Whether logging should be on/off.
+ *
+ * @return Whether the method succeeded or not.
+ */
+ public boolean setLoggingEnabled(boolean loggingEnabled) {
+ if (mLoggingEnabled == loggingEnabled) {
+ return false;
+ }
+ mBrightnessMapper.setLoggingEnabled(loggingEnabled);
+ mLoggingEnabled = loggingEnabled;
+ return true;
}
public int getAutomaticScreenBrightness() {
@@ -290,12 +340,12 @@
}
final int oldPolicy = mDisplayPolicy;
mDisplayPolicy = policy;
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "Display policy transitioning from " + oldPolicy + " to " + policy);
}
if (!isInteractivePolicy(policy) && isInteractivePolicy(oldPolicy)) {
mHandler.sendEmptyMessageDelayed(MSG_INVALIDATE_SHORT_TERM_MODEL,
- SHORT_TERM_MODEL_TIMEOUT_MILLIS);
+ mShortTermModelTimeout);
} else if (isInteractivePolicy(policy) && !isInteractivePolicy(oldPolicy)) {
mHandler.removeMessages(MSG_INVALIDATE_SHORT_TERM_MODEL);
}
@@ -317,7 +367,7 @@
mBrightnessMapper.addUserDataPoint(mAmbientLux, brightness);
mShortTermModelValid = true;
mShortTermModelAnchor = mAmbientLux;
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "ShortTermModel: anchor=" + mShortTermModelAnchor);
}
return true;
@@ -330,7 +380,7 @@
}
private void invalidateShortTermModel() {
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "ShortTermModel: invalidate user data");
}
mShortTermModelValid = false;
@@ -377,13 +427,17 @@
pw.println(" mAmbientLightRingBuffer=" + mAmbientLightRingBuffer);
pw.println(" mScreenAutoBrightness=" + mScreenAutoBrightness);
pw.println(" mDisplayPolicy=" + DisplayPowerRequest.policyToString(mDisplayPolicy));
+ pw.println(" mShortTermModelTimeout=" + mShortTermModelTimeout);
pw.println(" mShortTermModelAnchor=" + mShortTermModelAnchor);
pw.println(" mShortTermModelValid=" + mShortTermModelValid);
pw.println(" mBrightnessAdjustmentSamplePending=" + mBrightnessAdjustmentSamplePending);
pw.println(" mBrightnessAdjustmentSampleOldLux=" + mBrightnessAdjustmentSampleOldLux);
pw.println(" mBrightnessAdjustmentSampleOldBrightness="
+ mBrightnessAdjustmentSampleOldBrightness);
- pw.println(" mShortTermModelValid=" + mShortTermModelValid);
+ pw.println(" mForegroundAppPackageName=" + mForegroundAppPackageName);
+ pw.println(" mPendingForegroundAppPackageName=" + mPendingForegroundAppPackageName);
+ pw.println(" mForegroundAppCategory=" + mForegroundAppCategory);
+ pw.println(" mPendingForegroundAppCategory=" + mPendingForegroundAppCategory);
pw.println();
mBrightnessMapper.dump(pw);
@@ -399,6 +453,7 @@
mLightSensorEnabled = true;
mLightSensorEnableTime = SystemClock.uptimeMillis();
mCurrentLightSensorRate = mInitialLightSensorRate;
+ registerForegroundAppUpdater();
mSensorManager.registerListener(mLightSensorListener, mLightSensor,
mCurrentLightSensorRate * 1000, mHandler);
return true;
@@ -411,6 +466,7 @@
mAmbientLightRingBuffer.clear();
mCurrentLightSensorRate = -1;
mHandler.removeMessages(MSG_UPDATE_AMBIENT_LUX);
+ unregisterForegroundAppUpdater();
mSensorManager.unregisterListener(mLightSensorListener);
}
return false;
@@ -441,7 +497,7 @@
private void adjustLightSensorRate(int lightSensorRate) {
// if the light sensor rate changed, update the sensor listener
if (lightSensorRate != mCurrentLightSensorRate) {
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "adjustLightSensorRate: " +
"previousRate=" + mCurrentLightSensorRate + ", " +
"currentRate=" + lightSensorRate);
@@ -458,7 +514,7 @@
}
private void setAmbientLux(float lux) {
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "setAmbientLux(" + lux + ")");
}
if (lux < 0) {
@@ -476,7 +532,7 @@
final float maxAmbientLux =
mShortTermModelAnchor + mShortTermModelAnchor * SHORT_TERM_MODEL_THRESHOLD_RATIO;
if (minAmbientLux < mAmbientLux && mAmbientLux < maxAmbientLux) {
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "ShortTermModel: re-validate user data, ambient lux is " +
minAmbientLux + " < " + mAmbientLux + " < " + maxAmbientLux);
}
@@ -490,7 +546,7 @@
}
private float calculateAmbientLux(long now, long horizon) {
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "calculateAmbientLux(" + now + ", " + horizon + ")");
}
final int N = mAmbientLightRingBuffer.size();
@@ -509,7 +565,7 @@
break;
}
}
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "calculateAmbientLux: selected endIndex=" + endIndex + ", point=(" +
mAmbientLightRingBuffer.getTime(endIndex) + ", " +
mAmbientLightRingBuffer.getLux(endIndex) + ")");
@@ -527,7 +583,7 @@
final long startTime = eventTime - now;
float weight = calculateWeight(startTime, endTime);
float lux = mAmbientLightRingBuffer.getLux(i);
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "calculateAmbientLux: [" + startTime + ", " + endTime + "]: " +
"lux=" + lux + ", " +
"weight=" + weight);
@@ -536,7 +592,7 @@
sum += lux * weight;
endTime = startTime;
}
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "calculateAmbientLux: " +
"totalWeight=" + totalWeight + ", " +
"newAmbientLux=" + (sum / totalWeight));
@@ -591,7 +647,7 @@
final long timeWhenSensorWarmedUp =
mLightSensorWarmUpTimeConfig + mLightSensorEnableTime;
if (time < timeWhenSensorWarmedUp) {
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "updateAmbientLux: Sensor not ready yet: " +
"time=" + time + ", " +
"timeWhenSensorWarmedUp=" + timeWhenSensorWarmedUp);
@@ -602,7 +658,7 @@
}
setAmbientLux(calculateAmbientLux(time, AMBIENT_LIGHT_SHORT_HORIZON_MILLIS));
mAmbientLuxValid = true;
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "updateAmbientLux: Initializing: " +
"mAmbientLightRingBuffer=" + mAmbientLightRingBuffer + ", " +
"mAmbientLux=" + mAmbientLux);
@@ -630,10 +686,10 @@
&& fastAmbientLux <= mAmbientDarkeningThreshold
&& nextDarkenTransition <= time)) {
setAmbientLux(fastAmbientLux);
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "updateAmbientLux: "
+ ((fastAmbientLux > mAmbientLux) ? "Brightened" : "Darkened") + ": "
- + "mAmbientBrighteningThreshold=" + mAmbientBrighteningThreshold + ", "
+ + "mBrighteningLuxThreshold=" + mAmbientBrighteningThreshold + ", "
+ "mAmbientLightRingBuffer=" + mAmbientLightRingBuffer + ", "
+ "mAmbientLux=" + mAmbientLux);
}
@@ -650,7 +706,7 @@
// weighted ambient lux or not.
nextTransitionTime =
nextTransitionTime > time ? nextTransitionTime : time + mNormalLightSensorRate;
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "updateAmbientLux: Scheduling ambient lux update for " +
nextTransitionTime + TimeUtils.formatUptime(nextTransitionTime));
}
@@ -662,7 +718,8 @@
return;
}
- float value = mBrightnessMapper.getBrightness(mAmbientLux);
+ float value = mBrightnessMapper.getBrightness(mAmbientLux, mForegroundAppPackageName,
+ mForegroundAppCategory);
int newScreenAutoBrightness =
clampScreenBrightness(Math.round(value * PowerManager.BRIGHTNESS_ON));
@@ -673,7 +730,7 @@
if (mScreenAutoBrightness != -1
&& newScreenAutoBrightness > mScreenDarkeningThreshold
&& newScreenAutoBrightness < mScreenBrighteningThreshold) {
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "ignoring newScreenAutoBrightness: " + mScreenDarkeningThreshold
+ " < " + newScreenAutoBrightness + " < " + mScreenBrighteningThreshold);
}
@@ -681,8 +738,7 @@
}
if (mScreenAutoBrightness != newScreenAutoBrightness) {
-
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "updateAutoBrightness: " +
"mScreenAutoBrightness=" + mScreenAutoBrightness + ", " +
"newScreenAutoBrightness=" + newScreenAutoBrightness);
@@ -729,7 +785,7 @@
if (mBrightnessAdjustmentSamplePending) {
mBrightnessAdjustmentSamplePending = false;
if (mAmbientLuxValid && mScreenAutoBrightness >= 0) {
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "Auto-brightness adjustment changed by user: " +
"lux=" + mAmbientLux + ", " +
"brightness=" + mScreenAutoBrightness + ", " +
@@ -745,6 +801,83 @@
}
}
+ // Register a TaskStackListener to call back to us onTaskStackChanged, so we can update the
+ // foreground app's package name and category and correct the brightness accordingly.
+ private void registerForegroundAppUpdater() {
+ try {
+ mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
+ // This will not get called until the foreground app changes for the first time, so
+ // call it explicitly to get the current foreground app's info.
+ updateForegroundApp();
+ } catch (RemoteException e) {
+ if (mLoggingEnabled) {
+ Slog.e(TAG, "Failed to register foreground app updater: " + e);
+ }
+ // Nothing to do.
+ }
+ }
+
+ private void unregisterForegroundAppUpdater() {
+ try {
+ mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
+ } catch (RemoteException e) {
+ // Nothing to do.
+ }
+ mForegroundAppPackageName = null;
+ mForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
+ }
+
+ // Set the foreground app's package name and category, so brightness can be corrected per app.
+ private void updateForegroundApp() {
+ if (mLoggingEnabled) {
+ Slog.d(TAG, "Attempting to update foreground app");
+ }
+ // The ActivityTaskManager's lock tends to get contended, so this is done in a background
+ // thread and applied via this thread's handler synchronously.
+ BackgroundThread.getHandler().post(new Runnable() {
+ public void run() {
+ try {
+ // The foreground app is the top activity of the focused tasks stack.
+ final StackInfo info = mActivityTaskManager.getFocusedStackInfo();
+ if (info == null || info.topActivity == null) {
+ return;
+ }
+ final String packageName = info.topActivity.getPackageName();
+ // If the app didn't change, there's nothing to do. Otherwise, we have to
+ // update the category and re-apply the brightness correction.
+ if (mForegroundAppPackageName != null
+ && mForegroundAppPackageName.equals(packageName)) {
+ return;
+ }
+ mPendingForegroundAppPackageName = packageName;
+ mPendingForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
+ try {
+ ApplicationInfo app = mPackageManager.getApplicationInfo(packageName,
+ PackageManager.MATCH_ANY_USER);
+ mPendingForegroundAppCategory = app.category;
+ } catch (PackageManager.NameNotFoundException e) {
+ // Nothing to do
+ }
+ mHandler.sendEmptyMessage(MSG_UPDATE_FOREGROUND_APP_SYNC);
+ } catch (RemoteException e) {
+ // Nothing to do
+ }
+ }
+ });
+ }
+
+ private void updateForegroundAppSync() {
+ if (mLoggingEnabled) {
+ Slog.d(TAG, "Updating foreground app: packageName=" + mPendingForegroundAppPackageName
+ + ", category=" + mPendingForegroundAppCategory);
+ }
+ mForegroundAppPackageName = mPendingForegroundAppPackageName;
+ mPendingForegroundAppPackageName = null;
+ mForegroundAppCategory = mPendingForegroundAppCategory;
+ mPendingForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
+ updateAutoBrightness(true /* sendUpdate */);
+ }
+
private final class AutomaticBrightnessHandler extends Handler {
public AutomaticBrightnessHandler(Looper looper) {
super(looper, null, true /*async*/);
@@ -764,6 +897,14 @@
case MSG_INVALIDATE_SHORT_TERM_MODEL:
invalidateShortTermModel();
break;
+
+ case MSG_UPDATE_FOREGROUND_APP:
+ updateForegroundApp();
+ break;
+
+ case MSG_UPDATE_FOREGROUND_APP_SYNC:
+ updateForegroundAppSync();
+ break;
}
}
}
@@ -784,6 +925,15 @@
}
};
+ // Call back whenever the tasks stack changes, which includes tasks being created, removed, and
+ // moving to top.
+ class TaskStackListenerImpl extends TaskStackListener {
+ @Override
+ public void onTaskStackChanged() {
+ mHandler.sendEmptyMessage(MSG_UPDATE_FOREGROUND_APP);
+ }
+ }
+
/** Callbacks to request updates to the display's power state. */
interface Callbacks {
void updateBrightness();
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index 76c191d..9fce644 100644
--- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -17,9 +17,11 @@
package com.android.server.display;
import android.annotation.Nullable;
+import android.content.pm.ApplicationInfo;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.hardware.display.BrightnessConfiguration;
+import android.hardware.display.BrightnessCorrection;
import android.os.PowerManager;
import android.util.MathUtils;
import android.util.Pair;
@@ -42,11 +44,12 @@
*/
public abstract class BrightnessMappingStrategy {
private static final String TAG = "BrightnessMappingStrategy";
- private static final boolean DEBUG = false;
private static final float LUX_GRAD_SMOOTHING = 0.25f;
private static final float MAX_GRAD = 1.0f;
+ protected boolean mLoggingEnabled;
+
private static final Plog PLOG = Plog.createSystemPlog(TAG);
@Nullable
@@ -161,6 +164,22 @@
}
/**
+ * Enable/disable logging.
+ *
+ * @param loggingEnabled
+ * Whether logging should be on/off.
+ *
+ * @return Whether the method succeeded or not.
+ */
+ public boolean setLoggingEnabled(boolean loggingEnabled) {
+ if (mLoggingEnabled == loggingEnabled) {
+ return false;
+ }
+ mLoggingEnabled = loggingEnabled;
+ return true;
+ }
+
+ /**
* Sets the {@link BrightnessConfiguration}.
*
* @param config The new configuration. If {@code null} is passed, the default configuration is
@@ -170,15 +189,33 @@
public abstract boolean setBrightnessConfiguration(@Nullable BrightnessConfiguration config);
/**
- * Returns the desired brightness of the display based on the current ambient lux.
+ * Returns the desired brightness of the display based on the current ambient lux, including
+ * any context-related corrections.
*
* The returned brightness will be in the range [0, 1.0], where 1.0 is the display at max
* brightness and 0 is the display at minimum brightness.
*
* @param lux The current ambient brightness in lux.
+ * @param packageName the foreground app package name.
+ * @param category the foreground app package category.
* @return The desired brightness of the display normalized to the range [0, 1.0].
*/
- public abstract float getBrightness(float lux);
+ public abstract float getBrightness(float lux, String packageName,
+ @ApplicationInfo.Category int category);
+
+ /**
+ * Returns the desired brightness of the display based on the current ambient lux.
+ *
+ * The returned brightness wil be in the range [0, 1.0], where 1.0 is the display at max
+ * brightness and 0 is the display at minimum brightness.
+ *
+ * @param lux The current ambient brightness in lux.
+ *
+ * @return The desired brightness of the display normalized to the range [0, 1.0].
+ */
+ public float getBrightness(float lux) {
+ return getBrightness(lux, null /* packageName */, ApplicationInfo.CATEGORY_UNDEFINED);
+ }
/**
* Returns the current auto-brightness adjustment.
@@ -239,13 +276,13 @@
public abstract void dump(PrintWriter pw);
- private static float normalizeAbsoluteBrightness(int brightness) {
+ protected float normalizeAbsoluteBrightness(int brightness) {
brightness = MathUtils.constrain(brightness,
PowerManager.BRIGHTNESS_OFF, PowerManager.BRIGHTNESS_ON);
return (float) brightness / PowerManager.BRIGHTNESS_ON;
}
- private static Pair<float[], float[]> insertControlPoint(
+ private Pair<float[], float[]> insertControlPoint(
float[] luxLevels, float[] brightnessLevels, float lux, float brightness) {
final int idx = findInsertionPoint(luxLevels, lux);
final float[] newLuxLevels;
@@ -278,7 +315,7 @@
* This assumes that {@code arr} is sorted. If all values in {@code arr} are greater
* than val, then it will return the length of arr as the insertion point.
*/
- private static int findInsertionPoint(float[] arr, float val) {
+ private int findInsertionPoint(float[] arr, float val) {
for (int i = 0; i < arr.length; i++) {
if (val <= arr[i]) {
return i;
@@ -287,8 +324,8 @@
return arr.length;
}
- private static void smoothCurve(float[] lux, float[] brightness, int idx) {
- if (DEBUG) {
+ private void smoothCurve(float[] lux, float[] brightness, int idx) {
+ if (mLoggingEnabled) {
PLOG.logCurve("unsmoothed curve", lux, brightness);
}
float prevLux = lux[idx];
@@ -323,19 +360,19 @@
prevBrightness = newBrightness;
brightness[i] = newBrightness;
}
- if (DEBUG) {
+ if (mLoggingEnabled) {
PLOG.logCurve("smoothed curve", lux, brightness);
}
}
- private static float permissibleRatio(float currLux, float prevLux) {
+ private float permissibleRatio(float currLux, float prevLux) {
return MathUtils.exp(MAX_GRAD
* (MathUtils.log(currLux + LUX_GRAD_SMOOTHING)
- MathUtils.log(prevLux + LUX_GRAD_SMOOTHING)));
}
- private static float inferAutoBrightnessAdjustment(float maxGamma,
- float desiredBrightness, float currentBrightness) {
+ protected float inferAutoBrightnessAdjustment(float maxGamma, float desiredBrightness,
+ float currentBrightness) {
float adjustment = 0;
float gamma = Float.NaN;
// Extreme edge cases: use a simpler heuristic, as proper gamma correction around the edges
@@ -355,7 +392,7 @@
adjustment = -MathUtils.log(gamma) / MathUtils.log(maxGamma);
}
adjustment = MathUtils.constrain(adjustment, -1, +1);
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "inferAutoBrightnessAdjustment: " + maxGamma + "^" + -adjustment + "=" +
MathUtils.pow(maxGamma, -adjustment) + " == " + gamma);
Slog.d(TAG, "inferAutoBrightnessAdjustment: " + currentBrightness + "^" + gamma + "=" +
@@ -364,16 +401,16 @@
return adjustment;
}
- private static Pair<float[], float[]> getAdjustedCurve(float[] lux, float[] brightness,
+ protected Pair<float[], float[]> getAdjustedCurve(float[] lux, float[] brightness,
float userLux, float userBrightness, float adjustment, float maxGamma) {
float[] newLux = lux;
float[] newBrightness = Arrays.copyOf(brightness, brightness.length);
- if (DEBUG) {
+ if (mLoggingEnabled) {
PLOG.logCurve("unadjusted curve", newLux, newBrightness);
}
adjustment = MathUtils.constrain(adjustment, -1, 1);
float gamma = MathUtils.pow(maxGamma, -adjustment);
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "getAdjustedCurve: " + maxGamma + "^" + -adjustment + "=" +
MathUtils.pow(maxGamma, -adjustment) + " == " + gamma);
}
@@ -382,7 +419,7 @@
newBrightness[i] = MathUtils.pow(newBrightness[i], gamma);
}
}
- if (DEBUG) {
+ if (mLoggingEnabled) {
PLOG.logCurve("gamma adjusted curve", newLux, newBrightness);
}
if (userLux != -1) {
@@ -390,7 +427,7 @@
userBrightness);
newLux = curve.first;
newBrightness = curve.second;
- if (DEBUG) {
+ if (mLoggingEnabled) {
PLOG.logCurve("gamma and user adjusted curve", newLux, newBrightness);
// This is done for comparison.
curve = insertControlPoint(lux, brightness, userLux, userBrightness);
@@ -440,7 +477,7 @@
mAutoBrightnessAdjustment = 0;
mUserLux = -1;
mUserBrightness = -1;
- if (DEBUG) {
+ if (mLoggingEnabled) {
PLOG.start("simple mapping strategy");
}
computeSpline();
@@ -452,7 +489,8 @@
}
@Override
- public float getBrightness(float lux) {
+ public float getBrightness(float lux, String packageName,
+ @ApplicationInfo.Category int category) {
return mSpline.interpolate(lux);
}
@@ -467,7 +505,7 @@
if (adjustment == mAutoBrightnessAdjustment) {
return false;
}
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "setAutoBrightnessAdjustment: " + mAutoBrightnessAdjustment + " => " +
adjustment);
PLOG.start("auto-brightness adjustment");
@@ -485,7 +523,7 @@
@Override
public void addUserDataPoint(float lux, float brightness) {
float unadjustedBrightness = getUnadjustedBrightness(lux);
- if (DEBUG){
+ if (mLoggingEnabled) {
Slog.d(TAG, "addUserDataPoint: (" + lux + "," + brightness + ")");
PLOG.start("add user data point")
.logPoint("user data point", lux, brightness)
@@ -494,7 +532,7 @@
float adjustment = inferAutoBrightnessAdjustment(mMaxGamma,
brightness /* desiredBrightness */,
unadjustedBrightness /* currentBrightness */);
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "addUserDataPoint: " + mAutoBrightnessAdjustment + " => " +
adjustment);
}
@@ -507,7 +545,7 @@
@Override
public void clearUserDataPoints() {
if (mUserLux != -1) {
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "clearUserDataPoints: " + mAutoBrightnessAdjustment + " => 0");
PLOG.start("clear user data points")
.logPoint("user data point", mUserLux, mUserBrightness);
@@ -614,7 +652,7 @@
mBacklightToNitsSpline = Spline.createSpline(normalizedBacklight, nits);
mDefaultConfig = config;
- if (DEBUG) {
+ if (mLoggingEnabled) {
PLOG.start("physical mapping strategy");
}
mConfig = config;
@@ -629,7 +667,7 @@
if (config.equals(mConfig)) {
return false;
}
- if (DEBUG) {
+ if (mLoggingEnabled) {
PLOG.start("brightness configuration");
}
mConfig = config;
@@ -638,9 +676,17 @@
}
@Override
- public float getBrightness(float lux) {
+ public float getBrightness(float lux, String packageName,
+ @ApplicationInfo.Category int category) {
float nits = mBrightnessSpline.interpolate(lux);
float backlight = mNitsToBacklightSpline.interpolate(nits);
+ // Correct the brightness according to the current application and its category, but
+ // only if no user data point is set (as this will oevrride the user setting).
+ if (mUserLux == -1) {
+ backlight = correctBrightness(backlight, packageName, category);
+ } else if (mLoggingEnabled) {
+ Slog.d(TAG, "user point set, correction not applied");
+ }
return backlight;
}
@@ -655,7 +701,7 @@
if (adjustment == mAutoBrightnessAdjustment) {
return false;
}
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "setAutoBrightnessAdjustment: " + mAutoBrightnessAdjustment + " => " +
adjustment);
PLOG.start("auto-brightness adjustment");
@@ -673,7 +719,7 @@
@Override
public void addUserDataPoint(float lux, float brightness) {
float unadjustedBrightness = getUnadjustedBrightness(lux);
- if (DEBUG){
+ if (mLoggingEnabled) {
Slog.d(TAG, "addUserDataPoint: (" + lux + "," + brightness + ")");
PLOG.start("add user data point")
.logPoint("user data point", lux, brightness)
@@ -682,7 +728,7 @@
float adjustment = inferAutoBrightnessAdjustment(mMaxGamma,
brightness /* desiredBrightness */,
unadjustedBrightness /* currentBrightness */);
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "addUserDataPoint: " + mAutoBrightnessAdjustment + " => " +
adjustment);
}
@@ -695,7 +741,7 @@
@Override
public void clearUserDataPoints() {
if (mUserLux != -1) {
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "clearUserDataPoints: " + mAutoBrightnessAdjustment + " => 0");
PLOG.start("clear user data points")
.logPoint("user data point", mUserLux, mUserBrightness);
@@ -758,5 +804,21 @@
Spline spline = Spline.createSpline(curve.first, curve.second);
return mNitsToBacklightSpline.interpolate(spline.interpolate(lux));
}
+
+ private float correctBrightness(float brightness, String packageName, int category) {
+ if (packageName != null) {
+ BrightnessCorrection correction = mConfig.getCorrectionByPackageName(packageName);
+ if (correction != null) {
+ return correction.apply(brightness);
+ }
+ }
+ if (category != ApplicationInfo.CATEGORY_UNDEFINED) {
+ BrightnessCorrection correction = mConfig.getCorrectionByCategory(category);
+ if (correction != null) {
+ return correction.apply(brightness);
+ }
+ }
+ return brightness;
+ }
}
}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 0a1a9a2..b1ba05c 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -2144,6 +2144,14 @@
mContext.getPackageName());
}
+ void setAutoBrightnessLoggingEnabled(boolean enabled) {
+ if (mDisplayPowerController != null) {
+ synchronized (mSyncRoot) {
+ mDisplayPowerController.setAutoBrightnessLoggingEnabled(enabled);
+ }
+ }
+ }
+
private boolean validatePackageName(int uid, String packageName) {
if (packageName != null) {
String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid);
diff --git a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
index 27cad1e..abbfc7b 100644
--- a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
+++ b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
@@ -17,14 +17,9 @@
package com.android.server.display;
import android.content.Intent;
-import android.os.RemoteException;
-import android.os.ResultReceiver;
-import android.os.ShellCallback;
import android.os.ShellCommand;
-import android.util.Slog;
import java.io.PrintWriter;
-import java.lang.NumberFormatException;
class DisplayManagerShellCommand extends ShellCommand {
private static final String TAG = "DisplayManagerShellCommand";
@@ -46,6 +41,10 @@
return setBrightness();
case "reset-brightness-configuration":
return resetBrightnessConfiguration();
+ case "ab-logging-enable":
+ return setAutoBrightnessLoggingEnabled(true);
+ case "ab-logging-disable":
+ return setAutoBrightnessLoggingEnabled(false);
default:
return handleDefaultCommands(cmd);
}
@@ -62,6 +61,10 @@
pw.println(" Sets the current brightness to BRIGHTNESS (a number between 0 and 1).");
pw.println(" reset-brightness-configuration");
pw.println(" Reset the brightness to its default configuration.");
+ pw.println(" ab-logging-enable");
+ pw.println(" Enable auto-brightness logging.");
+ pw.println(" ab-logging-disable");
+ pw.println(" Disable auto-brightness logging.");
pw.println();
Intent.printIntentArgsHelp(pw , "");
}
@@ -89,4 +92,9 @@
mService.resetBrightnessConfiguration();
return 0;
}
+
+ private int setAutoBrightnessLoggingEnabled(boolean enabled) {
+ mService.setAutoBrightnessLoggingEnabled(enabled);
+ return 0;
+ }
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 249270b..db3928e 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -461,6 +461,8 @@
+ initialLightSensorRate + ") to be less than or equal to "
+ "config_autoBrightnessLightSensorRate (" + lightSensorRate + ").");
}
+ int shortTermModelTimeout = resources.getInteger(
+ com.android.internal.R.integer.config_autoBrightnessShortTermModelTimeout);
mBrightnessMapper = BrightnessMappingStrategy.create(resources);
if (mBrightnessMapper != null) {
@@ -470,7 +472,8 @@
mScreenBrightnessRangeMaximum, dozeScaleFactor, lightSensorRate,
initialLightSensorRate, brighteningLightDebounce, darkeningLightDebounce,
autoBrightnessResetAmbientLuxAfterWarmUp, ambientBrightnessThresholds,
- screenBrightnessThresholds);
+ screenBrightnessThresholds, shortTermModelTimeout,
+ context.getPackageManager());
} else {
mUseSoftwareAutoBrightnessConfig = false;
}
@@ -1836,4 +1839,10 @@
mHandler.sendMessage(msg);
}
}
+
+ void setAutoBrightnessLoggingEnabled(boolean enabled) {
+ if (mAutomaticBrightnessController != null) {
+ mAutomaticBrightnessController.setLoggingEnabled(enabled);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/display/PersistentDataStore.java b/services/core/java/com/android/server/display/PersistentDataStore.java
index 89cef62..9aec43b 100644
--- a/services/core/java/com/android/server/display/PersistentDataStore.java
+++ b/services/core/java/com/android/server/display/PersistentDataStore.java
@@ -16,13 +16,6 @@
package com.android.server.display;
-import com.android.internal.util.FastXmlSerializer;
-import com.android.internal.util.XmlUtils;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
import android.annotation.Nullable;
import android.graphics.Point;
import android.hardware.display.BrightnessConfiguration;
@@ -30,13 +23,20 @@
import android.util.AtomicFile;
import android.util.Slog;
import android.util.SparseArray;
-import android.util.Pair;
import android.util.SparseLongArray;
import android.util.TimeUtils;
import android.util.Xml;
import android.view.Display;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.XmlUtils;
+
+import libcore.io.IoUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
@@ -50,12 +50,9 @@
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
-import java.util.List;
import java.util.Map;
import java.util.Objects;
-import libcore.io.IoUtils;
-
/**
* Manages persistent state recorded by the display manager service as an XML file.
* Caller must acquire lock on the data store before accessing it.
@@ -110,14 +107,9 @@
private static final String TAG_BRIGHTNESS_CONFIGURATIONS = "brightness-configurations";
private static final String TAG_BRIGHTNESS_CONFIGURATION = "brightness-configuration";
- private static final String TAG_BRIGHTNESS_CURVE = "brightness-curve";
- private static final String TAG_BRIGHTNESS_POINT = "brightness-point";
private static final String ATTR_USER_SERIAL = "user-serial";
private static final String ATTR_PACKAGE_NAME = "package-name";
private static final String ATTR_TIME_STAMP = "timestamp";
- private static final String ATTR_LUX = "lux";
- private static final String ATTR_NITS = "nits";
- private static final String ATTR_DESCRIPTION = "description";
// Remembered Wifi display devices.
private ArrayList<WifiDisplay> mRememberedWifiDisplays = new ArrayList<WifiDisplay>();
@@ -646,7 +638,8 @@
}
try {
- BrightnessConfiguration config = loadConfigurationFromXml(parser);
+ BrightnessConfiguration config =
+ BrightnessConfiguration.loadFromXml(parser);
if (userSerial >= 0 && config != null) {
mConfigurations.put(userSerial, config);
if (timeStamp != -1) {
@@ -663,56 +656,6 @@
}
}
- private static BrightnessConfiguration loadConfigurationFromXml(XmlPullParser parser)
- throws IOException, XmlPullParserException {
- final int outerDepth = parser.getDepth();
- String description = null;
- Pair<float[], float[]> curve = null;
- while (XmlUtils.nextElementWithin(parser, outerDepth)) {
- if (TAG_BRIGHTNESS_CURVE.equals(parser.getName())) {
- description = parser.getAttributeValue(null, ATTR_DESCRIPTION);
- curve = loadCurveFromXml(parser);
- }
- }
- if (curve == null) {
- return null;
- }
- final BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder(
- curve.first, curve.second);
- builder.setDescription(description);
- return builder.build();
- }
-
- private static Pair<float[], float[]> loadCurveFromXml(XmlPullParser parser)
- throws IOException, XmlPullParserException {
- final int outerDepth = parser.getDepth();
- List<Float> luxLevels = new ArrayList<>();
- List<Float> nitLevels = new ArrayList<>();
- while (XmlUtils.nextElementWithin(parser, outerDepth)) {
- if (TAG_BRIGHTNESS_POINT.equals(parser.getName())) {
- luxLevels.add(loadFloat(parser.getAttributeValue(null, ATTR_LUX)));
- nitLevels.add(loadFloat(parser.getAttributeValue(null, ATTR_NITS)));
- }
- }
- final int N = luxLevels.size();
- float[] lux = new float[N];
- float[] nits = new float[N];
- for (int i = 0; i < N; i++) {
- lux[i] = luxLevels.get(i);
- nits[i] = nitLevels.get(i);
- }
- return Pair.create(lux, nits);
- }
-
- private static float loadFloat(String val) {
- try {
- return Float.parseFloat(val);
- } catch (NullPointerException | NumberFormatException e) {
- Slog.e(TAG, "Failed to parse float loading brightness config", e);
- return Float.NEGATIVE_INFINITY;
- }
- }
-
public void saveToXml(XmlSerializer serializer) throws IOException {
for (int i = 0; i < mConfigurations.size(); i++) {
final int userSerial = mConfigurations.keyAt(i);
@@ -728,27 +671,11 @@
if (timestamp != -1) {
serializer.attribute(null, ATTR_TIME_STAMP, Long.toString(timestamp));
}
- saveConfigurationToXml(serializer, config);
+ config.saveToXml(serializer);
serializer.endTag(null, TAG_BRIGHTNESS_CONFIGURATION);
}
}
- private static void saveConfigurationToXml(XmlSerializer serializer,
- BrightnessConfiguration config) throws IOException {
- serializer.startTag(null, TAG_BRIGHTNESS_CURVE);
- if (config.getDescription() != null) {
- serializer.attribute(null, ATTR_DESCRIPTION, config.getDescription());
- }
- final Pair<float[], float[]> curve = config.getCurve();
- for (int i = 0; i < curve.first.length; i++) {
- serializer.startTag(null, TAG_BRIGHTNESS_POINT);
- serializer.attribute(null, ATTR_LUX, Float.toString(curve.first[i]));
- serializer.attribute(null, ATTR_NITS, Float.toString(curve.second[i]));
- serializer.endTag(null, TAG_BRIGHTNESS_POINT);
- }
- serializer.endTag(null, TAG_BRIGHTNESS_CURVE);
- }
-
public void dump(final PrintWriter pw, final String prefix) {
for (int i = 0; i < mConfigurations.size(); i++) {
final int userSerial = mConfigurations.keyAt(i);
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index ba05b49..bffa8f4 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -18,13 +18,6 @@
import static android.Manifest.permission.BIND_DREAM_SERVICE;
-import com.android.internal.hardware.AmbientDisplayConfiguration;
-import com.android.internal.util.DumpUtils;
-import com.android.server.FgThread;
-import com.android.server.LocalServices;
-import com.android.server.SystemService;
-
-import android.Manifest;
import android.app.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -53,6 +46,12 @@
import android.util.Slog;
import android.view.Display;
+import com.android.internal.hardware.AmbientDisplayConfiguration;
+import com.android.internal.util.DumpUtils;
+import com.android.server.FgThread;
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -84,6 +83,7 @@
private boolean mCurrentDreamCanDoze;
private boolean mCurrentDreamIsDozing;
private boolean mCurrentDreamIsWaking;
+ private boolean mForceAmbientDisplayEnabled;
private int mCurrentDreamDozeScreenState = Display.STATE_UNKNOWN;
private int mCurrentDreamDozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT;
@@ -139,6 +139,7 @@
pw.println("mCurrentDreamCanDoze=" + mCurrentDreamCanDoze);
pw.println("mCurrentDreamIsDozing=" + mCurrentDreamIsDozing);
pw.println("mCurrentDreamIsWaking=" + mCurrentDreamIsWaking);
+ pw.println("mForceAmbientDisplayEnabled=" + mForceAmbientDisplayEnabled);
pw.println("mCurrentDreamDozeScreenState="
+ Display.stateToString(mCurrentDreamDozeScreenState));
pw.println("mCurrentDreamDozeScreenBrightness=" + mCurrentDreamDozeScreenBrightness);
@@ -257,6 +258,16 @@
}
}
+ private void forceAmbientDisplayEnabledInternal(boolean enabled) {
+ if (DEBUG) {
+ Slog.d(TAG, "Force ambient display enabled: " + enabled);
+ }
+
+ synchronized (mLock) {
+ mForceAmbientDisplayEnabled = enabled;
+ }
+ }
+
private ComponentName chooseDreamForUser(boolean doze, int userId) {
if (doze) {
ComponentName dozeComponent = getDozeComponent(userId);
@@ -328,7 +339,7 @@
}
private ComponentName getDozeComponent(int userId) {
- if (mDozeConfig.enabled(userId)) {
+ if (mForceAmbientDisplayEnabled || mDozeConfig.enabled(userId)) {
return ComponentName.unflattenFromString(mDozeConfig.ambientDisplayComponent());
} else {
return null;
@@ -631,6 +642,18 @@
Binder.restoreCallingIdentity(ident);
}
}
+
+ @Override // Binder call
+ public void forceAmbientDisplayEnabled(boolean enabled) {
+ checkPermission(android.Manifest.permission.DEVICE_POWER);
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ forceAmbientDisplayEnabledInternal(enabled);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
}
private final class LocalService extends DreamManagerInternal {
diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java
index 5210270..1b787b8 100644
--- a/services/core/java/com/android/server/hdmi/Constants.java
+++ b/services/core/java/com/android/server/hdmi/Constants.java
@@ -273,6 +273,16 @@
// TODO(OEM): Set this to true to enable 'Set Menu Language' feature. False by default.
static final String PROPERTY_SET_MENU_LANGUAGE = "ro.hdmi.set_menu_language";
+ /**
+ * Property to disable muting logic in System Audio Control handling. Default is true.
+ *
+ * <p>True means enabling muting logic.
+ * <p>False means never mute device.
+ */
+ // TODO(OEM): Change property to ro and set to true to disable muting.
+ static final String PROPERTY_SYSTEM_AUDIO_MODE_MUTING_ENABLE =
+ "persist.sys.hdmi.property_system_audio_mode_muting_enable";
+
// Set to false to allow playback device to go to suspend mode even
// when it's an active source. True by default.
static final String PROPERTY_KEEP_AWAKE = "persist.sys.hdmi.keep_awake";
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java b/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java
index 11faa56..2da698b 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java
@@ -252,6 +252,10 @@
return (HdmiCecLocalDevicePlayback) mSource;
}
+ protected final HdmiCecLocalDeviceSource source() {
+ return (HdmiCecLocalDeviceSource) mSource;
+ }
+
protected final HdmiCecLocalDeviceTv tv() {
return (HdmiCecLocalDeviceTv) mSource;
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index 7860122..c338e21 100755
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -27,10 +27,12 @@
import android.view.InputDevice;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
+
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
import com.android.server.hdmi.HdmiControlService.SendMessageCallback;
+
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
@@ -51,6 +53,11 @@
// Within the timer, a received <User Control Pressed> will start "Press and Hold" behavior.
// When it expires, we can assume <User Control Release> is received.
private static final int FOLLOWER_SAFETY_TIMEOUT = 550;
+ /**
+ * Return value of {@link #getLocalPortFromPhysicalAddress(int)}
+ */
+ private static final int TARGET_NOT_UNDER_LOCAL_DEVICE = -1;
+ private static final int TARGET_SAME_PHYSICAL_ADDRESS = 0;
protected final HdmiControlService mService;
protected final int mDeviceType;
@@ -434,10 +441,14 @@
return true;
}
+ // Audio System device with no Playback device type
+ // needs to refactor this function if it's also a switch
protected boolean handleRoutingChange(HdmiCecMessage message) {
return false;
}
+ // Audio System device with no Playback device type
+ // needs to refactor this function if it's also a switch
protected boolean handleRoutingInformation(HdmiCecMessage message) {
return false;
}
@@ -1033,4 +1044,45 @@
pw.println("mActiveSource: " + mActiveSource);
pw.println(String.format("mActiveRoutingPath: 0x%04x", mActiveRoutingPath));
}
+
+ /**
+ * Method to parse target physical address to the port number on the current device.
+ *
+ * <p>This check assumes target address is valid.
+ * @param targetPhysicalAddress is the physical address of the target device
+ * @return
+ * <p>If the target device is under the current device, return the port number of current device
+ * that the target device is connected to.
+ *
+ * <p>If the target device has the same physical address as the current device, return
+ * {@link #TARGET_SAME_PHYSICAL_ADDRESS}.
+ *
+ * <p>If the target device is not under the current device, return
+ * {@link #TARGET_NOT_UNDER_LOCAL_DEVICE}.
+ */
+ protected int getLocalPortFromPhysicalAddress(int targetPhysicalAddress) {
+ int myPhysicalAddress = mService.getPhysicalAddress();
+ if (myPhysicalAddress == targetPhysicalAddress) {
+ return TARGET_SAME_PHYSICAL_ADDRESS;
+ }
+ int finalMask = 0xF000;
+ int mask;
+ int port = 0;
+ for (mask = 0x0F00; mask > 0x000F; mask >>= 4) {
+ if ((myPhysicalAddress & mask) == 0) {
+ port = mask & targetPhysicalAddress;
+ break;
+ } else {
+ finalMask |= mask;
+ }
+ }
+ if (finalMask != 0xFFFF && (finalMask & targetPhysicalAddress) == myPhysicalAddress) {
+ while (mask != 0x000F) {
+ mask >>= 4;
+ port >>= 4;
+ }
+ return port;
+ }
+ return TARGET_NOT_UNDER_LOCAL_DEVICE;
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
index 2ac04d1..d8a2d89 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
@@ -36,7 +36,7 @@
* Represent a logical device of type {@link HdmiDeviceInfo#DEVICE_AUDIO_SYSTEM} residing in Android
* system.
*/
-public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDevice {
+public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
private static final String TAG = "HdmiCecLocalDeviceAudioSystem";
@@ -60,16 +60,29 @@
// AVR as audio receiver.
@ServiceThreadOnly private boolean mArcEstablished = false;
+ /**
+ * Return value of {@link #getLocalPortFromPhysicalAddress(int)}
+ */
+ private static final int TARGET_NOT_UNDER_LOCAL_DEVICE = -1;
+ private static final int TARGET_SAME_PHYSICAL_ADDRESS = 0;
+
+ // Local active port number used for Routing Control.
+ // Default 0 means HOME is the current active path. Temp solution only.
+ // TODO(amyjojo): adding system constants for Atom inputs port and TIF mapping.
+ private int mLocalActivePath = 0;
+
protected HdmiCecLocalDeviceAudioSystem(HdmiControlService service) {
super(service, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
mSystemAudioControlFeatureEnabled = true;
// TODO(amyjojo) make System Audio Control controllable by users
/*mSystemAudioControlFeatureEnabled =
mService.readBooleanSetting(Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED, true);*/
+ // TODO(b/80297700): set read-only property in config instead of setting here
+ SystemProperties.set(Constants.PROPERTY_SYSTEM_AUDIO_MODE_MUTING_ENABLE, "false");
mAutoDeviceOff = mService.readBooleanSetting(
- Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED, true);
+ Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED, true);
mAutoTvOff = mService.readBooleanSetting(
- Global.HDMI_CONTROL_AUTO_TV_OFF_ENABLED, true);
+ Global.HDMI_CONTROL_AUTO_TV_OFF_ENABLED, true);
}
@Override
@@ -130,29 +143,17 @@
int systemAudioOnPowerOnProp, boolean lastSystemAudioControlStatus) {
if ((systemAudioOnPowerOnProp == ALWAYS_SYSTEM_AUDIO_CONTROL_ON_POWER_ON)
|| ((systemAudioOnPowerOnProp == USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON)
- && lastSystemAudioControlStatus)) {
+ && lastSystemAudioControlStatus)) {
addAndStartAction(new SystemAudioInitiationActionFromAvr(this));
}
}
- @ServiceThreadOnly
- protected boolean handleActiveSource(HdmiCecMessage message) {
- assertRunOnServiceThread();
- int logicalAddress = message.getSource();
- int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
- ActiveSource activeSource = ActiveSource.of(logicalAddress, physicalAddress);
- if (!mActiveSource.equals(activeSource)) {
- setActiveSource(activeSource);
- }
- return true;
- }
-
@Override
@ServiceThreadOnly
protected int getPreferredAddress() {
assertRunOnServiceThread();
return SystemProperties.getInt(
- Constants.PROPERTY_PREFERRED_ADDRESS_AUDIO_SYSTEM, Constants.ADDR_UNREGISTERED);
+ Constants.PROPERTY_PREFERRED_ADDRESS_AUDIO_SYSTEM, Constants.ADDR_UNREGISTERED);
}
@Override
@@ -296,7 +297,7 @@
for (int i = 0; i < params.length; i++) {
byte val = params[i];
audioFormatCodes[i] =
- val >= 1 && val <= Constants.AUDIO_CODEC_MAX ? val : Constants.AUDIO_CODEC_NONE;
+ val >= 1 && val <= Constants.AUDIO_CODEC_MAX ? val : Constants.AUDIO_CODEC_NONE;
}
return audioFormatCodes;
}
@@ -306,7 +307,23 @@
protected boolean handleSystemAudioModeRequest(HdmiCecMessage message) {
assertRunOnServiceThread();
boolean systemAudioStatusOn = message.getParams().length != 0;
- if (!setSystemAudioMode(systemAudioStatusOn)) {
+ // Check if the request comes from a non-TV device.
+ // Need to check if TV supports System Audio Control
+ // if non-TV device tries to turn on the feature
+ if (message.getSource() != Constants.ADDR_TV) {
+ if (systemAudioStatusOn) {
+ handleSystemAudioModeOnFromNonTvDevice(message);
+ return true;
+ }
+ } else {
+ // If TV request the feature on
+ // cache TV supporting System Audio Control
+ // until Audio System loses its physical address.
+ setTvSystemAudioModeSupport(true);
+ }
+ // If TV or Audio System does not support the feature,
+ // will send abort command.
+ if (!checkSupportAndSetSystemAudioMode(systemAudioStatusOn)) {
mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
return true;
}
@@ -321,7 +338,8 @@
@ServiceThreadOnly
protected boolean handleSetSystemAudioMode(HdmiCecMessage message) {
assertRunOnServiceThread();
- if (!setSystemAudioMode(HdmiUtils.parseCommandParamSystemAudioStatus(message))) {
+ if (!checkSupportAndSetSystemAudioMode(
+ HdmiUtils.parseCommandParamSystemAudioStatus(message))) {
mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
}
return true;
@@ -331,7 +349,8 @@
@ServiceThreadOnly
protected boolean handleSystemAudioModeStatus(HdmiCecMessage message) {
assertRunOnServiceThread();
- if (!setSystemAudioMode(HdmiUtils.parseCommandParamSystemAudioStatus(message))) {
+ if (!checkSupportAndSetSystemAudioMode(
+ HdmiUtils.parseCommandParamSystemAudioStatus(message))) {
mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
}
return true;
@@ -363,7 +382,7 @@
private void notifyArcStatusToAudioService(boolean enabled) {
// Note that we don't set any name to ARC.
mService.getAudioManager()
- .setWiredDeviceConnectionState(AudioSystem.DEVICE_IN_HDMI, enabled ? 1 : 0, "", "");
+ .setWiredDeviceConnectionState(AudioSystem.DEVICE_IN_HDMI, enabled ? 1 : 0, "", "");
}
private void reportAudioStatus(HdmiCecMessage message) {
@@ -379,7 +398,16 @@
mAddress, message.getSource(), scaledVolume, mute));
}
- protected boolean setSystemAudioMode(boolean newSystemAudioMode) {
+ /**
+ * Method to check if device support System Audio Control. If so, wake up device if necessary.
+ *
+ * <p> then call {@link #setSystemAudioMode(boolean)} to turn on or off System Audio Mode
+ * @param newSystemAudioMode turning feature on or off. True is on. False is off.
+ * @return true or false.
+ *
+ * <p>False when device does not support the feature. Otherwise returns true.
+ */
+ protected boolean checkSupportAndSetSystemAudioMode(boolean newSystemAudioMode) {
if (!isSystemAudioControlFeatureEnabled()) {
HdmiLogger.debug(
"Cannot turn "
@@ -395,11 +423,37 @@
if (newSystemAudioMode && mService.isPowerStandbyOrTransient()) {
mService.wakeUp();
}
+ setSystemAudioMode(newSystemAudioMode);
+ return true;
+ }
+
+ /**
+ * Real work to turn on or off System Audio Mode.
+ *
+ * Use {@link #checkSupportAndSetSystemAudioMode(boolean)}
+ * if trying to turn on or off the feature.
+ */
+ private void setSystemAudioMode(boolean newSystemAudioMode) {
int targetPhysicalAddress = getActiveSource().physicalAddress;
- if (newSystemAudioMode && !isPhysicalAddressMeOrBelow(targetPhysicalAddress)) {
+ int port = getLocalPortFromPhysicalAddress(targetPhysicalAddress);
+ if (newSystemAudioMode && port >= 0) {
switchToAudioInput();
}
- // TODO(b/80297700): Mute device when TV terminates the system audio control
+ // Mute device when feature is turned off and unmute device when feature is turned on.
+ // PROPERTY_SYSTEM_AUDIO_MODE_MUTING_ENABLE is false when device never needs to be muted.
+ boolean currentMuteStatus =
+ mService.getAudioManager().isStreamMute(AudioManager.STREAM_MUSIC);
+ if (SystemProperties.getBoolean(
+ Constants.PROPERTY_SYSTEM_AUDIO_MODE_MUTING_ENABLE, true)
+ && currentMuteStatus == newSystemAudioMode) {
+ mService.getAudioManager()
+ .adjustStreamVolume(
+ AudioManager.STREAM_MUSIC,
+ newSystemAudioMode
+ ? AudioManager.ADJUST_UNMUTE
+ : AudioManager.ADJUST_MUTE,
+ 0);
+ }
updateAudioManagerForSystemAudio(newSystemAudioMode);
synchronized (mLock) {
if (mSystemAudioActivated != newSystemAudioMode) {
@@ -407,31 +461,6 @@
mService.announceSystemAudioModeChange(newSystemAudioMode);
}
}
- return true;
- }
-
- /**
- * Method to check if the target device belongs to the subtree of the current device or not.
- *
- * <p>Return true if it does or if the two devices share the same physical address.
- *
- * <p>This check assumes both device physical address and target address are valid.
- *
- * @param targetPhysicalAddress is the physical address of the target device
- */
- protected boolean isPhysicalAddressMeOrBelow(int targetPhysicalAddress) {
- int myPhysicalAddress = mService.getPhysicalAddress();
- int xor = targetPhysicalAddress ^ myPhysicalAddress;
- // Return true if two addresses are the same
- // or if they only differs for one byte, but not the first byte,
- // and myPhysicalAddress is 0 after that byte
- if (xor == 0
- || ((xor & 0x0f00) == xor && (myPhysicalAddress & 0x0fff) == 0)
- || ((xor & 0x00f0) == xor && (myPhysicalAddress & 0x00ff) == 0)
- || ((xor & 0x000f) == xor && (myPhysicalAddress & 0x000f) == 0)) {
- return true;
- }
- return false;
}
protected void switchToAudioInput() {
@@ -475,7 +504,7 @@
return;
}
- if (setSystemAudioMode(false)) {
+ if (checkSupportAndSetSystemAudioMode(false)) {
// send <Set System Audio Mode> [“Off”]
mService.sendCecCommand(
HdmiCecMessageBuilder.buildSetSystemAudioMode(
@@ -498,6 +527,7 @@
* <p>The result of the query may be cached until Audio device type is put in standby or loses
* its physical address.
*/
+ // TODO(amyjojo): making mTvSystemAudioModeSupport null originally and fix the logic.
void queryTvSystemAudioModeSupport(TvSystemAudioModeSupportedCallback callback) {
if (!mTvSystemAudioModeSupport) {
addAndStartAction(new DetectTvSystemAudioModeSupportAction(this, callback));
@@ -506,6 +536,37 @@
}
}
+ /**
+ * Handler of System Audio Mode Request on from non TV device
+ */
+ void handleSystemAudioModeOnFromNonTvDevice(HdmiCecMessage message) {
+ if (!isSystemAudioControlFeatureEnabled()) {
+ HdmiLogger.debug(
+ "Cannot turn on" + "system audio mode "
+ + "because the System Audio Control feature is disabled.");
+ mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
+ return;
+ }
+ // Wake up device if it is still on standby
+ if (mService.isPowerStandbyOrTransient()) {
+ mService.wakeUp();
+ }
+ // Check if TV supports System Audio Control.
+ // Handle broadcasting setSystemAudioMode on or aborting message on callback.
+ queryTvSystemAudioModeSupport(new TvSystemAudioModeSupportedCallback() {
+ public void onResult(boolean supported) {
+ if (supported) {
+ setSystemAudioMode(true);
+ mService.sendCecCommand(
+ HdmiCecMessageBuilder.buildSetSystemAudioMode(
+ mAddress, Constants.ADDR_BROADCAST, true));
+ } else {
+ mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
+ }
+ }
+ });
+ }
+
void setTvSystemAudioModeSupport(boolean supported) {
mTvSystemAudioModeSupport = supported;
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index d45b00b..be7588a 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -26,6 +26,7 @@
import android.provider.Settings.Global;
import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.LocalePicker;
import com.android.internal.app.LocalePicker.LocaleInfo;
import com.android.internal.util.IndentingPrintWriter;
@@ -35,12 +36,10 @@
import java.util.List;
import java.util.Locale;
-import java.util.List;
-
/**
* Represent a logical device of type Playback residing in Android system.
*/
-final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDevice {
+final class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
private static final String TAG = "HdmiCecLocalDevicePlayback";
private static final boolean WAKE_ON_HOTPLUG =
@@ -62,6 +61,11 @@
// If true, turn off TV upon standby. False by default.
private boolean mAutoTvOff;
+ // Local active port number used for Routing Control.
+ // Default 0 means HOME is the current active path. Temp solution only.
+ // TODO(amyjojo): adding system constants for input ports to TIF mapping.
+ private int mLocalActivePath = 0;
+
HdmiCecLocalDevicePlayback(HdmiControlService service) {
super(service, HdmiDeviceInfo.DEVICE_PLAYBACK);
@@ -100,25 +104,6 @@
}
@ServiceThreadOnly
- void oneTouchPlay(IHdmiControlCallback callback) {
- assertRunOnServiceThread();
- List<OneTouchPlayAction> actions = getActions(OneTouchPlayAction.class);
- if (!actions.isEmpty()) {
- Slog.i(TAG, "oneTouchPlay already in progress");
- actions.get(0).addCallback(callback);
- return;
- }
- OneTouchPlayAction action = OneTouchPlayAction.create(this, Constants.ADDR_TV,
- callback);
- if (action == null) {
- Slog.w(TAG, "Cannot initiate oneTouchPlay");
- invokeCallback(callback, HdmiControlManager.RESULT_EXCEPTION);
- return;
- }
- addAndStartAction(action);
- }
-
- @ServiceThreadOnly
void queryDisplayStatus(IHdmiControlCallback callback) {
assertRunOnServiceThread();
List<DevicePowerStatusAction> actions = getActions(DevicePowerStatusAction.class);
@@ -227,21 +212,6 @@
return !getWakeLock().isHeld();
}
- @Override
- @ServiceThreadOnly
- protected boolean handleActiveSource(HdmiCecMessage message) {
- assertRunOnServiceThread();
- int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
- mayResetActiveSource(physicalAddress);
- return true; // Broadcast message.
- }
-
- private void mayResetActiveSource(int physicalAddress) {
- if (physicalAddress != mService.getPhysicalAddress()) {
- setActiveSource(false);
- }
- }
-
@ServiceThreadOnly
protected boolean handleUserControlPressed(HdmiCecMessage message) {
assertRunOnServiceThread();
@@ -254,10 +224,21 @@
protected boolean handleSetStreamPath(HdmiCecMessage message) {
assertRunOnServiceThread();
int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
- maySetActiveSource(physicalAddress);
- maySendActiveSource(message.getSource());
- wakeUpIfActiveSource();
- return true; // Broadcast message.
+ // If current device is the target path, set to Active Source.
+ // If the path is under the current device, should switch
+ int port = getLocalPortFromPhysicalAddress(physicalAddress);
+ if (port == 0) {
+ setActiveSource(true);
+ maySendActiveSource(message.getSource());
+ wakeUpIfActiveSource();
+ } else if (port > 0) {
+ // Wake up the device if the power is in standby mode for routing
+ if (mService.isPowerStandbyOrTransient()) {
+ mService.wakeUp();
+ }
+ routeToPort(port);
+ }
+ return true;
}
// Samsung model we tested sends <Routing Change> and <Request Active Source>
@@ -306,14 +287,6 @@
}
}
- @Override
- @ServiceThreadOnly
- protected boolean handleRequestActiveSource(HdmiCecMessage message) {
- assertRunOnServiceThread();
- maySendActiveSource(message.getSource());
- return true; // Broadcast message.
- }
-
@ServiceThreadOnly
protected boolean handleSetMenuLanguage(HdmiCecMessage message) {
assertRunOnServiceThread();
@@ -383,6 +356,16 @@
checkIfPendingActionsCleared();
}
+ private void routeToPort(int portId) {
+ // TODO(AMYJOJO): route to specific input of the port
+ mLocalActivePath = portId;
+ }
+
+ @VisibleForTesting
+ protected int getLocalActivePath() {
+ return mLocalActivePath;
+ }
+
@Override
protected void dump(final IndentingPrintWriter pw) {
super.dump(pw);
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
new file mode 100644
index 0000000..f9180b7
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceSource.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.hdmi;
+
+import android.hardware.hdmi.HdmiControlManager;
+import android.hardware.hdmi.IHdmiControlCallback;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
+
+import java.util.List;
+
+/**
+ * Represent a logical source device residing in Android system.
+ */
+abstract class HdmiCecLocalDeviceSource extends HdmiCecLocalDevice {
+
+ private static final String TAG = "HdmiCecLocalDeviceSource";
+
+ private boolean mIsActiveSource = false;
+
+ protected HdmiCecLocalDeviceSource(HdmiControlService service, int deviceType) {
+ super(service, deviceType);
+ }
+
+ @Override
+ @ServiceThreadOnly
+ void onHotplug(int portId, boolean connected) {
+ assertRunOnServiceThread();
+ mCecMessageCache.flushAll();
+ // We'll not clear mIsActiveSource on the hotplug event to pass CETC 11.2.2-2 ~ 3.
+ if (mService.isPowerStandbyOrTransient()) {
+ mService.wakeUp();
+ }
+ }
+
+ @ServiceThreadOnly
+ void oneTouchPlay(IHdmiControlCallback callback) {
+ assertRunOnServiceThread();
+ List<OneTouchPlayAction> actions = getActions(OneTouchPlayAction.class);
+ if (!actions.isEmpty()) {
+ Slog.i(TAG, "oneTouchPlay already in progress");
+ actions.get(0).addCallback(callback);
+ return;
+ }
+ OneTouchPlayAction action = OneTouchPlayAction.create(this, Constants.ADDR_TV,
+ callback);
+ if (action == null) {
+ Slog.w(TAG, "Cannot initiate oneTouchPlay");
+ invokeCallback(callback, HdmiControlManager.RESULT_EXCEPTION);
+ return;
+ }
+ addAndStartAction(action);
+ }
+
+ @ServiceThreadOnly
+ private void invokeCallback(IHdmiControlCallback callback, int result) {
+ assertRunOnServiceThread();
+ try {
+ callback.onComplete(result);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Invoking callback failed:" + e);
+ }
+ }
+
+ @ServiceThreadOnly
+ protected boolean handleActiveSource(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+ int logicalAddress = message.getSource();
+ int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
+ ActiveSource activeSource = ActiveSource.of(logicalAddress, physicalAddress);
+ if (physicalAddress != mService.getPhysicalAddress()
+ || !mActiveSource.equals(activeSource)) {
+ setActiveSource(activeSource);
+ setActiveSource(false);
+ }
+ return true;
+ }
+
+ @Override
+ @ServiceThreadOnly
+ protected boolean handleRequestActiveSource(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+ if (mIsActiveSource) {
+ mService.sendCecCommand(HdmiCecMessageBuilder.buildActiveSource(
+ mAddress, mService.getPhysicalAddress()));
+ }
+ return true;
+ }
+
+ @ServiceThreadOnly
+ void setActiveSource(boolean on) {
+ assertRunOnServiceThread();
+ mIsActiveSource = on;
+ }
+}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index e3a4084..10f6f92 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -1830,9 +1830,13 @@
@ServiceThreadOnly
private void oneTouchPlay(final IHdmiControlCallback callback) {
assertRunOnServiceThread();
- HdmiCecLocalDevicePlayback source = playback();
+ HdmiCecLocalDeviceSource source = playback();
if (source == null) {
- Slog.w(TAG, "Local playback device not available");
+ source = audioSystem();
+ }
+
+ if (source == null) {
+ Slog.w(TAG, "Local source device not available");
invokeCallback(callback, HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE);
return;
}
diff --git a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
index 5c66316..c7ba7cc 100644
--- a/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
+++ b/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
@@ -16,8 +16,8 @@
package com.android.server.hdmi;
import android.hardware.hdmi.HdmiControlManager;
-import android.hardware.hdmi.IHdmiControlCallback;
import android.hardware.hdmi.HdmiPlaybackClient.OneTouchPlayCallback;
+import android.hardware.hdmi.IHdmiControlCallback;
import android.os.RemoteException;
import android.util.Slog;
@@ -55,7 +55,7 @@
private int mPowerStatusCounter = 0;
// Factory method. Ensures arguments are valid.
- static OneTouchPlayAction create(HdmiCecLocalDevicePlayback source,
+ static OneTouchPlayAction create(HdmiCecLocalDeviceSource source,
int targetAddress, IHdmiControlCallback callback) {
if (source == null || callback == null) {
Slog.e(TAG, "Wrong arguments");
@@ -84,8 +84,8 @@
private void broadcastActiveSource() {
sendCommand(HdmiCecMessageBuilder.buildActiveSource(getSourceAddress(), getSourcePath()));
- // Because only playback device can create this action, it's safe to cast.
- playback().setActiveSource(true);
+ // Because only source device can create this action, it's safe to cast.
+ source().setActiveSource(true);
}
private void queryDevicePowerStatus() {
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioInitiationActionFromAvr.java b/services/core/java/com/android/server/hdmi/SystemAudioInitiationActionFromAvr.java
index 2fdcb51..a6e6965 100644
--- a/services/core/java/com/android/server/hdmi/SystemAudioInitiationActionFromAvr.java
+++ b/services/core/java/com/android/server/hdmi/SystemAudioInitiationActionFromAvr.java
@@ -16,6 +16,7 @@
package com.android.server.hdmi;
import android.hardware.tv.cec.V1_0.SendMessageResult;
+
import com.android.internal.annotations.VisibleForTesting;
/**
@@ -91,7 +92,7 @@
mSendRequestActiveSourceRetryCount++;
sendRequestActiveSource();
} else {
- audioSystem().setSystemAudioMode(false);
+ audioSystem().checkSupportAndSetSystemAudioMode(false);
finish();
}
}
@@ -106,7 +107,7 @@
mSendSetSystemAudioModeRetryCount++;
sendSetSystemAudioMode(on, dest);
} else {
- audioSystem().setSystemAudioMode(false);
+ audioSystem().checkSupportAndSetSystemAudioMode(false);
finish();
}
}
@@ -115,7 +116,7 @@
private void handleActiveSourceTimeout() {
HdmiLogger.debug("Cannot get active source.");
- audioSystem().setSystemAudioMode(false);
+ audioSystem().checkSupportAndSetSystemAudioMode(false);
finish();
}
@@ -123,12 +124,12 @@
audioSystem().queryTvSystemAudioModeSupport(
supported -> {
if (supported) {
- if (audioSystem().setSystemAudioMode(true)) {
+ if (audioSystem().checkSupportAndSetSystemAudioMode(true)) {
sendSetSystemAudioMode(true, Constants.ADDR_BROADCAST);
}
finish();
} else {
- audioSystem().setSystemAudioMode(false);
+ audioSystem().checkSupportAndSetSystemAudioMode(false);
finish();
}
});
diff --git a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
index a3ebe24..8e26097 100644
--- a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
+++ b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
@@ -274,10 +274,14 @@
}
synchronized (mLock) {
- final S service = getServiceForUserLocked(userId);
- if (service != null) {
- service.setTemporaryServiceLocked(componentName, durationMs);
+ final S oldService = peekServiceForUserLocked(userId);
+ if (oldService != null) {
+ oldService.removeSelfFromCacheLocked();
}
+ mServiceNameResolver.setTemporaryService(userId, componentName, durationMs);
+
+ // Must update the service on cache so its initialization code is triggered
+ updateCachedServiceLocked(userId);
}
}
@@ -500,6 +504,7 @@
protected void dumpLocked(@NonNull String prefix, @NonNull PrintWriter pw) {
boolean realDebug = debug;
boolean realVerbose = verbose;
+ final String prefix2 = " ";
try {
// Temporarily turn on full logging;
@@ -510,6 +515,13 @@
if (mServiceNameResolver != null) {
pw.print(prefix); pw.print("Name resolver: ");
mServiceNameResolver.dumpShort(pw); pw.println();
+ final UserManager um = getContext().getSystemService(UserManager.class);
+ final List<UserInfo> users = um.getUsers();
+ for (int i = 0; i < users.size(); i++) {
+ final int userId = users.get(i).id;
+ pw.print(prefix2); pw.print(userId); pw.print(": ");
+ mServiceNameResolver.dumpShort(pw, userId); pw.println();
+ }
}
pw.print(prefix); pw.print("Disabled users: "); pw.println(mDisabledUsers);
pw.print(prefix); pw.print("Allow instant service: "); pw.println(mAllowInstantService);
@@ -522,7 +534,6 @@
pw.println("none");
} else {
pw.println(size);
- final String prefix2 = " ";
for (int i = 0; i < size; i++) {
pw.print(prefix); pw.print("Service at "); pw.print(i); pw.println(": ");
final S service = mServicesCache.valueAt(i);
diff --git a/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java b/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java
index 556e489..cd9ebcd 100644
--- a/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java
+++ b/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java
@@ -214,7 +214,7 @@
/**
* Gets the current name of the service, which is either the default service or the
- * {@link #setTemporaryServiceLocked(String, int) temporary one}.
+ * {@link AbstractMasterSystemService#setTemporaryService(int, String, int) temporary one}.
*/
protected final @Nullable String getComponentNameLocked() {
return mMaster.mServiceNameResolver.getServiceName(mUserId);
@@ -228,17 +228,6 @@
}
/**
- * Temporary sets the service implementation.
- *
- * @param componentName name of the new component
- * @param durationMs how long the change will be valid (the service will be automatically reset
- * to the default component after this timeout expires).
- */
- protected final void setTemporaryServiceLocked(@NonNull String componentName, int durationMs) {
- mMaster.mServiceNameResolver.setTemporaryService(mUserId, componentName, durationMs);
- }
-
- /**
* Resets the temporary service implementation to the default component.
*/
protected final void resetTemporaryServiceLocked() {
diff --git a/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java b/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java
index 7027246..7f198ac 100644
--- a/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java
+++ b/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java
@@ -159,7 +159,7 @@
@Override
public String toString() {
- return "FrameworkResourcesServiceNamer: temps=" + mTemporaryServiceNames;
+ return "FrameworkResourcesServiceNamer[temps=" + mTemporaryServiceNames + "]";
}
// TODO(b/117779333): support proto
diff --git a/services/core/java/com/android/server/infra/SecureSettingsServiceNameResolver.java b/services/core/java/com/android/server/infra/SecureSettingsServiceNameResolver.java
index bcaa918..cac7f53 100644
--- a/services/core/java/com/android/server/infra/SecureSettingsServiceNameResolver.java
+++ b/services/core/java/com/android/server/infra/SecureSettingsServiceNameResolver.java
@@ -56,4 +56,9 @@
public void dumpShort(@NonNull PrintWriter pw, @UserIdInt int userId) {
pw.print("defaultService="); pw.print(getDefaultServiceName(userId));
}
+
+ @Override
+ public String toString() {
+ return "SecureSettingsServiceNameResolver[" + mProperty + "]";
+ }
}
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index d96b6cb..e7c3c7b 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -1951,6 +1951,11 @@
}
// Native callback.
+ private int getPointerDisplayId() {
+ return mWindowManagerCallbacks.getPointerDisplayId();
+ }
+
+ // Native callback.
private String[] getKeyboardLayoutOverlay(InputDeviceIdentifier identifier) {
if (!mSystemReady) {
return null;
@@ -2017,6 +2022,8 @@
KeyEvent event, int policyFlags);
public int getPointerLayer();
+
+ public int getPointerDisplayId();
}
/**
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 5a7739c..840c6e4 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -291,6 +291,8 @@
new DebugFlag("debug.optimize_startinput", false);
}
+ @UserIdInt
+ private int mLastSwitchUserId;
final Context mContext;
final Resources mRes;
@@ -1436,6 +1438,8 @@
Slog.w(TAG, "Couldn't get current user ID; guessing it's 0", e);
}
+ mLastSwitchUserId = userId;
+
// mSettings should be created before buildInputMethodListLocked
mSettings = new InputMethodSettings(
mRes, context.getContentResolver(), mMethodMap, mMethodList, userId, !mSystemReady);
@@ -1523,6 +1527,8 @@
if (DEBUG) Slog.d(TAG, "Switching user stage 3/3. newUserId=" + newUserId
+ " selectedIme=" + mSettings.getSelectedInputMethod());
+
+ mLastSwitchUserId = newUserId;
}
void updateCurrentProfileIds() {
@@ -1668,40 +1674,93 @@
@Override
public List<InputMethodInfo> getInputMethodList() {
- return getInputMethodList(false /* isVrOnly */);
+ final int callingUserId = UserHandle.getCallingUserId();
+ synchronized (mMethodMap) {
+ final int[] resolvedUserIds = InputMethodUtils.resolveUserId(callingUserId,
+ mSettings.getCurrentUserId(), null);
+ if (resolvedUserIds.length != 1) {
+ return Collections.emptyList();
+ }
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ return getInputMethodListLocked(false /* isVrOnly */, resolvedUserIds[0]);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
}
@Override
public List<InputMethodInfo> getVrInputMethodList() {
- return getInputMethodList(true /* isVrOnly */);
- }
-
- private List<InputMethodInfo> getInputMethodList(final boolean isVrOnly) {
+ final int callingUserId = UserHandle.getCallingUserId();
synchronized (mMethodMap) {
- // TODO: Make this work even for non-current users?
- if (!calledFromValidUserLocked()) {
+ final int[] resolvedUserIds = InputMethodUtils.resolveUserId(callingUserId,
+ mSettings.getCurrentUserId(), null);
+ if (resolvedUserIds.length != 1) {
return Collections.emptyList();
}
- ArrayList<InputMethodInfo> methodList = new ArrayList<>();
- for (InputMethodInfo info : mMethodList) {
-
- if (info.isVrOnly() == isVrOnly) {
- methodList.add(info);
- }
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ return getInputMethodListLocked(true /* isVrOnly */, resolvedUserIds[0]);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
- return methodList;
}
}
@Override
public List<InputMethodInfo> getEnabledInputMethodList() {
+ final int callingUserId = UserHandle.getCallingUserId();
synchronized (mMethodMap) {
- // TODO: Make this work even for non-current users?
- if (!calledFromValidUserLocked()) {
+ final int[] resolvedUserIds = InputMethodUtils.resolveUserId(callingUserId,
+ mSettings.getCurrentUserId(), null);
+ if (resolvedUserIds.length != 1) {
return Collections.emptyList();
}
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ return getEnabledInputMethodListLocked(resolvedUserIds[0]);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ @GuardedBy("mMethodMap")
+ private List<InputMethodInfo> getInputMethodListLocked(boolean isVrOnly,
+ @UserIdInt int userId) {
+ final ArrayList<InputMethodInfo> methodList;
+ if (userId == mSettings.getCurrentUserId()) {
+ // Create a copy.
+ methodList = new ArrayList<>(mMethodList);
+ } else {
+ final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>();
+ methodList = new ArrayList<>();
+ final ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap =
+ new ArrayMap<>();
+ AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
+ queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap, methodMap,
+ methodList);
+ }
+ methodList.removeIf(imi -> imi.isVrOnly() != isVrOnly);
+ return methodList;
+ }
+
+ @GuardedBy("mMethodMap")
+ private List<InputMethodInfo> getEnabledInputMethodListLocked(@UserIdInt int userId) {
+ if (userId == mSettings.getCurrentUserId()) {
return mSettings.getEnabledInputMethodListLocked();
}
+ final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>();
+ final ArrayList<InputMethodInfo> methodList = new ArrayList<>();
+ final ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap =
+ new ArrayMap<>();
+ AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
+ queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap, methodMap,
+ methodList);
+ final InputMethodSettings settings = new InputMethodSettings(mContext.getResources(),
+ mContext.getContentResolver(), methodMap, methodList, userId, true);
+ return settings.getEnabledInputMethodListLocked();
}
/**
@@ -1711,11 +1770,27 @@
@Override
public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(String imiId,
boolean allowsImplicitlySelectedSubtypes) {
+ final int callingUserId = UserHandle.getCallingUserId();
synchronized (mMethodMap) {
- // TODO: Make this work even for non-current users?
- if (!calledFromValidUserLocked()) {
+ final int[] resolvedUserIds = InputMethodUtils.resolveUserId(callingUserId,
+ mSettings.getCurrentUserId(), null);
+ if (resolvedUserIds.length != 1) {
return Collections.emptyList();
}
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ return getEnabledInputMethodSubtypeListLocked(imiId,
+ allowsImplicitlySelectedSubtypes, resolvedUserIds[0]);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ @GuardedBy("mMethodMap")
+ private List<InputMethodSubtype> getEnabledInputMethodSubtypeListLocked(String imiId,
+ boolean allowsImplicitlySelectedSubtypes, @UserIdInt int userId) {
+ if (userId == mSettings.getCurrentUserId()) {
final InputMethodInfo imi;
if (imiId == null && mCurMethodId != null) {
imi = mMethodMap.get(mCurMethodId);
@@ -1728,6 +1803,21 @@
return mSettings.getEnabledInputMethodSubtypeListLocked(
mContext, imi, allowsImplicitlySelectedSubtypes);
}
+ final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>();
+ final ArrayList<InputMethodInfo> methodList = new ArrayList<>();
+ final ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap =
+ new ArrayMap<>();
+ AdditionalSubtypeUtils.load(additionalSubtypeMap, userId);
+ queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap, methodMap,
+ methodList);
+ final InputMethodInfo imi = methodMap.get(imiId);
+ if (imi == null) {
+ return Collections.emptyList();
+ }
+ final InputMethodSettings settings = new InputMethodSettings(mContext.getResources(),
+ mContext.getContentResolver(), methodMap, methodList, userId, true);
+ return settings.getEnabledInputMethodSubtypeListLocked(
+ mContext, imi, allowsImplicitlySelectedSubtypes);
}
/**
@@ -4424,6 +4514,10 @@
return refreshDebugProperties();
}
+ if ("get-last-switch-user-id".equals(cmd)) {
+ return mService.getLastSwitchUserId(this);
+ }
+
// For existing "adb shell ime <command>".
if ("ime".equals(cmd)) {
final String imeCommand = getNextArg();
@@ -4516,6 +4610,15 @@
// ----------------------------------------------------------------------
// Shell command handlers:
+ @BinderThread
+ @ShellCommandResult
+ private int getLastSwitchUserId(@NonNull ShellCommand shellCommand) {
+ synchronized (mMethodMap) {
+ shellCommand.getOutPrintWriter().println(mLastSwitchUserId);
+ return ShellCommandResult.SUCCESS;
+ }
+ }
+
/**
* Handles {@code adb shell ime list}.
* @param shellCommand {@link ShellCommand} object that is handling this command.
@@ -4526,6 +4629,7 @@
private int handleShellCommandListInputMethods(@NonNull ShellCommand shellCommand) {
boolean all = false;
boolean brief = false;
+ int userIdToBeResolved = UserHandle.USER_CURRENT;
while (true) {
final String nextOption = shellCommand.getNextOption();
if (nextOption == null) {
@@ -4538,19 +4642,34 @@
case "-s":
brief = true;
break;
+ case "-u":
+ case "--user":
+ userIdToBeResolved = UserHandle.parseUserArg(shellCommand.getNextArgRequired());
+ break;
}
}
- final List<InputMethodInfo> methods = all ?
- getInputMethodList() : getEnabledInputMethodList();
- final PrintWriter pr = shellCommand.getOutPrintWriter();
- final Printer printer = x -> pr.println(x);
- final int N = methods.size();
- for (int i = 0; i < N; ++i) {
- if (brief) {
- pr.println(methods.get(i).getId());
- } else {
- pr.print(methods.get(i).getId()); pr.println(":");
- methods.get(i).dump(printer, " ");
+ synchronized (mMethodMap) {
+ final PrintWriter pr = shellCommand.getOutPrintWriter();
+ final int[] userIds = InputMethodUtils.resolveUserId(userIdToBeResolved,
+ mSettings.getCurrentUserId(), shellCommand.getErrPrintWriter());
+ for (int userId : userIds) {
+ final List<InputMethodInfo> methods = all
+ ? getInputMethodListLocked(false, userId)
+ : getEnabledInputMethodListLocked(userId);
+ if (userIds.length > 1) {
+ pr.print("User #");
+ pr.print(userId);
+ pr.println(":");
+ }
+ for (InputMethodInfo info : methods) {
+ if (brief) {
+ pr.println(info.getId());
+ } else {
+ pr.print(info.getId());
+ pr.println(":");
+ info.dump(pr::println, " ");
+ }
+ }
}
}
return ShellCommandResult.SUCCESS;
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
index 1137bf9..2f76871 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java
@@ -29,9 +29,12 @@
import android.os.Build;
import android.os.LocaleList;
import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManagerInternal;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.ArrayMap;
+import android.util.IntArray;
import android.util.Pair;
import android.util.Printer;
import android.util.Slog;
@@ -42,8 +45,10 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.inputmethod.StartInputFlags;
+import com.android.server.LocalServices;
import com.android.server.textservices.TextServicesManagerInternal;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
@@ -1286,4 +1291,56 @@
return true;
}
+ /**
+ * Converts a user ID, which can be a pseudo user ID such as {@link UserHandle#USER_ALL} to a
+ * list of real user IDs.
+ *
+ * <p>Currently this method also converts profile user ID to profile parent user ID.</p>
+ *
+ * @param userIdToBeResolved A user ID. Two pseudo user ID {@link UserHandle#USER_CURRENT} and
+ * {@link UserHandle#USER_ALL} are also supported
+ * @param currentUserId A real user ID, which will be used when {@link UserHandle#USER_CURRENT}
+ * is specified in {@code userIdToBeResolved}.
+ * @param warningWriter A {@link PrintWriter} to output some debug messages. {@code null} if
+ * no debug message is required.
+ * @return An integer array that contain user IDs.
+ */
+ static int[] resolveUserId(@UserIdInt int userIdToBeResolved,
+ @UserIdInt int currentUserId, @Nullable PrintWriter warningWriter) {
+ final UserManagerInternal userManagerInternal =
+ LocalServices.getService(UserManagerInternal.class);
+
+ if (userIdToBeResolved == UserHandle.USER_ALL) {
+ final IntArray result = new IntArray();
+ for (int userId : userManagerInternal.getUserIds()) {
+ final int parentUserId = userManagerInternal.getProfileParentId(userId);
+ if (result.indexOf(parentUserId) < 0) {
+ result.add(parentUserId);
+ }
+ }
+ return result.toArray();
+ }
+
+ final int sourceUserId;
+ if (userIdToBeResolved == UserHandle.USER_CURRENT) {
+ sourceUserId = currentUserId;
+ } else if (userIdToBeResolved < 0) {
+ if (warningWriter != null) {
+ warningWriter.print("Pseudo user ID ");
+ warningWriter.print(userIdToBeResolved);
+ warningWriter.println(" is not supported.");
+ }
+ return new int[]{};
+ } else if (userManagerInternal.exists(userIdToBeResolved)) {
+ sourceUserId = userIdToBeResolved;
+ } else {
+ if (warningWriter != null) {
+ warningWriter.print("User #");
+ warningWriter.print(userIdToBeResolved);
+ warningWriter.println(" does not exit.");
+ }
+ return new int[]{};
+ }
+ return new int[]{userManagerInternal.getProfileParentId(sourceUserId)};
+ }
}
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 3f9d928..2464ca7 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -332,6 +332,44 @@
}
}
+ private static class MaxJobCounts {
+ private final KeyValueListParser.IntValue mTotal;
+ private final KeyValueListParser.IntValue mBg;
+
+ private MaxJobCounts(int totalDefault, String totalKey, int bgDefault, String bgKey) {
+ mTotal = new KeyValueListParser.IntValue(totalKey, totalDefault);
+ mBg = new KeyValueListParser.IntValue(bgKey, bgDefault);
+ }
+
+ public void parse(KeyValueListParser parser) {
+ mTotal.parse(parser);
+ mBg.parse(parser);
+
+ if (mBg.getValue() > mTotal.getValue()) {
+ mBg.setValue(mTotal.getValue());
+ }
+
+ }
+
+ public int getTotalMax() {
+ return mTotal.getValue();
+ }
+
+ public int getBgMax() {
+ return mBg.getValue();
+ }
+
+ public void dump(PrintWriter pw, String prefix) {
+ mTotal.dump(pw, prefix);
+ mBg.dump(pw, prefix);
+ }
+
+ public void dumpProto(ProtoOutputStream proto, long tagTotal, long tagBg) {
+ mTotal.dumpProto(proto, tagTotal);
+ mBg.dumpProto(proto, tagBg);
+ }
+ }
+
/**
* All times are in milliseconds. These constants are kept synchronized with the system
* global Settings. Any access to this class or its fields should be done while
@@ -492,6 +530,43 @@
* memory state.
*/
int BG_CRITICAL_JOB_COUNT = DEFAULT_BG_CRITICAL_JOB_COUNT;
+
+ // Max job counts for screen on / off, for each memory trim level.
+ // TODO Remove the old configs such as FG_JOB_COUNT and BG_*_COUNT, once the code switches
+ // to the below configs.
+
+ final MaxJobCounts MAX_JOB_COUNTS_ON_NORMAL = new MaxJobCounts(
+ 4, "max_job_total_on_normal",
+ 2, "max_job_bg_on_normal");
+
+ final MaxJobCounts MAX_JOB_COUNTS_ON_MODERATE = new MaxJobCounts(
+ 4, "max_job_total_on_moderate",
+ 1, "max_job_bg_on_moderate");
+
+ final MaxJobCounts MAX_JOB_COUNTS_ON_LOW = new MaxJobCounts(
+ 4, "max_job_total_on_low",
+ 1, "max_job_bg_on_low");
+
+ final MaxJobCounts MAX_JOB_COUNTS_ON_CRITICAL = new MaxJobCounts(
+ 2, "max_job_total_on_critical",
+ 1, "max_job_bg_on_critical");
+
+ final MaxJobCounts MAX_JOB_COUNTS_OFF_NORMAL = new MaxJobCounts(
+ 8, "max_job_total_off_normal",
+ 4, "max_job_bg_off_normal");
+
+ final MaxJobCounts MAX_JOB_COUNTS_OFF_MODERATE = new MaxJobCounts(
+ 6, "max_job_total_off_moderate",
+ 4, "max_job_bg_off_moderate");
+
+ final MaxJobCounts MAX_JOB_COUNTS_OFF_LOW = new MaxJobCounts(
+ 4, "max_job_total_off_low",
+ 1, "max_job_bg_off_low");
+
+ final MaxJobCounts MAX_JOB_COUNTS_OFF_CRITICAL = new MaxJobCounts(
+ 2, "max_job_total_off_critical",
+ 1, "max_job_bg_off_critical");
+
/**
* The maximum number of times we allow a job to have itself rescheduled before
* giving up on it, for standard jobs.
@@ -566,7 +641,7 @@
/**
* The quota window size of the particular standby bucket. Apps in this standby bucket are
- * expected to run only {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
+ * expected to run only {@link #QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
* WINDOW_SIZE_MS.
*/
public long QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS =
@@ -574,7 +649,7 @@
/**
* The quota window size of the particular standby bucket. Apps in this standby bucket are
- * expected to run only {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
+ * expected to run only {@link #QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
* WINDOW_SIZE_MS.
*/
public long QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS =
@@ -582,7 +657,7 @@
/**
* The quota window size of the particular standby bucket. Apps in this standby bucket are
- * expected to run only {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
+ * expected to run only {@link #QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
* WINDOW_SIZE_MS.
*/
public long QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS =
@@ -590,7 +665,7 @@
/**
* The quota window size of the particular standby bucket. Apps in this standby bucket are
- * expected to run only {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
+ * expected to run only {@link #QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
* WINDOW_SIZE_MS.
*/
public long QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS =
@@ -653,6 +728,17 @@
if ((FG_JOB_COUNT+BG_CRITICAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
BG_CRITICAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
}
+
+ MAX_JOB_COUNTS_ON_NORMAL.parse(mParser);
+ MAX_JOB_COUNTS_ON_MODERATE.parse(mParser);
+ MAX_JOB_COUNTS_ON_LOW.parse(mParser);
+ MAX_JOB_COUNTS_ON_CRITICAL.parse(mParser);
+
+ MAX_JOB_COUNTS_OFF_NORMAL.parse(mParser);
+ MAX_JOB_COUNTS_OFF_MODERATE.parse(mParser);
+ MAX_JOB_COUNTS_OFF_LOW.parse(mParser);
+ MAX_JOB_COUNTS_OFF_CRITICAL.parse(mParser);
+
MAX_STANDARD_RESCHEDULE_COUNT = mParser.getInt(KEY_MAX_STANDARD_RESCHEDULE_COUNT,
DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT);
MAX_WORK_RESCHEDULE_COUNT = mParser.getInt(KEY_MAX_WORK_RESCHEDULE_COUNT,
@@ -717,6 +803,17 @@
pw.printPair(KEY_BG_MODERATE_JOB_COUNT, BG_MODERATE_JOB_COUNT).println();
pw.printPair(KEY_BG_LOW_JOB_COUNT, BG_LOW_JOB_COUNT).println();
pw.printPair(KEY_BG_CRITICAL_JOB_COUNT, BG_CRITICAL_JOB_COUNT).println();
+
+ MAX_JOB_COUNTS_ON_NORMAL.dump(pw, "");
+ MAX_JOB_COUNTS_ON_MODERATE.dump(pw, "");
+ MAX_JOB_COUNTS_ON_LOW.dump(pw, "");
+ MAX_JOB_COUNTS_ON_CRITICAL.dump(pw, "");
+
+ MAX_JOB_COUNTS_OFF_NORMAL.dump(pw, "");
+ MAX_JOB_COUNTS_OFF_MODERATE.dump(pw, "");
+ MAX_JOB_COUNTS_OFF_LOW.dump(pw, "");
+ MAX_JOB_COUNTS_OFF_CRITICAL.dump(pw, "");
+
pw.printPair(KEY_MAX_STANDARD_RESCHEDULE_COUNT, MAX_STANDARD_RESCHEDULE_COUNT).println();
pw.printPair(KEY_MAX_WORK_RESCHEDULE_COUNT, MAX_WORK_RESCHEDULE_COUNT).println();
pw.printPair(KEY_MIN_LINEAR_BACKOFF_TIME, MIN_LINEAR_BACKOFF_TIME).println();
@@ -767,6 +864,9 @@
proto.write(ConstantsProto.BG_MODERATE_JOB_COUNT, BG_MODERATE_JOB_COUNT);
proto.write(ConstantsProto.BG_LOW_JOB_COUNT, BG_LOW_JOB_COUNT);
proto.write(ConstantsProto.BG_CRITICAL_JOB_COUNT, BG_CRITICAL_JOB_COUNT);
+
+ // TODO Dump max job counts.
+
proto.write(ConstantsProto.MAX_STANDARD_RESCHEDULE_COUNT, MAX_STANDARD_RESCHEDULE_COUNT);
proto.write(ConstantsProto.MAX_WORK_RESCHEDULE_COUNT, MAX_WORK_RESCHEDULE_COUNT);
proto.write(ConstantsProto.MIN_LINEAR_BACKOFF_TIME_MS, MIN_LINEAR_BACKOFF_TIME);
diff --git a/services/core/java/com/android/server/location/GnssConfiguration.java b/services/core/java/com/android/server/location/GnssConfiguration.java
new file mode 100644
index 0000000..29465ad
--- /dev/null
+++ b/services/core/java/com/android/server/location/GnssConfiguration.java
@@ -0,0 +1,365 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location;
+
+import android.content.Context;
+import android.os.PersistableBundle;
+import android.os.SystemProperties;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+import libcore.io.IoUtils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Properties;
+
+/**
+ * A utility class to hold GNSS configuration properties.
+ *
+ * The trigger to load/reload the configuration parameters should be managed by the class
+ * that owns an instance of this class.
+ *
+ * Instances of this class are not thread-safe and should either be used from a single thread
+ * or with external synchronization when used by multiple threads.
+ */
+class GnssConfiguration {
+ private static final String TAG = "GnssConfiguration";
+
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ //TODO(b/33112647): Create gps_debug.conf with commented career parameters.
+ private static final String DEBUG_PROPERTIES_FILE = "/etc/gps_debug.conf";
+
+ // config.xml properties
+ private static final String CONFIG_SUPL_HOST = "SUPL_HOST";
+ private static final String CONFIG_SUPL_PORT = "SUPL_PORT";
+ private static final String CONFIG_C2K_HOST = "C2K_HOST";
+ private static final String CONFIG_C2K_PORT = "C2K_PORT";
+ private static final String CONFIG_SUPL_VER = "SUPL_VER";
+ private static final String CONFIG_SUPL_MODE = "SUPL_MODE";
+ private static final String CONFIG_SUPL_ES = "SUPL_ES";
+ private static final String CONFIG_LPP_PROFILE = "LPP_PROFILE";
+ private static final String CONFIG_A_GLONASS_POS_PROTOCOL_SELECT =
+ "A_GLONASS_POS_PROTOCOL_SELECT";
+ private static final String CONFIG_USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL =
+ "USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL";
+ private static final String CONFIG_GPS_LOCK = "GPS_LOCK";
+ private static final String CONFIG_ES_EXTENSION_SEC = "ES_EXTENSION_SEC";
+
+ // Limit on NI emergency mode time extension after emergency sessions ends
+ private static final int MAX_EMERGENCY_MODE_EXTENSION_SECONDS = 300; // 5 minute maximum
+
+ // Persist property for LPP_PROFILE
+ static final String LPP_PROFILE = "persist.sys.gps.lpp";
+
+ // Represents an HAL interface version. Instances of this class are created in the JNI layer
+ // and returned through native methods.
+ private static class HalInterfaceVersion {
+ final int mMajor;
+ final int mMinor;
+
+ HalInterfaceVersion(int major, int minor) {
+ mMajor = major;
+ mMinor = minor;
+ }
+ }
+
+ /**
+ * Properties loaded from PROPERTIES_FILE.
+ */
+ private Properties mProperties;
+
+ private int mEsExtensionSec = 0;
+
+ private final Context mContext;
+
+ GnssConfiguration(Context context) {
+ mContext = context;
+ mProperties = new Properties();
+ }
+
+ /**
+ * Returns the full set of properties loaded.
+ */
+ Properties getProperties() {
+ return mProperties;
+ }
+
+ /**
+ * Returns the value of config parameter ES_EXTENSION_SEC. The value is range checked
+ * and constrained to min/max limits.
+ */
+ int getEsExtensionSec() {
+ return mEsExtensionSec;
+ }
+
+ /**
+ * Returns the value of config parameter SUPL_HOST or {@code null} if no value is
+ * provided.
+ */
+ String getSuplHost() {
+ return mProperties.getProperty(CONFIG_SUPL_HOST);
+ }
+
+ /**
+ * Returns the value of config parameter SUPL_PORT or {@code defaultPort} if no value is
+ * provided or if there is an error parsing the configured value.
+ */
+ int getSuplPort(int defaultPort) {
+ return getIntConfig(CONFIG_SUPL_PORT, defaultPort);
+ }
+
+ /**
+ * Returns the value of config parameter C2K_HOST or {@code null} if no value is
+ * provided.
+ */
+ String getC2KHost() {
+ return mProperties.getProperty(CONFIG_C2K_HOST);
+ }
+
+ /**
+ * Returns the value of config parameter C2K_PORT or {@code defaultPort} if no value is
+ * provided or if there is an error parsing the configured value.
+ */
+ int getC2KPort(int defaultPort) {
+ return getIntConfig(CONFIG_C2K_PORT, defaultPort);
+ }
+
+ /**
+ * Returns the value of config parameter SUPL_MODE or {@code defaultMode} if no value is
+ * provided or if there is an error parsing the configured value.
+ */
+ int getSuplMode(int defaultMode) {
+ return getIntConfig(CONFIG_SUPL_MODE, defaultMode);
+ }
+
+ /**
+ * Returns the value of config parameter SUPL_ES or {@code defaultSuplEs} if no value is
+ * provided or if there is an error parsing the configured value.
+ */
+ int getSuplEs(int defaulSuplEs) {
+ return getIntConfig(CONFIG_SUPL_ES, defaulSuplEs);
+ }
+
+ /**
+ * Returns the value of config parameter LPP_PROFILE or {@code null} if no value is
+ * provided.
+ */
+ String getLppProfile() {
+ return mProperties.getProperty(CONFIG_LPP_PROFILE);
+ }
+
+ /**
+ * Updates the GNSS HAL satellite blacklist.
+ */
+ void setSatelliteBlacklist(int[] constellations, int[] svids) {
+ native_set_satellite_blacklist(constellations, svids);
+ }
+
+ interface SetCarrierProperty {
+ boolean set(int value);
+ }
+
+ /**
+ * Loads the GNSS properties from carrier config file followed by the properties from
+ * gps debug config file.
+ */
+ void reloadGpsProperties() {
+ if (DEBUG) Log.d(TAG, "Reset GPS properties, previous size = " + mProperties.size());
+ loadPropertiesFromCarrierConfig();
+
+ String lpp_prof = SystemProperties.get(LPP_PROFILE);
+ if (!TextUtils.isEmpty(lpp_prof)) {
+ // override default value of this if lpp_prof is not empty
+ mProperties.setProperty(CONFIG_LPP_PROFILE, lpp_prof);
+ }
+ /*
+ * Overlay carrier properties from a debug configuration file.
+ */
+ loadPropertiesFromGpsDebugConfig(mProperties);
+
+ mEsExtensionSec = getRangeCheckedConfigEsExtensionSec();
+
+ final HalInterfaceVersion gnssConfigurationIfaceVersion =
+ native_get_gnss_configuration_version();
+ if (gnssConfigurationIfaceVersion != null) {
+ // Set to a range checked value.
+ if (isConfigEsExtensionSecSupported(gnssConfigurationIfaceVersion)
+ && !native_set_es_extension_sec(mEsExtensionSec)) {
+ Log.e(TAG, "Unable to set " + CONFIG_ES_EXTENSION_SEC + ": " + mEsExtensionSec);
+ }
+
+ Map<String, SetCarrierProperty> map = new HashMap<String, SetCarrierProperty>() {
+ {
+ put(CONFIG_SUPL_VER, GnssConfiguration::native_set_supl_version);
+ put(CONFIG_SUPL_MODE, GnssConfiguration::native_set_supl_mode);
+
+ if (isConfigSuplEsSupported(gnssConfigurationIfaceVersion)) {
+ put(CONFIG_SUPL_ES, GnssConfiguration::native_set_supl_es);
+ }
+
+ put(CONFIG_LPP_PROFILE, GnssConfiguration::native_set_lpp_profile);
+ put(CONFIG_A_GLONASS_POS_PROTOCOL_SELECT,
+ GnssConfiguration::native_set_gnss_pos_protocol_select);
+ put(CONFIG_USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL,
+ GnssConfiguration::native_set_emergency_supl_pdn);
+
+ if (isConfigGpsLockSupported(gnssConfigurationIfaceVersion)) {
+ put(CONFIG_GPS_LOCK, GnssConfiguration::native_set_gps_lock);
+ }
+ }
+ };
+
+ for (Entry<String, SetCarrierProperty> entry : map.entrySet()) {
+ String propertyName = entry.getKey();
+ String propertyValueString = mProperties.getProperty(propertyName);
+ if (propertyValueString != null) {
+ try {
+ int propertyValueInt = Integer.decode(propertyValueString);
+ boolean result = entry.getValue().set(propertyValueInt);
+ if (!result) {
+ Log.e(TAG, "Unable to set " + propertyName);
+ }
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "Unable to parse propertyName: " + propertyValueString);
+ }
+ }
+ }
+ } else if (DEBUG) {
+ Log.d(TAG, "Skipped configuration update because GNSS configuration in GPS HAL is not"
+ + " supported");
+ }
+ }
+
+ /**
+ * Loads GNSS properties from carrier config file.
+ */
+ void loadPropertiesFromCarrierConfig() {
+ CarrierConfigManager configManager = (CarrierConfigManager)
+ mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ if (configManager == null) {
+ return;
+ }
+ PersistableBundle configs = configManager.getConfigForSubId(
+ SubscriptionManager.getDefaultDataSubscriptionId());
+ if (configs == null) {
+ if (DEBUG) Log.d(TAG, "SIM not ready, use default carrier config.");
+ configs = CarrierConfigManager.getDefaultConfig();
+ }
+ for (String configKey : configs.keySet()) {
+ if (configKey.startsWith(CarrierConfigManager.Gps.KEY_PREFIX)) {
+ String key = configKey
+ .substring(CarrierConfigManager.Gps.KEY_PREFIX.length())
+ .toUpperCase();
+ Object value = configs.get(configKey);
+ if (value instanceof String) {
+ // All GPS properties are of String type; convert so.
+ if (DEBUG) Log.d(TAG, "Gps config: " + key + " = " + value);
+ mProperties.setProperty(key, (String) value);
+ }
+ }
+ }
+ }
+
+ private void loadPropertiesFromGpsDebugConfig(Properties properties) {
+ try {
+ File file = new File(DEBUG_PROPERTIES_FILE);
+ FileInputStream stream = null;
+ try {
+ stream = new FileInputStream(file);
+ properties.load(stream);
+ } finally {
+ IoUtils.closeQuietly(stream);
+ }
+ } catch (IOException e) {
+ if (DEBUG) Log.d(TAG, "Could not open GPS configuration file " + DEBUG_PROPERTIES_FILE);
+ }
+ }
+
+ private int getRangeCheckedConfigEsExtensionSec() {
+ int emergencyExtensionSeconds = getIntConfig(CONFIG_ES_EXTENSION_SEC, 0);
+ if (emergencyExtensionSeconds > MAX_EMERGENCY_MODE_EXTENSION_SECONDS) {
+ Log.w(TAG, CONFIG_ES_EXTENSION_SEC + ": " + emergencyExtensionSeconds
+ + " too high, reset to " + MAX_EMERGENCY_MODE_EXTENSION_SECONDS);
+ emergencyExtensionSeconds = MAX_EMERGENCY_MODE_EXTENSION_SECONDS;
+ } else if (emergencyExtensionSeconds < 0) {
+ Log.w(TAG, CONFIG_ES_EXTENSION_SEC + ": " + emergencyExtensionSeconds
+ + " is negative, reset to zero.");
+ emergencyExtensionSeconds = 0;
+ }
+ return emergencyExtensionSeconds;
+ }
+
+ private int getIntConfig(String configParameter, int defaultValue) {
+ String valueString = mProperties.getProperty(configParameter);
+ if (TextUtils.isEmpty(valueString)) {
+ return defaultValue;
+ }
+ try {
+ return Integer.parseInt(valueString);
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "Unable to parse config parameter " + configParameter + " value: "
+ + valueString + ". Using default value: " + defaultValue);
+ return defaultValue;
+ }
+ }
+
+ private static boolean isConfigEsExtensionSecSupported(
+ HalInterfaceVersion gnssConfiguartionIfaceVersion) {
+ // ES_EXTENSION_SEC is introduced in @2.0::IGnssConfiguration.hal
+ return gnssConfiguartionIfaceVersion.mMajor >= 2;
+ }
+
+ private static boolean isConfigSuplEsSupported(
+ HalInterfaceVersion gnssConfiguartionIfaceVersion) {
+ // SUPL_ES is deprecated in @2.0::IGnssConfiguration.hal
+ return gnssConfiguartionIfaceVersion.mMajor < 2;
+ }
+
+ private static boolean isConfigGpsLockSupported(
+ HalInterfaceVersion gnssConfiguartionIfaceVersion) {
+ // GPS_LOCK is deprecated in @2.0::IGnssConfiguration.hal
+ return gnssConfiguartionIfaceVersion.mMajor < 2;
+ }
+
+ private static native HalInterfaceVersion native_get_gnss_configuration_version();
+
+ private static native boolean native_set_supl_version(int version);
+
+ private static native boolean native_set_supl_mode(int mode);
+
+ private static native boolean native_set_supl_es(int es);
+
+ private static native boolean native_set_lpp_profile(int lppProfile);
+
+ private static native boolean native_set_gnss_pos_protocol_select(int gnssPosProtocolSelect);
+
+ private static native boolean native_set_gps_lock(int gpsLock);
+
+ private static native boolean native_set_emergency_supl_pdn(int emergencySuplPdn);
+
+ private static native boolean native_set_satellite_blacklist(int[] constellations, int[] svIds);
+
+ private static native boolean native_set_es_extension_sec(int emergencyExtensionSeconds);
+}
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 05d77ab..3c81a45 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -74,12 +74,7 @@
import com.android.server.location.GnssSatelliteBlacklistHelper.GnssSatelliteBlacklistCallback;
import com.android.server.location.NtpTimeHelper.InjectNtpTimeCallback;
-import libcore.io.IoUtils;
-
-import java.io.File;
import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.IOException;
import java.io.PrintWriter;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
@@ -87,11 +82,7 @@
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Properties;
/**
* A GNSS implementation of LocationProvider used by LocationManager.
@@ -147,7 +138,6 @@
private static final int LOCATION_HAS_SPEED_ACCURACY = 64;
private static final int LOCATION_HAS_BEARING_ACCURACY = 128;
-
// IMPORTANT - the GPS_DELETE_* symbols here must match GnssAidingData enum in IGnss.hal
private static final int GPS_DELETE_EPHEMERIS = 0x0001;
private static final int GPS_DELETE_ALMANAC = 0x0002;
@@ -200,9 +190,6 @@
private static final int AGPS_RIL_REQUEST_SETID_IMSI = 1;
private static final int AGPS_RIL_REQUEST_SETID_MSISDN = 2;
- //TODO(b/33112647): Create gps_debug.conf with commented career parameters.
- private static final String DEBUG_PROPERTIES_FILE = "/etc/gps_debug.conf";
-
// ref. location info
private static final int AGPS_REF_LOCATION_TYPE_GSM_CELLID = 1;
private static final int AGPS_REF_LOCATION_TYPE_UMTS_CELLID = 2;
@@ -375,7 +362,7 @@
* Properties loaded from PROPERTIES_FILE.
* It must be accessed only inside {@link #mHandler}.
*/
- private Properties mProperties;
+ private GnssConfiguration mGnssConfiguration;
private String mSuplServerHost;
private int mSuplServerPort = TCP_MIN_PORT;
@@ -411,10 +398,6 @@
private final static String ALARM_WAKEUP = "com.android.internal.location.ALARM_WAKEUP";
private final static String ALARM_TIMEOUT = "com.android.internal.location.ALARM_TIMEOUT";
- // Persist property for LPP_PROFILE
- private final static String LPP_PROFILE = "persist.sys.gps.lpp";
-
-
private final PowerManager mPowerManager;
private final AlarmManager mAlarmManager;
private final PendingIntent mWakeupIntent;
@@ -502,7 +485,7 @@
*/
@Override
public void onUpdateSatelliteBlacklist(int[] constellations, int[] svids) {
- mHandler.post(() -> native_set_satellite_blacklist(constellations, svids));
+ mHandler.post(() -> mGnssConfiguration.setSatelliteBlacklist(constellations, svids));
}
private void subscriptionOrCarrierConfigChanged(Context context) {
@@ -525,17 +508,17 @@
}
if (isKeepLppProfile) {
// load current properties for the carrier
- loadPropertiesFromCarrierConfig(context, mProperties);
- String lpp_profile = mProperties.getProperty("LPP_PROFILE");
+ mGnssConfiguration.loadPropertiesFromCarrierConfig();
+ String lpp_profile = mGnssConfiguration.getLppProfile();
// set the persist property LPP_PROFILE for the value
if (lpp_profile != null) {
- SystemProperties.set(LPP_PROFILE, lpp_profile);
+ SystemProperties.set(GnssConfiguration.LPP_PROFILE, lpp_profile);
}
} else {
// reset the persist property
- SystemProperties.set(LPP_PROFILE, "");
+ SystemProperties.set(GnssConfiguration.LPP_PROFILE, "");
}
- reloadGpsProperties(context, mProperties);
+ reloadGpsProperties();
mNIHandler.setSuplEsEnabled(mSuplEsEnabled);
}
} else {
@@ -564,133 +547,14 @@
return native_is_supported();
}
- interface SetCarrierProperty {
- boolean set(int value);
- }
-
- private void reloadGpsProperties(Context context, Properties properties) {
- if (DEBUG) Log.d(TAG, "Reset GPS properties, previous size = " + properties.size());
- loadPropertiesFromCarrierConfig(context, properties);
-
- String lpp_prof = SystemProperties.get(LPP_PROFILE);
- if (!TextUtils.isEmpty(lpp_prof)) {
- // override default value of this if lpp_prof is not empty
- properties.setProperty("LPP_PROFILE", lpp_prof);
- }
- /*
- * Overlay carrier properties from a debug configuration file.
- */
- loadPropertiesFromGpsDebugConfig(properties);
+ private void reloadGpsProperties() {
+ mGnssConfiguration.reloadGpsProperties();
+ setSuplHostPort();
// TODO: we should get rid of C2K specific setting.
- setSuplHostPort(properties.getProperty("SUPL_HOST"),
- properties.getProperty("SUPL_PORT"));
- mC2KServerHost = properties.getProperty("C2K_HOST");
- String portString = properties.getProperty("C2K_PORT");
- if (mC2KServerHost != null && portString != null) {
- try {
- mC2KServerPort = Integer.parseInt(portString);
- } catch (NumberFormatException e) {
- Log.e(TAG, "unable to parse C2K_PORT: " + portString);
- }
- }
- if (native_is_gnss_configuration_supported()) {
- Map<String, SetCarrierProperty> map = new HashMap<String, SetCarrierProperty>() {
- {
- put("SUPL_VER", GnssLocationProvider::native_set_supl_version);
- put("SUPL_MODE", GnssLocationProvider::native_set_supl_mode);
- put("SUPL_ES", GnssLocationProvider::native_set_supl_es);
- put("LPP_PROFILE", GnssLocationProvider::native_set_lpp_profile);
- put("A_GLONASS_POS_PROTOCOL_SELECT",
- GnssLocationProvider::native_set_gnss_pos_protocol_select);
- put("USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL",
- GnssLocationProvider::native_set_emergency_supl_pdn);
- put("GPS_LOCK", GnssLocationProvider::native_set_gps_lock);
- }
- };
-
- for (Entry<String, SetCarrierProperty> entry : map.entrySet()) {
- String propertyName = entry.getKey();
- String propertyValueString = properties.getProperty(propertyName);
- if (propertyValueString != null) {
- try {
- int propertyValueInt = Integer.decode(propertyValueString);
- boolean result = entry.getValue().set(propertyValueInt);
- if (!result) {
- Log.e(TAG, "Unable to set " + propertyName);
- }
- } catch (NumberFormatException e) {
- Log.e(TAG, "unable to parse propertyName: " + propertyValueString);
- }
- }
- }
- } else if (DEBUG) {
- Log.d(TAG, "Skipped configuration update because GNSS configuration in GPS HAL is not"
- + " supported");
- }
-
- // SUPL_ES configuration.
- String suplESProperty = mProperties.getProperty("SUPL_ES");
- if (suplESProperty != null) {
- try {
- mSuplEsEnabled = (Integer.parseInt(suplESProperty) == 1);
- } catch (NumberFormatException e) {
- Log.e(TAG, "unable to parse SUPL_ES: " + suplESProperty);
- }
- }
-
- String emergencyExtensionSecondsString
- = properties.getProperty("ES_EXTENSION_SEC", "0");
- try {
- int emergencyExtensionSeconds =
- Integer.parseInt(emergencyExtensionSecondsString);
- mNIHandler.setEmergencyExtensionSeconds(emergencyExtensionSeconds);
- } catch (NumberFormatException e) {
- Log.e(TAG, "unable to parse ES_EXTENSION_SEC: "
- + emergencyExtensionSecondsString);
- }
- }
-
- private void loadPropertiesFromCarrierConfig(Context context, Properties properties) {
- CarrierConfigManager configManager = (CarrierConfigManager)
- mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
- if (configManager == null) {
- return;
- }
- PersistableBundle configs = configManager.getConfigForSubId(
- SubscriptionManager.getDefaultDataSubscriptionId());
- if (configs == null) {
- if (DEBUG) Log.d(TAG, "SIM not ready, use default carrier config.");
- configs = CarrierConfigManager.getDefaultConfig();
- }
- for (String configKey : configs.keySet()) {
- if (configKey.startsWith(CarrierConfigManager.Gps.KEY_PREFIX)) {
- String key = configKey
- .substring(CarrierConfigManager.Gps.KEY_PREFIX.length())
- .toUpperCase();
- Object value = configs.get(configKey);
- if (value instanceof String) {
- // All GPS properties are of String type; convert so.
- if (DEBUG) Log.d(TAG, "Gps config: " + key + " = " + value);
- properties.setProperty(key, (String) value);
- }
- }
- }
- }
-
- private void loadPropertiesFromGpsDebugConfig(Properties properties) {
- try {
- File file = new File(DEBUG_PROPERTIES_FILE);
- FileInputStream stream = null;
- try {
- stream = new FileInputStream(file);
- properties.load(stream);
- } finally {
- IoUtils.closeQuietly(stream);
- }
-
- } catch (IOException e) {
- if (DEBUG) Log.d(TAG, "Could not open GPS configuration file " + DEBUG_PROPERTIES_FILE);
- }
+ mC2KServerHost = mGnssConfiguration.getC2KHost();
+ mC2KServerPort = mGnssConfiguration.getC2KPort(TCP_MIN_PORT);
+ mNIHandler.setEmergencyExtensionSeconds(mGnssConfiguration.getEsExtensionSec());
+ mSuplEsEnabled = mGnssConfiguration.getSuplEs(0) == 1;
}
public GnssLocationProvider(Context context, LocationProviderManager locationProviderManager,
@@ -733,7 +597,7 @@
// relative long time, so the ctor() is kept to create objects needed by this instance,
// while IO initialization and registration is delegated to our internal handler
// this approach is just fine because events are posted to our handler anyway
- mProperties = new Properties();
+ mGnssConfiguration = new GnssConfiguration(mContext);
sendMessage(INITIALIZE_HANDLER, 0, null);
// Create a GPS net-initiated handler.
@@ -918,7 +782,8 @@
mDownloadXtraWakeLock.acquire(DOWNLOAD_XTRA_DATA_TIMEOUT_MS);
Log.i(TAG, "WakeLock acquired by handleDownloadXtraData()");
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
- GpsXtraDownloader xtraDownloader = new GpsXtraDownloader(mProperties);
+ GpsXtraDownloader xtraDownloader = new GpsXtraDownloader(
+ mGnssConfiguration.getProperties());
byte[] data = xtraDownloader.downloadXtraData();
if (data != null) {
if (DEBUG) Log.d(TAG, "calling native_inject_xtra_data");
@@ -963,17 +828,9 @@
}
}
- private void setSuplHostPort(String hostString, String portString) {
- if (hostString != null) {
- mSuplServerHost = hostString;
- }
- if (portString != null) {
- try {
- mSuplServerPort = Integer.parseInt(portString);
- } catch (NumberFormatException e) {
- Log.e(TAG, "unable to parse SUPL_PORT: " + portString);
- }
- }
+ private void setSuplHostPort() {
+ mSuplServerHost = mGnssConfiguration.getSuplHost();
+ mSuplServerPort = mGnssConfiguration.getSuplPort(TCP_MIN_PORT);
if (mSuplServerHost != null
&& mSuplServerPort > TCP_MIN_PORT
&& mSuplServerPort <= TCP_MAX_PORT) {
@@ -986,23 +843,17 @@
* Checks what SUPL mode to use, according to the AGPS mode as well as the
* allowed mode from properties.
*
- * @param properties GPS properties
* @param agpsEnabled whether AGPS is enabled by settings value
* @param singleShot whether "singleshot" is needed
* @return SUPL mode (MSA vs MSB vs STANDALONE)
*/
- private int getSuplMode(Properties properties, boolean agpsEnabled, boolean singleShot) {
+ private int getSuplMode(boolean agpsEnabled, boolean singleShot) {
if (agpsEnabled) {
- String modeString = properties.getProperty("SUPL_MODE");
- int suplMode = 0;
- if (!TextUtils.isEmpty(modeString)) {
- try {
- suplMode = Integer.parseInt(modeString);
- } catch (NumberFormatException e) {
- Log.e(TAG, "unable to parse SUPL_MODE: " + modeString);
- return GPS_POSITION_MODE_STANDALONE;
- }
+ int suplMode = mGnssConfiguration.getSuplMode(0);
+ if (suplMode == 0) {
+ return GPS_POSITION_MODE_STANDALONE;
}
+
// MS-Based is the preferred mode for Assisted-GPS position computation, so we favor
// such mode when it is available
if (hasCapability(GPS_CAPABILITY_MSB) && (suplMode & AGPS_SUPL_MODE_MSB) != 0) {
@@ -1307,7 +1158,7 @@
boolean agpsEnabled =
(Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.ASSISTED_GPS_ENABLED, 1) != 0);
- mPositionMode = getSuplMode(mProperties, agpsEnabled, singleShot);
+ mPositionMode = getSuplMode(agpsEnabled, singleShot);
if (DEBUG) {
String mode;
@@ -1670,7 +1521,7 @@
// re-calls native_init() and other setup.
handleEnable();
// resend configuration into the restarted HAL service.
- reloadGpsProperties(mContext, mProperties);
+ reloadGpsProperties();
}
});
}
@@ -2072,7 +1923,7 @@
// load default GPS configuration
// (this configuration might change in the future based on SIM changes)
- reloadGpsProperties(mContext, mProperties);
+ reloadGpsProperties();
// TODO: When this object "finishes" we should unregister by invoking
// SubscriptionManager.getInstance(mContext).unregister
@@ -2227,8 +2078,6 @@
private static native boolean native_is_supported();
- private static native boolean native_is_gnss_configuration_supported();
-
private static native void native_init_once();
private native boolean native_init();
@@ -2284,21 +2133,4 @@
int lac, int cid);
private native void native_agps_set_id(int type, String setid);
-
- // GNSS Configuration
- private static native boolean native_set_supl_version(int version);
-
- private static native boolean native_set_supl_mode(int mode);
-
- private static native boolean native_set_supl_es(int es);
-
- private static native boolean native_set_lpp_profile(int lppProfile);
-
- private static native boolean native_set_gnss_pos_protocol_select(int gnssPosProtocolSelect);
-
- private static native boolean native_set_gps_lock(int gpsLock);
-
- private static native boolean native_set_emergency_supl_pdn(int emergencySuplPdn);
-
- private static native boolean native_set_satellite_blacklist(int[] constellations, int[] svIds);
-}
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index b70c64e..8ecceb9 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -116,7 +116,7 @@
private boolean mIsActive = false;
private boolean mDestroyed = false;
- private long mDuration;
+ private long mDuration = -1;
private String mMetadataDescription;
public MediaSessionRecord(int ownerPid, int ownerUid, int userId, String ownerPackageName,
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 052d579..7f2e047 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -40,6 +40,8 @@
import android.media.AudioSystem;
import android.media.IAudioService;
import android.media.IRemoteVolumeController;
+import android.media.MediaController2;
+import android.media.Session2CommandGroup;
import android.media.Session2Token;
import android.media.session.IActiveSessionsListener;
import android.media.session.ICallback;
@@ -54,6 +56,7 @@
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.IBinder;
import android.os.Message;
import android.os.PowerManager;
@@ -73,6 +76,7 @@
import android.view.KeyEvent;
import android.view.ViewConfiguration;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.DumpUtils;
import com.android.server.LocalServices;
import com.android.server.SystemService;
@@ -97,17 +101,23 @@
private static final int MEDIA_KEY_LISTENER_TIMEOUT = 1000;
private final SessionManagerImpl mSessionManagerImpl;
-
- // Keeps the full user id for each user.
- private final SparseIntArray mFullUserIds = new SparseIntArray();
- private final SparseArray<FullUserRecord> mUserRecords = new SparseArray<FullUserRecord>();
- private final ArrayList<SessionsListenerRecord> mSessionsListeners
- = new ArrayList<SessionsListenerRecord>();
- private final Object mLock = new Object();
private final MessageHandler mHandler = new MessageHandler();
private final PowerManager.WakeLock mMediaEventWakeLock;
private final int mLongPressTimeout;
private final INotificationManager mNotificationManager;
+ private final Object mLock = new Object();
+ // Keeps the full user id for each user.
+ @GuardedBy("mLock")
+ private final SparseIntArray mFullUserIds = new SparseIntArray();
+ @GuardedBy("mLock")
+ private final SparseArray<FullUserRecord> mUserRecords = new SparseArray<FullUserRecord>();
+ @GuardedBy("mLock")
+ private final ArrayList<SessionsListenerRecord> mSessionsListeners
+ = new ArrayList<SessionsListenerRecord>();
+ // TODO: Keep session2 info in MediaSessionStack for prioritizing both session1 and session2 in
+ // one place.
+ @GuardedBy("mLock")
+ private final List<Session2Token> mSession2Tokens = new ArrayList<>();
private KeyguardManager mKeyguardManager;
private IAudioService mAudioService;
@@ -722,6 +732,10 @@
pw.println(indent + "Restored MediaButtonReceiverComponentType: "
+ mRestoredMediaButtonReceiverComponentType);
mPriorityStack.dump(pw, indent);
+ pw.println(indent + "Session2Tokens - " + mSession2Tokens.size());
+ for (Session2Token session2Token : mSession2Tokens) {
+ pw.println(indent + " " + session2Token);
+ }
}
@Override
@@ -904,7 +918,17 @@
if (DEBUG) {
Log.d(TAG, "Session2 is created " + sessionToken);
}
- // TODO: Keep the session.
+ if (uid != sessionToken.getUid()) {
+ throw new SecurityException("Unexpected Session2Token's UID, expected=" + uid
+ + " but actually=" + sessionToken.getUid());
+ }
+ Controller2Callback callback = new Controller2Callback(sessionToken);
+ // Note: It's safe not to keep controller here because it wouldn't be GC'ed until
+ // it's closed.
+ // TODO: Keep controller as well for better readability
+ // because the GC behavior isn't straightforward.
+ MediaController2 controller = new MediaController2(getContext(), sessionToken,
+ new HandlerExecutor(mHandler), callback);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -1930,4 +1954,26 @@
obtainMessage(MSG_SESSIONS_CHANGED, userIdInteger).sendToTarget();
}
}
+
+ private class Controller2Callback extends MediaController2.ControllerCallback {
+ private final Session2Token mToken;
+
+ Controller2Callback(Session2Token token) {
+ mToken = token;
+ }
+
+ @Override
+ public void onConnected(MediaController2 controller, Session2CommandGroup allowedCommands) {
+ synchronized (mLock) {
+ mSession2Tokens.add(mToken);
+ }
+ }
+
+ @Override
+ public void onDisconnected(MediaController2 controller) {
+ synchronized (mLock) {
+ mSession2Tokens.remove(mToken);
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index dc3bfbc..8c274e4 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -368,7 +368,7 @@
mStatsObservers = checkNotNull(statsObservers, "missing NetworkStatsObservers");
mSystemDir = checkNotNull(systemDir, "missing systemDir");
mBaseDir = checkNotNull(baseDir, "missing baseDir");
- mUseBpfTrafficStats = new File("/sys/fs/bpf/traffic_uid_stats_map").exists();
+ mUseBpfTrafficStats = new File("/sys/fs/bpf/map_netd_app_uid_stats_map").exists();
}
private void registerLocalService() {
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index f9e31ae..5412e94 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -85,9 +85,6 @@
// One minute over PM WATCHDOG_TIMEOUT
private static final long WAKELOCK_TIMEOUT_MS = WATCHDOG_TIMEOUT + 1000 * 60;
- /** Special library name that skips shared libraries check during compilation. */
- public static final String SKIP_SHARED_LIBRARY_CHECK = "&";
-
@GuardedBy("mInstallLock")
private final Installer mInstaller;
private final Object mInstallLock;
@@ -397,23 +394,23 @@
Slog.e(TAG, "Could not infer CE/DE storage for package " + info.packageName);
return DEX_OPT_FAILED;
}
- Log.d(TAG, "Running dexopt on: " + path
- + " pkg=" + info.packageName + " isa=" + dexUseInfo.getLoaderIsas()
- + " dexoptFlags=" + printDexoptFlags(dexoptFlags)
- + " target-filter=" + compilerFilter);
-
- String classLoaderContext;
+ String classLoaderContext = null;
if (dexUseInfo.isUnknownClassLoaderContext() || dexUseInfo.isVariableClassLoaderContext()) {
- // If we have an unknown (not yet set), or a variable class loader chain, compile
- // without a context and mark the oat file with SKIP_SHARED_LIBRARY_CHECK. Note that
- // this might lead to a incorrect compilation.
- // TODO(calin): We should just extract in this case.
- classLoaderContext = SKIP_SHARED_LIBRARY_CHECK;
+ // If we have an unknown (not yet set), or a variable class loader chain. Just extract
+ // the dex file.
+ compilerFilter = "extract";
} else {
classLoaderContext = dexUseInfo.getClassLoaderContext();
}
int reason = options.getCompilationReason();
+ Log.d(TAG, "Running dexopt on: " + path
+ + " pkg=" + info.packageName + " isa=" + dexUseInfo.getLoaderIsas()
+ + " reason=" + getReasonName(reason)
+ + " dexoptFlags=" + printDexoptFlags(dexoptFlags)
+ + " target-filter=" + compilerFilter
+ + " class-loader-context=" + classLoaderContext);
+
try {
for (String isa : dexUseInfo.getLoaderIsas()) {
// Reuse the same dexopt path as for the primary apks. We don't need all the
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 3cb7714..5cb6c34 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -57,6 +57,7 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionInfo;
+import android.content.pm.PackageInstaller.SessionInfo.StagedSessionErrorCode;
import android.content.pm.PackageInstaller.SessionParams;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
@@ -982,9 +983,9 @@
mSealed = true;
// Read transfers from the original owner stay open, but as the session's data
- // cannot be modified anymore, there is no leak of information.
- // For staged sessions, the validation is performed by StagingManager.
- if (!params.isMultiPackage && !params.isStaged) {
+ // cannot be modified anymore, there is no leak of information. For staged sessions,
+ // further validation may be performed by the staging manager.
+ if (!params.isMultiPackage) {
final PackageInfo pkgInfo = mPm.getPackageInfo(
params.appPackageName, PackageManager.GET_SIGNATURES
| PackageManager.MATCH_STATIC_SHARED_LIBRARIES /*flags*/, userId);
@@ -1328,18 +1329,15 @@
mSigningDetails = apk.signingDetails;
mResolvedBaseFile = addedFile;
- assertApkConsistentLocked(String.valueOf(addedFile), apk);
-
- if (mSigningDetails == PackageParser.SigningDetails.UNKNOWN) {
- try {
- // STOPSHIP: For APEX we should also implement proper APK Signature verification.
- mSigningDetails = ApkSignatureVerifier.plsCertsNoVerifyOnlyCerts(
- pkgInfo.applicationInfo.sourceDir,
- PackageParser.SigningDetails.SignatureSchemeVersion.JAR);
- } catch (PackageParserException e) {
- throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
- "Couldn't obtain signatures from base APK");
- }
+ // STOPSHIP: Ensure that we remove the non-staged version of APEX installs in production
+ // because we currently do not verify that signatures are consistent with the previously
+ // installed version in that case.
+ //
+ // When that happens, this hack can be reverted and we can rely on APEXd to map between
+ // APEX files and their package names instead of parsing it out of the AndroidManifest
+ // such as here.
+ if (params.appPackageName == null) {
+ params.appPackageName = mPackageName;
}
}
@@ -2012,6 +2010,16 @@
}
}
+ /** {@hide} */
+ void setStagedSessionFailed(@StagedSessionErrorCode int errorCode) {
+ synchronized (mLock) {
+ mStagedSessionReady = false;
+ mStagedSessionApplied = false;
+ mStagedSessionFailed = true;
+ mStagedSessionErrorCode = errorCode;
+ }
+ }
+
private void destroyInternal() {
synchronized (mLock) {
mSealed = true;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 522ab0b..3301962 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -15618,13 +15618,25 @@
if (!hasDynamicLibraries) {
return null;
}
+ final boolean isUpdatedSystemApp = pkg.isUpdatedSystemApp();
+ // We may not yet have disabled the updated package yet, so be sure to grab the
+ // current setting if that's the case.
+ final PackageSetting updatedSystemPs = isUpdatedSystemApp
+ ? scanResult.request.disabledPkgSetting == null
+ ? scanResult.request.oldPkgSetting
+ : scanResult.request.disabledPkgSetting
+ : null;
+ if (isUpdatedSystemApp && (updatedSystemPs.pkg == null
+ || updatedSystemPs.pkg.libraryNames == null)) {
+ Slog.w(TAG, "Package " + pkg.packageName + " declares libraries that are not "
+ + "declared on the system image; skipping");
+ return null;
+ }
final ArrayList<SharedLibraryInfo> infos =
new ArrayList<>(scanResult.dynamicSharedLibraryInfos.size());
- final boolean updatedSystemApp = pkg.isUpdatedSystemApp();
for (SharedLibraryInfo info : scanResult.dynamicSharedLibraryInfos) {
- String name = info.getName();
- boolean allowed = false;
- if (updatedSystemApp) {
+ final String name = info.getName();
+ if (isUpdatedSystemApp) {
// New library entries can only be added through the
// system image. This is important to get rid of a lot
// of nasty edge cases: for example if we allowed a non-
@@ -15634,36 +15646,20 @@
// have allowed apps on the device which aren't compatible
// with it. Better to just have the restriction here, be
// conservative, and create many fewer cases that can negatively
- // impact the user experience. We may not yet have disabled the
- // updated package yet, so be sure to grab the current setting if
- // that's the case.
- final PackageSetting sysPs = scanResult.request.disabledPkgSetting == null
- ? scanResult.request.oldPkgSetting
- : scanResult.request.disabledPkgSetting;
- if (sysPs.pkg != null && sysPs.pkg.libraryNames != null) {
- for (int j = 0; j < sysPs.pkg.libraryNames.size(); j++) {
- if (name.equals(sysPs.pkg.libraryNames.get(j))) {
- allowed = true;
- break;
- }
- }
- }
- } else {
- allowed = true;
- }
- if (allowed) {
- if (sharedLibExists(
- name, SharedLibraryInfo.VERSION_UNDEFINED, existingSharedLibraries)) {
- Slog.w(TAG, "Package " + pkg.packageName + " library "
- + name + " already exists; skipping");
+ // impact the user experience.
+ if (!updatedSystemPs.pkg.libraryNames.contains(name)) {
+ Slog.w(TAG, "Package " + pkg.packageName + " declares library " + name
+ + " that is not declared on system image; skipping");
continue;
}
- infos.add(info);
- } else {
- Slog.w(TAG, "Package " + pkg.packageName + " declares lib "
- + name + " that is not declared on system image; skipping");
+ }
+ if (sharedLibExists(
+ name, SharedLibraryInfo.VERSION_UNDEFINED, existingSharedLibraries)) {
+ Slog.w(TAG, "Package " + pkg.packageName + " declares library " + name
+ + " that already exists; skipping");
continue;
}
+ infos.add(info);
}
return infos;
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 2e9d26a..c9d298c 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -2293,9 +2293,7 @@
break;
case "--apex":
sessionParams.installFlags |= PackageManager.INSTALL_APEX;
- // TODO(b/118865310): APEX packages should always imply
- // sessionParams.isStaged(). Enforce this when the staged
- // install workflow is complete.
+ sessionParams.setStaged();
break;
case "--multi-package":
sessionParams.setMultiPackage();
diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java
index b47d966..b4154c7 100644
--- a/services/core/java/com/android/server/pm/SELinuxMMAC.java
+++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java
@@ -17,8 +17,8 @@
package com.android.server.pm;
import android.content.pm.PackageParser;
-import android.content.pm.Signature;
import android.content.pm.PackageParser.SigningDetails;
+import android.content.pm.Signature;
import android.os.Environment;
import android.util.Slog;
import android.util.Xml;
@@ -81,6 +81,13 @@
sMacPermissions.add(new File(
Environment.getRootDirectory(), "/etc/selinux/plat_mac_permissions.xml"));
+ // Product mac permissions (optional).
+ final File productMacPermission = new File(
+ Environment.getProductDirectory(), "/etc/selinux/product_mac_permissions.xml");
+ if (productMacPermission.exists()) {
+ sMacPermissions.add(productMacPermission);
+ }
+
// Vendor mac permissions.
// The filename has been renamed from nonplat_mac_permissions to
// vendor_mac_permissions. Either of them should exist.
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 3beda6a..48ee9dc 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -17,10 +17,24 @@
package com.android.server.pm;
import android.annotation.NonNull;
+import android.apex.ApexInfo;
+import android.apex.ApexInfoList;
+import android.apex.IApexService;
import android.content.pm.PackageInstaller;
+import android.content.pm.PackageInstaller.SessionInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageParser.PackageParserException;
+import android.content.pm.PackageParser.SigningDetails;
+import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion;
import android.content.pm.ParceledListSlice;
+import android.content.pm.Signature;
import android.os.Handler;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.text.TextUtils;
+import android.util.Slog;
import android.util.SparseArray;
+import android.util.apk.ApkSignatureVerifier;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.BackgroundThread;
@@ -71,15 +85,98 @@
return new ParceledListSlice<>(result);
}
- void commitSession(@NonNull PackageInstallerSession sessionInfo) {
- updateStoredSession(sessionInfo);
+ private static boolean validateApexSignatureLocked(String apexPath, String packageName) {
+ final SigningDetails signingDetails;
+ try {
+ signingDetails = ApkSignatureVerifier.verify(apexPath, SignatureSchemeVersion.JAR);
+ } catch (PackageParserException e) {
+ Slog.e(TAG, "Unable to parse APEX package: " + apexPath, e);
+ return false;
+ }
- mBgHandler.post(() -> {
- // TODO(b/118865310): Dispatch the session to apexd/PackageManager for verification. For
- // now we directly mark it as ready.
- sessionInfo.setStagedSessionReady();
- mPm.sendSessionUpdatedBroadcast(sessionInfo.generateInfo(), sessionInfo.userId);
- });
+ final IApexService apex = IApexService.Stub.asInterface(
+ ServiceManager.getService("apexservice"));
+ final ApexInfo apexInfo;
+ try {
+ apexInfo = apex.getActivePackage(packageName);
+ } catch (RemoteException re) {
+ Slog.e(TAG, "Unable to contact APEXD", re);
+ return false;
+ }
+
+ if (apexInfo == null || TextUtils.isEmpty(apexInfo.packageName)) {
+ // TODO: What is the right thing to do here ? This implies there's no active package
+ // with the given name. This should never be the case in production (where we only
+ // accept updates to existing APEXes) but may be required for testing.
+ return true;
+ }
+
+ final SigningDetails existingSigningDetails;
+ try {
+ existingSigningDetails = ApkSignatureVerifier.verify(
+ apexInfo.packagePath, SignatureSchemeVersion.JAR);
+ } catch (PackageParserException e) {
+ Slog.e(TAG, "Unable to parse APEX package: " + apexInfo.packagePath, e);
+ return false;
+ }
+
+ // Now that we have both sets of signatures, demand that they're an exact match.
+ if (Signature.areExactMatch(existingSigningDetails.signatures, signingDetails.signatures)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ private static boolean submitSessionToApexService(int sessionId, ApexInfoList apexInfoList) {
+ final IApexService apex = IApexService.Stub.asInterface(
+ ServiceManager.getService("apexservice"));
+ boolean success;
+ try {
+ success = apex.submitStagedSession(sessionId, apexInfoList);
+ } catch (RemoteException re) {
+ Slog.e(TAG, "Unable to contact apexservice", re);
+ return false;
+ }
+ return success;
+ }
+
+ void preRebootVerification(@NonNull PackageInstallerSession session) {
+ boolean success = true;
+ if ((session.params.installFlags & PackageManager.INSTALL_APEX) != 0) {
+
+ final ApexInfoList apexInfoList = new ApexInfoList();
+
+ if (!submitSessionToApexService(session.sessionId, apexInfoList)) {
+ success = false;
+ } else {
+ // For APEXes, we validate the signature here before we mark the session as ready,
+ // so we fail the session early if there is a signature mismatch. For APKs, the
+ // signature verification will be done by the package manager at the point at which
+ // it applies the staged install.
+ //
+ // TODO: Decide whether we want to fail fast by detecting signature mismatches right
+ // away.
+ for (ApexInfo apexPackage : apexInfoList.apexInfos) {
+ if (!validateApexSignatureLocked(apexPackage.packagePath,
+ apexPackage.packageName)) {
+ success = false;
+ break;
+ }
+ }
+ }
+ }
+ if (success) {
+ session.setStagedSessionReady();
+ } else {
+ session.setStagedSessionFailed(SessionInfo.VERIFICATION_FAILED);
+ }
+ mPm.sendSessionUpdatedBroadcast(session.generateInfo(false), session.userId);
+ }
+
+ void commitSession(@NonNull PackageInstallerSession session) {
+ updateStoredSession(session);
+ mBgHandler.post(() -> preRebootVerification(session));
}
void createSession(@NonNull PackageInstallerSession sessionInfo) {
diff --git a/services/core/java/com/android/server/pm/dex/DexoptUtils.java b/services/core/java/com/android/server/pm/dex/DexoptUtils.java
index 93ee44c..91ad11e 100644
--- a/services/core/java/com/android/server/pm/dex/DexoptUtils.java
+++ b/services/core/java/com/android/server/pm/dex/DexoptUtils.java
@@ -22,7 +22,6 @@
import android.util.SparseArray;
import com.android.internal.os.ClassLoaderFactory;
-import com.android.server.pm.PackageDexOptimizer;
import java.io.File;
import java.util.List;
@@ -275,15 +274,11 @@
/**
* Encodes a single class loader dependency starting from {@param path} and
* {@param classLoaderName}.
- * When classpath is {@link PackageDexOptimizer#SKIP_SHARED_LIBRARY_CHECK}, the method returns
- * the same. This special property is used only during OTA.
* NOTE: Keep this in sync with the dexopt expectations! Right now that is either "PCL[path]"
* for a PathClassLoader or "DLC[path]" for a DelegateLastClassLoader.
*/
/*package*/ static String encodeClassLoader(String classpath, String classLoaderName) {
- if (classpath.equals(PackageDexOptimizer.SKIP_SHARED_LIBRARY_CHECK)) {
- return classpath;
- }
+ classpath.getClass(); // Throw NPE if classpath is null
String classLoaderDexoptEncoding = classLoaderName;
if (ClassLoaderFactory.isPathClassLoaderName(classLoaderName)) {
classLoaderDexoptEncoding = "PCL";
@@ -306,16 +301,10 @@
/**
* Links to dependencies together in a format accepted by dexopt.
* For the special case when either of cl1 or cl2 equals
- * {@link PackageDexOptimizer#SKIP_SHARED_LIBRARY_CHECK}, the method returns the same. This
- * property is used only during OTA.
* NOTE: Keep this in sync with the dexopt expectations! Right now that is a list of split
* dependencies {@see encodeClassLoader} separated by ';'.
*/
/*package*/ static String encodeClassLoaderChain(String cl1, String cl2) {
- if (cl1.equals(PackageDexOptimizer.SKIP_SHARED_LIBRARY_CHECK) ||
- cl2.equals(PackageDexOptimizer.SKIP_SHARED_LIBRARY_CHECK)) {
- return PackageDexOptimizer.SKIP_SHARED_LIBRARY_CHECK;
- }
if (cl1.isEmpty()) return cl2;
if (cl2.isEmpty()) return cl1;
return cl1 + ";" + cl2;
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 01bff07..410f864 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -89,6 +89,7 @@
import android.util.SparseBooleanArray;
import android.util.Xml;
import android.view.Display;
+import android.view.DisplayInfo;
import android.view.IWindowManager;
import com.android.internal.R;
@@ -600,6 +601,20 @@
// scale if the crop height winds up not matching the recommended metrics
needScale = (wpData.mHeight != cropHint.height());
+ //make sure screen aspect ratio is preserved if width is scaled under screen size
+ if (needScale) {
+ final DisplayInfo displayInfo = new DisplayInfo();
+ mDisplayManager.getDisplay(DEFAULT_DISPLAY).getDisplayInfo(displayInfo);
+ final float scaleByHeight = (float) wpData.mHeight / (float) cropHint.height();
+ final int newWidth = (int) (cropHint.width() * scaleByHeight);
+ if (newWidth < displayInfo.logicalWidth) {
+ final float screenAspectRatio =
+ (float) displayInfo.logicalHeight / (float) displayInfo.logicalWidth;
+ cropHint.bottom = (int) (cropHint.width() * screenAspectRatio);
+ needCrop = true;
+ }
+ }
+
if (DEBUG) {
Slog.v(TAG, "crop: w=" + cropHint.width() + " h=" + cropHint.height());
Slog.v(TAG, "dims: w=" + wpData.mWidth + " h=" + wpData.mHeight);
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 2157ef6..801c1e7 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -2356,7 +2356,7 @@
public void onAnimationLeashDestroyed(Transaction t) {
super.onAnimationLeashDestroyed(t);
if (mAnimationBoundsLayer != null) {
- t.destroy(mAnimationBoundsLayer);
+ t.reparent(mAnimationBoundsLayer, null);
mAnimationBoundsLayer = null;
}
diff --git a/services/core/java/com/android/server/wm/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java
index aea071f..c39060e 100644
--- a/services/core/java/com/android/server/wm/Dimmer.java
+++ b/services/core/java/com/android/server/wm/Dimmer.java
@@ -129,7 +129,7 @@
final DimAnimatable dimAnimatable = new DimAnimatable(dimLayer);
mSurfaceAnimator = new SurfaceAnimator(dimAnimatable, () -> {
if (!mDimming) {
- dimAnimatable.getPendingTransaction().destroy(mDimLayer);
+ dimAnimatable.getPendingTransaction().reparent(mDimLayer, null);
}
}, mHost.mWmService);
}
@@ -300,7 +300,7 @@
if (!mDimState.mDimming) {
if (!mDimState.mAnimateExit) {
- t.destroy(mDimState.mDimLayer);
+ t.reparent(mDimState.mDimLayer, null);
} else {
startDimExit(mLastRequestedDimContainer, mDimState.mSurfaceAnimator, t);
}
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 607ee76..786a306 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -127,6 +127,7 @@
* {@code true} when {@link #closeLocked()} is called.
*/
private boolean mIsClosing;
+ IBinder mTransferTouchFromToken;
DragState(WindowManagerService service, DragDropController controller, IBinder token,
SurfaceControl surface, int flags, IBinder localWin) {
@@ -177,6 +178,8 @@
mTmpClipRect.set(0, 0, mDisplaySize.x, mDisplaySize.y);
t.setWindowCrop(mInputSurface, mTmpClipRect);
+ t.transferTouchFocus(mTransferTouchFromToken, h.token);
+ mTransferTouchFromToken = null;
}
/**
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index 639ed02..f9c9d33 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -1,5 +1,6 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
@@ -9,7 +10,6 @@
import android.os.Debug;
import android.os.IBinder;
import android.util.Slog;
-import android.view.InputApplicationHandle;
import android.view.KeyEvent;
import android.view.WindowManager;
@@ -204,6 +204,37 @@
+ WindowManagerService.TYPE_LAYER_OFFSET;
}
+ /** Callback to get pointer display id. */
+ @Override
+ public int getPointerDisplayId() {
+ synchronized (mService.mGlobalLock) {
+ // If desktop mode is not enabled, show on the default display.
+ if (!mService.mForceDesktopModeOnExternalDisplays) {
+ return DEFAULT_DISPLAY;
+ }
+
+ // Look for the topmost freeform display.
+ int firstExternalDisplayId = DEFAULT_DISPLAY;
+ for (int i = mService.mRoot.mChildren.size() - 1; i >= 0; --i) {
+ final DisplayContent displayContent = mService.mRoot.mChildren.get(i);
+ // Heuristic solution here. Currently when "Freeform windows" developer option is
+ // enabled we automatically put secondary displays in freeform mode and emulating
+ // "desktop mode". It also makes sense to show the pointer on the same display.
+ if (displayContent.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
+ return displayContent.getDisplayId();
+ }
+
+ if (firstExternalDisplayId == DEFAULT_DISPLAY
+ && displayContent.getDisplayId() != DEFAULT_DISPLAY) {
+ firstExternalDisplayId = displayContent.getDisplayId();
+ }
+ }
+
+ // Look for the topmost non-default display
+ return firstExternalDisplayId;
+ }
+ }
+
/** Waits until the built-in input devices have been configured. */
public boolean waitForInputDevicesReady(long timeoutMillis) {
synchronized (mInputDevicesReadyMonitor) {
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index cc57b93..bc01f7c 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -202,6 +202,9 @@
}
private void notifyPendingInsetsControlChanged() {
+ if (mPendingControlChanged.isEmpty()) {
+ return;
+ }
mDisplayContent.mWmService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
for (int i = mPendingControlChanged.size() - 1; i >= 0; i--) {
final WindowState controllingWin = mPendingControlChanged.valueAt(i);
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index 11068ce..9d9b48a 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -280,7 +280,7 @@
}
mService.mAnimationTransferMap.remove(mAnimation);
if (mLeash != null && destroyLeash) {
- t.destroy(mLeash);
+ t.reparent(mLeash, null);
scheduleAnim = true;
}
mLeash = null;
diff --git a/services/core/java/com/android/server/wm/TaskPositioningController.java b/services/core/java/com/android/server/wm/TaskPositioningController.java
index e15bf5b..9163165 100644
--- a/services/core/java/com/android/server/wm/TaskPositioningController.java
+++ b/services/core/java/com/android/server/wm/TaskPositioningController.java
@@ -24,6 +24,7 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Handler;
+import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.util.Slog;
@@ -50,6 +51,7 @@
private @Nullable TaskPositioner mTaskPositioner;
private final Rect mTmpClipRect = new Rect();
+ private IBinder mTransferTouchFromToken;
boolean isPositioningLocked() {
return mTaskPositioner != null;
@@ -102,6 +104,8 @@
mTmpClipRect.set(0, 0, p.x, p.y);
t.setWindowCrop(mInputSurface, mTmpClipRect);
+ t.transferTouchFocus(mTransferTouchFromToken, h.token);
+ mTransferTouchFromToken = null;
}
boolean startMovingTask(IWindow window, float startX, float startY) {
@@ -170,7 +174,6 @@
mPositioningDisplay = displayContent;
mTaskPositioner = TaskPositioner.create(mService);
- mTaskPositioner.register(displayContent);
// We need to grab the touch focus so that the touch events during the
// resizing/scrolling are not sent to the app. 'win' is the main window
@@ -181,12 +184,8 @@
&& displayContent.mCurrentFocus.mAppToken == win.mAppToken) {
transferFocusFromWin = displayContent.mCurrentFocus;
}
- if (!mInputManager.transferTouchFocus(
- transferFocusFromWin.mInputChannel, mTaskPositioner.mServerChannel)) {
- Slog.e(TAG_WM, "startPositioningLocked: Unable to transfer touch focus");
- cleanUpTaskPositioner();
- return false;
- }
+ mTransferTouchFromToken = transferFocusFromWin.mInputChannel.getToken();
+ mTaskPositioner.register(displayContent);
mTaskPositioner.startDrag(win, resize, preserveOrientation, startX, startY);
return true;
diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java
index 1d56f04..4a553cf 100644
--- a/services/core/java/com/android/server/wm/TaskRecord.java
+++ b/services/core/java/com/android/server/wm/TaskRecord.java
@@ -974,10 +974,11 @@
*/
boolean isSameIntentFilter(ActivityRecord r) {
final Intent intent = new Intent(r.intent);
- // Correct the activity intent for aliasing. The task record intent will always be based on
- // the real activity that will be launched not the alias, so we need to use an intent with
- // the component name pointing to the real activity not the alias in the activity record.
- intent.setComponent(r.mActivityComponent);
+ // Make sure the component are the same if the input activity has the same real activity
+ // as the one in the task because either one of them could be the alias activity.
+ if (Objects.equals(realActivity, r.mActivityComponent) && this.intent != null) {
+ intent.setComponent(this.intent.getComponent());
+ }
return intent.filterEquals(this.intent);
}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 32c5a3b..25e61f8 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -326,7 +326,7 @@
}
if (mSurfaceControl != null) {
- mPendingTransaction.destroy(mSurfaceControl);
+ mPendingTransaction.reparent(mSurfaceControl, null);
// Merge to parent transaction to ensure the transactions on this WindowContainer are
// applied in native even if WindowContainer is removed.
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 646fdd9..1691dc0 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -158,8 +158,9 @@
default boolean registerInputChannel(
DragState state, Display display, InputManagerService service,
InputChannel source) {
+ state.mTransferTouchFromToken = source.getToken();
state.register(display);
- return service.transferTouchFocus(source, state.getInputChannel());
+ return true;
}
/**
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index b4fe837..c8c5e8f 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -107,6 +107,7 @@
"android.hardware.gnss@1.0",
"android.hardware.gnss@1.1",
"android.hardware.gnss@2.0",
+ "android.hardware.input.classifier@1.0",
"android.hardware.ir@1.0",
"android.hardware.light@2.0",
"android.hardware.power@1.0",
@@ -119,6 +120,7 @@
"android.hardware.vibrator@1.0",
"android.hardware.vibrator@1.1",
"android.hardware.vibrator@1.2",
+ "android.hardware.vibrator@1.3",
"android.hardware.vr@1.0",
"android.frameworks.schedulerservice@1.0",
"android.frameworks.sensorservice@1.0",
diff --git a/services/core/jni/com_android_server_VibratorService.cpp b/services/core/jni/com_android_server_VibratorService.cpp
index defcfd9..63dca62 100644
--- a/services/core/jni/com_android_server_VibratorService.cpp
+++ b/services/core/jni/com_android_server_VibratorService.cpp
@@ -22,6 +22,7 @@
#include <android/hardware/vibrator/1.1/types.h>
#include <android/hardware/vibrator/1.2/IVibrator.h>
#include <android/hardware/vibrator/1.2/types.h>
+#include <android/hardware/vibrator/1.3/IVibrator.h>
#include "jni.h"
#include <nativehelper/JNIHelp.h>
@@ -42,6 +43,7 @@
namespace V1_0 = android::hardware::vibrator::V1_0;
namespace V1_1 = android::hardware::vibrator::V1_1;
namespace V1_2 = android::hardware::vibrator::V1_2;
+namespace V1_3 = android::hardware::vibrator::V1_3;
namespace android {
@@ -136,6 +138,19 @@
}
}
+static jboolean vibratorSupportsExternalControl(JNIEnv*, jobject) {
+ return halCall(&V1_3::IVibrator::supportsExternalControl).withDefault(false);
+}
+
+static void vibratorSetExternalControl(JNIEnv*, jobject, jboolean enabled) {
+ Status status = halCall(&V1_3::IVibrator::setExternalControl, static_cast<uint32_t>(enabled))
+ .withDefault(Status::UNKNOWN_ERROR);
+ if (status != Status::OK) {
+ ALOGE("Failed to set vibrator external control (%" PRIu32 ").",
+ static_cast<uint32_t>(status));
+ }
+}
+
static jlong vibratorPerformEffect(JNIEnv*, jobject, jlong effect, jint strength) {
Status status;
uint32_t lengthMs;
@@ -187,7 +202,9 @@
{ "vibratorOff", "()V", (void*)vibratorOff },
{ "vibratorSupportsAmplitudeControl", "()Z", (void*)vibratorSupportsAmplitudeControl},
{ "vibratorSetAmplitude", "(I)V", (void*)vibratorSetAmplitude},
- { "vibratorPerformEffect", "(JJ)J", (void*)vibratorPerformEffect}
+ { "vibratorPerformEffect", "(JJ)J", (void*)vibratorPerformEffect},
+ { "vibratorSupportsExternalControl", "()Z", (void*)vibratorSupportsExternalControl},
+ { "vibratorSetExternalControl", "(Z)V", (void*)vibratorSetExternalControl},
};
int register_android_server_VibratorService(JNIEnv *env)
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 43d2dcf..0929e20 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -107,6 +107,7 @@
jmethodID getLongPressTimeout;
jmethodID getPointerLayer;
jmethodID getPointerIcon;
+ jmethodID getPointerDisplayId;
jmethodID getKeyboardLayoutOverlay;
jmethodID getDeviceAlias;
jmethodID getTouchCalibrationForInputDevice;
@@ -174,15 +175,6 @@
loadSystemIconAsSpriteWithPointerIcon(env, contextObj, style, &pointerIcon, outSpriteIcon);
}
-static void updatePointerControllerFromViewport(
- sp<PointerController> controller, const DisplayViewport* const viewport) {
- if (controller != nullptr && viewport != nullptr) {
- const int32_t width = viewport->logicalRight - viewport->logicalLeft;
- const int32_t height = viewport->logicalBottom - viewport->logicalTop;
- controller->setDisplayViewport(width, height, viewport->orientation);
- }
-}
-
enum {
WM_ACTION_PASS_TO_USER = 1,
};
@@ -310,14 +302,19 @@
// Input devices to be disabled
SortedVector<int32_t> disabledInputDevices;
+
+ // Associated Pointer controller display.
+ int32_t pointerDisplayId;
} mLocked GUARDED_BY(mLock);
std::atomic<bool> mInteractive;
- void updateInactivityTimeoutLocked(const sp<PointerController>& controller);
+ void updateInactivityTimeoutLocked();
void handleInterceptActions(jint wmActions, nsecs_t when, uint32_t& policyFlags);
void ensureSpriteControllerLocked();
-
+ const DisplayViewport* findDisplayViewportLocked(int32_t displayId);
+ int32_t getPointerDisplayId();
+ void updatePointerDisplayLocked();
static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName);
static inline JNIEnv* jniEnv() {
@@ -342,6 +339,7 @@
mLocked.pointerGesturesEnabled = true;
mLocked.showTouches = false;
mLocked.pointerCapture = false;
+ mLocked.pointerDisplayId = ADISPLAY_ID_DEFAULT;
}
mInteractive = true;
@@ -391,9 +389,10 @@
return false;
}
-static const DisplayViewport* findInternalViewport(const std::vector<DisplayViewport>& viewports) {
- for (const DisplayViewport& v : viewports) {
- if (v.type == ViewportType::VIEWPORT_INTERNAL) {
+const DisplayViewport* NativeInputManager::findDisplayViewportLocked(int32_t displayId)
+ REQUIRES(mLock) {
+ for (const DisplayViewport& v : mLocked.viewports) {
+ if (v.displayId == displayId) {
return &v;
}
}
@@ -420,20 +419,14 @@
}
}
- const DisplayViewport* newInternalViewport = findInternalViewport(viewports);
- {
+ // Get the preferred pointer controller displayId.
+ int32_t pointerDisplayId = getPointerDisplayId();
+
+ { // acquire lock
AutoMutex _l(mLock);
- const DisplayViewport* oldInternalViewport = findInternalViewport(mLocked.viewports);
- // Internal viewport has changed if there wasn't one earlier, and there is one now, or,
- // if they are different.
- const bool internalViewportChanged = (newInternalViewport != nullptr) &&
- (oldInternalViewport == nullptr || (*oldInternalViewport != *newInternalViewport));
- if (internalViewportChanged) {
- sp<PointerController> controller = mLocked.pointerController.promote();
- updatePointerControllerFromViewport(controller, newInternalViewport);
- }
mLocked.viewports = viewports;
- }
+ mLocked.pointerDisplayId = pointerDisplayId;
+ } // release lock
mInputManager->getReader()->requestRefreshConfiguration(
InputReaderConfiguration::CHANGE_DISPLAY_INFO);
@@ -556,15 +549,42 @@
controller = new PointerController(this, mLooper, mLocked.spriteController);
mLocked.pointerController = controller;
-
- const DisplayViewport* internalViewport = findInternalViewport(mLocked.viewports);
- updatePointerControllerFromViewport(controller, internalViewport);
-
- updateInactivityTimeoutLocked(controller);
+ updateInactivityTimeoutLocked();
}
+
+ updatePointerDisplayLocked();
+
return controller;
}
+int32_t NativeInputManager::getPointerDisplayId() {
+ JNIEnv* env = jniEnv();
+ jint pointerDisplayId = env->CallIntMethod(mServiceObj,
+ gServiceClassInfo.getPointerDisplayId);
+ if (checkAndClearExceptionFromCallback(env, "getPointerDisplayId")) {
+ pointerDisplayId = ADISPLAY_ID_DEFAULT;
+ }
+
+ return pointerDisplayId;
+}
+
+void NativeInputManager::updatePointerDisplayLocked() REQUIRES(mLock) {
+ ATRACE_CALL();
+
+ sp<PointerController> controller = mLocked.pointerController.promote();
+ if (controller != nullptr) {
+ const DisplayViewport* viewport = findDisplayViewportLocked(mLocked.pointerDisplayId);
+ if (viewport == nullptr) {
+ ALOGW("Can't find pointer display viewport, fallback to default display.");
+ viewport = findDisplayViewportLocked(ADISPLAY_ID_DEFAULT);
+ }
+
+ if (viewport != nullptr) {
+ controller->setDisplayViewport(*viewport);
+ }
+ }
+}
+
void NativeInputManager::ensureSpriteControllerLocked() REQUIRES(mLock) {
if (mLocked.spriteController == nullptr) {
JNIEnv* env = jniEnv();
@@ -821,16 +841,16 @@
if (mLocked.systemUiVisibility != visibility) {
mLocked.systemUiVisibility = visibility;
-
- sp<PointerController> controller = mLocked.pointerController.promote();
- if (controller != nullptr) {
- updateInactivityTimeoutLocked(controller);
- }
+ updateInactivityTimeoutLocked();
}
}
-void NativeInputManager::updateInactivityTimeoutLocked(const sp<PointerController>& controller)
- REQUIRES(mLock) {
+void NativeInputManager::updateInactivityTimeoutLocked() REQUIRES(mLock) {
+ sp<PointerController> controller = mLocked.pointerController.promote();
+ if (controller == nullptr) {
+ return;
+ }
+
bool lightsOut = mLocked.systemUiVisibility & ASYSTEM_UI_VISIBILITY_STATUS_BAR_HIDDEN;
controller->setInactivityTimeout(lightsOut
? PointerController::INACTIVITY_TIMEOUT_SHORT
@@ -1824,6 +1844,9 @@
GET_METHOD_ID(gServiceClassInfo.getPointerIcon, clazz,
"getPointerIcon", "()Landroid/view/PointerIcon;");
+ GET_METHOD_ID(gServiceClassInfo.getPointerDisplayId, clazz,
+ "getPointerDisplayId", "()I");
+
GET_METHOD_ID(gServiceClassInfo.getKeyboardLayoutOverlay, clazz,
"getKeyboardLayoutOverlay",
"(Landroid/hardware/input/InputDeviceIdentifier;)[Ljava/lang/String;");
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 6d8fc1c..b290bc5 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -135,6 +135,7 @@
using IGnss_V2_0 = android::hardware::gnss::V2_0::IGnss;
using IGnssConfiguration_V1_0 = android::hardware::gnss::V1_0::IGnssConfiguration;
using IGnssConfiguration_V1_1 = android::hardware::gnss::V1_1::IGnssConfiguration;
+using IGnssConfiguration_V2_0 = android::hardware::gnss::V2_0::IGnssConfiguration;
using IGnssMeasurement_V1_0 = android::hardware::gnss::V1_0::IGnssMeasurement;
using IGnssMeasurement_V1_1 = android::hardware::gnss::V1_1::IGnssMeasurement;
using IGnssMeasurement_V2_0 = android::hardware::gnss::V2_0::IGnssMeasurement;
@@ -179,6 +180,7 @@
sp<IGnssDebug> gnssDebugIface = nullptr;
sp<IGnssConfiguration_V1_0> gnssConfigurationIface = nullptr;
sp<IGnssConfiguration_V1_1> gnssConfigurationIface_V1_1 = nullptr;
+sp<IGnssConfiguration_V2_0> gnssConfigurationIface_V2_0 = nullptr;
sp<IGnssNi> gnssNiIface = nullptr;
sp<IGnssMeasurement_V1_0> gnssMeasurementIface = nullptr;
sp<IGnssMeasurement_V1_1> gnssMeasurementIface_V1_1 = nullptr;
@@ -313,6 +315,15 @@
}
}
+static jobject createHalInterfaceVersionJavaObject(JNIEnv* env, jint major, jint minor) {
+ jclass versionClass =
+ env->FindClass("com/android/server/location/GnssConfiguration$HalInterfaceVersion");
+ jmethodID versionCtor = env->GetMethodID(versionClass, "<init>", "(II)V");
+ jobject version = env->NewObject(versionClass, versionCtor, major, minor);
+ env->DeleteLocalRef(versionClass);
+ return version;
+}
+
struct ScopedJniString {
ScopedJniString(JNIEnv* env, jstring javaString) : mEnv(env), mJavaString(javaString) {
mNativeString = mEnv->GetStringUTFChars(mJavaString, nullptr);
@@ -1421,10 +1432,19 @@
gnssNiIface = gnssNi;
}
- if (gnssHal_V1_1 != nullptr) {
+ if (gnssHal_V2_0 != nullptr) {
+ auto gnssConfiguration = gnssHal_V2_0->getExtensionGnssConfiguration_2_0();
+ if (!gnssConfiguration.isOk()) {
+ ALOGD("Unable to get a handle to GnssConfiguration_V2_0");
+ } else {
+ gnssConfigurationIface_V2_0 = gnssConfiguration;
+ gnssConfigurationIface_V1_1 = gnssConfigurationIface_V2_0;
+ gnssConfigurationIface = gnssConfigurationIface_V2_0;
+ }
+ } else if (gnssHal_V1_1 != nullptr) {
auto gnssConfiguration = gnssHal_V1_1->getExtensionGnssConfiguration_1_1();
if (!gnssConfiguration.isOk()) {
- ALOGD("Unable to get a handle to GnssConfiguration");
+ ALOGD("Unable to get a handle to GnssConfiguration_V1_1");
} else {
gnssConfigurationIface_V1_1 = gnssConfiguration;
gnssConfigurationIface = gnssConfigurationIface_V1_1;
@@ -1463,9 +1483,23 @@
return (agnssRilIface != nullptr) ? JNI_TRUE : JNI_FALSE;
}
-static jboolean android_location_gpsLocationProvider_is_gnss_configuration_supported(
- JNIEnv* /* env */, jclass /* jclazz */) {
- return (gnssConfigurationIface != nullptr) ? JNI_TRUE : JNI_FALSE;
+static jobject android_location_GnssConfiguration_get_gnss_configuration_version(
+ JNIEnv* env, jclass /* jclazz */) {
+ jint major, minor;
+ if (gnssConfigurationIface_V2_0 != nullptr) {
+ major = 2;
+ minor = 0;
+ } else if (gnssConfigurationIface_V1_1 != nullptr) {
+ major = 1;
+ minor = 1;
+ } else if (gnssConfigurationIface != nullptr) {
+ major = 1;
+ minor = 0;
+ } else {
+ return nullptr;
+ }
+
+ return createHalInterfaceVersionJavaObject(env, major, minor);
}
static jboolean android_location_GnssLocationProvider_init(JNIEnv* env, jobject obj) {
@@ -2278,9 +2312,9 @@
return boolToJbool(result.isOk());
}
-static jboolean android_location_GnssLocationProvider_set_emergency_supl_pdn(JNIEnv*,
- jobject,
- jint emergencySuplPdn) {
+static jboolean android_location_GnssConfiguration_set_emergency_supl_pdn(JNIEnv*,
+ jobject,
+ jint emergencySuplPdn) {
if (gnssConfigurationIface == nullptr) {
ALOGE("no GNSS configuration interface available");
return JNI_FALSE;
@@ -2294,7 +2328,7 @@
}
}
-static jboolean android_location_GnssLocationProvider_set_supl_version(JNIEnv*,
+static jboolean android_location_GnssConfiguration_set_supl_version(JNIEnv*,
jobject,
jint version) {
if (gnssConfigurationIface == nullptr) {
@@ -2309,9 +2343,14 @@
}
}
-static jboolean android_location_GnssLocationProvider_set_supl_es(JNIEnv*,
- jobject,
- jint suplEs) {
+static jboolean android_location_GnssConfiguration_set_supl_es(JNIEnv*,
+ jobject,
+ jint suplEs) {
+ if (gnssConfigurationIface_V2_0 != nullptr) {
+ ALOGI("Config parameter SUPL_ES is deprecated in IGnssConfiguration.hal version 2.0.");
+ return JNI_FALSE;
+ }
+
if (gnssConfigurationIface == nullptr) {
ALOGE("no GNSS configuration interface available");
return JNI_FALSE;
@@ -2325,9 +2364,9 @@
}
}
-static jboolean android_location_GnssLocationProvider_set_supl_mode(JNIEnv*,
- jobject,
- jint mode) {
+static jboolean android_location_GnssConfiguration_set_supl_mode(JNIEnv*,
+ jobject,
+ jint mode) {
if (gnssConfigurationIface == nullptr) {
ALOGE("no GNSS configuration interface available");
return JNI_FALSE;
@@ -2341,9 +2380,14 @@
}
}
-static jboolean android_location_GnssLocationProvider_set_gps_lock(JNIEnv*,
- jobject,
- jint gpsLock) {
+static jboolean android_location_GnssConfiguration_set_gps_lock(JNIEnv*,
+ jobject,
+ jint gpsLock) {
+ if (gnssConfigurationIface_V2_0 != nullptr) {
+ ALOGI("Config parameter GPS_LOCK is deprecated in IGnssConfiguration.hal version 2.0.");
+ return JNI_FALSE;
+ }
+
if (gnssConfigurationIface == nullptr) {
ALOGE("no GNSS configuration interface available");
return JNI_FALSE;
@@ -2357,7 +2401,7 @@
}
}
-static jboolean android_location_GnssLocationProvider_set_lpp_profile(JNIEnv*,
+static jboolean android_location_GnssConfiguration_set_lpp_profile(JNIEnv*,
jobject,
jint lppProfile) {
if (gnssConfigurationIface == nullptr) {
@@ -2374,9 +2418,9 @@
}
}
-static jboolean android_location_GnssLocationProvider_set_gnss_pos_protocol_select(JNIEnv*,
- jobject,
- jint gnssPosProtocol) {
+static jboolean android_location_GnssConfiguration_set_gnss_pos_protocol_select(JNIEnv*,
+ jobject,
+ jint gnssPosProtocol) {
if (gnssConfigurationIface == nullptr) {
ALOGE("no GNSS configuration interface available");
return JNI_FALSE;
@@ -2390,7 +2434,7 @@
}
}
-static jboolean android_location_GnssLocationProvider_set_satellite_blacklist(
+static jboolean android_location_GnssConfiguration_set_satellite_blacklist(
JNIEnv* env, jobject, jintArray constellations, jintArray sv_ids) {
if (gnssConfigurationIface_V1_1 == nullptr) {
ALOGI("No GNSS Satellite Blacklist interface available");
@@ -2431,6 +2475,27 @@
}
}
+static jboolean android_location_GnssConfiguration_set_es_extension_sec(
+ JNIEnv*, jobject, jint emergencyExtensionSeconds) {
+ if (gnssConfigurationIface == nullptr) {
+ ALOGE("no GNSS configuration interface available");
+ return JNI_FALSE;
+ }
+
+ if (gnssConfigurationIface_V2_0 == nullptr) {
+ ALOGI("Config parameter ES_EXTENSION_SEC is not supported in IGnssConfiguration.hal"
+ " versions earlier than 2.0.");
+ return JNI_FALSE;
+ }
+
+ auto result = gnssConfigurationIface_V2_0->setEsExtensionSec(emergencyExtensionSeconds);
+ if (result.isOk()) {
+ return result;
+ } else {
+ return JNI_FALSE;
+ }
+}
+
static jint android_location_GnssBatchingProvider_get_batch_size(JNIEnv*, jclass) {
if (gnssBatchingIface == nullptr) {
return 0; // batching not supported, size = 0
@@ -2498,17 +2563,14 @@
android_location_GnssLocationProvider_class_init_native)},
{"native_is_supported", "()Z", reinterpret_cast<void *>(
android_location_GnssLocationProvider_is_supported)},
- {"native_is_gnss_configuration_supported", "()Z",
- reinterpret_cast<void *>(
- android_location_gpsLocationProvider_is_gnss_configuration_supported)},
{"native_init_once", "()V", reinterpret_cast<void *>(
android_location_GnssLocationProvider_init_once)},
{"native_init", "()Z", reinterpret_cast<void *>(android_location_GnssLocationProvider_init)},
{"native_cleanup", "()V", reinterpret_cast<void *>(
android_location_GnssLocationProvider_cleanup)},
{"native_set_position_mode",
- "(IIIIIZ)Z",
- reinterpret_cast<void*>(android_location_GnssLocationProvider_set_position_mode)},
+ "(IIIIIZ)Z",
+ reinterpret_cast<void*>(android_location_GnssLocationProvider_set_position_mode)},
{"native_start", "()Z", reinterpret_cast<void*>(android_location_GnssLocationProvider_start)},
{"native_stop", "()Z", reinterpret_cast<void*>(android_location_GnssLocationProvider_stop)},
{"native_delete_aiding_data",
@@ -2545,31 +2607,6 @@
{"native_get_internal_state",
"()Ljava/lang/String;",
reinterpret_cast<void *>(android_location_GnssLocationProvider_get_internal_state)},
- {"native_set_supl_es",
- "(I)Z",
- reinterpret_cast<void *>(android_location_GnssLocationProvider_set_supl_es)},
- {"native_set_supl_version",
- "(I)Z",
- reinterpret_cast<void *>(android_location_GnssLocationProvider_set_supl_version)},
- {"native_set_supl_mode",
- "(I)Z",
- reinterpret_cast<void *>(android_location_GnssLocationProvider_set_supl_mode)},
- {"native_set_lpp_profile",
- "(I)Z",
- reinterpret_cast<void *>(android_location_GnssLocationProvider_set_lpp_profile)},
- {"native_set_gnss_pos_protocol_select",
- "(I)Z",
- reinterpret_cast<void *>(
- android_location_GnssLocationProvider_set_gnss_pos_protocol_select)},
- {"native_set_gps_lock",
- "(I)Z",
- reinterpret_cast<void *>(android_location_GnssLocationProvider_set_gps_lock)},
- {"native_set_emergency_supl_pdn",
- "(I)Z",
- reinterpret_cast<void *>(android_location_GnssLocationProvider_set_emergency_supl_pdn)},
- {"native_set_satellite_blacklist",
- "([I[I)Z",
- reinterpret_cast<void *>(android_location_GnssLocationProvider_set_satellite_blacklist)},
};
static const JNINativeMethod sMethodsBatching[] = {
@@ -2663,6 +2700,42 @@
reinterpret_cast<void *>(android_location_GnssNetworkConnectivityHandler_agps_data_conn_failed)},
};
+static const JNINativeMethod sConfigurationMethods[] = {
+ /* name, signature, funcPtr */
+ {"native_get_gnss_configuration_version",
+ "()Lcom/android/server/location/GnssConfiguration$HalInterfaceVersion;",
+ reinterpret_cast<void *>(
+ android_location_GnssConfiguration_get_gnss_configuration_version)},
+ {"native_set_supl_es",
+ "(I)Z",
+ reinterpret_cast<void *>(android_location_GnssConfiguration_set_supl_es)},
+ {"native_set_supl_version",
+ "(I)Z",
+ reinterpret_cast<void *>(android_location_GnssConfiguration_set_supl_version)},
+ {"native_set_supl_mode",
+ "(I)Z",
+ reinterpret_cast<void *>(android_location_GnssConfiguration_set_supl_mode)},
+ {"native_set_lpp_profile",
+ "(I)Z",
+ reinterpret_cast<void *>(android_location_GnssConfiguration_set_lpp_profile)},
+ {"native_set_gnss_pos_protocol_select",
+ "(I)Z",
+ reinterpret_cast<void *>(
+ android_location_GnssConfiguration_set_gnss_pos_protocol_select)},
+ {"native_set_gps_lock",
+ "(I)Z",
+ reinterpret_cast<void *>(android_location_GnssConfiguration_set_gps_lock)},
+ {"native_set_emergency_supl_pdn",
+ "(I)Z",
+ reinterpret_cast<void *>(android_location_GnssConfiguration_set_emergency_supl_pdn)},
+ {"native_set_satellite_blacklist",
+ "([I[I)Z",
+ reinterpret_cast<void *>(android_location_GnssConfiguration_set_satellite_blacklist)},
+ {"native_set_es_extension_sec",
+ "(I)Z",
+ reinterpret_cast<void *>(android_location_GnssConfiguration_set_es_extension_sec)},
+};
+
int register_android_server_location_GnssLocationProvider(JNIEnv* env) {
jniRegisterNativeMethods(
env,
@@ -2689,6 +2762,11 @@
"com/android/server/location/GnssNetworkConnectivityHandler",
sNetworkConnectivityMethods,
NELEM(sNetworkConnectivityMethods));
+ jniRegisterNativeMethods(
+ env,
+ "com/android/server/location/GnssConfiguration",
+ sConfigurationMethods,
+ NELEM(sConfigurationMethods));
return jniRegisterNativeMethods(
env,
"com/android/server/location/GnssLocationProvider",
diff --git a/services/net/Android.bp b/services/net/Android.bp
index e0ae68f..ae697b7 100644
--- a/services/net/Android.bp
+++ b/services/net/Android.bp
@@ -2,3 +2,19 @@
name: "services.net",
srcs: ["java/**/*.java"],
}
+
+// TODO: move to networking module with DhcpClient and remove lib
+java_library {
+ name: "dhcp-packet-lib",
+ srcs: [
+ "java/android/net/dhcp/*Packet.java",
+ ]
+}
+
+// TODO: move to networking module with IpNeighborMonitor/ConnectivityPacketTracker and remove lib
+java_library {
+ name: "frameworks-net-shared-utils",
+ srcs: [
+ "java/android/net/util/FdEventsReader.java",
+ ]
+}
\ No newline at end of file
diff --git a/services/net/java/android/net/dhcp/DhcpPacket.java b/services/net/java/android/net/dhcp/DhcpPacket.java
index 6ba7d94..ce8b7e7 100644
--- a/services/net/java/android/net/dhcp/DhcpPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpPacket.java
@@ -1,8 +1,5 @@
package android.net.dhcp;
-import static android.net.util.NetworkConstants.IPV4_MAX_MTU;
-import static android.net.util.NetworkConstants.IPV4_MIN_MTU;
-
import android.annotation.Nullable;
import android.net.DhcpResults;
import android.net.LinkAddress;
@@ -37,6 +34,9 @@
public abstract class DhcpPacket {
protected static final String TAG = "DhcpPacket";
+ // TODO: use NetworkStackConstants.IPV4_MIN_MTU once this class is moved to the network stack.
+ private static final int IPV4_MIN_MTU = 68;
+
// dhcpcd has a minimum lease of 20 seconds, but DhcpStateMachine would refuse to wake up the
// CPU for anything shorter than 5 minutes. For sanity's sake, this must be higher than the
// DHCP client timeout.
diff --git a/services/net/java/android/net/ip/IpServer.java b/services/net/java/android/net/ip/IpServer.java
index 493350d..8b22f68 100644
--- a/services/net/java/android/net/ip/IpServer.java
+++ b/services/net/java/android/net/ip/IpServer.java
@@ -17,20 +17,26 @@
package android.net.ip;
import static android.net.NetworkUtils.numericToInetAddress;
-import static android.net.util.NetworkConstants.asByte;
+import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
import static android.net.util.NetworkConstants.FF;
import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH;
+import static android.net.util.NetworkConstants.asByte;
+import android.content.Context;
import android.net.ConnectivityManager;
import android.net.INetd;
+import android.net.INetworkStackStatusCallback;
import android.net.INetworkStatsService;
import android.net.InterfaceConfiguration;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
+import android.net.NetworkStack;
import android.net.RouteInfo;
-import android.net.dhcp.DhcpServer;
-import android.net.dhcp.DhcpServingParams;
+import android.net.dhcp.DhcpServerCallbacks;
+import android.net.dhcp.DhcpServingParamsParcel;
+import android.net.dhcp.DhcpServingParamsParcelExt;
+import android.net.dhcp.IDhcpServer;
import android.net.ip.RouterAdvertisementDaemon.RaParams;
import android.net.util.InterfaceParams;
import android.net.util.InterfaceSet;
@@ -126,6 +132,10 @@
}
public static class Dependencies {
+ private final Context mContext;
+ public Dependencies(Context context) {
+ mContext = context;
+ }
public RouterAdvertisementDaemon getRouterAdvertisementDaemon(InterfaceParams ifParams) {
return new RouterAdvertisementDaemon(ifParams);
}
@@ -138,9 +148,12 @@
return NetdService.getInstance();
}
- public DhcpServer makeDhcpServer(Looper looper, String ifName,
- DhcpServingParams params, SharedLog log) {
- return new DhcpServer(looper, ifName, params, log);
+ /**
+ * Create a DhcpServer instance to be used by IpServer.
+ */
+ public void makeDhcpServer(String ifName, DhcpServingParamsParcel params,
+ DhcpServerCallbacks cb) {
+ mContext.getSystemService(NetworkStack.class).makeDhcpServer(ifName, params, cb);
}
}
@@ -197,7 +210,10 @@
// Advertisements (otherwise, we do not add them to mLinkProperties at all).
private LinkProperties mLastIPv6LinkProperties;
private RouterAdvertisementDaemon mRaDaemon;
- private DhcpServer mDhcpServer;
+
+ // To be accessed only on the handler thread
+ private int mDhcpServerStartIndex = 0;
+ private IDhcpServer mDhcpServer;
private RaParams mLastRaParams;
public IpServer(
@@ -252,35 +268,109 @@
private boolean startIPv4() { return configureIPv4(true); }
+ /**
+ * Convenience wrapper around INetworkStackStatusCallback to run callbacks on the IpServer
+ * handler.
+ *
+ * <p>Different instances of this class can be created for each call to IDhcpServer methods,
+ * with different implementations of the callback, to differentiate handling of success/error in
+ * each call.
+ */
+ private abstract class OnHandlerStatusCallback extends INetworkStackStatusCallback.Stub {
+ @Override
+ public void onStatusAvailable(int statusCode) {
+ getHandler().post(() -> callback(statusCode));
+ }
+
+ public abstract void callback(int statusCode);
+ }
+
+ private class DhcpServerCallbacksImpl extends DhcpServerCallbacks {
+ private final int mStartIndex;
+
+ private DhcpServerCallbacksImpl(int startIndex) {
+ mStartIndex = startIndex;
+ }
+
+ @Override
+ public void onDhcpServerCreated(int statusCode, IDhcpServer server) throws RemoteException {
+ getHandler().post(() -> {
+ // We are on the handler thread: mDhcpServerStartIndex can be read safely.
+ if (mStartIndex != mDhcpServerStartIndex) {
+ // This start request is obsolete. When the |server| binder token goes out of
+ // scope, the garbage collector will finalize it, which causes the network stack
+ // process garbage collector to collect the server itself.
+ return;
+ }
+
+ if (statusCode != STATUS_SUCCESS) {
+ mLog.e("Error obtaining DHCP server: " + statusCode);
+ handleError();
+ return;
+ }
+
+ mDhcpServer = server;
+ try {
+ mDhcpServer.start(new OnHandlerStatusCallback() {
+ @Override
+ public void callback(int startStatusCode) {
+ if (startStatusCode != STATUS_SUCCESS) {
+ mLog.e("Error starting DHCP server: " + startStatusCode);
+ handleError();
+ }
+ }
+ });
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ });
+ }
+
+ private void handleError() {
+ mLastError = ConnectivityManager.TETHER_ERROR_DHCPSERVER_ERROR;
+ transitionTo(mInitialState);
+ }
+ }
+
private boolean startDhcp(Inet4Address addr, int prefixLen) {
if (mUsingLegacyDhcp) {
return true;
}
- final DhcpServingParams params;
- try {
- params = new DhcpServingParams.Builder()
- .setDefaultRouters(addr)
- .setDhcpLeaseTimeSecs(DHCP_LEASE_TIME_SECS)
- .setDnsServers(addr)
- .setServerAddr(new LinkAddress(addr, prefixLen))
- .setMetered(true)
- .build();
- // TODO: also advertise link MTU
- } catch (DhcpServingParams.InvalidParameterException e) {
- Log.e(TAG, "Invalid DHCP parameters", e);
- return false;
- }
+ final DhcpServingParamsParcel params;
+ params = new DhcpServingParamsParcelExt()
+ .setDefaultRouters(addr)
+ .setDhcpLeaseTimeSecs(DHCP_LEASE_TIME_SECS)
+ .setDnsServers(addr)
+ .setServerAddr(new LinkAddress(addr, prefixLen))
+ .setMetered(true);
+ // TODO: also advertise link MTU
- mDhcpServer = mDeps.makeDhcpServer(getHandler().getLooper(), mIfaceName, params,
- mLog.forSubComponent("DHCP"));
- mDhcpServer.start();
+ mDhcpServerStartIndex++;
+ mDeps.makeDhcpServer(
+ mIfaceName, params, new DhcpServerCallbacksImpl(mDhcpServerStartIndex));
return true;
}
private void stopDhcp() {
+ // Make all previous start requests obsolete so servers are not started later
+ mDhcpServerStartIndex++;
+
if (mDhcpServer != null) {
- mDhcpServer.stop();
- mDhcpServer = null;
+ try {
+ mDhcpServer.stop(new OnHandlerStatusCallback() {
+ @Override
+ public void callback(int statusCode) {
+ if (statusCode != STATUS_SUCCESS) {
+ mLog.e("Error stopping DHCP server: " + statusCode);
+ mLastError = ConnectivityManager.TETHER_ERROR_DHCPSERVER_ERROR;
+ // Not much more we can do here
+ }
+ }
+ });
+ mDhcpServer = null;
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
}
}
diff --git a/services/net/java/android/net/util/NetworkConstants.java b/services/net/java/android/net/util/NetworkConstants.java
index 3defe56..c183b81 100644
--- a/services/net/java/android/net/util/NetworkConstants.java
+++ b/services/net/java/android/net/util/NetworkConstants.java
@@ -16,9 +16,6 @@
package android.net.util;
-import java.nio.ByteBuffer;
-
-
/**
* Networking protocol constants.
*
@@ -81,8 +78,6 @@
* - https://tools.ietf.org/html/rfc791
*/
public static final int IPV4_HEADER_MIN_LEN = 20;
- public static final int IPV4_MIN_MTU = 68;
- public static final int IPV4_MAX_MTU = 65_535;
public static final int IPV4_IHL_MASK = 0xf;
public static final int IPV4_FLAGS_OFFSET = 6;
public static final int IPV4_FRAGMENT_MASK = 0x1fff;
diff --git a/services/net/java/android/net/util/SharedLog.java b/services/net/java/android/net/util/SharedLog.java
index 74bc147..8b7b59d 100644
--- a/services/net/java/android/net/util/SharedLog.java
+++ b/services/net/java/android/net/util/SharedLog.java
@@ -32,6 +32,7 @@
*
* All access to class methods other than dump() must be on the same thread.
*
+ * TODO: this is a copy of SharedLog in the NetworkStack. Remove after Tethering is migrated.
* @hide
*/
public class SharedLog {
diff --git a/services/robotests/backup/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java b/services/robotests/backup/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
index 423512c..aca48b6 100644
--- a/services/robotests/backup/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
@@ -12,6 +12,7 @@
import static org.testng.Assert.expectThrows;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.Application;
import android.app.backup.BackupDataInput;
import android.app.backup.FullBackupDataOutput;
@@ -62,6 +63,7 @@
private static final String TEST_PACKAGE_INSTALLER = "com.test.package.installer";
private static final Long TEST_PACKAGE_VERSION_CODE = 100L;
+ private @UserIdInt int mUserId;
private PackageManager mPackageManager;
private ShadowApplicationPackageManager mShadowPackageManager;
private File mFilesDir;
@@ -72,6 +74,7 @@
public void setUp() throws Exception {
Application application = RuntimeEnvironment.application;
+ mUserId = UserHandle.USER_SYSTEM;
mPackageManager = application.getPackageManager();
mShadowPackageManager = (ShadowApplicationPackageManager) shadowOf(mPackageManager);
@@ -352,7 +355,7 @@
byte[] obbBytes = "obb".getBytes();
File obbFile = createObbFileAndWrite(obbDir, obbBytes);
- mBackupWriter.backupObb(packageInfo);
+ mBackupWriter.backupObb(mUserId, packageInfo);
byte[] writtenBytes = getWrittenBytes(mBackupDataOutputFile, /* includeTarHeader */ false);
assertThat(writtenBytes).isEqualTo(obbBytes);
@@ -366,7 +369,7 @@
File obbDir = createObbDirForPackage(packageInfo.packageName);
// No obb file created.
- mBackupWriter.backupObb(packageInfo);
+ mBackupWriter.backupObb(mUserId, packageInfo);
assertThat(mBackupDataOutputFile.length()).isEqualTo(0);
}
diff --git a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
index 5e84ab0..7dac795 100644
--- a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
@@ -71,6 +71,7 @@
import android.annotation.Nullable;
import android.app.Application;
+import android.app.ApplicationPackageManager;
import android.app.IBackupAgent;
import android.app.backup.BackupAgent;
import android.app.backup.BackupDataInput;
@@ -134,6 +135,8 @@
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
+import org.robolectric.annotation.Implements;
+import org.robolectric.annotation.Resetter;
import org.robolectric.shadows.ShadowLooper;
import org.robolectric.shadows.ShadowPackageManager;
import org.robolectric.shadows.ShadowQueuedWork;
@@ -157,6 +160,7 @@
@Config(
shadows = {
FrameworkShadowLooper.class,
+ KeyValueBackupTaskTest.ShadowApplicationPackageManager.class,
ShadowBackupDataInput.class,
ShadowBackupDataOutput.class,
ShadowEventLog.class,
@@ -244,6 +248,7 @@
@After
public void tearDown() throws Exception {
ShadowBackupDataInput.reset();
+ ShadowApplicationPackageManager.reset();
}
@Test
@@ -2435,7 +2440,8 @@
mPackageManager.setApplicationEnabledSetting(
packageData.packageName, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0);
PackageInfo packageInfo = getPackageInfo(packageData);
- mShadowPackageManager.addPackage(packageInfo);
+ mShadowPackageManager.installPackage(packageInfo);
+ ShadowApplicationPackageManager.setPackageInfo(packageInfo);
mContext.sendBroadcast(getPackageAddedIntent(packageData));
// Run the backup looper because on the receiver we post MSG_SCHEDULE_BACKUP_PACKAGE
mShadowBackupLooper.runToEndOfTasks();
@@ -2848,4 +2854,29 @@
throw mException;
}
}
+
+ /**
+ * Extends {@link org.robolectric.shadows.ShadowApplicationPackageManager} to return the correct
+ * package in user-specific invocations.
+ */
+ @Implements(value = ApplicationPackageManager.class)
+ public static class ShadowApplicationPackageManager
+ extends org.robolectric.shadows.ShadowApplicationPackageManager {
+ private static PackageInfo sPackageInfo;
+
+ static void setPackageInfo(PackageInfo packageInfo) {
+ sPackageInfo = packageInfo;
+ }
+
+ @Override
+ protected PackageInfo getPackageInfoAsUser(String packageName, int flags, int userId) {
+ return sPackageInfo;
+ }
+
+ /** Clear {@link #sPackageInfo}. */
+ @Resetter
+ public static void reset() {
+ sPackageInfo = null;
+ }
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
index 5286104..7049b21 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
@@ -17,6 +17,7 @@
import static com.android.server.hdmi.Constants.ADDR_AUDIO_SYSTEM;
import static com.android.server.hdmi.Constants.ADDR_BROADCAST;
+import static com.android.server.hdmi.Constants.ADDR_TUNER_1;
import static com.android.server.hdmi.Constants.ADDR_TV;
import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
import static com.android.server.hdmi.HdmiControlService.STANDBY_SCREEN_OFF;
@@ -34,7 +35,6 @@
import com.android.server.hdmi.HdmiCecLocalDevice.ActiveSource;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -60,74 +60,76 @@
private int mMusicVolume;
private int mMusicMaxVolume;
private boolean mMusicMute;
+ private int mAvrPhysicalAddress;
@Before
public void setUp() {
mHdmiControlService =
- new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
- @Override
- AudioManager getAudioManager() {
- return new AudioManager() {
- @Override
- public int getStreamVolume(int streamType) {
- switch (streamType) {
- case STREAM_MUSIC:
- return mMusicVolume;
- default:
- return 0;
- }
+ new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
+ @Override
+ AudioManager getAudioManager() {
+ return new AudioManager() {
+ @Override
+ public int getStreamVolume(int streamType) {
+ switch (streamType) {
+ case STREAM_MUSIC:
+ return mMusicVolume;
+ default:
+ return 0;
}
+ }
- @Override
- public boolean isStreamMute(int streamType) {
- switch (streamType) {
- case STREAM_MUSIC:
- return mMusicMute;
- default:
- return false;
- }
+ @Override
+ public boolean isStreamMute(int streamType) {
+ switch (streamType) {
+ case STREAM_MUSIC:
+ return mMusicMute;
+ default:
+ return false;
}
+ }
- @Override
- public int getStreamMaxVolume(int streamType) {
- switch (streamType) {
- case STREAM_MUSIC:
- return mMusicMaxVolume;
- default:
- return 100;
- }
+ @Override
+ public int getStreamMaxVolume(int streamType) {
+ switch (streamType) {
+ case STREAM_MUSIC:
+ return mMusicMaxVolume;
+ default:
+ return 100;
}
+ }
- @Override
- public void adjustStreamVolume(
- int streamType, int direction, int flags) {
- switch (streamType) {
- case STREAM_MUSIC:
- if (direction == AudioManager.ADJUST_UNMUTE) {
- mMusicMute = false;
- } else if (direction == AudioManager.ADJUST_MUTE) {
- mMusicMute = true;
- }
- default:
- }
+ @Override
+ public void adjustStreamVolume(
+ int streamType, int direction, int flags) {
+ switch (streamType) {
+ case STREAM_MUSIC:
+ if (direction == AudioManager.ADJUST_UNMUTE) {
+ mMusicMute = false;
+ } else if (direction == AudioManager.ADJUST_MUTE) {
+ mMusicMute = true;
+ }
+ break;
+ default:
}
+ }
- @Override
- public void setWiredDeviceConnectionState(
- int type, int state, String address, String name) {
- // Do nothing.
- }
- };
- }
+ @Override
+ public void setWiredDeviceConnectionState(
+ int type, int state, String address, String name) {
+ // Do nothing.
+ }
+ };
+ }
- @Override
- void wakeUp() {}
+ @Override
+ void wakeUp() {}
- @Override
- boolean isControlEnabled() {
- return true;
- }
- };
+ @Override
+ boolean isControlEnabled() {
+ return true;
+ }
+ };
mMyLooper = mTestLooper.getLooper();
mHdmiCecLocalDeviceAudioSystem = new HdmiCecLocalDeviceAudioSystem(mHdmiControlService);
@@ -135,7 +137,7 @@
mHdmiControlService.setIoLooper(mMyLooper);
mNativeWrapper = new FakeNativeWrapper();
mHdmiCecController =
- HdmiCecController.createWithNativeWrapper(mHdmiControlService, mNativeWrapper);
+ HdmiCecController.createWithNativeWrapper(mHdmiControlService, mNativeWrapper);
mHdmiControlService.setCecController(mHdmiCecController);
mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
@@ -145,7 +147,10 @@
mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
mTestLooper.dispatchAll();
mNativeWrapper.clearResultMessages();
+ mAvrPhysicalAddress = 0x2000;
+ mNativeWrapper.setPhysicalAddress(mAvrPhysicalAddress);
SystemProperties.set(Constants.PROPERTY_ARC_SUPPORT, "true");
+ SystemProperties.set(Constants.PROPERTY_SYSTEM_AUDIO_MODE_MUTING_ENABLE, "true");
}
@Test
@@ -167,16 +172,16 @@
@Test
public void handleGiveSystemAudioModeStatus_originalOff() throws Exception {
HdmiCecMessage expectedMessage =
- HdmiCecMessageBuilder.buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, false);
+ HdmiCecMessageBuilder.buildReportSystemAudioMode(
+ ADDR_AUDIO_SYSTEM, ADDR_TV, false);
HdmiCecMessage messageGive =
HdmiCecMessageBuilder.buildGiveSystemAudioModeStatus(ADDR_TV, ADDR_AUDIO_SYSTEM);
assertThat(mHdmiCecLocalDeviceAudioSystem.handleGiveSystemAudioModeStatus(messageGive))
- .isTrue();
+ .isTrue();
mTestLooper.dispatchAll();
assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
}
- @Ignore("b/80297700")
@Test
public void handleRequestShortAudioDescriptor_featureDisabled() throws Exception {
HdmiCecMessage expectedMessage =
@@ -188,9 +193,9 @@
mHdmiCecLocalDeviceAudioSystem.setSystemAudioControlFeatureEnabled(false);
assertThat(
- mHdmiCecLocalDeviceAudioSystem.handleRequestShortAudioDescriptor(
- MESSAGE_REQUEST_SAD_LCPM))
- .isTrue();
+ mHdmiCecLocalDeviceAudioSystem.handleRequestShortAudioDescriptor(
+ MESSAGE_REQUEST_SAD_LCPM))
+ .isTrue();
mTestLooper.dispatchAll();
assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
}
@@ -204,11 +209,11 @@
Constants.MESSAGE_REQUEST_SHORT_AUDIO_DESCRIPTOR,
Constants.ABORT_NOT_IN_CORRECT_MODE);
- mHdmiCecLocalDeviceAudioSystem.setSystemAudioMode(false);
+ mHdmiCecLocalDeviceAudioSystem.checkSupportAndSetSystemAudioMode(false);
assertThat(
- mHdmiCecLocalDeviceAudioSystem.handleRequestShortAudioDescriptor(
- MESSAGE_REQUEST_SAD_LCPM))
- .isEqualTo(true);
+ mHdmiCecLocalDeviceAudioSystem.handleRequestShortAudioDescriptor(
+ MESSAGE_REQUEST_SAD_LCPM))
+ .isEqualTo(true);
mTestLooper.dispatchAll();
assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
}
@@ -222,11 +227,11 @@
Constants.MESSAGE_REQUEST_SHORT_AUDIO_DESCRIPTOR,
Constants.ABORT_UNABLE_TO_DETERMINE);
- mHdmiCecLocalDeviceAudioSystem.setSystemAudioMode(true);
+ mHdmiCecLocalDeviceAudioSystem.checkSupportAndSetSystemAudioMode(true);
assertThat(
- mHdmiCecLocalDeviceAudioSystem.handleRequestShortAudioDescriptor(
- MESSAGE_REQUEST_SAD_LCPM))
- .isEqualTo(true);
+ mHdmiCecLocalDeviceAudioSystem.handleRequestShortAudioDescriptor(
+ MESSAGE_REQUEST_SAD_LCPM))
+ .isEqualTo(true);
mTestLooper.dispatchAll();
assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
}
@@ -242,23 +247,22 @@
HdmiCecMessage expectedMessage =
HdmiCecMessageBuilder.buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, false);
assertThat(mHdmiCecLocalDeviceAudioSystem.handleGiveSystemAudioModeStatus(messageGive))
- .isTrue();
+ .isTrue();
mTestLooper.dispatchAll();
assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
// Check if correctly turned on
mNativeWrapper.clearResultMessages();
expectedMessage =
- HdmiCecMessageBuilder.buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, true);
+ HdmiCecMessageBuilder.buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, true);
assertThat(mHdmiCecLocalDeviceAudioSystem.handleSetSystemAudioMode(messageSet)).isTrue();
mTestLooper.dispatchAll();
assertThat(mHdmiCecLocalDeviceAudioSystem.handleGiveSystemAudioModeStatus(messageGive))
- .isTrue();
+ .isTrue();
mTestLooper.dispatchAll();
assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
assertThat(mMusicMute).isFalse();
}
- @Ignore("b/80297700")
@Test
public void handleSystemAudioModeRequest_turnOffByTv() throws Exception {
assertThat(mMusicMute).isFalse();
@@ -272,25 +276,26 @@
HdmiCecMessageBuilder.buildSetSystemAudioMode(
ADDR_AUDIO_SYSTEM, ADDR_BROADCAST, false);
assertThat(mHdmiCecLocalDeviceAudioSystem.handleSystemAudioModeRequest(messageRequestOff))
- .isTrue();
+ .isTrue();
mTestLooper.dispatchAll();
assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
mNativeWrapper.clearResultMessages();
expectedMessage =
- HdmiCecMessageBuilder.buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, false);
+ HdmiCecMessageBuilder.buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, false);
assertThat(mHdmiCecLocalDeviceAudioSystem.handleGiveSystemAudioModeStatus(messageGive))
- .isTrue();
+ .isTrue();
mTestLooper.dispatchAll();
assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
assertThat(mMusicMute).isTrue();
}
- @Ignore("b/80297700")
@Test
public void onStandbyAudioSystem_currentSystemAudioControlOn() throws Exception {
+ mHdmiCecLocalDeviceAudioSystem.setAutoDeviceOff(false);
+ mHdmiCecLocalDeviceAudioSystem.setAutoTvOff(false);
// Set system audio control on first
- mHdmiCecLocalDeviceAudioSystem.setSystemAudioMode(true);
+ mHdmiCecLocalDeviceAudioSystem.checkSupportAndSetSystemAudioMode(true);
// Check if standby correctly turns off the feature
mHdmiCecLocalDeviceAudioSystem.onStandby(false, STANDBY_SCREEN_OFF);
mTestLooper.dispatchAll();
@@ -307,9 +312,9 @@
mHdmiCecLocalDeviceAudioSystem.systemAudioControlOnPowerOn(
Constants.ALWAYS_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, true);
assertThat(
- mHdmiCecLocalDeviceAudioSystem.getActions(
- SystemAudioInitiationActionFromAvr.class))
- .isNotEmpty();
+ mHdmiCecLocalDeviceAudioSystem.getActions(
+ SystemAudioInitiationActionFromAvr.class))
+ .isNotEmpty();
}
@Test
@@ -318,9 +323,9 @@
mHdmiCecLocalDeviceAudioSystem.systemAudioControlOnPowerOn(
Constants.NEVER_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, false);
assertThat(
- mHdmiCecLocalDeviceAudioSystem.getActions(
- SystemAudioInitiationActionFromAvr.class))
- .isEmpty();
+ mHdmiCecLocalDeviceAudioSystem.getActions(
+ SystemAudioInitiationActionFromAvr.class))
+ .isEmpty();
}
@Test
@@ -329,9 +334,9 @@
mHdmiCecLocalDeviceAudioSystem.systemAudioControlOnPowerOn(
Constants.USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, false);
assertThat(
- mHdmiCecLocalDeviceAudioSystem.getActions(
- SystemAudioInitiationActionFromAvr.class))
- .isEmpty();
+ mHdmiCecLocalDeviceAudioSystem.getActions(
+ SystemAudioInitiationActionFromAvr.class))
+ .isEmpty();
}
@Test
@@ -340,9 +345,9 @@
mHdmiCecLocalDeviceAudioSystem.systemAudioControlOnPowerOn(
Constants.USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, true);
assertThat(
- mHdmiCecLocalDeviceAudioSystem.getActions(
- SystemAudioInitiationActionFromAvr.class))
- .isNotEmpty();
+ mHdmiCecLocalDeviceAudioSystem.getActions(
+ SystemAudioInitiationActionFromAvr.class))
+ .isNotEmpty();
}
@Test
@@ -352,12 +357,12 @@
assertThat(mHdmiCecLocalDeviceAudioSystem.handleActiveSource(message)).isTrue();
mTestLooper.dispatchAll();
assertThat(mHdmiCecLocalDeviceAudioSystem.getActiveSource().equals(expectedActiveSource))
- .isTrue();
+ .isTrue();
}
@Test
public void terminateSystemAudioMode_systemAudioModeOff() throws Exception {
- mHdmiCecLocalDeviceAudioSystem.setSystemAudioMode(false);
+ mHdmiCecLocalDeviceAudioSystem.checkSupportAndSetSystemAudioMode(false);
assertThat(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated()).isFalse();
mMusicMute = false;
HdmiCecMessage message =
@@ -369,10 +374,9 @@
assertThat(mNativeWrapper.getResultMessages()).isEmpty();
}
- @Ignore("b/80297700")
@Test
public void terminateSystemAudioMode_systemAudioModeOn() throws Exception {
- mHdmiCecLocalDeviceAudioSystem.setSystemAudioMode(true);
+ mHdmiCecLocalDeviceAudioSystem.checkSupportAndSetSystemAudioMode(true);
assertThat(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated()).isTrue();
mMusicMute = false;
HdmiCecMessage expectedMessage =
@@ -386,42 +390,48 @@
}
@Test
- public void isPhysicalAddressMeOrBelow_isMe() throws Exception {
+ public void pathToPort_isMe() throws Exception {
int targetPhysicalAddress = 0x1000;
mNativeWrapper.setPhysicalAddress(0x1000);
- assertThat(mHdmiCecLocalDeviceAudioSystem.isPhysicalAddressMeOrBelow(targetPhysicalAddress))
- .isTrue();
+ assertThat(mHdmiCecLocalDeviceAudioSystem
+ .getLocalPortFromPhysicalAddress(targetPhysicalAddress))
+ .isEqualTo(0);
}
@Test
- public void isPhysicalAddressMeOrBelow_isBelow() throws Exception {
+ public void pathToPort_isBelow() throws Exception {
int targetPhysicalAddress = 0x1100;
mNativeWrapper.setPhysicalAddress(0x1000);
- assertThat(mHdmiCecLocalDeviceAudioSystem.isPhysicalAddressMeOrBelow(targetPhysicalAddress))
- .isTrue();
+ assertThat(mHdmiCecLocalDeviceAudioSystem
+ .getLocalPortFromPhysicalAddress(targetPhysicalAddress))
+ .isEqualTo(1);
}
@Test
- public void isPhysicalAddressMeOrBelow_neitherMeNorBelow() throws Exception {
+ public void pathToPort_neitherMeNorBelow() throws Exception {
int targetPhysicalAddress = 0x3000;
mNativeWrapper.setPhysicalAddress(0x2000);
- assertThat(mHdmiCecLocalDeviceAudioSystem.isPhysicalAddressMeOrBelow(targetPhysicalAddress))
- .isFalse();
+ assertThat(mHdmiCecLocalDeviceAudioSystem
+ .getLocalPortFromPhysicalAddress(targetPhysicalAddress))
+ .isEqualTo(-1);
targetPhysicalAddress = 0x2200;
mNativeWrapper.setPhysicalAddress(0x3300);
- assertThat(mHdmiCecLocalDeviceAudioSystem.isPhysicalAddressMeOrBelow(targetPhysicalAddress))
- .isFalse();
+ assertThat(mHdmiCecLocalDeviceAudioSystem
+ .getLocalPortFromPhysicalAddress(targetPhysicalAddress))
+ .isEqualTo(-1);
targetPhysicalAddress = 0x2213;
mNativeWrapper.setPhysicalAddress(0x2212);
- assertThat(mHdmiCecLocalDeviceAudioSystem.isPhysicalAddressMeOrBelow(targetPhysicalAddress))
- .isFalse();
+ assertThat(mHdmiCecLocalDeviceAudioSystem
+ .getLocalPortFromPhysicalAddress(targetPhysicalAddress))
+ .isEqualTo(-1);
targetPhysicalAddress = 0x2340;
mNativeWrapper.setPhysicalAddress(0x2310);
- assertThat(mHdmiCecLocalDeviceAudioSystem.isPhysicalAddressMeOrBelow(targetPhysicalAddress))
- .isFalse();
+ assertThat(mHdmiCecLocalDeviceAudioSystem
+ .getLocalPortFromPhysicalAddress(targetPhysicalAddress))
+ .isEqualTo(-1);
}
@Test
@@ -451,7 +461,7 @@
assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcInitiate(message)).isTrue();
mTestLooper.dispatchAll();
assertThat(mHdmiCecLocalDeviceAudioSystem.getActions(ArcInitiationActionFromAvr.class))
- .isNotEmpty();
+ .isNotEmpty();
}
@Test
@@ -466,7 +476,7 @@
assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcTermination(message)).isTrue();
mTestLooper.dispatchAll();
assertThat(mHdmiCecLocalDeviceAudioSystem.getActions(ArcTerminationActionFromAvr.class))
- .isNotEmpty();
+ .isNotEmpty();
}
@Test
@@ -513,4 +523,38 @@
mTestLooper.dispatchAll();
assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
}
+
+ public void handleSystemAudioModeRequest_fromNonTV_tVNotSupport() {
+ HdmiCecMessage message =
+ HdmiCecMessageBuilder.buildSystemAudioModeRequest(
+ ADDR_TUNER_1, ADDR_AUDIO_SYSTEM,
+ mAvrPhysicalAddress, true);
+ HdmiCecMessage expectedMessage =
+ HdmiCecMessageBuilder.buildFeatureAbortCommand(
+ ADDR_AUDIO_SYSTEM,
+ ADDR_TUNER_1,
+ Constants.MESSAGE_SYSTEM_AUDIO_MODE_REQUEST,
+ Constants.ABORT_REFUSED);
+
+ assertThat(mHdmiCecLocalDeviceAudioSystem.handleSystemAudioModeRequest(message)).isTrue();
+ mTestLooper.dispatchAll();
+ assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
+ }
+
+ @Test
+ public void handleSystemAudioModeRequest_fromNonTV_tVSupport() {
+ HdmiCecMessage message =
+ HdmiCecMessageBuilder.buildSystemAudioModeRequest(
+ ADDR_TUNER_1, ADDR_AUDIO_SYSTEM,
+ mAvrPhysicalAddress, true);
+ HdmiCecMessage expectedMessage =
+ HdmiCecMessageBuilder.buildSetSystemAudioMode(
+ ADDR_AUDIO_SYSTEM, Constants.ADDR_BROADCAST, true);
+ mHdmiCecLocalDeviceAudioSystem.setTvSystemAudioModeSupport(true);
+
+
+ assertThat(mHdmiCecLocalDeviceAudioSystem.handleSystemAudioModeRequest(message)).isTrue();
+ mTestLooper.dispatchAll();
+ assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
new file mode 100644
index 0000000..76f638c
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.hdmi;
+
+import static com.android.server.hdmi.Constants.ADDR_TV;
+import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Looper;
+import android.os.test.TestLooper;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.ArrayList;
+
+@SmallTest
+@RunWith(JUnit4.class)
+/** Tests for {@link HdmiCecLocalDevicePlayback} class. */
+public class HdmiCecLocalDevicePlaybackTest {
+
+ private HdmiControlService mHdmiControlService;
+ private HdmiCecController mHdmiCecController;
+ private HdmiCecLocalDevicePlayback mHdmiCecLocalDevicePlayback;
+ private FakeNativeWrapper mNativeWrapper;
+ private Looper mMyLooper;
+ private TestLooper mTestLooper = new TestLooper();
+ private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
+ private int mPlaybackPhysicalAddress;
+
+ @Before
+ public void setUp() {
+ mHdmiControlService =
+ new HdmiControlService(InstrumentationRegistry.getTargetContext()) {
+ @Override
+ void wakeUp() {
+ }
+
+ @Override
+ boolean isControlEnabled() {
+ return true;
+ }
+ };
+
+ mMyLooper = mTestLooper.getLooper();
+ mHdmiCecLocalDevicePlayback = new HdmiCecLocalDevicePlayback(mHdmiControlService);
+ mHdmiCecLocalDevicePlayback.init();
+ mHdmiControlService.setIoLooper(mMyLooper);
+ mNativeWrapper = new FakeNativeWrapper();
+ mHdmiCecController =
+ HdmiCecController.createWithNativeWrapper(mHdmiControlService, mNativeWrapper);
+ mHdmiControlService.setCecController(mHdmiCecController);
+ mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
+ mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
+ mLocalDevices.add(mHdmiCecLocalDevicePlayback);
+ mHdmiControlService.initPortInfo();
+ mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+ mTestLooper.dispatchAll();
+ mNativeWrapper.clearResultMessages();
+ mPlaybackPhysicalAddress = 0x2000;
+ mNativeWrapper.setPhysicalAddress(mPlaybackPhysicalAddress);
+ }
+
+ @Test
+ public void handleSetStreamPath_underCurrentDevice() {
+ assertThat(mHdmiCecLocalDevicePlayback.getLocalActivePath()).isEqualTo(0);
+ HdmiCecMessage message =
+ HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV, 0x2100);
+ assertThat(mHdmiCecLocalDevicePlayback.handleSetStreamPath(message)).isTrue();
+ assertThat(mHdmiCecLocalDevicePlayback.getLocalActivePath()).isEqualTo(1);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java
index 813fa82..d9faaa4 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java
@@ -16,8 +16,6 @@
package com.android.server.pm.dex;
-import static com.android.server.pm.PackageDexOptimizer.SKIP_SHARED_LIBRARY_CHECK;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
@@ -31,16 +29,16 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
import dalvik.system.DelegateLastClassLoader;
import dalvik.system.DexClassLoader;
import dalvik.system.PathClassLoader;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
import java.io.File;
-import java.util.Arrays;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -412,12 +410,6 @@
@Test
public void testEncodeClassLoader() {
- assertEquals(SKIP_SHARED_LIBRARY_CHECK, DexoptUtils.encodeClassLoader(
- SKIP_SHARED_LIBRARY_CHECK, "dalvik.system.PathClassLoader"));
- assertEquals(SKIP_SHARED_LIBRARY_CHECK, DexoptUtils.encodeClassLoader(
- SKIP_SHARED_LIBRARY_CHECK, "dalvik.system.DexClassLoader"));
- assertEquals(SKIP_SHARED_LIBRARY_CHECK, DexoptUtils.encodeClassLoader(
- SKIP_SHARED_LIBRARY_CHECK, "dalvik.system.DelegateLastClassLoader"));
assertEquals("PCL[xyz]", DexoptUtils.encodeClassLoader("xyz",
"dalvik.system.PathClassLoader"));
assertEquals("PCL[xyz]", DexoptUtils.encodeClassLoader("xyz",
@@ -435,15 +427,8 @@
@Test
public void testEncodeClassLoaderChain() {
- assertEquals(SKIP_SHARED_LIBRARY_CHECK, DexoptUtils.encodeClassLoaderChain(
- SKIP_SHARED_LIBRARY_CHECK, "PCL[a]"));
- assertEquals(SKIP_SHARED_LIBRARY_CHECK, DexoptUtils.encodeClassLoaderChain("PCL[a]",
- SKIP_SHARED_LIBRARY_CHECK));
assertEquals("PCL[a];DLC[b]", DexoptUtils.encodeClassLoaderChain("PCL[a]",
"DLC[b]"));
- assertEquals(SKIP_SHARED_LIBRARY_CHECK, DexoptUtils.encodeClassLoaderChain("PCL[a]",
- SKIP_SHARED_LIBRARY_CHECK));
-
try {
DexoptUtils.encodeClassLoaderChain("a", null);
fail(); // exception is expected
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
index ee336a8..bd29d2a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -169,6 +169,7 @@
private final ActivityTaskManagerService mService;
private ComponentName mComponent;
+ private String mTargetActivity;
private TaskRecord mTaskRecord;
private int mUid;
private boolean mCreateTask;
@@ -185,6 +186,11 @@
return this;
}
+ ActivityBuilder setTargetActivity(String targetActivity) {
+ mTargetActivity = targetActivity;
+ return this;
+ }
+
static ComponentName getDefaultComponent() {
return ComponentName.createRelative(DEFAULT_COMPONENT_PACKAGE_NAME,
DEFAULT_COMPONENT_PACKAGE_NAME);
@@ -239,6 +245,10 @@
aInfo.applicationInfo = new ApplicationInfo();
aInfo.applicationInfo.packageName = mComponent.getPackageName();
aInfo.applicationInfo.uid = mUid;
+ aInfo.packageName = mComponent.getPackageName();
+ if (mTargetActivity != null) {
+ aInfo.targetActivity = mTargetActivity;
+ }
aInfo.flags |= mActivityFlags;
aInfo.launchMode = mLaunchMode;
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
index dcfb879..d0b9225 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
@@ -87,8 +87,8 @@
verify(mSpec).startAnimation(any(), any(), callbackCaptor.capture());
callbackCaptor.getValue().onAnimationFinished(mSpec);
- verify(mTransaction).destroy(eq(leash));
- verify(mTransaction).destroy(eq(animationBoundsLayer));
+ verify(mTransaction).reparent(eq(leash), eq(null));
+ verify(mTransaction).reparent(eq(animationBoundsLayer), eq(null));
assertThat(mToken.mNeedsAnimationBoundsLayer).isFalse();
}
@@ -100,8 +100,8 @@
final SurfaceControl animationBoundsLayer = mToken.mAnimationBoundsLayer;
mToken.mSurfaceAnimator.cancelAnimation();
- verify(mTransaction).destroy(eq(leash));
- verify(mTransaction).destroy(eq(animationBoundsLayer));
+ verify(mTransaction).reparent(eq(leash), eq(null));
+ verify(mTransaction).reparent(eq(animationBoundsLayer), eq(null));
assertThat(mToken.mNeedsAnimationBoundsLayer).isFalse();
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
index ee1c8df..f99cd4b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
@@ -212,7 +212,7 @@
mDimmer.updateDims(mTransaction, new Rect());
verify(mSurfaceAnimatorStarter).startAnimation(any(SurfaceAnimator.class), any(
SurfaceControl.Transaction.class), any(AnimationAdapter.class), anyBoolean());
- verify(mHost.getPendingTransaction()).destroy(dimLayer);
+ verify(mHost.getPendingTransaction()).reparent(dimLayer, null);
}
@Test
@@ -269,7 +269,7 @@
mDimmer.updateDims(mTransaction, new Rect());
verify(mSurfaceAnimatorStarter, never()).startAnimation(any(SurfaceAnimator.class), any(
SurfaceControl.Transaction.class), any(AnimationAdapter.class), anyBoolean());
- verify(mTransaction).destroy(dimLayer);
+ verify(mTransaction).reparent(dimLayer, null);
}
private SurfaceControl getDimLayer() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
index d14f30d..ad80cd6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
@@ -96,7 +96,7 @@
callbackCaptor.getValue().onAnimationFinished(mSpec);
assertNotAnimating(mAnimatable);
assertTrue(mAnimatable.mFinishedCallbackCalled);
- verify(mTransaction).destroy(eq(mAnimatable.mLeash));
+ verify(mTransaction).reparent(eq(mAnimatable.mLeash), eq(null));
// TODO: Verify reparenting once we use mPendingTransaction to reparent it back
}
@@ -106,7 +106,7 @@
final SurfaceControl firstLeash = mAnimatable.mLeash;
mAnimatable.mSurfaceAnimator.startAnimation(mTransaction, mSpec2, true /* hidden */);
- verify(mTransaction).destroy(eq(firstLeash));
+ verify(mTransaction).reparent(eq(firstLeash), eq(null));
assertFalse(mAnimatable.mFinishedCallbackCalled);
final ArgumentCaptor<OnAnimationFinishedCallback> callbackCaptor = ArgumentCaptor.forClass(
@@ -133,7 +133,7 @@
assertNotAnimating(mAnimatable);
verify(mSpec).onAnimationCancelled(any());
assertTrue(mAnimatable.mFinishedCallbackCalled);
- verify(mTransaction).destroy(eq(mAnimatable.mLeash));
+ verify(mTransaction).reparent(eq(mAnimatable.mLeash), eq(null));
}
@Test
@@ -155,7 +155,7 @@
verifyZeroInteractions(mSpec);
assertNotAnimating(mAnimatable);
assertTrue(mAnimatable.mFinishedCallbackCalled);
- verify(mTransaction).destroy(eq(mAnimatable.mLeash));
+ verify(mTransaction).reparent(eq(mAnimatable.mLeash), eq(null));
}
@Test
@@ -171,11 +171,11 @@
assertNotAnimating(mAnimatable);
assertAnimating(mAnimatable2);
assertEquals(leash, mAnimatable2.mSurfaceAnimator.mLeash);
- verify(mTransaction, never()).destroy(eq(leash));
+ verify(mTransaction, never()).reparent(eq(leash), eq(null));
callbackCaptor.getValue().onAnimationFinished(mSpec);
assertNotAnimating(mAnimatable2);
assertTrue(mAnimatable2.mFinishedCallbackCalled);
- verify(mTransaction).destroy(eq(leash));
+ verify(mTransaction).reparent(eq(leash), eq(null));
}
@Test
@@ -198,7 +198,7 @@
mDeferFinishAnimatable.mEndDeferFinishCallback.run();
assertNotAnimating(mAnimatable2);
assertTrue(mDeferFinishAnimatable.mFinishedCallbackCalled);
- verify(mTransaction).destroy(eq(mDeferFinishAnimatable.mLeash));
+ verify(mTransaction).reparent(eq(mDeferFinishAnimatable.mLeash), eq(null));
}
private void assertAnimating(MyAnimatable animatable) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
index 00bec3f..8a98cbe 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -268,19 +268,38 @@
/** Ensures that the alias intent won't have target component resolved. */
@Test
public void testTaskIntentActivityAlias() {
- final String aliasActivity = DEFAULT_COMPONENT_PACKAGE_NAME + ".aliasActivity";
- final String targetActivity = DEFAULT_COMPONENT_PACKAGE_NAME + ".targetActivity";
+ final String aliasClassName = DEFAULT_COMPONENT_PACKAGE_NAME + ".aliasActivity";
+ final String targetClassName = DEFAULT_COMPONENT_PACKAGE_NAME + ".targetActivity";
+ final ComponentName aliasComponent =
+ new ComponentName(DEFAULT_COMPONENT_PACKAGE_NAME, aliasClassName);
+ final ComponentName targetComponent =
+ new ComponentName(DEFAULT_COMPONENT_PACKAGE_NAME, targetClassName);
+
final Intent intent = new Intent();
- intent.setComponent(new ComponentName(DEFAULT_COMPONENT_PACKAGE_NAME, aliasActivity));
+ intent.setComponent(aliasComponent);
final ActivityInfo info = new ActivityInfo();
info.applicationInfo = new ApplicationInfo();
info.packageName = DEFAULT_COMPONENT_PACKAGE_NAME;
- info.targetActivity = targetActivity;
+ info.targetActivity = targetClassName;
final TaskRecord task = TaskRecord.create(mService, 1 /* taskId */, info, intent,
null /* taskDescription */);
- assertEquals("The alias activity component should be saved in task intent.", aliasActivity,
+ assertEquals("The alias activity component should be saved in task intent.", aliasClassName,
task.intent.getComponent().getClassName());
+
+ ActivityRecord aliasActivity = new ActivityBuilder(mService).setComponent(
+ aliasComponent).setTargetActivity(targetClassName).build();
+ assertEquals("Should be the same intent filter.", true,
+ task.isSameIntentFilter(aliasActivity));
+
+ ActivityRecord targetActivity = new ActivityBuilder(mService).setComponent(
+ targetComponent).build();
+ assertEquals("Should be the same intent filter.", true,
+ task.isSameIntentFilter(targetActivity));
+
+ ActivityRecord defaultActivity = new ActivityBuilder(mService).build();
+ assertEquals("Should not be the same intent filter.", false,
+ task.isSameIntentFilter(defaultActivity));
}
private void testStackBoundsConfiguration(int windowingMode, Rect parentBounds, Rect bounds,
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 3813891..638cb03 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -198,8 +198,8 @@
mDisplayContent.mInputMethodTarget = null;
}
- // Wait until everything is really cleaned up.
- waitUntilHandlersIdle();
+ // Cleaned up everything in Handler.
+ WmServiceUtils.cleanupWindowManagerHandlers();
} catch (Exception e) {
Log.e(TAG, "Failed to tear down test", e);
throw e;
diff --git a/services/tests/wmtests/src/com/android/server/wm/WmServiceUtils.java b/services/tests/wmtests/src/com/android/server/wm/WmServiceUtils.java
index 2465e5d8..05ac8c1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WmServiceUtils.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WmServiceUtils.java
@@ -171,6 +171,16 @@
return sService;
}
+ static void cleanupWindowManagerHandlers() {
+ final WindowManagerService wm = getWindowManagerService();
+ if (wm == null) {
+ return;
+ }
+ wm.mH.removeCallbacksAndMessages(null);
+ wm.mAnimationHandler.removeCallbacksAndMessages(null);
+ SurfaceAnimationThread.getHandler().removeCallbacksAndMessages(null);
+ }
+
static void waitUntilWindowManagerHandlersIdle() {
final WindowManagerService wm = getWindowManagerService();
if (wm == null) {
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
index 38efc74..3d7cbb5 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
@@ -657,9 +657,8 @@
return;
}
- if (event.status != SoundTrigger.RECOGNITION_STATUS_GET_STATE_RESPONSE) {
- model.setStopped();
- }
+ model.setStopped();
+
try {
callback.onGenericSoundTriggerDetected((GenericRecognitionEvent) event);
} catch (DeadObjectException e) {
@@ -802,9 +801,7 @@
return;
}
- if (event.status != SoundTrigger.RECOGNITION_STATUS_GET_STATE_RESPONSE) {
- modelData.setStopped();
- }
+ modelData.setStopped();
try {
modelData.getCallback().onKeyphraseDetected((KeyphraseRecognitionEvent) event);
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 2820836..dcaa499 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -239,6 +239,30 @@
"android.telecom.event.HANDOVER_FAILED";
public static class Details {
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ prefix = { "DIRECTION_" },
+ value = {DIRECTION_UNKNOWN, DIRECTION_INCOMING, DIRECTION_OUTGOING})
+ public @interface CallDirection {}
+
+ /**
+ * Indicates that the call is neither and incoming nor an outgoing call. This can be the
+ * case for calls reported directly by a {@link ConnectionService} in special cases such as
+ * call handovers.
+ */
+ public static final int DIRECTION_UNKNOWN = -1;
+
+ /**
+ * Indicates that the call is an incoming call.
+ */
+ public static final int DIRECTION_INCOMING = 0;
+
+ /**
+ * Indicates that the call is an outgoing call.
+ */
+ public static final int DIRECTION_OUTGOING = 1;
+
/** Call can currently be put on hold or unheld. */
public static final int CAPABILITY_HOLD = 0x00000001;
@@ -519,6 +543,7 @@
private final Bundle mIntentExtras;
private final long mCreationTimeMillis;
private final CallIdentification mCallIdentification;
+ private final @CallDirection int mCallDirection;
/**
* Whether the supplied capabilities supports the specified capability.
@@ -838,6 +863,14 @@
return mCallIdentification;
}
+ /**
+ * Indicates whether the call is an incoming or outgoing call.
+ * @return The call's direction.
+ */
+ public @CallDirection int getCallDirection() {
+ return mCallDirection;
+ }
+
@Override
public boolean equals(Object o) {
if (o instanceof Details) {
@@ -859,7 +892,8 @@
areBundlesEqual(mExtras, d.mExtras) &&
areBundlesEqual(mIntentExtras, d.mIntentExtras) &&
Objects.equals(mCreationTimeMillis, d.mCreationTimeMillis) &&
- Objects.equals(mCallIdentification, d.mCallIdentification);
+ Objects.equals(mCallIdentification, d.mCallIdentification) &&
+ Objects.equals(mCallDirection, d.mCallDirection);
}
return false;
}
@@ -881,7 +915,8 @@
mExtras,
mIntentExtras,
mCreationTimeMillis,
- mCallIdentification);
+ mCallIdentification,
+ mCallDirection);
}
/** {@hide} */
@@ -902,7 +937,8 @@
Bundle extras,
Bundle intentExtras,
long creationTimeMillis,
- CallIdentification callIdentification) {
+ CallIdentification callIdentification,
+ int callDirection) {
mTelecomCallId = telecomCallId;
mHandle = handle;
mHandlePresentation = handlePresentation;
@@ -920,6 +956,7 @@
mIntentExtras = intentExtras;
mCreationTimeMillis = creationTimeMillis;
mCallIdentification = callIdentification;
+ mCallDirection = callDirection;
}
/** {@hide} */
@@ -941,7 +978,8 @@
parcelableCall.getExtras(),
parcelableCall.getIntentExtras(),
parcelableCall.getCreationTimeMillis(),
- parcelableCall.getCallIdentification());
+ parcelableCall.getCallIdentification(),
+ parcelableCall.getCallDirection());
}
@Override
diff --git a/telecomm/java/android/telecom/CallIdentification.java b/telecomm/java/android/telecom/CallIdentification.java
index 97af06c..87834fd 100644
--- a/telecomm/java/android/telecom/CallIdentification.java
+++ b/telecomm/java/android/telecom/CallIdentification.java
@@ -250,8 +250,8 @@
mDetails = details;
mPhoto = photo;
mNuisanceConfidence = nuisanceConfidence;
- mCallScreeningAppName = callScreeningPackageName;
- mCallScreeningPackageName = callScreeningAppName;
+ mCallScreeningAppName = callScreeningAppName;
+ mCallScreeningPackageName = callScreeningPackageName;
}
private String mName;
@@ -430,4 +430,22 @@
return Objects.hash(mName, mDescription, mDetails, mPhoto, mNuisanceConfidence,
mCallScreeningAppName, mCallScreeningPackageName);
}
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("[CallId mName=");
+ sb.append(Log.pii(mName));
+ sb.append(", mDesc=");
+ sb.append(mDescription);
+ sb.append(", mDet=");
+ sb.append(mDetails);
+ sb.append(", conf=");
+ sb.append(mNuisanceConfidence);
+ sb.append(", appName=");
+ sb.append(mCallScreeningAppName);
+ sb.append(", pkgName=");
+ sb.append(mCallScreeningPackageName);
+ return sb.toString();
+ }
}
diff --git a/telecomm/java/android/telecom/CallScreeningService.java b/telecomm/java/android/telecom/CallScreeningService.java
index be96b3c..826ad82 100644
--- a/telecomm/java/android/telecom/CallScreeningService.java
+++ b/telecomm/java/android/telecom/CallScreeningService.java
@@ -21,6 +21,7 @@
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
+import android.net.Uri;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -33,8 +34,9 @@
/**
* This service can be implemented by the default dialer (see
- * {@link TelecomManager#getDefaultDialerPackage()}) to allow or disallow incoming calls before
- * they are shown to a user.
+ * {@link TelecomManager#getDefaultDialerPackage()}) or a third party app to allow or disallow
+ * incoming calls before they are shown to a user. This service can also provide
+ * {@link CallIdentification} information for calls.
* <p>
* Below is an example manifest registration for a {@code CallScreeningService}.
* <pre>
@@ -56,6 +58,34 @@
* information about a {@link Call.Details call} which will be shown to the user in the
* Dialer app.</li>
* </ol>
+ * <p>
+ * <h2>Becoming the {@link CallScreeningService}</h2>
+ * Telecom will bind to a single app chosen by the user which implements the
+ * {@link CallScreeningService} API when there are new incoming and outgoing calls.
+ * <p>
+ * The code snippet below illustrates how your app can request that it fills the call screening
+ * role.
+ * <pre>
+ * {@code
+ * private static final int REQUEST_ID = 1;
+ *
+ * public void requestRole() {
+ * RoleManager roleManager = (RoleManager) getSystemService(ROLE_SERVICE);
+ * Intent intent = roleManager.createRequestRoleIntent("android.app.role.CALL_SCREENING_APP");
+ * startActivityForResult(intent, REQUEST_ID);
+ * }
+ *
+ * @Override
+ * public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ * if (requestCode == REQUEST_ID) {
+ * if (resultCode == android.app.Activity.RESULT_OK) {
+ * // Your app is now the call screening app
+ * } else {
+ * // Your app is not the call screening app
+ * }
+ * }
+ * }
+ * </pre>
*/
public abstract class CallScreeningService extends Service {
/**
@@ -222,30 +252,46 @@
}
/**
- * Called when a new incoming call is added.
- * {@link CallScreeningService#respondToCall(Call.Details, CallScreeningService.CallResponse)}
- * should be called to allow or disallow the call.
+ * Called when a new incoming or outgoing call is added which is not in the user's contact list.
+ * <p>
+ * A {@link CallScreeningService} must indicate whether an incoming call is allowed or not by
+ * calling
+ * {@link CallScreeningService#respondToCall(Call.Details, CallScreeningService.CallResponse)}.
+ * Your app can tell if a call is an incoming call by checking to see if
+ * {@link Call.Details#getCallDirection()} is {@link Call.Details#DIRECTION_INCOMING}.
+ * <p>
+ * For incoming or outgoing calls, the {@link CallScreeningService} can call
+ * {@link #provideCallIdentification(Call.Details, CallIdentification)} in order to provide
+ * {@link CallIdentification} for the call.
* <p>
* Note: The {@link Call.Details} instance provided to a call screening service will only have
* the following properties set. The rest of the {@link Call.Details} properties will be set to
* their default value or {@code null}.
* <ul>
- * <li>{@link Call.Details#getState()}</li>
+ * <li>{@link Call.Details#getCallDirection()}</li>
* <li>{@link Call.Details#getConnectTimeMillis()}</li>
* <li>{@link Call.Details#getCreationTimeMillis()}</li>
* <li>{@link Call.Details#getHandle()}</li>
* <li>{@link Call.Details#getHandlePresentation()}</li>
* </ul>
+ * <p>
+ * Only calls where the {@link Call.Details#getHandle() handle} {@link Uri#getScheme() scheme}
+ * is {@link PhoneAccount#SCHEME_TEL} are passed for call
+ * screening. Further, only calls which are not in the user's contacts are passed for
+ * screening. For outgoing calls, no post-dial digits are passed.
*
- * @param callDetails Information about a new incoming call, see {@link Call.Details}.
+ * @param callDetails Information about a new call, see {@link Call.Details}.
*/
public abstract void onScreenCall(@NonNull Call.Details callDetails);
/**
- * Responds to the given call, either allowing it or disallowing it.
+ * Responds to the given incoming call, either allowing it or disallowing it.
* <p>
* The {@link CallScreeningService} calls this method to inform the system whether the call
* should be silently blocked or not.
+ * <p>
+ * Calls to this method are ignored unless the {@link Call.Details#getCallDirection()} is
+ * {@link Call.Details#DIRECTION_INCOMING}.
*
* @param callDetails The call to allow.
* <p>
diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java
index 1aeeca7..f5f0af7 100644
--- a/telecomm/java/android/telecom/InCallService.java
+++ b/telecomm/java/android/telecom/InCallService.java
@@ -40,11 +40,30 @@
import java.util.List;
/**
- * This service is implemented by any app that wishes to provide the user-interface for managing
- * phone calls. Telecom binds to this service while there exists a live (active or incoming) call,
- * and uses it to notify the in-call app of any live and recently disconnected calls. An app must
- * first be set as the default phone app (See {@link TelecomManager#getDefaultDialerPackage()})
- * before the telecom service will bind to its {@code InCallService} implementation.
+ * This service is implemented by an app that wishes to provide functionality for managing
+ * phone calls.
+ * <p>
+ * There are three types of apps which Telecom can bind to when there exists a live (active or
+ * incoming) call:
+ * <ol>
+ * <li>Default Dialer/Phone app - the default dialer/phone app is one which provides the
+ * in-call user interface while the device is in a call. A device is bundled with a system
+ * provided default dialer/phone app. The user may choose a single app to take over this role
+ * from the system app.</li>
+ * <li>Default Car-mode Dialer/Phone app - the default car-mode dialer/phone app is one which
+ * provides the in-call user interface while the device is in a call and the device is in car
+ * mode. The user may choose a single app to fill this role.</li>
+ * <li>Call Companion app - a call companion app is one which provides no user interface itself,
+ * but exposes call information to another display surface, such as a wearable device. The
+ * user may choose multiple apps to fill this role.</li>
+ * </ol>
+ * <p>
+ * Apps which wish to fulfill one of the above roles use the {@link android.app.role.RoleManager}
+ * to request that they fill the desired role.
+ *
+ * <h2>Becoming the Default Phone App</h2>
+ * An app filling the role of the default phone app provides a user interface while the device is in
+ * a call, and the device is not in car mode.
* <p>
* Below is an example manifest registration for an {@code InCallService}. The meta-data
* {@link TelecomManager#METADATA_IN_CALL_SERVICE_UI} indicates that this particular
@@ -82,12 +101,34 @@
* }
* </pre>
* <p>
- * When a user installs your application and runs it for the first time, you should prompt the user
- * to see if they would like your application to be the new default phone app. See the
- * {@link TelecomManager#ACTION_CHANGE_DEFAULT_DIALER} intent documentation for more information on
- * how to do this.
+ * When a user installs your application and runs it for the first time, you should use the
+ * {@link android.app.role.RoleManager} to prompt the user to see if they would like your app to
+ * be the new default phone app.
+ * <p id="requestRole">
+ * The code below shows how your app can request to become the default phone/dialer app:
+ * <pre>
+ * {@code
+ * private static final int REQUEST_ID = 1;
+ *
+ * public void requestRole() {
+ * RoleManager roleManager = (RoleManager) getSystemService(ROLE_SERVICE);
+ * Intent intent = roleManager.createRequestRoleIntent(RoleManager.ROLE_DIALER);
+ * startActivityForResult(intent, REQUEST_ID);
+ * }
+ *
+ * @Override
+ * public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ * if (requestCode == REQUEST_ID) {
+ * if (resultCode == android.app.Activity.RESULT_OK) {
+ * // Your app is now the default dialer app
+ * } else {
+ * // Your app is not the default dialer app
+ * }
+ * }
+ * }
+ * </pre>
* <p id="incomingCallNotification">
- * <h2>Showing the Incoming Call Notification</h2>
+ * <h3>Showing the Incoming Call Notification</h3>
* When your app receives a new incoming call via {@link InCallService#onCallAdded(Call)}, it is
* responsible for displaying an incoming call UI for the incoming call. It should do this using
* {@link android.app.NotificationManager} APIs to post a new incoming call notification.
@@ -121,7 +162,7 @@
* heads-up notification if the user is actively using the phone. When the user is not using the
* phone, your full-screen incoming call UI is used instead.
* For example:
- * <pre><code>
+ * <pre><code>{@code
* // Create an intent which triggers your fullscreen incoming call user interface.
* Intent intent = new Intent(Intent.ACTION_MAIN, null);
* intent.setFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION | Intent.FLAG_ACTIVITY_NEW_TASK);
@@ -151,7 +192,49 @@
* NotificationManager notificationManager = mContext.getSystemService(
* NotificationManager.class);
* notificationManager.notify(YOUR_CHANNEL_ID, YOUR_TAG, YOUR_ID, builder.build());
- * </code></pre>
+ * }</pre>
+ * <p>
+ * <h2>Becoming the Default Car-mode Phone App</h2>
+ * An app filling the role of the default car-mode dialer/phone app provides a user interface while
+ * the device is in a call, and in car mode. See
+ * {@link android.app.UiModeManager#ACTION_ENTER_CAR_MODE} for more information about car mode.
+ * When the device is in car mode, Telecom binds to the default car-mode dialer/phone app instead
+ * of the usual dialer/phone app.
+ * <p>
+ * Similar to the requirements for becoming the default dialer/phone app, your app must declare a
+ * manifest entry for its {@link InCallService} implementation. Your manifest entry should ensure
+ * the following conditions are met:
+ * <ul>
+ * <li>Do NOT declare the {@link TelecomManager#METADATA_IN_CALL_SERVICE_UI} metadata.</li>
+ * <li>Set the {@link TelecomManager#METADATA_IN_CALL_SERVICE_CAR_MODE_UI} metadata to
+ * {@code true}<li>
+ * <li>Your app must request the permission
+ * {@link android.Manifest.permission.CALL_COMPANION_APP}.</li>
+ * </ul>
+ * <p>
+ * Your app should request to fill the role {@code android.app.role.CAR_MODE_DIALER_APP} in order to
+ * become the default (see <a href="#requestRole">above</a> for how to request your app fills this
+ * role).
+ *
+ * <h2>Becoming a Call Companion App</h2>
+ * An app which fills the companion app role does not directly provide a user interface while the
+ * device is in a call. Instead, it is typically used to relay information about calls to another
+ * display surface, such as a wearable device.
+ * <p>
+ * Similar to the requirements for becoming the default dialer/phone app, your app must declare a
+ * manifest entry for its {@link InCallService} implementation. Your manifest entry should
+ * ensure the following conditions are met:
+ * <ul>
+ * <li>Do NOT declare the {@link TelecomManager#METADATA_IN_CALL_SERVICE_UI} metadata.</li>
+ * <li>Do NOT declare the {@link TelecomManager#METADATA_IN_CALL_SERVICE_CAR_MODE_UI}
+ * metadata.</li>
+ * <li>Your app must request the permission
+ * {@link android.Manifest.permission.CALL_COMPANION_APP}.</li>
+ * </ul>
+ * <p>
+ * Your app should request to fill the role {@code android.app.role.CALL_COMPANION_APP} in order to
+ * become a call companion app (see <a href="#requestRole">above</a> for how to request your app
+ * fills this role).
*/
public abstract class InCallService extends Service {
diff --git a/telecomm/java/android/telecom/ParcelableCall.java b/telecomm/java/android/telecom/ParcelableCall.java
index 911786e..f7dec83 100644
--- a/telecomm/java/android/telecom/ParcelableCall.java
+++ b/telecomm/java/android/telecom/ParcelableCall.java
@@ -24,6 +24,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
+import android.telecom.Call.Details.CallDirection;
import java.util.ArrayList;
import java.util.Collections;
@@ -64,6 +65,7 @@
private final Bundle mExtras;
private final long mCreationTimeMillis;
private final CallIdentification mCallIdentification;
+ private final int mCallDirection;
public ParcelableCall(
String id,
@@ -92,7 +94,8 @@
Bundle intentExtras,
Bundle extras,
long creationTimeMillis,
- CallIdentification callIdentification) {
+ CallIdentification callIdentification,
+ int callDirection) {
mId = id;
mState = state;
mDisconnectCause = disconnectCause;
@@ -120,6 +123,7 @@
mExtras = extras;
mCreationTimeMillis = creationTimeMillis;
mCallIdentification = callIdentification;
+ mCallDirection = callDirection;
}
/** The unique ID of the call. */
@@ -318,6 +322,13 @@
return mCallIdentification;
}
+ /**
+ * Indicates whether the call is an incoming or outgoing call.
+ */
+ public @CallDirection int getCallDirection() {
+ return mCallDirection;
+ }
+
/** Responsible for creating ParcelableCall objects for deserialized Parcels. */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public static final Parcelable.Creator<ParcelableCall> CREATOR =
@@ -356,6 +367,7 @@
ParcelableRttCall rttCall = source.readParcelable(classLoader);
long creationTimeMillis = source.readLong();
CallIdentification callIdentification = source.readParcelable(classLoader);
+ int callDirection = source.readInt();
return new ParcelableCall(
id,
state,
@@ -383,7 +395,8 @@
intentExtras,
extras,
creationTimeMillis,
- callIdentification);
+ callIdentification,
+ callDirection);
}
@Override
@@ -429,6 +442,7 @@
destination.writeParcelable(mRttCall, 0);
destination.writeLong(mCreationTimeMillis);
destination.writeParcelable(mCallIdentification, 0);
+ destination.writeInt(mCallDirection);
}
@Override
diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java
index 893dbe3..61c6b48 100644
--- a/telephony/java/android/telephony/CellSignalStrengthLte.java
+++ b/telephony/java/android/telephony/CellSignalStrengthLte.java
@@ -30,7 +30,7 @@
public final class CellSignalStrengthLte extends CellSignalStrength implements Parcelable {
private static final String LOG_TAG = "CellSignalStrengthLte";
- private static final boolean DBG = true;
+ private static final boolean DBG = false;
/**
* Indicates the unknown or undetectable RSSI value in ASU.
diff --git a/telephony/java/android/telephony/DataFailCause.java b/telephony/java/android/telephony/DataFailCause.java
index c53b37d..26ec6de 100644
--- a/telephony/java/android/telephony/DataFailCause.java
+++ b/telephony/java/android/telephony/DataFailCause.java
@@ -17,6 +17,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.SystemApi;
import android.content.Context;
import android.os.PersistableBundle;
@@ -34,6 +35,7 @@
* Returned as the reason for a data connection failure as defined by modem and some local errors.
* @hide
*/
+@SystemApi
public final class DataFailCause {
/** There is no failure */
public static final int NONE = 0;
@@ -101,8 +103,8 @@
public static final int PDN_CONN_DOES_NOT_EXIST = 0x36;
/** Multiple connections to a same PDN is not allowed. */
public static final int MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED = 0x37;
- /** Packet Data Protocol (PDP) */
- public static final int MAX_ACTIVE_PDP_CONTEXT_REACHED = 0x41;
+ /** Max number of Packet Data Protocol (PDP) context reached. */
+ public static final int ACTIVE_PDP_CONTEXT_MAX_NUMBER_REACHED = 0x41;
/** Unsupported APN in current public land mobile network (PLMN). */
public static final int UNSUPPORTED_APN_IN_CURRENT_PLMN = 0x42;
/** Invalid transaction id. */
@@ -165,22 +167,36 @@
// Local errors generated by Vendor RIL
// specified in ril.h
+ /** Data fail due to registration failure. */
public static final int REGISTRATION_FAIL = -1;
+ /** Data fail due to GPRS registration failure. */
public static final int GPRS_REGISTRATION_FAIL = -2;
+ /** Data call drop due to network/modem disconnect. */
public static final int SIGNAL_LOST = -3; /* no retry */
+ /**
+ * Preferred technology has changed, must retry with parameters appropriate for new technology.
+ */
public static final int PREF_RADIO_TECH_CHANGED = -4;
+ /** data call was disconnected because radio was resetting, powered off. */
public static final int RADIO_POWER_OFF = -5; /* no retry */
+ /** Data call was disconnected by modem because tethered. */
public static final int TETHERED_CALL_ACTIVE = -6; /* no retry */
+ /** Data call fail due to unspecific errors. */
public static final int ERROR_UNSPECIFIED = 0xFFFF;
// Errors generated by the Framework
// specified here
+ /** Unknown data failure cause. */
public static final int UNKNOWN = 0x10000;
+ /** Data fail due to radio not unavailable. */
public static final int RADIO_NOT_AVAILABLE = 0x10001; /* no retry */
+ /** @hide */
public static final int UNACCEPTABLE_NETWORK_PARAMETER = 0x10002; /* no retry */
+ /** @hide */
public static final int CONNECTION_TO_DATACONNECTIONAC_BROKEN = 0x10003;
+ /** Data connection was lost. */
public static final int LOST_CONNECTION = 0x10004;
- /** Data was reset by framework. */
+ /** @hide */
public static final int RESET_BY_FRAMEWORK = 0x10005;
/** @hide */
@@ -216,7 +232,7 @@
ESM_INFO_NOT_RECEIVED,
PDN_CONN_DOES_NOT_EXIST,
MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED,
- MAX_ACTIVE_PDP_CONTEXT_REACHED,
+ ACTIVE_PDP_CONTEXT_MAX_NUMBER_REACHED,
UNSUPPORTED_APN_IN_CURRENT_PLMN,
INVALID_TRANSACTION_ID,
MESSAGE_INCORRECT_SEMANTIC,
@@ -308,8 +324,8 @@
sFailCauseMap.put(PDN_CONN_DOES_NOT_EXIST, "PDN_CONN_DOES_NOT_EXIST");
sFailCauseMap.put(MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED,
"MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED");
- sFailCauseMap.put(MAX_ACTIVE_PDP_CONTEXT_REACHED,
- "MAX_ACTIVE_PDP_CONTEXT_REACHED");
+ sFailCauseMap.put(ACTIVE_PDP_CONTEXT_MAX_NUMBER_REACHED,
+ "ACTIVE_PDP_CONTEXT_MAX_NUMBER_REACHED");
sFailCauseMap.put(UNSUPPORTED_APN_IN_CURRENT_PLMN,
"UNSUPPORTED_APN_IN_CURRENT_PLMN");
sFailCauseMap.put(INVALID_TRANSACTION_ID, "INVALID_TRANSACTION_ID");
@@ -369,6 +385,9 @@
sFailCauseMap.put(RESET_BY_FRAMEWORK, "RESET_BY_FRAMEWORK");
}
+ private DataFailCause() {
+ }
+
/**
* Map of subId -> set of data call setup permanent failure for the carrier.
*/
@@ -382,6 +401,8 @@
* @param cause data disconnect cause
* @param subId subscription index
* @return true if the fail cause code needs platform to trigger a modem restart.
+ *
+ * @hide
*/
public static boolean isRadioRestartFailure(@NonNull Context context, @FailCause int cause,
int subId) {
@@ -410,6 +431,7 @@
return false;
}
+ /** @hide */
public static boolean isPermanentFailure(@NonNull Context context, @FailCause int failCause,
int subId) {
synchronized (sPermanentFailureCache) {
@@ -469,6 +491,7 @@
}
}
+ /** @hide */
public static boolean isEventLoggable(@FailCause int dataFailCause) {
return (dataFailCause == OPERATOR_BARRED) || (dataFailCause == INSUFFICIENT_RESOURCES)
|| (dataFailCause == UNKNOWN_PDP_ADDRESS_TYPE)
@@ -488,11 +511,13 @@
|| (dataFailCause == UNACCEPTABLE_NETWORK_PARAMETER);
}
+ /** @hide */
public static String toString(@FailCause int dataFailCause) {
int cause = getFailCause(dataFailCause);
return (cause == UNKNOWN) ? "UNKNOWN(" + dataFailCause + ")" : sFailCauseMap.get(cause);
}
+ /** @hide */
public static int getFailCause(@FailCause int failCause) {
if (sFailCauseMap.containsKey(failCause)) {
return failCause;
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index 9317aa7..e27b385 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -184,14 +184,17 @@
public static final int LISTEN_PRECISE_CALL_STATE = 0x00000800;
/**
- * Listen for precise changes and fails on the data connection (cellular).
+ * Listen for {@link PreciseDataConnectionState} on the data connection (cellular).
+ *
* {@more}
* Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
* READ_PRECISE_PHONE_STATE}
*
* @see #onPreciseDataConnectionStateChanged
+ *
* @hide
*/
+ @SystemApi
public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE = 0x00001000;
/**
@@ -564,10 +567,11 @@
/**
* Callback invoked when data connection state changes with precise information.
+ * @param dataConnectionState {@link PreciseDataConnectionState}
*
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
public void onPreciseDataConnectionStateChanged(
PreciseDataConnectionState dataConnectionState) {
// default implementation empty
diff --git a/telephony/java/android/telephony/PreciseDataConnectionState.java b/telephony/java/android/telephony/PreciseDataConnectionState.java
index 8373899..57a1826 100644
--- a/telephony/java/android/telephony/PreciseDataConnectionState.java
+++ b/telephony/java/android/telephony/PreciseDataConnectionState.java
@@ -16,10 +16,12 @@
package android.telephony;
+import android.annotation.SystemApi;
import android.annotation.UnsupportedAppUsage;
import android.net.LinkProperties;
import android.os.Parcel;
import android.os.Parcelable;
+import android.telephony.data.ApnSetting;
import java.util.Objects;
@@ -31,7 +33,7 @@
* <ul>
* <li>Data connection state.
* <li>Network type of the connection.
- * <li>APN type.
+ * <li>APN types.
* <li>APN.
* <li>The properties of the network link.
* <li>Data connection fail cause.
@@ -39,14 +41,15 @@
*
* @hide
*/
-public class PreciseDataConnectionState implements Parcelable {
+@SystemApi
+public final class PreciseDataConnectionState implements Parcelable {
- private int mState = TelephonyManager.DATA_UNKNOWN;
- private int mNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
- private String mAPNType = "";
+ private @TelephonyManager.DataState int mState = TelephonyManager.DATA_UNKNOWN;
+ private @TelephonyManager.NetworkType int mNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ private @DataFailCause.FailCause int mFailCause = DataFailCause.NONE;
+ private @ApnSetting.ApnType int mAPNTypes = ApnSetting.TYPE_NONE;
private String mAPN = "";
private LinkProperties mLinkProperties = null;
- private String mFailCause = "";
/**
* Constructor
@@ -54,11 +57,14 @@
* @hide
*/
@UnsupportedAppUsage
- public PreciseDataConnectionState(int state, int networkType, String apnType, String apn,
- LinkProperties linkProperties, String failCause) {
+ public PreciseDataConnectionState(@TelephonyManager.DataState int state,
+ @TelephonyManager.NetworkType int networkType,
+ @ApnSetting.ApnType int apnTypes, String apn,
+ LinkProperties linkProperties,
+ @DataFailCause.FailCause int failCause) {
mState = state;
mNetworkType = networkType;
- mAPNType = apnType;
+ mAPNTypes = apnTypes;
mAPN = apn;
mLinkProperties = linkProperties;
mFailCause = failCause;
@@ -74,73 +80,52 @@
/**
* Construct a PreciseDataConnectionState object from the given parcel.
+ *
+ * @hide
*/
private PreciseDataConnectionState(Parcel in) {
mState = in.readInt();
mNetworkType = in.readInt();
- mAPNType = in.readString();
+ mAPNTypes = in.readInt();
mAPN = in.readString();
mLinkProperties = (LinkProperties)in.readParcelable(null);
- mFailCause = in.readString();
+ mFailCause = in.readInt();
}
/**
- * Get data connection state
- *
- * @see TelephonyManager#DATA_UNKNOWN
- * @see TelephonyManager#DATA_DISCONNECTED
- * @see TelephonyManager#DATA_CONNECTING
- * @see TelephonyManager#DATA_CONNECTED
- * @see TelephonyManager#DATA_SUSPENDED
+ * Returns the state of data connection that supported the apn types returned by
+ * {@link #getDataConnectionApnTypeBitMask()}
*/
- @UnsupportedAppUsage
- public int getDataConnectionState() {
+ public @TelephonyManager.DataState int getDataConnectionState() {
return mState;
}
/**
- * Get data connection network type
- *
- * @see TelephonyManager#NETWORK_TYPE_UNKNOWN
- * @see TelephonyManager#NETWORK_TYPE_GPRS
- * @see TelephonyManager#NETWORK_TYPE_EDGE
- * @see TelephonyManager#NETWORK_TYPE_UMTS
- * @see TelephonyManager#NETWORK_TYPE_CDMA
- * @see TelephonyManager#NETWORK_TYPE_EVDO_0
- * @see TelephonyManager#NETWORK_TYPE_EVDO_A
- * @see TelephonyManager#NETWORK_TYPE_1xRTT
- * @see TelephonyManager#NETWORK_TYPE_HSDPA
- * @see TelephonyManager#NETWORK_TYPE_HSUPA
- * @see TelephonyManager#NETWORK_TYPE_HSPA
- * @see TelephonyManager#NETWORK_TYPE_IDEN
- * @see TelephonyManager#NETWORK_TYPE_EVDO_B
- * @see TelephonyManager#NETWORK_TYPE_LTE
- * @see TelephonyManager#NETWORK_TYPE_EHRPD
- * @see TelephonyManager#NETWORK_TYPE_HSPAP
+ * Returns the network type associated with this data connection.
+ * @hide
*/
- @UnsupportedAppUsage
- public int getDataConnectionNetworkType() {
+ public @TelephonyManager.NetworkType int getDataConnectionNetworkType() {
return mNetworkType;
}
/**
- * Get data connection APN type
+ * Returns the data connection APN types supported by this connection and triggers
+ * {@link PreciseDataConnectionState} change.
*/
- @UnsupportedAppUsage
- public String getDataConnectionAPNType() {
- return mAPNType;
+ public @ApnSetting.ApnType int getDataConnectionApnTypeBitMask() {
+ return mAPNTypes;
}
/**
- * Get data connection APN.
+ * Returns APN {@link ApnSetting} of this data connection.
*/
- @UnsupportedAppUsage
- public String getDataConnectionAPN() {
+ public String getDataConnectionApn() {
return mAPN;
}
/**
- * Get the properties of the network link.
+ * Get the properties of the network link {@link LinkProperties}.
+ * @hide
*/
@UnsupportedAppUsage
public LinkProperties getDataConnectionLinkProperties() {
@@ -148,10 +133,9 @@
}
/**
- * Get data connection fail cause, in case there was a failure.
+ * Returns data connection fail cause, in case there was a failure.
*/
- @UnsupportedAppUsage
- public String getDataConnectionFailCause() {
+ public @DataFailCause.FailCause int getDataConnectionFailCause() {
return mFailCause;
}
@@ -164,10 +148,10 @@
public void writeToParcel(Parcel out, int flags) {
out.writeInt(mState);
out.writeInt(mNetworkType);
- out.writeString(mAPNType);
+ out.writeInt(mAPNTypes);
out.writeString(mAPN);
out.writeParcelable(mLinkProperties, flags);
- out.writeString(mFailCause);
+ out.writeInt(mFailCause);
}
public static final Parcelable.Creator<PreciseDataConnectionState> CREATOR
@@ -184,56 +168,23 @@
@Override
public int hashCode() {
- return Objects.hash(mState, mNetworkType, mAPNType, mAPN, mLinkProperties, mFailCause);
+ return Objects.hash(mState, mNetworkType, mAPNTypes, mAPN, mLinkProperties,
+ mFailCause);
}
@Override
public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (obj == null) {
+
+ if (!(obj instanceof PreciseDataConnectionState)) {
return false;
}
- if (getClass() != obj.getClass()) {
- return false;
- }
+
PreciseDataConnectionState other = (PreciseDataConnectionState) obj;
- if (mAPN == null) {
- if (other.mAPN != null) {
- return false;
- }
- } else if (!mAPN.equals(other.mAPN)) {
- return false;
- }
- if (mAPNType == null) {
- if (other.mAPNType != null) {
- return false;
- }
- } else if (!mAPNType.equals(other.mAPNType)) {
- return false;
- }
- if (mFailCause == null) {
- if (other.mFailCause != null) {
- return false;
- }
- } else if (!mFailCause.equals(other.mFailCause)) {
- return false;
- }
- if (mLinkProperties == null) {
- if (other.mLinkProperties != null) {
- return false;
- }
- } else if (!mLinkProperties.equals(other.mLinkProperties)) {
- return false;
- }
- if (mNetworkType != other.mNetworkType) {
- return false;
- }
- if (mState != other.mState) {
- return false;
- }
- return true;
+ return Objects.equals(mAPN, other.mAPN) && mAPNTypes == other.mAPNTypes
+ && mFailCause == other.mFailCause
+ && Objects.equals(mLinkProperties, other.mLinkProperties)
+ && mNetworkType == other.mNetworkType
+ && mState == other.mState;
}
@Override
@@ -242,10 +193,10 @@
sb.append("Data Connection state: " + mState);
sb.append(", Network type: " + mNetworkType);
- sb.append(", APN type: " + mAPNType);
+ sb.append(", APN types: " + ApnSetting.getApnTypesStringFromBitmask(mAPNTypes));
sb.append(", APN: " + mAPN);
sb.append(", Link properties: " + mLinkProperties);
- sb.append(", Fail cause: " + mFailCause);
+ sb.append(", Fail cause: " + DataFailCause.toString(mFailCause));
return sb.toString();
}
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index ef185c5..2271069 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -369,7 +369,7 @@
public int getLevel() {
int level = getPrimary().getLevel();
if (level < SIGNAL_STRENGTH_NONE_OR_UNKNOWN || level > SIGNAL_STRENGTH_GREAT) {
- log("Invalid Level " + level + ", this=" + this);
+ loge("Invalid Level " + level + ", this=" + this);
return SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
}
return getPrimary().getLevel();
@@ -657,9 +657,16 @@
}
/**
- * log
+ * log warning
*/
private static void log(String s) {
Rlog.w(LOG_TAG, s);
}
+
+ /**
+ * log error
+ */
+ private static void loge(String s) {
+ Rlog.e(LOG_TAG, s);
+ }
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 8dfdb2f..6484005 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -4585,9 +4585,18 @@
}
}
- /** Data connection state: Unknown. Used before we know the state.
- * @hide
- */
+ /** @hide */
+ @IntDef(prefix = {"DATA_"}, value = {
+ DATA_UNKNOWN,
+ DATA_DISCONNECTED,
+ DATA_CONNECTING,
+ DATA_CONNECTED,
+ DATA_SUSPENDED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DataState{}
+
+ /** Data connection state: Unknown. Used before we know the state. */
public static final int DATA_UNKNOWN = -1;
/** Data connection state: Disconnected. IP traffic not available. */
public static final int DATA_DISCONNECTED = 0;
@@ -5849,9 +5858,14 @@
/**
* Returns the IMS Service Table (IST) that was loaded from the ISIM.
+ *
+ * See 3GPP TS 31.103 (Section 4.2.7) for the definition and more information on this table.
+ *
* @return IMS Service Table or null if not present or not loaded
* @hide
*/
+ @SystemApi
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public String getIsimIst() {
try {
IPhoneSubInfo info = getSubscriberInfo();
diff --git a/telephony/java/android/telephony/emergency/EmergencyNumber.java b/telephony/java/android/telephony/emergency/EmergencyNumber.java
index fe062d5..a94b163 100644
--- a/telephony/java/android/telephony/emergency/EmergencyNumber.java
+++ b/telephony/java/android/telephony/emergency/EmergencyNumber.java
@@ -29,6 +29,7 @@
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
/**
@@ -198,21 +199,53 @@
EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_DEFAULT);
}
+ /**
+ * Indicated the framework does not know whether an emergency call should be placed using
+ * emergency or normal call routing. This means the underlying radio or IMS implementation is
+ * free to determine for itself how to route the call.
+ */
+ public static final int EMERGENCY_CALL_ROUTING_UNKNOWN = 0;
+ /**
+ * Indicates the radio or IMS implementation must handle the call through emergency routing.
+ */
+ public static final int EMERGENCY_CALL_ROUTING_EMERGENCY = 1;
+ /**
+ * Indicates the radio or IMS implementation must handle the call through normal call routing.
+ */
+ public static final int EMERGENCY_CALL_ROUTING_NORMAL = 2;
+
+ /**
+ * The routing to tell how to handle the call for the corresponding emergency number.
+ *
+ * @hide
+ */
+ @IntDef(flag = false, prefix = { "EMERGENCY_CALL_ROUTING_" }, value = {
+ EMERGENCY_CALL_ROUTING_UNKNOWN,
+ EMERGENCY_CALL_ROUTING_EMERGENCY,
+ EMERGENCY_CALL_ROUTING_NORMAL
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface EmergencyCallRouting {}
+
+
private final String mNumber;
private final String mCountryIso;
private final String mMnc;
private final int mEmergencyServiceCategoryBitmask;
private final int mEmergencyNumberSourceBitmask;
+ private final int mEmergencyCallRouting;
/** @hide */
- public EmergencyNumber(@NonNull String number, @NonNull String countryIso,
- @NonNull String mnc, int emergencyServiceCategories,
- int emergencyNumberSources) {
+ public EmergencyNumber(@NonNull String number, @NonNull String countryIso, @NonNull String mnc,
+ @EmergencyServiceCategories int emergencyServiceCategories,
+ @EmergencyNumberSources int emergencyNumberSources,
+ @EmergencyCallRouting int emergencyCallRouting) {
this.mNumber = number;
this.mCountryIso = countryIso;
this.mMnc = mnc;
this.mEmergencyServiceCategoryBitmask = emergencyServiceCategories;
this.mEmergencyNumberSourceBitmask = emergencyNumberSources;
+ this.mEmergencyCallRouting = emergencyCallRouting;
}
/** @hide */
@@ -222,8 +255,33 @@
mMnc = source.readString();
mEmergencyServiceCategoryBitmask = source.readInt();
mEmergencyNumberSourceBitmask = source.readInt();
+ mEmergencyCallRouting = source.readInt();
}
+ @Override
+ /** @hide */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mNumber);
+ dest.writeString(mCountryIso);
+ dest.writeString(mMnc);
+ dest.writeInt(mEmergencyServiceCategoryBitmask);
+ dest.writeInt(mEmergencyNumberSourceBitmask);
+ dest.writeInt(mEmergencyCallRouting);
+ }
+
+ public static final Parcelable.Creator<EmergencyNumber> CREATOR =
+ new Parcelable.Creator<EmergencyNumber>() {
+ @Override
+ public EmergencyNumber createFromParcel(Parcel in) {
+ return new EmergencyNumber(in);
+ }
+
+ @Override
+ public EmergencyNumber[] newArray(int size) {
+ return new EmergencyNumber[size];
+ }
+ };
+
/**
* Get the dialing number of the emergency number.
*
@@ -352,14 +410,17 @@
return (mEmergencyNumberSourceBitmask & sources) == sources;
}
- @Override
- /** @hide */
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeString(mNumber);
- dest.writeString(mCountryIso);
- dest.writeString(mMnc);
- dest.writeInt(mEmergencyServiceCategoryBitmask);
- dest.writeInt(mEmergencyNumberSourceBitmask);
+ /**
+ * Returns the emergency call routing information.
+ *
+ * <p>Some regions require some emergency numbers which are not routed using typical emergency
+ * call processing, but are instead placed as regular phone calls. The emergency call routing
+ * field provides information about how an emergency call will be routed when it is placed.
+ *
+ * @return the emergency call routing requirement
+ */
+ public @EmergencyCallRouting int getEmergencyCallRouting() {
+ return mEmergencyCallRouting;
}
@Override
@@ -373,7 +434,8 @@
return "EmergencyNumber:" + "Number-" + mNumber + "|CountryIso-" + mCountryIso
+ "|Mnc-" + mMnc
+ "|ServiceCategories-" + Integer.toBinaryString(mEmergencyServiceCategoryBitmask)
- + "|Sources-" + Integer.toBinaryString(mEmergencyNumberSourceBitmask);
+ + "|Sources-" + Integer.toBinaryString(mEmergencyNumberSourceBitmask)
+ + "|Routing-" + Integer.toBinaryString(mEmergencyCallRouting);
}
@Override
@@ -381,7 +443,19 @@
if (!EmergencyNumber.class.isInstance(o)) {
return false;
}
- return (o == this || toString().equals(o.toString()));
+ EmergencyNumber other = (EmergencyNumber) o;
+ return mNumber.equals(other.mNumber)
+ && mCountryIso.equals(other.mCountryIso)
+ && mMnc.equals(other.mMnc)
+ && mEmergencyServiceCategoryBitmask == other.mEmergencyServiceCategoryBitmask
+ && mEmergencyNumberSourceBitmask == other.mEmergencyNumberSourceBitmask
+ && mEmergencyCallRouting == other.mEmergencyCallRouting;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mNumber, mCountryIso, mMnc, mEmergencyServiceCategoryBitmask,
+ mEmergencyNumberSourceBitmask, mEmergencyCallRouting);
}
/**
@@ -462,12 +536,12 @@
continue;
}
for (int j = i + 1; j < emergencyNumberList.size(); j++) {
- if (isSameEmergencyNumber(
+ if (areSameEmergencyNumbers(
emergencyNumberList.get(i), emergencyNumberList.get(j))) {
Rlog.e(LOG_TAG, "Found unexpected duplicate numbers: "
+ emergencyNumberList.get(i) + " vs " + emergencyNumberList.get(j));
// Set the merged emergency number in the current position
- emergencyNumberList.set(i, mergeNumbers(
+ emergencyNumberList.set(i, mergeSameEmergencyNumbers(
emergencyNumberList.get(i), emergencyNumberList.get(j)));
// Mark the emergency number has been merged
mergedEmergencyNumber.add(emergencyNumberList.get(j));
@@ -486,8 +560,8 @@
* Check if two emergency numbers are the same.
*
* A unique EmergencyNumber has a unique combination of ‘number’, ‘mcc’, 'mnc' and
- * 'categories' fields. Multiple Emergency Number Sources should be merged into one bitfield
- * for the same EmergencyNumber.
+ * 'categories', and 'routing' fields. Multiple Emergency Number Sources should be
+ * merged into one bitfield for the same EmergencyNumber.
*
* @param first first EmergencyNumber to compare
* @param second second EmergencyNumber to compare
@@ -495,8 +569,8 @@
*
* @hide
*/
- public static boolean isSameEmergencyNumber(@NonNull EmergencyNumber first,
- @NonNull EmergencyNumber second) {
+ public static boolean areSameEmergencyNumbers(@NonNull EmergencyNumber first,
+ @NonNull EmergencyNumber second) {
if (!first.getNumber().equals(second.getNumber())) {
return false;
}
@@ -510,11 +584,15 @@
!= second.getEmergencyServiceCategoryBitmask()) {
return false;
}
+ if (first.getEmergencyCallRouting() != second.getEmergencyCallRouting()) {
+ return false;
+ }
return true;
}
/**
- * Get a merged EmergencyNumber for two numbers if they are the same.
+ * Get a merged EmergencyNumber from two same emergency numbers. Two emergency numbers are
+ * the same if {@link #areSameEmergencyNumbers} returns {@code true}.
*
* @param first first EmergencyNumber to compare
* @param second second EmergencyNumber to compare
@@ -522,27 +600,15 @@
*
* @hide
*/
- public static EmergencyNumber mergeNumbers(@NonNull EmergencyNumber first,
- @NonNull EmergencyNumber second) {
- if (isSameEmergencyNumber(first, second)) {
+ public static EmergencyNumber mergeSameEmergencyNumbers(@NonNull EmergencyNumber first,
+ @NonNull EmergencyNumber second) {
+ if (areSameEmergencyNumbers(first, second)) {
return new EmergencyNumber(first.getNumber(), first.getCountryIso(), first.getMnc(),
first.getEmergencyServiceCategoryBitmask(),
first.getEmergencyNumberSourceBitmask()
- | second.getEmergencyNumberSourceBitmask());
+ | second.getEmergencyNumberSourceBitmask(),
+ first.getEmergencyCallRouting());
}
return null;
}
-
- public static final Parcelable.Creator<EmergencyNumber> CREATOR =
- new Parcelable.Creator<EmergencyNumber>() {
- @Override
- public EmergencyNumber createFromParcel(Parcel in) {
- return new EmergencyNumber(in);
- }
-
- @Override
- public EmergencyNumber[] newArray(int size) {
- return new EmergencyNumber[size];
- }
- };
}
diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java
index cb6fcd7..9c8d078 100644
--- a/telephony/java/android/telephony/ims/ImsCallProfile.java
+++ b/telephony/java/android/telephony/ims/ImsCallProfile.java
@@ -24,9 +24,11 @@
import android.os.Parcelable;
import android.telecom.VideoProfile;
import android.telephony.emergency.EmergencyNumber;
+import android.telephony.emergency.EmergencyNumber.EmergencyCallRouting;
import android.telephony.emergency.EmergencyNumber.EmergencyServiceCategories;
import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.PhoneConstants;
import java.lang.annotation.Retention;
@@ -321,6 +323,20 @@
EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED;
/**
+ * The emergency call routing, only valid if {@link #getServiceType} returns
+ * {@link #SERVICE_TYPE_EMERGENCY}
+ *
+ * If valid, the value is any of the following constants:
+ * <ol>
+ * <li>{@link EmergencyNumber#EMERGENCY_CALL_ROUTING_UNKNOWN} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_CALL_ROUTING_NORMAL} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_CALL_ROUTING_EMERGENCY} </li>
+ * </ol>
+ */
+ private @EmergencyCallRouting int mEmergencyCallRouting =
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN;
+
+ /**
* Extras associated with this {@link ImsCallProfile}.
* <p>
* Valid data types include:
@@ -503,10 +519,12 @@
@Override
public String toString() {
- return "{ serviceType=" + mServiceType +
- ", callType=" + mCallType +
- ", restrictCause=" + mRestrictCause +
- ", mediaProfile=" + mMediaProfile.toString() + " }";
+ return "{ serviceType=" + mServiceType
+ + ", callType=" + mCallType
+ + ", restrictCause=" + mRestrictCause
+ + ", mediaProfile=" + mMediaProfile.toString()
+ + ", emergencyServiceCategories=" + mEmergencyCallRouting
+ + ", emergencyCallRouting=" + mEmergencyCallRouting + " }";
}
@Override
@@ -522,6 +540,7 @@
out.writeBundle(filteredExtras);
out.writeParcelable(mMediaProfile, 0);
out.writeInt(mEmergencyServiceCategories);
+ out.writeInt(mEmergencyCallRouting);
}
private void readFromParcel(Parcel in) {
@@ -530,6 +549,7 @@
mCallExtras = in.readBundle();
mMediaProfile = in.readParcelable(ImsStreamMediaProfile.class.getClassLoader());
mEmergencyServiceCategories = in.readInt();
+ mEmergencyCallRouting = in.readInt();
}
public static final Creator<ImsCallProfile> CREATOR = new Creator<ImsCallProfile>() {
@@ -740,6 +760,21 @@
}
/**
+ * Set the emergency service categories and emergency call routing. The set value is valid
+ * only if {@link #getServiceType} returns {@link #SERVICE_TYPE_EMERGENCY}
+ *
+ * Reference: 3gpp 23.167, Section 6 - Functional description;
+ * 3gpp 22.101, Section 10 - Emergency Calls.
+ *
+ * @hide
+ */
+ public void setEmergencyCallInfo(EmergencyNumber num) {
+ setEmergencyServiceCategories(num.getEmergencyServiceCategoryBitmask());
+ setEmergencyCallRouting(num.getEmergencyCallRouting());
+ }
+
+
+ /**
* Set the emergency service categories. The set value is valid only if
* {@link #getServiceType} returns {@link #SERVICE_TYPE_EMERGENCY}
*
@@ -758,12 +793,29 @@
* Reference: 3gpp 23.167, Section 6 - Functional description;
* 3gpp 22.101, Section 10 - Emergency Calls.
*/
+ @VisibleForTesting
public void setEmergencyServiceCategories(
@EmergencyServiceCategories int emergencyServiceCategories) {
mEmergencyServiceCategories = emergencyServiceCategories;
}
/**
+ * Set the emergency call routing, only valid if {@link #getServiceType} returns
+ * {@link #SERVICE_TYPE_EMERGENCY}
+ *
+ * If valid, the value is any of the following constants:
+ * <ol>
+ * <li>{@link EmergencyNumber#EMERGENCY_CALL_ROUTING_UNKNOWN} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_CALL_ROUTING_NORMAL} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_CALL_ROUTING_EMERGENCY} </li>
+ * </ol>
+ */
+ @VisibleForTesting
+ public void setEmergencyCallRouting(@EmergencyCallRouting int emergencyCallRouting) {
+ mEmergencyCallRouting = emergencyCallRouting;
+ }
+
+ /**
* Get the emergency service categories, only valid if {@link #getServiceType} returns
* {@link #SERVICE_TYPE_EMERGENCY}
*
@@ -787,4 +839,19 @@
public @EmergencyServiceCategories int getEmergencyServiceCategories() {
return mEmergencyServiceCategories;
}
+
+ /**
+ * Get the emergency call routing, only valid if {@link #getServiceType} returns
+ * {@link #SERVICE_TYPE_EMERGENCY}
+ *
+ * If valid, the value is any of the following constants:
+ * <ol>
+ * <li>{@link EmergencyNumber#EMERGENCY_CALL_ROUTING_UNKNOWN} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_CALL_ROUTING_NORMAL} </li>
+ * <li>{@link EmergencyNumber#EMERGENCY_CALL_ROUTING_EMERGENCY} </li>
+ * </ol>
+ */
+ public @EmergencyCallRouting int getEmergencyCallRouting() {
+ return mEmergencyCallRouting;
+ }
}
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
index 122626f..e2350fe 100644
--- a/telephony/java/android/telephony/ims/ImsMmTelManager.java
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -714,7 +714,7 @@
* @see #setVoWiFiRoamingSetting(boolean)
*/
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
- @WiFiCallingMode int getVoWiFiRoamingModeSetting() {
+ public @WiFiCallingMode int getVoWiFiRoamingModeSetting() {
try {
return getITelephony().getVoWiFiRoamingModeSetting(mSubId);
} catch (RemoteException e) {
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 02a6f31..5632c63 100644
--- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -68,7 +68,7 @@
int backgroundCallState);
void notifyDisconnectCause(int disconnectCause, int preciseDisconnectCause);
void notifyPreciseDataConnectionFailed(String apnType, String apn,
- String failCause);
+ int failCause);
void notifyCellInfoForSubscriber(in int subId, in List<CellInfo> cellInfo);
void notifySrvccStateChanged(in int subId, in int lteState);
void notifySimActivationStateChangedForPhoneId(in int phoneId, in int subId,
diff --git a/tests/ActivityViewTest/Android.mk b/tests/ActivityViewTest/Android.mk
index 9c80764..9c7ca7e 100644
--- a/tests/ActivityViewTest/Android.mk
+++ b/tests/ActivityViewTest/Android.mk
@@ -1,7 +1,7 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_PACKAGE_NAME := ActivityViewTest
LOCAL_PRIVATE_PLATFORM_APIS := true
diff --git a/tests/ActivityViewTest/AndroidManifest.xml b/tests/ActivityViewTest/AndroidManifest.xml
index de54cc9..0be1ea0 100644
--- a/tests/ActivityViewTest/AndroidManifest.xml
+++ b/tests/ActivityViewTest/AndroidManifest.xml
@@ -35,17 +35,20 @@
<activity android:name=".ActivityViewActivity"
android:label="AV"
- android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density">
+ android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density"
+ android:windowSoftInputMode="stateHidden|adjustResize">
</activity>
<activity android:name=".ActivityViewResizeActivity"
android:label="AV Resize"
- android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density">
+ android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density"
+ android:windowSoftInputMode="stateHidden|adjustResize">
</activity>
<activity android:name=".ActivityViewScrollActivity"
android:label="AV Scroll"
- android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density">
+ android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density"
+ android:windowSoftInputMode="stateHidden">
</activity>
<activity android:name=".ActivityViewTestActivity"
diff --git a/tests/ActivityViewTest/res/layout/activity_view_test_activity.xml b/tests/ActivityViewTest/res/layout/activity_view_test_activity.xml
index f7ec562..338d68a 100644
--- a/tests/ActivityViewTest/res/layout/activity_view_test_activity.xml
+++ b/tests/ActivityViewTest/res/layout/activity_view_test_activity.xml
@@ -15,6 +15,7 @@
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/test_activity_root"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -60,11 +61,10 @@
android:background="#00000000"
android:gravity="center" />
- <View
- android:id="@+id/touch_intercept_view"
+ <EditText
+ android:id="@+id/test_activity_edittext"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="#00000000"
- />
-
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_margin="16dp" />
</RelativeLayout>
\ No newline at end of file
diff --git a/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewTestActivity.java b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewTestActivity.java
index 0d62786..ba2c764 100644
--- a/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewTestActivity.java
+++ b/tests/ActivityViewTest/src/com/google/android/test/activityview/ActivityViewTestActivity.java
@@ -29,26 +29,24 @@
import android.view.ViewTreeObserver;
import android.widget.TextView;
-public class ActivityViewTestActivity extends Activity implements View.OnTouchListener {
+public class ActivityViewTestActivity extends Activity {
+ private View mRoot;
private TextView mTextView;
private TextView mWidthTextView;
private TextView mHeightTextView;
private TextView mTouchStateTextView;
- private View mTouchInterceptView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_view_test_activity);
-
+ mRoot = findViewById(R.id.test_activity_root);
mTextView = findViewById(R.id.test_activity_title);
mWidthTextView = findViewById(R.id.test_activity_width_text);
mHeightTextView = findViewById(R.id.test_activity_height_text);
mTouchStateTextView = findViewById(R.id.test_activity_touch_state);
- mTouchInterceptView = findViewById(R.id.touch_intercept_view);
- mTouchInterceptView.setOnTouchListener(this);
- ViewTreeObserver viewTreeObserver = mTouchInterceptView.getViewTreeObserver();
+ ViewTreeObserver viewTreeObserver = mRoot.getViewTreeObserver();
if (viewTreeObserver.isAlive()) {
viewTreeObserver.addOnGlobalLayoutListener(this::updateDimensionTexts);
}
@@ -90,8 +88,8 @@
}
private void updateDimensionTexts() {
- mWidthTextView.setText("" + mTouchInterceptView.getWidth());
- mHeightTextView.setText("" + mTouchInterceptView.getHeight());
+ mWidthTextView.setText("" + mRoot.getWidth());
+ mHeightTextView.setText("" + mRoot.getHeight());
}
private void updateTouchState(MotionEvent event) {
@@ -108,8 +106,8 @@
}
@Override
- public boolean onTouch(View v, MotionEvent event) {
+ public boolean dispatchTouchEvent(MotionEvent event) {
updateTouchState(event);
- return true;
+ return super.dispatchTouchEvent(event);
}
}
diff --git a/tests/net/java/android/net/LinkPropertiesTest.java b/tests/net/java/android/net/LinkPropertiesTest.java
index 9695e9a..f82b380 100644
--- a/tests/net/java/android/net/LinkPropertiesTest.java
+++ b/tests/net/java/android/net/LinkPropertiesTest.java
@@ -53,6 +53,8 @@
private static InetAddress DNS1 = NetworkUtils.numericToInetAddress("75.208.7.1");
private static InetAddress DNS2 = NetworkUtils.numericToInetAddress("69.78.7.1");
private static InetAddress DNS6 = NetworkUtils.numericToInetAddress("2001:4860:4860::8888");
+ private static InetAddress PCSCFV6 = NetworkUtils.numericToInetAddress(
+ "2001:0db8:85a3:0000:0000:8a2e:0370:1");
private static InetAddress GATEWAY1 = NetworkUtils.numericToInetAddress("75.208.8.1");
private static InetAddress GATEWAY2 = NetworkUtils.numericToInetAddress("69.78.8.1");
private static InetAddress GATEWAY61 = NetworkUtils.numericToInetAddress("fe80::6:0000:613");
@@ -86,6 +88,9 @@
assertTrue(source.isIdenticalValidatedPrivateDnses(target));
assertTrue(target.isIdenticalValidatedPrivateDnses(source));
+ assertTrue(source.isIdenticalPcscfs(target));
+ assertTrue(target.isIdenticalPcscfs(source));
+
assertTrue(source.isIdenticalRoutes(target));
assertTrue(target.isIdenticalRoutes(source));
@@ -128,6 +133,8 @@
// set 2 dnses
source.addDnsServer(DNS1);
source.addDnsServer(DNS2);
+ // set 1 pcscf
+ source.addPcscfServer(PCSCFV6);
// set 2 gateways
source.addRoute(new RouteInfo(GATEWAY1));
source.addRoute(new RouteInfo(GATEWAY2));
@@ -141,6 +148,7 @@
target.addLinkAddress(LINKADDRV6);
target.addDnsServer(DNS1);
target.addDnsServer(DNS2);
+ target.addPcscfServer(PCSCFV6);
target.addRoute(new RouteInfo(GATEWAY1));
target.addRoute(new RouteInfo(GATEWAY2));
target.setMtu(MTU);
@@ -154,6 +162,7 @@
target.addLinkAddress(LINKADDRV6);
target.addDnsServer(DNS1);
target.addDnsServer(DNS2);
+ target.addPcscfServer(PCSCFV6);
target.addRoute(new RouteInfo(GATEWAY1));
target.addRoute(new RouteInfo(GATEWAY2));
target.setMtu(MTU);
@@ -167,6 +176,7 @@
target.addLinkAddress(LINKADDRV6);
target.addDnsServer(DNS1);
target.addDnsServer(DNS2);
+ target.addPcscfServer(PCSCFV6);
target.addRoute(new RouteInfo(GATEWAY1));
target.addRoute(new RouteInfo(GATEWAY2));
target.setMtu(MTU);
@@ -179,6 +189,21 @@
// change dnses
target.addDnsServer(NetworkUtils.numericToInetAddress("75.208.7.2"));
target.addDnsServer(DNS2);
+ target.addPcscfServer(PCSCFV6);
+ target.addRoute(new RouteInfo(GATEWAY1));
+ target.addRoute(new RouteInfo(GATEWAY2));
+ target.setMtu(MTU);
+ assertFalse(source.equals(target));
+
+ target.clear();
+ target.setInterfaceName(NAME);
+ target.addLinkAddress(LINKADDRV4);
+ target.addLinkAddress(LINKADDRV6);
+ target.addDnsServer(NetworkUtils.numericToInetAddress("75.208.7.2"));
+ target.addDnsServer(DNS2);
+ // change pcscf
+ target.addPcscfServer(NetworkUtils.numericToInetAddress(
+ "2001::1"));
target.addRoute(new RouteInfo(GATEWAY1));
target.addRoute(new RouteInfo(GATEWAY2));
target.setMtu(MTU);
diff --git a/tests/net/java/android/net/ip/IpServerTest.java b/tests/net/java/android/net/ip/IpServerTest.java
index 0178228..c3162af 100644
--- a/tests/net/java/android/net/ip/IpServerTest.java
+++ b/tests/net/java/android/net/ip/IpServerTest.java
@@ -22,20 +22,26 @@
import static android.net.ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR;
import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
import static android.net.ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR;
+import static android.net.NetworkUtils.intToInet4AddressHTH;
+import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
import static android.net.ip.IpServer.STATE_AVAILABLE;
import static android.net.ip.IpServer.STATE_TETHERED;
import static android.net.ip.IpServer.STATE_UNAVAILABLE;
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 static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -48,8 +54,9 @@
import android.net.LinkProperties;
import android.net.MacAddress;
import android.net.RouteInfo;
-import android.net.dhcp.DhcpServer;
-import android.net.dhcp.DhcpServingParams;
+import android.net.dhcp.DhcpServingParamsParcel;
+import android.net.dhcp.IDhcpServer;
+import android.net.dhcp.IDhcpServerCallbacks;
import android.net.util.InterfaceParams;
import android.net.util.InterfaceSet;
import android.net.util.SharedLog;
@@ -82,16 +89,18 @@
private static final InterfaceParams TEST_IFACE_PARAMS = new InterfaceParams(
IFACE_NAME, 42 /* index */, MacAddress.ALL_ZEROS_ADDRESS, 1500 /* defaultMtu */);
+ private static final int MAKE_DHCPSERVER_TIMEOUT_MS = 1000;
+
@Mock private INetworkManagementService mNMService;
@Mock private INetworkStatsService mStatsService;
@Mock private IpServer.Callback mCallback;
@Mock private InterfaceConfiguration mInterfaceConfiguration;
@Mock private SharedLog mSharedLog;
- @Mock private DhcpServer mDhcpServer;
+ @Mock private IDhcpServer mDhcpServer;
@Mock private RouterAdvertisementDaemon mRaDaemon;
@Mock private IpServer.Dependencies mDependencies;
- @Captor private ArgumentCaptor<DhcpServingParams> mDhcpParamsCaptor;
+ @Captor private ArgumentCaptor<DhcpServingParamsParcel> mDhcpParamsCaptor;
private final TestLooper mLooper = new TestLooper();
private final ArgumentCaptor<LinkProperties> mLinkPropertiesCaptor =
@@ -112,8 +121,18 @@
mLooper.dispatchAll();
reset(mNMService, mStatsService, mCallback);
when(mNMService.getInterfaceConfig(IFACE_NAME)).thenReturn(mInterfaceConfiguration);
- when(mDependencies.makeDhcpServer(
- any(), any(), mDhcpParamsCaptor.capture(), any())).thenReturn(mDhcpServer);
+
+ doAnswer(inv -> {
+ final IDhcpServerCallbacks cb = inv.getArgument(2);
+ new Thread(() -> {
+ try {
+ cb.onDhcpServerCreated(STATUS_SUCCESS, mDhcpServer);
+ } catch (RemoteException e) {
+ fail(e.getMessage());
+ }
+ }).run();
+ return null;
+ }).when(mDependencies).makeDhcpServer(any(), mDhcpParamsCaptor.capture(), any());
when(mDependencies.getRouterAdvertisementDaemon(any())).thenReturn(mRaDaemon);
when(mDependencies.getInterfaceParams(IFACE_NAME)).thenReturn(TEST_IFACE_PARAMS);
@@ -399,21 +418,20 @@
initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, true /* usingLegacyDhcp */);
dispatchTetherConnectionChanged(UPSTREAM_IFACE);
- verify(mDependencies, never()).makeDhcpServer(any(), any(), any(), any());
+ verify(mDependencies, never()).makeDhcpServer(any(), any(), any());
}
- private void assertDhcpStarted(IpPrefix expectedPrefix) {
- verify(mDependencies, times(1)).makeDhcpServer(
- eq(mLooper.getLooper()), eq(IFACE_NAME), any(), eq(mSharedLog));
- verify(mDhcpServer, times(1)).start();
- final DhcpServingParams params = mDhcpParamsCaptor.getValue();
+ private void assertDhcpStarted(IpPrefix expectedPrefix) throws Exception {
+ verify(mDependencies, times(1)).makeDhcpServer(eq(IFACE_NAME), any(), any());
+ verify(mDhcpServer, timeout(MAKE_DHCPSERVER_TIMEOUT_MS).times(1)).start(any());
+ final DhcpServingParamsParcel params = mDhcpParamsCaptor.getValue();
// Last address byte is random
- assertTrue(expectedPrefix.contains(params.serverAddr.getAddress()));
- assertEquals(expectedPrefix.getPrefixLength(), params.serverAddr.getPrefixLength());
- assertEquals(1, params.defaultRouters.size());
- assertEquals(params.serverAddr.getAddress(), params.defaultRouters.iterator().next());
- assertEquals(1, params.dnsServers.size());
- assertEquals(params.serverAddr.getAddress(), params.dnsServers.iterator().next());
+ assertTrue(expectedPrefix.contains(intToInet4AddressHTH(params.serverAddr)));
+ assertEquals(expectedPrefix.getPrefixLength(), params.serverAddrPrefixLength);
+ assertEquals(1, params.defaultRouters.length);
+ assertEquals(params.serverAddr, params.defaultRouters[0]);
+ assertEquals(1, params.dnsServers.length);
+ assertEquals(params.serverAddr, params.dnsServers[0]);
assertEquals(DHCP_LEASE_TIME_SECS, params.dhcpLeaseTimeSecs);
}
@@ -458,7 +476,7 @@
addr4 = addr;
break;
}
- assertTrue("missing IPv4 address", addr4 != null);
+ assertNotNull("missing IPv4 address", addr4);
// Assert the presence of the associated directly connected route.
final RouteInfo directlyConnected = new RouteInfo(addr4, null, lp.getInterfaceName());
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index e6b43d2..1ea83c2 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -27,6 +27,7 @@
import static android.net.ConnectivityManager.TETHERING_WIFI;
import static android.net.ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
import static android.net.ConnectivityManager.TYPE_MOBILE;
+import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE;
@@ -37,6 +38,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.notNull;
import static org.mockito.Matchers.anyInt;
@@ -47,6 +49,8 @@
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -74,8 +78,9 @@
import android.net.NetworkState;
import android.net.NetworkUtils;
import android.net.RouteInfo;
-import android.net.dhcp.DhcpServer;
-import android.net.dhcp.DhcpServingParams;
+import android.net.dhcp.DhcpServerCallbacks;
+import android.net.dhcp.DhcpServingParamsParcel;
+import android.net.dhcp.IDhcpServer;
import android.net.ip.IpServer;
import android.net.ip.RouterAdvertisementDaemon;
import android.net.util.InterfaceParams;
@@ -86,7 +91,6 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.INetworkManagementService;
-import android.os.Looper;
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -129,6 +133,8 @@
private static final String TEST_USB_IFNAME = "test_rndis0";
private static final String TEST_WLAN_IFNAME = "test_wlan0";
+ private static final int DHCPSERVER_START_TIMEOUT_MS = 1000;
+
@Mock private ApplicationInfo mApplicationInfo;
@Mock private Context mContext;
@Mock private INetworkManagementService mNMService;
@@ -143,9 +149,11 @@
@Mock private UpstreamNetworkMonitor mUpstreamNetworkMonitor;
@Mock private IPv6TetheringCoordinator mIPv6TetheringCoordinator;
@Mock private RouterAdvertisementDaemon mRouterAdvertisementDaemon;
- @Mock private DhcpServer mDhcpServer;
+ @Mock private IDhcpServer mDhcpServer;
@Mock private INetd mNetd;
+ private final MockIpServerDependencies mIpServerDependencies =
+ spy(new MockIpServerDependencies());
private final MockTetheringDependencies mTetheringDependencies =
new MockTetheringDependencies();
@@ -185,6 +193,47 @@
}
}
+ public class MockIpServerDependencies extends IpServer.Dependencies {
+ MockIpServerDependencies() {
+ super(null);
+ }
+
+ @Override
+ public RouterAdvertisementDaemon getRouterAdvertisementDaemon(
+ InterfaceParams ifParams) {
+ return mRouterAdvertisementDaemon;
+ }
+
+ @Override
+ public InterfaceParams getInterfaceParams(String ifName) {
+ assertTrue("Non-mocked interface " + ifName,
+ ifName.equals(TEST_USB_IFNAME)
+ || ifName.equals(TEST_WLAN_IFNAME)
+ || ifName.equals(TEST_MOBILE_IFNAME));
+ final String[] ifaces = new String[] {
+ TEST_USB_IFNAME, TEST_WLAN_IFNAME, TEST_MOBILE_IFNAME };
+ return new InterfaceParams(ifName, ArrayUtils.indexOf(ifaces, ifName) + IFINDEX_OFFSET,
+ MacAddress.ALL_ZEROS_ADDRESS);
+ }
+
+ @Override
+ public INetd getNetdService() {
+ return mNetd;
+ }
+
+ @Override
+ public void makeDhcpServer(String ifName, DhcpServingParamsParcel params,
+ DhcpServerCallbacks cb) {
+ new Thread(() -> {
+ try {
+ cb.onDhcpServerCreated(STATUS_SUCCESS, mDhcpServer);
+ } catch (RemoteException e) {
+ fail(e.getMessage());
+ }
+ }).run();
+ }
+ }
+
public class MockTetheringDependencies extends TetheringDependencies {
StateMachine upstreamNetworkMonitorMasterSM;
ArrayList<IpServer> ipv6CoordinatorNotifyList;
@@ -216,35 +265,8 @@
}
@Override
- public IpServer.Dependencies getIpServerDependencies() {
- return new IpServer.Dependencies() {
- @Override
- public RouterAdvertisementDaemon getRouterAdvertisementDaemon(
- InterfaceParams ifParams) {
- return mRouterAdvertisementDaemon;
- }
-
- @Override
- public InterfaceParams getInterfaceParams(String ifName) {
- final String[] ifaces = new String[] {
- TEST_USB_IFNAME, TEST_WLAN_IFNAME, TEST_MOBILE_IFNAME };
- final int index = ArrayUtils.indexOf(ifaces, ifName);
- assertTrue("Non-mocked interface: " + ifName, index >= 0);
- return new InterfaceParams(ifName, index + IFINDEX_OFFSET,
- MacAddress.ALL_ZEROS_ADDRESS);
- }
-
- @Override
- public INetd getNetdService() {
- return mNetd;
- }
-
- @Override
- public DhcpServer makeDhcpServer(Looper looper, String ifName,
- DhcpServingParams params, SharedLog log) {
- return mDhcpServer;
- }
- };
+ public IpServer.Dependencies getIpServerDependencies(Context context) {
+ return mIpServerDependencies;
}
@Override
@@ -546,7 +568,7 @@
sendIPv6TetherUpdates(upstreamState);
verify(mRouterAdvertisementDaemon, never()).buildNewRa(any(), notNull());
- verify(mDhcpServer, times(1)).start();
+ verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any());
}
@Test
@@ -557,7 +579,7 @@
runUsbTethering(upstreamState);
sendIPv6TetherUpdates(upstreamState);
- verify(mDhcpServer, never()).start();
+ verify(mIpServerDependencies, never()).makeDhcpServer(any(), any(), any());
}
@Test
@@ -581,7 +603,7 @@
verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
verify(mRouterAdvertisementDaemon, times(1)).start();
- verify(mDhcpServer, times(1)).start();
+ verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any());
sendIPv6TetherUpdates(upstreamState);
verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull());
@@ -595,7 +617,7 @@
verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME);
verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
- verify(mDhcpServer, times(1)).start();
+ verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any());
verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME,
TEST_XLAT_MOBILE_IFNAME);
@@ -612,7 +634,7 @@
runUsbTethering(upstreamState);
verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
- verify(mDhcpServer, times(1)).start();
+ verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any());
verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
// Then 464xlat comes up
@@ -636,7 +658,7 @@
verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
// DHCP not restarted on downstream (still times(1))
- verify(mDhcpServer, times(1)).start();
+ verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any());
}
@Test
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index 11b408f..9968bda 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -775,6 +775,7 @@
const AtomDecl &attributionDecl) {
for (set<vector<java_type_t>>::const_iterator signature = signatures.begin();
signature != signatures.end(); signature++) {
+ fprintf(out, " /** @hide */\n");
fprintf(out, " public static native int %s(int code", method_name.c_str());
int argIndex = 1;
for (vector<java_type_t>::const_iterator arg = signature->begin();
@@ -820,6 +821,7 @@
}
// Method header (signature)
+ fprintf(out, " /** @hide */\n");
fprintf(out, " public static void write(int code");
int argIndex = 1;
for (vector<java_type_t>::const_iterator arg = signature->begin();
@@ -900,6 +902,7 @@
if (non_chained_decl != atom_code_to_non_chained_decl_map.end()) {
write_java_usage(out, "write_non_chained", constant, *non_chained_decl->second);
}
+ fprintf(out, " * @hide\n");
fprintf(out, " */\n");
fprintf(out, " public static final int %s = %d;\n", constant.c_str(), atom->code);
}
@@ -916,6 +919,7 @@
field->name.c_str());
for (map<int, string>::const_iterator value = field->enumValues.begin();
value != field->enumValues.end(); value++) {
+ fprintf(out, " /** @hide */\n");
fprintf(out, " public static final int %s__%s__%s = %d;\n",
make_constant_name(atom->message).c_str(),
make_constant_name(field->name).c_str(),
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index fef0ed7..07f7cb3 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -60,7 +60,7 @@
ParceledListSlice getConfiguredNetworks(String packageName);
- ParceledListSlice getPrivilegedConfiguredNetworks();
+ ParceledListSlice getPrivilegedConfiguredNetworks(String packageName);
Map getAllMatchingFqdnsForScanResults(in List<ScanResult> scanResult);
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index ad2ed81..b265c40 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -16,6 +16,10 @@
package android.net.wifi;
+import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
+import static android.Manifest.permission.ACCESS_WIFI_STATE;
+import static android.Manifest.permission.READ_WIFI_CREDENTIAL;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -1136,6 +1140,10 @@
/**
* Return a list of all the networks configured for the current foreground
* user.
+ *
+ * Requires the same permissions as {@link #getScanResults}.
+ * If such access is not allowed, this API will always return an empty list.
+ *
* Not all fields of WifiConfiguration are returned. Only the following
* fields are filled in:
* <ul>
@@ -1162,6 +1170,7 @@
* {@link android.os.Build.VERSION_CODES#Q} or above, this API will always return an empty list.
*/
@Deprecated
+ @RequiresPermission(allOf = {ACCESS_COARSE_LOCATION, ACCESS_WIFI_STATE})
public List<WifiConfiguration> getConfiguredNetworks() {
try {
ParceledListSlice<WifiConfiguration> parceledList =
@@ -1177,11 +1186,11 @@
/** @hide */
@SystemApi
- @RequiresPermission(android.Manifest.permission.READ_WIFI_CREDENTIAL)
+ @RequiresPermission(allOf = {ACCESS_COARSE_LOCATION, ACCESS_WIFI_STATE, READ_WIFI_CREDENTIAL})
public List<WifiConfiguration> getPrivilegedConfiguredNetworks() {
try {
ParceledListSlice<WifiConfiguration> parceledList =
- mService.getPrivilegedConfiguredNetworks();
+ mService.getPrivilegedConfiguredNetworks(mContext.getOpPackageName());
if (parceledList == null) {
return Collections.emptyList();
}
diff --git a/wifi/java/com/android/server/wifi/AbstractWifiService.java b/wifi/java/com/android/server/wifi/BaseWifiService.java
similarity index 90%
rename from wifi/java/com/android/server/wifi/AbstractWifiService.java
rename to wifi/java/com/android/server/wifi/BaseWifiService.java
index 3d328a1..4e29dd1 100644
--- a/wifi/java/com/android/server/wifi/AbstractWifiService.java
+++ b/wifi/java/com/android/server/wifi/BaseWifiService.java
@@ -44,7 +44,7 @@
import java.util.Map;
/**
- * Abstract class implementing IWifiManager with stub methods throwing runtime exceptions.
+ * Empty concrete class implementing IWifiManager with stub methods throwing runtime exceptions.
*
* This class is meant to be extended by real implementations of IWifiManager in order to facilitate
* cross-repo changes to WiFi internal APIs, including the introduction of new APIs, the removal of
@@ -55,10 +55,13 @@
* then given a short grace period to update themselves before the @Deprecated stub is removed for
* good. If the API scheduled for removal has a replacement or an overload (signature change),
* these should be introduced before the stub is removed to allow children to migrate.
+ *
+ * When a new API is added to IWifiManager.aidl, a stub should be added in BaseWifiService as
+ * well otherwise compilation will fail.
*/
-public abstract class AbstractWifiService extends IWifiManager.Stub {
+public class BaseWifiService extends IWifiManager.Stub {
- private static final String TAG = AbstractWifiService.class.getSimpleName();
+ private static final String TAG = BaseWifiService.class.getSimpleName();
@Override
public int getSupportedFeatures() {
@@ -81,29 +84,7 @@
}
@Override
- public ParceledListSlice getPrivilegedConfiguredNetworks() {
- throw new UnsupportedOperationException();
- }
-
- /**
- * Returns a WifiConfiguration matching this ScanResult
- * @param scanResult a single ScanResult Object
- * @return
- * @deprecated use {@link #getAllMatchingWifiConfigs(List)} instead.
- */
- @Deprecated
- public WifiConfiguration getMatchingWifiConfig(ScanResult scanResult) {
- throw new UnsupportedOperationException();
- }
-
- /**
- * Returns all matching WifiConfigurations for this ScanResult.
- * @param scanResult a single ScanResult Object
- * @return
- * @deprecated use {@link #getAllMatchingWifiConfigs(List)} instead.
- */
- @Deprecated
- public List<WifiConfiguration> getAllMatchingWifiConfigs(ScanResult scanResult) {
+ public ParceledListSlice getPrivilegedConfiguredNetworks(String packageName) {
throw new UnsupportedOperationException();
}
@@ -113,18 +94,6 @@
throw new UnsupportedOperationException();
}
- /**
- * Returns a list of Hotspot 2.0 OSU (Online Sign-Up) providers associated with the given AP.
- *
- * @param scanResult a single ScanResult Object
- * @return
- * @deprecated use {@link #getMatchingOsuProviders(List)} instead.
- */
- @Deprecated
- public List<OsuProvider> getMatchingOsuProviders(ScanResult scanResult) {
- throw new UnsupportedOperationException();
- }
-
@Override
public Map<OsuProvider, List<ScanResult>> getMatchingOsuProviders(
List<ScanResult> scanResults) {