Merge "Exclude IWLAN network type from isInService check"
diff --git a/Android.bp b/Android.bp
index eb9cbbb..f6d49e9 100644
--- a/Android.bp
+++ b/Android.bp
@@ -872,6 +872,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..0bc9ee4e 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 b11e6cd..99d4b95 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -10200,6 +10200,7 @@
field public static final java.lang.String ACTION_PACKAGE_RESTARTED = "android.intent.action.PACKAGE_RESTARTED";
field public static final java.lang.String ACTION_PACKAGE_VERIFIED = "android.intent.action.PACKAGE_VERIFIED";
field public static final java.lang.String ACTION_PASTE = "android.intent.action.PASTE";
+ field public static final java.lang.String ACTION_PERMISSION_USAGE_DETAILS = "android.intent.action.PERMISSION_USAGE_DETAILS";
field public static final java.lang.String ACTION_PICK = "android.intent.action.PICK";
field public static final java.lang.String ACTION_PICK_ACTIVITY = "android.intent.action.PICK_ACTIVITY";
field public static final java.lang.String ACTION_POWER_CONNECTED = "android.intent.action.ACTION_POWER_CONNECTED";
@@ -10324,6 +10325,7 @@
field public static final java.lang.String EXTRA_NOT_UNKNOWN_SOURCE = "android.intent.extra.NOT_UNKNOWN_SOURCE";
field public static final java.lang.String EXTRA_ORIGINATING_URI = "android.intent.extra.ORIGINATING_URI";
field public static final java.lang.String EXTRA_PACKAGE_NAME = "android.intent.extra.PACKAGE_NAME";
+ field public static final java.lang.String EXTRA_PERMISSION_USAGE_PERMISSIONS = "android.intent.extra.PERMISSION_USAGE_PERMISSIONS";
field public static final java.lang.String EXTRA_PHONE_NUMBER = "android.intent.extra.PHONE_NUMBER";
field public static final java.lang.String EXTRA_PROCESS_TEXT = "android.intent.extra.PROCESS_TEXT";
field public static final java.lang.String EXTRA_PROCESS_TEXT_READONLY = "android.intent.extra.PROCESS_TEXT_READONLY";
@@ -25360,12 +25362,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();
@@ -25373,10 +25378,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();
@@ -25390,6 +25397,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);
@@ -27146,12 +27154,15 @@
method public void onSessionEvent(java.lang.String, android.os.Bundle);
}
- public static final class MediaController.PlaybackInfo {
+ public static final class MediaController.PlaybackInfo implements android.os.Parcelable {
+ method public int describeContents();
method public android.media.AudioAttributes getAudioAttributes();
method public int getCurrentVolume();
method public int getMaxVolume();
method public int getPlaybackType();
method public int getVolumeControl();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.media.session.MediaController.PlaybackInfo> CREATOR;
field public static final int PLAYBACK_TYPE_LOCAL = 1; // 0x1
field public static final int PLAYBACK_TYPE_REMOTE = 2; // 0x2
}
@@ -42759,10 +42770,10 @@
ctor public CallRedirectionService();
method public final void cancelCall();
method public final android.os.IBinder onBind(android.content.Intent);
- method public abstract void onPlaceCall(android.net.Uri, android.telecom.PhoneAccountHandle);
+ method public abstract void onPlaceCall(android.net.Uri, android.telecom.PhoneAccountHandle, boolean);
method public final boolean onUnbind(android.content.Intent);
method public final void placeCallUnmodified();
- method public final void redirectCall(android.net.Uri, android.telecom.PhoneAccountHandle);
+ method public final void redirectCall(android.net.Uri, android.telecom.PhoneAccountHandle, boolean);
field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.CallRedirectionService";
}
@@ -53112,6 +53123,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
diff --git a/api/system-current.txt b/api/system-current.txt
index e79ede8..ef3455d 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -927,6 +927,9 @@
method public java.util.Map<java.lang.String, java.lang.Integer> getAppStandbyBuckets();
method public void registerAppUsageObserver(int, java.lang.String[], long, java.util.concurrent.TimeUnit, android.app.PendingIntent);
method public void registerUsageSessionObserver(int, java.lang.String[], long, java.util.concurrent.TimeUnit, long, java.util.concurrent.TimeUnit, android.app.PendingIntent, android.app.PendingIntent);
+ method public void reportUsageStart(android.app.Activity, java.lang.String);
+ method public void reportUsageStart(android.app.Activity, java.lang.String, long);
+ method public void reportUsageStop(android.app.Activity, java.lang.String);
method public void setAppStandbyBucket(java.lang.String, int);
method public void setAppStandbyBuckets(java.util.Map<java.lang.String, java.lang.Integer>);
method public void unregisterAppUsageObserver(int);
@@ -1071,6 +1074,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";
@@ -3895,7 +3899,6 @@
method public void disable(int, android.net.wifi.WifiManager.ActionListener);
method public void disableEphemeralNetwork(java.lang.String);
method public void forget(int, android.net.wifi.WifiManager.ActionListener);
- method public java.util.List<android.net.wifi.WifiConfiguration> getAllMatchingWifiConfigs(java.util.List<android.net.wifi.ScanResult>);
method public java.util.List<android.net.wifi.WifiConfiguration> getPrivilegedConfiguredNetworks();
method public android.net.wifi.WifiConfiguration getWifiApConfiguration();
method public int getWifiApState();
@@ -4492,6 +4495,7 @@
field public static final int PAYLOAD_HASH_MISMATCH_ERROR = 10; // 0xa
field public static final int PAYLOAD_MISMATCHED_TYPE_ERROR = 6; // 0x6
field public static final int PAYLOAD_SIZE_MISMATCH_ERROR = 11; // 0xb
+ field public static final int PAYLOAD_TIMESTAMP_ERROR = 51; // 0x33
field public static final int POST_INSTALL_RUNNER_ERROR = 5; // 0x5
field public static final int SUCCESS = 0; // 0x0
field public static final int UPDATED_BUT_NOT_ACTIVE = 52; // 0x34
@@ -4596,6 +4600,17 @@
package android.permission {
+ public final class PermissionControllerManager {
+ method public void revokeRuntimePermissions(java.util.Map<java.lang.String, java.util.List<java.lang.String>>, boolean, int, java.util.concurrent.Executor, android.permission.PermissionControllerManager.OnRevokeRuntimePermissionsCallback);
+ field public static final int REASON_INSTALLER_POLICY_VIOLATION = 2; // 0x2
+ field public static final int REASON_MALWARE = 1; // 0x1
+ }
+
+ public static abstract class PermissionControllerManager.OnRevokeRuntimePermissionsCallback {
+ ctor public PermissionControllerManager.OnRevokeRuntimePermissionsCallback();
+ method public abstract void onRevokeRuntimePermissions(java.util.Map<java.lang.String, java.util.List<java.lang.String>>);
+ }
+
public abstract class PermissionControllerService extends android.app.Service {
ctor public PermissionControllerService();
method public final void attachBaseContext(android.content.Context);
@@ -4603,6 +4618,7 @@
method public abstract int onCountPermissionApps(java.util.List<java.lang.String>, boolean, boolean);
method public abstract java.util.List<android.permission.RuntimePermissionPresentationInfo> onGetAppPermissions(java.lang.String);
method public abstract void onRevokeRuntimePermission(java.lang.String, java.lang.String);
+ method public abstract java.util.Map<java.lang.String, java.util.List<java.lang.String>> onRevokeRuntimePermissions(java.util.Map<java.lang.String, java.util.List<java.lang.String>>, boolean, int, java.lang.String);
field public static final java.lang.String SERVICE_INTERFACE = "android.permission.PermissionControllerService";
}
@@ -5128,6 +5144,8 @@
public abstract class AugmentedAutofillService extends android.app.Service {
ctor public AugmentedAutofillService();
+ method protected final void dump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+ method protected void dump(java.io.PrintWriter, java.lang.String[]);
method public void onFillRequest(android.service.autofill.augmented.FillRequest, android.os.CancellationSignal, android.service.autofill.augmented.FillController, android.service.autofill.augmented.FillCallback);
field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.augmented.AugmentedAutofillService";
}
diff --git a/api/test-current.txt b/api/test-current.txt
index 1401cbb..8ea7f20 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;
}
@@ -985,6 +986,21 @@
}
+package android.permission {
+
+ public final class PermissionControllerManager {
+ method public void revokeRuntimePermissions(java.util.Map<java.lang.String, java.util.List<java.lang.String>>, boolean, int, java.util.concurrent.Executor, android.permission.PermissionControllerManager.OnRevokeRuntimePermissionsCallback);
+ field public static final int REASON_INSTALLER_POLICY_VIOLATION = 2; // 0x2
+ field public static final int REASON_MALWARE = 1; // 0x1
+ }
+
+ public static abstract class PermissionControllerManager.OnRevokeRuntimePermissionsCallback {
+ ctor public PermissionControllerManager.OnRevokeRuntimePermissionsCallback();
+ method public abstract void onRevokeRuntimePermissions(java.util.Map<java.lang.String, java.util.List<java.lang.String>>);
+ }
+
+}
+
package android.print {
public final class PrintJobInfo implements android.os.Parcelable {
diff --git a/cmds/input/src/com/android/commands/input/Input.java b/cmds/input/src/com/android/commands/input/Input.java
index 74edffb..b905e94 100644
--- a/cmds/input/src/com/android/commands/input/Input.java
+++ b/cmds/input/src/com/android/commands/input/Input.java
@@ -16,15 +16,20 @@
package com.android.commands.input;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.INVALID_DISPLAY;
+
import android.hardware.input.InputManager;
import android.os.SystemClock;
-import android.util.Log;
import android.view.InputDevice;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
+import com.android.internal.os.BaseCommand;
+
+import java.io.PrintStream;
import java.util.HashMap;
import java.util.Map;
@@ -33,9 +38,11 @@
* desired character output.
*/
-public class Input {
+public class Input extends BaseCommand {
private static final String TAG = "Input";
private static final String INVALID_ARGUMENTS = "Error: Invalid arguments for command: ";
+ private static final String INVALID_DISPLAY_ARGUMENTS =
+ "Error: Invalid arguments for display ID.";
private static final Map<String, Integer> SOURCES = new HashMap<String, Integer>() {{
put("keyboard", InputDevice.SOURCE_KEYBOARD);
@@ -50,6 +57,7 @@
put("joystick", InputDevice.SOURCE_JOYSTICK);
}};
+ private static final Map<String, InputCmd> COMMANDS = new HashMap<String, InputCmd>();
/**
* Command-line entry point.
@@ -60,217 +68,256 @@
(new Input()).run(args);
}
- private void run(String[] args) {
- if (args.length < 1) {
- showUsage();
- return;
- }
+ Input() {
+ COMMANDS.put("text", new InputText());
+ COMMANDS.put("keyevent", new InputKeyEvent());
+ COMMANDS.put("tap", new InputTap());
+ COMMANDS.put("swipe", new InputSwipe());
+ COMMANDS.put("draganddrop", new InputDragAndDrop());
+ COMMANDS.put("press", new InputPress());
+ COMMANDS.put("roll", new InputRoll());
+ }
- int index = 0;
- String command = args[index];
+ @Override
+ public void onRun() throws Exception {
+ String arg = nextArgRequired();
int inputSource = InputDevice.SOURCE_UNKNOWN;
- if (SOURCES.containsKey(command)) {
- inputSource = SOURCES.get(command);
- index++;
- command = args[index];
- }
- final int length = args.length - index;
- try {
- if (command.equals("text")) {
- if (length == 2) {
- inputSource = getSource(inputSource, InputDevice.SOURCE_KEYBOARD);
- sendText(inputSource, args[index+1]);
- return;
+ // Get source (optional).
+ if (SOURCES.containsKey(arg)) {
+ inputSource = SOURCES.get(arg);
+ arg = nextArgRequired();
+ }
+
+ // Get displayId (optional).
+ int displayId = INVALID_DISPLAY;
+ if ("-d".equals(arg)) {
+ displayId = getDisplayId();
+ arg = nextArgRequired();
+ }
+
+ // Get command and run.
+ InputCmd cmd = COMMANDS.get(arg);
+ if (cmd != null) {
+ try {
+ cmd.run(inputSource, displayId);
+ return;
+ } catch (NumberFormatException ex) {
+ throw new IllegalArgumentException(INVALID_ARGUMENTS + arg);
+ }
+ }
+
+ throw new IllegalArgumentException("Error: Unknown command: " + arg);
+ }
+
+ private int getDisplayId() {
+ String displayArg = nextArgRequired();
+ if ("INVALID_DISPLAY".equalsIgnoreCase(displayArg)) {
+ return INVALID_DISPLAY;
+ } else if ("DEFAULT_DISPLAY".equalsIgnoreCase(displayArg)) {
+ return DEFAULT_DISPLAY;
+ } else {
+ try {
+ final int displayId = Integer.parseInt(displayArg);
+ if (displayId == INVALID_DISPLAY) {
+ return INVALID_DISPLAY;
}
- } else if (command.equals("keyevent")) {
- if (length >= 2) {
- final boolean longpress = "--longpress".equals(args[index + 1]);
- final int start = longpress ? index + 2 : index + 1;
- inputSource = getSource(inputSource, InputDevice.SOURCE_KEYBOARD);
- if (args.length > start) {
- for (int i = start; i < args.length; i++) {
- int keyCode = KeyEvent.keyCodeFromString(args[i]);
- sendKeyEvent(inputSource, keyCode, longpress);
- }
- return;
+ return Math.max(displayId, 0);
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException(INVALID_DISPLAY_ARGUMENTS);
+ }
+ }
+ }
+
+ class InputText implements InputCmd {
+ @Override
+ public void run(int inputSource, int displayId) {
+ inputSource = getSource(inputSource, InputDevice.SOURCE_KEYBOARD);
+ sendText(inputSource, nextArgRequired(), displayId);
+ }
+
+ /**
+ * Convert the characters of string text into key event's and send to
+ * device.
+ *
+ * @param text is a string of characters you want to input to the device.
+ */
+ private void sendText(int source, final String text, int displayId) {
+ final StringBuffer buff = new StringBuffer(text);
+ boolean escapeFlag = false;
+ for (int i = 0; i < buff.length(); i++) {
+ if (escapeFlag) {
+ escapeFlag = false;
+ if (buff.charAt(i) == 's') {
+ buff.setCharAt(i, ' ');
+ buff.deleteCharAt(--i);
}
}
- } else if (command.equals("tap")) {
- if (length == 3) {
- inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
- sendTap(inputSource, Float.parseFloat(args[index+1]),
- Float.parseFloat(args[index+2]));
- return;
+ if (buff.charAt(i) == '%') {
+ escapeFlag = true;
}
- } else if (command.equals("swipe")) {
- int duration = -1;
- inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
- switch (length) {
- case 6:
- duration = Integer.parseInt(args[index+5]);
- case 5:
- sendSwipe(inputSource,
- Float.parseFloat(args[index+1]), Float.parseFloat(args[index+2]),
- Float.parseFloat(args[index+3]), Float.parseFloat(args[index+4]),
- duration);
- return;
- }
- } else if (command.equals("draganddrop")) {
- int duration = -1;
- inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
- switch (length) {
- case 6:
- duration = Integer.parseInt(args[index+5]);
- case 5:
- sendDragAndDrop(inputSource,
- Float.parseFloat(args[index+1]), Float.parseFloat(args[index+2]),
- Float.parseFloat(args[index+3]), Float.parseFloat(args[index+4]),
- duration);
- return;
- }
- } else if (command.equals("press")) {
- inputSource = getSource(inputSource, InputDevice.SOURCE_TRACKBALL);
- if (length == 1) {
- sendTap(inputSource, 0.0f, 0.0f);
- return;
- }
- } else if (command.equals("roll")) {
- inputSource = getSource(inputSource, InputDevice.SOURCE_TRACKBALL);
- if (length == 3) {
- sendMove(inputSource, Float.parseFloat(args[index+1]),
- Float.parseFloat(args[index+2]));
- return;
- }
- } else {
- System.err.println("Error: Unknown command: " + command);
- showUsage();
- return;
}
- } catch (NumberFormatException ex) {
+
+ final char[] chars = buff.toString().toCharArray();
+ final KeyCharacterMap kcm = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
+ final KeyEvent[] events = kcm.getEvents(chars);
+ for (int i = 0; i < events.length; i++) {
+ KeyEvent e = events[i];
+ if (source != e.getSource()) {
+ e.setSource(source);
+ }
+ e.setDisplayId(displayId);
+ injectKeyEvent(e);
+ }
}
- System.err.println(INVALID_ARGUMENTS + command);
- showUsage();
+ }
+
+ class InputKeyEvent implements InputCmd {
+ @Override
+ public void run(int inputSource, int displayId) {
+ String arg = nextArgRequired();
+ final boolean longpress = "--longpress".equals(arg);
+ if (longpress) {
+ arg = nextArgRequired();
+ }
+
+ do {
+ final int keycode = KeyEvent.keyCodeFromString(arg);
+ sendKeyEvent(inputSource, keycode, longpress, displayId);
+ } while ((arg = nextArg()) != null);
+ }
+
+ private void sendKeyEvent(int inputSource, int keyCode, boolean longpress, int displayId) {
+ final long now = SystemClock.uptimeMillis();
+ int repeatCount = 0;
+
+ KeyEvent event = new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keyCode, repeatCount,
+ 0 /*metaState*/, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /*scancode*/, 0/*flags*/,
+ inputSource);
+ event.setDisplayId(displayId);
+
+ injectKeyEvent(event);
+ if (longpress) {
+ repeatCount++;
+ injectKeyEvent(KeyEvent.changeTimeRepeat(event, now, repeatCount,
+ KeyEvent.FLAG_LONG_PRESS));
+ }
+ injectKeyEvent(KeyEvent.changeAction(event, KeyEvent.ACTION_UP));
+ }
+ }
+
+ class InputTap implements InputCmd {
+ @Override
+ public void run(int inputSource, int displayId) {
+ inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
+ sendTap(inputSource, Float.parseFloat(nextArgRequired()),
+ Float.parseFloat(nextArgRequired()), displayId);
+ }
+
+ void sendTap(int inputSource, float x, float y, int displayId) {
+ final long now = SystemClock.uptimeMillis();
+ injectMotionEvent(inputSource, MotionEvent.ACTION_DOWN, now, now, x, y, 1.0f,
+ displayId);
+ injectMotionEvent(inputSource, MotionEvent.ACTION_UP, now, now, x, y, 0.0f, displayId);
+ }
+ }
+
+ class InputPress extends InputTap {
+ @Override
+ public void run(int inputSource, int displayId) {
+ inputSource = getSource(inputSource, InputDevice.SOURCE_TRACKBALL);
+ sendTap(inputSource, 0.0f, 0.0f, displayId);
+ }
+ }
+
+ class InputSwipe implements InputCmd {
+ @Override
+ public void run(int inputSource, int displayId) {
+ inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
+ sendSwipe(inputSource, displayId, false);
+ }
+
+ void sendSwipe(int inputSource, int displayId, boolean isDragDrop) {
+ // Parse two points and duration.
+ final float x1 = Float.parseFloat(nextArgRequired());
+ final float y1 = Float.parseFloat(nextArgRequired());
+ final float x2 = Float.parseFloat(nextArgRequired());
+ final float y2 = Float.parseFloat(nextArgRequired());
+ String durationArg = nextArg();
+ int duration = durationArg != null ? Integer.parseInt(durationArg) : -1;
+ if (duration < 0) {
+ duration = 300;
+ }
+
+ final long down = SystemClock.uptimeMillis();
+ injectMotionEvent(inputSource, MotionEvent.ACTION_DOWN, down, down, x1, y1, 1.0f,
+ displayId);
+ if (isDragDrop) {
+ // long press until drag start.
+ try {
+ Thread.sleep(ViewConfiguration.getLongPressTimeout());
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ long now = SystemClock.uptimeMillis();
+ final long endTime = down + duration;
+ while (now < endTime) {
+ final long elapsedTime = now - down;
+ final float alpha = (float) elapsedTime / duration;
+ injectMotionEvent(inputSource, MotionEvent.ACTION_MOVE, down, now,
+ lerp(x1, x2, alpha), lerp(y1, y2, alpha), 1.0f, displayId);
+ now = SystemClock.uptimeMillis();
+ }
+ injectMotionEvent(inputSource, MotionEvent.ACTION_UP, down, now, x2, y2, 0.0f,
+ displayId);
+ }
+ }
+
+ class InputDragAndDrop extends InputSwipe {
+ @Override
+ public void run(int inputSource, int displayId) {
+ inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
+ sendSwipe(inputSource, displayId, true);
+ }
+ }
+
+ class InputRoll implements InputCmd {
+ @Override
+ public void run(int inputSource, int displayId) {
+ inputSource = getSource(inputSource, InputDevice.SOURCE_TRACKBALL);
+ sendMove(inputSource, Float.parseFloat(nextArgRequired()),
+ Float.parseFloat(nextArgRequired()), displayId);
+ }
+
+ /**
+ * Sends a simple zero-pressure move event.
+ *
+ * @param inputSource the InputDevice.SOURCE_* sending the input event
+ * @param dx change in x coordinate due to move
+ * @param dy change in y coordinate due to move
+ */
+ private void sendMove(int inputSource, float dx, float dy, int displayId) {
+ final long now = SystemClock.uptimeMillis();
+ injectMotionEvent(inputSource, MotionEvent.ACTION_MOVE, now, now, dx, dy, 0.0f,
+ displayId);
+ }
}
/**
- * Convert the characters of string text into key event's and send to
- * device.
- *
- * @param text is a string of characters you want to input to the device.
+ * Abstract class for command
+ * use nextArgRequired or nextArg to check next argument if necessary.
*/
- private void sendText(int source, String text) {
-
- StringBuffer buff = new StringBuffer(text);
-
- boolean escapeFlag = false;
- for (int i=0; i<buff.length(); i++) {
- if (escapeFlag) {
- escapeFlag = false;
- if (buff.charAt(i) == 's') {
- buff.setCharAt(i, ' ');
- buff.deleteCharAt(--i);
- }
- }
- if (buff.charAt(i) == '%') {
- escapeFlag = true;
- }
- }
-
- char[] chars = buff.toString().toCharArray();
-
- KeyCharacterMap kcm = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
- KeyEvent[] events = kcm.getEvents(chars);
- for(int i = 0; i < events.length; i++) {
- KeyEvent e = events[i];
- if (source != e.getSource()) {
- e.setSource(source);
- }
- injectKeyEvent(e);
- }
+ private interface InputCmd {
+ void run(int inputSource, int displayId);
}
- private void sendKeyEvent(int inputSource, int keyCode, boolean longpress) {
- long now = SystemClock.uptimeMillis();
- injectKeyEvent(new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keyCode, 0, 0,
- KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, inputSource));
- if (longpress) {
- injectKeyEvent(new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keyCode, 1, 0,
- KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_LONG_PRESS,
- inputSource));
- }
- injectKeyEvent(new KeyEvent(now, now, KeyEvent.ACTION_UP, keyCode, 0, 0,
- KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, inputSource));
- }
-
- private void sendTap(int inputSource, float x, float y) {
- long now = SystemClock.uptimeMillis();
- injectMotionEvent(inputSource, MotionEvent.ACTION_DOWN, now, x, y, 1.0f);
- injectMotionEvent(inputSource, MotionEvent.ACTION_UP, now, x, y, 0.0f);
- }
-
- private void sendSwipe(int inputSource, float x1, float y1, float x2, float y2, int duration) {
- if (duration < 0) {
- duration = 300;
- }
- long now = SystemClock.uptimeMillis();
- injectMotionEvent(inputSource, MotionEvent.ACTION_DOWN, now, x1, y1, 1.0f);
- long startTime = now;
- long endTime = startTime + duration;
- while (now < endTime) {
- long elapsedTime = now - startTime;
- float alpha = (float) elapsedTime / duration;
- injectMotionEvent(inputSource, MotionEvent.ACTION_MOVE, now, lerp(x1, x2, alpha),
- lerp(y1, y2, alpha), 1.0f);
- now = SystemClock.uptimeMillis();
- }
- injectMotionEvent(inputSource, MotionEvent.ACTION_UP, now, x2, y2, 0.0f);
- }
-
- private void sendDragAndDrop(int inputSource, float x1, float y1, float x2, float y2,
- int dragDuration) {
- if (dragDuration < 0) {
- dragDuration = 300;
- }
- long now = SystemClock.uptimeMillis();
- injectMotionEvent(inputSource, MotionEvent.ACTION_DOWN, now, x1, y1, 1.0f);
- try {
- Thread.sleep(ViewConfiguration.getLongPressTimeout());
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- now = SystemClock.uptimeMillis();
- long startTime = now;
- long endTime = startTime + dragDuration;
- while (now < endTime) {
- long elapsedTime = now - startTime;
- float alpha = (float) elapsedTime / dragDuration;
- injectMotionEvent(inputSource, MotionEvent.ACTION_MOVE, now, lerp(x1, x2, alpha),
- lerp(y1, y2, alpha), 1.0f);
- now = SystemClock.uptimeMillis();
- }
- injectMotionEvent(inputSource, MotionEvent.ACTION_UP, now, x2, y2, 0.0f);
- }
-
- /**
- * Sends a simple zero-pressure move event.
- *
- * @param inputSource the InputDevice.SOURCE_* sending the input event
- * @param dx change in x coordinate due to move
- * @param dy change in y coordinate due to move
- */
- private void sendMove(int inputSource, float dx, float dy) {
- long now = SystemClock.uptimeMillis();
- injectMotionEvent(inputSource, MotionEvent.ACTION_MOVE, now, dx, dy, 0.0f);
- }
-
- private void injectKeyEvent(KeyEvent event) {
- Log.i(TAG, "injectKeyEvent: " + event);
+ private static void injectKeyEvent(KeyEvent event) {
InputManager.getInstance().injectInputEvent(event,
InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH);
}
- private int getInputDeviceId(int inputSource) {
+ private static int getInputDeviceId(int inputSource) {
final int DEFAULT_DEVICE_ID = 0;
int[] devIds = InputDevice.getDeviceIds();
for (int devId : devIds) {
@@ -287,22 +334,27 @@
*
* @param inputSource the InputDevice.SOURCE_* sending the input event
* @param action the MotionEvent.ACTION_* for the event
+ * @param downTime the value of the ACTION_DOWN event happened
* @param when the value of SystemClock.uptimeMillis() at which the event happened
* @param x x coordinate of event
* @param y y coordinate of event
* @param pressure pressure of event
*/
- private void injectMotionEvent(int inputSource, int action, long when, float x, float y, float pressure) {
+ private static void injectMotionEvent(int inputSource, int action, long downTime, long when,
+ float x, float y, float pressure, int displayId) {
final float DEFAULT_SIZE = 1.0f;
final int DEFAULT_META_STATE = 0;
final float DEFAULT_PRECISION_X = 1.0f;
final float DEFAULT_PRECISION_Y = 1.0f;
final int DEFAULT_EDGE_FLAGS = 0;
- MotionEvent event = MotionEvent.obtain(when, when, action, x, y, pressure, DEFAULT_SIZE,
+ MotionEvent event = MotionEvent.obtain(downTime, when, action, x, y, pressure, DEFAULT_SIZE,
DEFAULT_META_STATE, DEFAULT_PRECISION_X, DEFAULT_PRECISION_Y,
getInputDeviceId(inputSource), DEFAULT_EDGE_FLAGS);
event.setSource(inputSource);
- Log.i(TAG, "injectMotionEvent: " + event);
+ if (displayId == INVALID_DISPLAY && (inputSource & InputDevice.SOURCE_CLASS_POINTER) != 0) {
+ displayId = DEFAULT_DISPLAY;
+ }
+ event.setDisplayId(displayId);
InputManager.getInstance().injectInputEvent(event,
InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH);
}
@@ -315,24 +367,29 @@
return inputSource == InputDevice.SOURCE_UNKNOWN ? defaultSource : inputSource;
}
- private void showUsage() {
- System.err.println("Usage: input [<source>] <command> [<arg>...]");
- System.err.println();
- System.err.println("The sources are: ");
+ @Override
+ public void onShowUsage(PrintStream out) {
+ out.println("Usage: input [<source>] [-d DISPLAY_ID] <command> [<arg>...]");
+ out.println();
+ out.println("The sources are: ");
for (String src : SOURCES.keySet()) {
- System.err.println(" " + src);
+ out.println(" " + src);
}
- System.err.println();
- System.err.println("The commands and default sources are:");
- System.err.println(" text <string> (Default: touchscreen)");
- System.err.println(" keyevent [--longpress] <key code number or name> ..."
+ out.println();
+ out.printf("-d: specify the display ID.\n"
+ + " (Default: %d for key event, %d for motion event if not specified.)",
+ INVALID_DISPLAY, DEFAULT_DISPLAY);
+ out.println();
+ out.println("The commands and default sources are:");
+ out.println(" text <string> (Default: touchscreen)");
+ out.println(" keyevent [--longpress] <key code number or name> ..."
+ " (Default: keyboard)");
- System.err.println(" tap <x> <y> (Default: touchscreen)");
- System.err.println(" swipe <x1> <y1> <x2> <y2> [duration(ms)]"
+ out.println(" tap <x> <y> (Default: touchscreen)");
+ out.println(" swipe <x1> <y1> <x2> <y2> [duration(ms)]"
+ " (Default: touchscreen)");
- System.err.println(" draganddrop <x1> <y1> <x2> <y2> [duration(ms)]"
+ out.println(" draganddrop <x1> <y1> <x2> <y2> [duration(ms)]"
+ " (Default: touchscreen)");
- System.err.println(" press (Default: trackball)");
- System.err.println(" roll <dx> <dy> (Default: trackball)");
+ out.println(" press (Default: trackball)");
+ out.println(" roll <dx> <dy> (Default: trackball)");
}
}
diff --git a/cmds/media/src/com/android/commands/media/Media.java b/cmds/media/src/com/android/commands/media/Media.java
index c6d2bc8..6788f7d 100644
--- a/cmds/media/src/com/android/commands/media/Media.java
+++ b/cmds/media/src/com/android/commands/media/Media.java
@@ -24,7 +24,7 @@
import android.media.session.ISessionController;
import android.media.session.ISessionControllerCallback;
import android.media.session.ISessionManager;
-import android.media.session.ParcelableVolumeInfo;
+import android.media.session.MediaController.PlaybackInfo;
import android.media.session.PlaybackState;
import android.os.Bundle;
import android.os.HandlerThread;
@@ -224,7 +224,7 @@
}
@Override
- public void onVolumeInfoChanged(ParcelableVolumeInfo info) throws RemoteException {
+ public void onVolumeInfoChanged(PlaybackInfo info) throws RemoteException {
System.out.println("onVolumeInfoChanged " + info);
}
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 041fbfa..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
@@ -2768,92 +2767,6 @@
Lcom/android/internal/telephony/Connection;->mIsIncoming:Z
Lcom/android/internal/telephony/Connection;->mNumberPresentation:I
Lcom/android/internal/telephony/Connection;->setVideoState(I)V
-Lcom/android/internal/telephony/dataconnection/ApnContext;->getApnType()Ljava/lang/String;
-Lcom/android/internal/telephony/dataconnection/ApnContext;->getReason()Ljava/lang/String;
-Lcom/android/internal/telephony/dataconnection/ApnContext;->getState()Lcom/android/internal/telephony/DctConstants$State;
-Lcom/android/internal/telephony/dataconnection/ApnContext;->isConnectable()Z
-Lcom/android/internal/telephony/dataconnection/ApnContext;->isDisconnected()Z
-Lcom/android/internal/telephony/dataconnection/ApnContext;->isEnabled()Z
-Lcom/android/internal/telephony/dataconnection/ApnContext;->isReady()Z
-Lcom/android/internal/telephony/dataconnection/ApnContext;->log(Ljava/lang/String;)V
-Lcom/android/internal/telephony/dataconnection/ApnContext;->mApnType:Ljava/lang/String;
-Lcom/android/internal/telephony/dataconnection/ApnContext;->mRefCount:I
-Lcom/android/internal/telephony/dataconnection/ApnContext;->mRefCountLock:Ljava/lang/Object;
-Lcom/android/internal/telephony/dataconnection/ApnContext;->setState(Lcom/android/internal/telephony/DctConstants$State;)V
-Lcom/android/internal/telephony/dataconnection/DataConnection$ConnectionParams;->mApnContext:Lcom/android/internal/telephony/dataconnection/ApnContext;
-Lcom/android/internal/telephony/dataconnection/DataConnection;->clearSettings()V
-Lcom/android/internal/telephony/dataconnection/DataConnection;->dumpToLog()V
-Lcom/android/internal/telephony/dataconnection/DataConnection;->initConnection(Lcom/android/internal/telephony/dataconnection/DataConnection$ConnectionParams;)Z
-Lcom/android/internal/telephony/dataconnection/DataConnection;->log(Ljava/lang/String;)V
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mActivatingState:Lcom/android/internal/telephony/dataconnection/DataConnection$DcActivatingState;
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mActiveState:Lcom/android/internal/telephony/dataconnection/DataConnection$DcActiveState;
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mConnectionParams:Lcom/android/internal/telephony/dataconnection/DataConnection$ConnectionParams;
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mDataRegState:I
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mDct:Lcom/android/internal/telephony/dataconnection/DcTracker;
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mDisconnectingErrorCreatingConnection:Lcom/android/internal/telephony/dataconnection/DataConnection$DcDisconnectionErrorCreatingConnection;
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mDisconnectingState:Lcom/android/internal/telephony/dataconnection/DataConnection$DcDisconnectingState;
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mDisconnectParams:Lcom/android/internal/telephony/dataconnection/DataConnection$DisconnectParams;
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mId:I
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mInactiveState:Lcom/android/internal/telephony/dataconnection/DataConnection$DcInactiveState;
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mLinkProperties:Landroid/net/LinkProperties;
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mNetworkInfo:Landroid/net/NetworkInfo;
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mPhone:Lcom/android/internal/telephony/Phone;
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mRilRat:I
-Lcom/android/internal/telephony/dataconnection/DataConnection;->notifyAllOfConnected(Ljava/lang/String;)V
-Lcom/android/internal/telephony/dataconnection/DataConnection;->notifyDisconnectCompleted(Lcom/android/internal/telephony/dataconnection/DataConnection$DisconnectParams;Z)V
-Lcom/android/internal/telephony/dataconnection/DataConnection;->onConnect(Lcom/android/internal/telephony/dataconnection/DataConnection$ConnectionParams;)V
-Lcom/android/internal/telephony/dataconnection/DataConnection;->tearDownData(Ljava/lang/Object;)V
-Lcom/android/internal/telephony/dataconnection/DataConnection;->updateTcpBufferSizes(I)V
-Lcom/android/internal/telephony/dataconnection/DcController;->lr(Ljava/lang/String;)V
-Lcom/android/internal/telephony/dataconnection/DcController;->mDcListActiveByCid:Ljava/util/HashMap;
-Lcom/android/internal/telephony/dataconnection/DcController;->mDct:Lcom/android/internal/telephony/dataconnection/DcTracker;
-Lcom/android/internal/telephony/dataconnection/DcController;->mDcTesterDeactivateAll:Lcom/android/internal/telephony/dataconnection/DcTesterDeactivateAll;
-Lcom/android/internal/telephony/dataconnection/DcTracker$RecoveryAction;->isAggressiveRecovery(I)Z
-Lcom/android/internal/telephony/dataconnection/DcTracker;->cancelReconnectAlarm(Lcom/android/internal/telephony/dataconnection/ApnContext;)V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->cleanUpAllConnections(Ljava/lang/String;)V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->createAllApnList()V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->getActiveApnTypes()[Ljava/lang/String;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->getOverallState()Lcom/android/internal/telephony/DctConstants$State;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->getUiccRecords(I)Lcom/android/internal/telephony/uicc/IccRecords;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->isConnected()Z
-Lcom/android/internal/telephony/dataconnection/DcTracker;->isDisconnected()Z
-Lcom/android/internal/telephony/dataconnection/DcTracker;->isOnlySingleDcAllowed(I)Z
-Lcom/android/internal/telephony/dataconnection/DcTracker;->log(Ljava/lang/String;)V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->loge(Ljava/lang/String;)V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mAllApnSettings:Ljava/util/ArrayList;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mApnContexts:Ljava/util/concurrent/ConcurrentHashMap;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mAttached:Ljava/util/concurrent/atomic/AtomicBoolean;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mAutoAttachOnCreation:Ljava/util/concurrent/atomic/AtomicBoolean;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mDataConnectionTracker:Landroid/os/Handler;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mDisconnectPendingCount:I
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mIccRecords:Ljava/util/concurrent/atomic/AtomicReference;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mIsPsRestricted:Z
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mIsScreenOn:Z
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mNetStatPollEnabled:Z
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mNetStatPollPeriod:I
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mPhone:Lcom/android/internal/telephony/Phone;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mPrioritySortedApnContexts:Ljava/util/PriorityQueue;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mProvisioningSpinner:Landroid/app/ProgressDialog;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mResolver:Landroid/content/ContentResolver;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mState:Lcom/android/internal/telephony/DctConstants$State;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->mSubscriptionManager:Landroid/telephony/SubscriptionManager;
-Lcom/android/internal/telephony/dataconnection/DcTracker;->onActionIntentDataStallAlarm(Landroid/content/Intent;)V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->onActionIntentProvisioningApnAlarm(Landroid/content/Intent;)V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->onRecordsLoadedOrSubIdChanged()V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->onTrySetupData(Lcom/android/internal/telephony/dataconnection/ApnContext;)Z
-Lcom/android/internal/telephony/dataconnection/DcTracker;->onTrySetupData(Ljava/lang/String;)Z
-Lcom/android/internal/telephony/dataconnection/DcTracker;->registerSettingsObserver()V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->resetPollStats()V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->restartDataStallAlarm()V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->setInitialAttachApn()V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->setPreferredApn(I)V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->setRadio(Z)V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->setupDataOnConnectableApns(Ljava/lang/String;)V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->startDataStallAlarm(Z)V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->startNetStatPoll()V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->stopDataStallAlarm()V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->stopNetStatPoll()V
-Lcom/android/internal/telephony/dataconnection/DcTracker;->unregisterForAllDataDisconnected(Landroid/os/Handler;)V
Lcom/android/internal/telephony/DctConstants$Activity;->DATAIN:Lcom/android/internal/telephony/DctConstants$Activity;
Lcom/android/internal/telephony/DctConstants$Activity;->DATAINANDOUT:Lcom/android/internal/telephony/DctConstants$Activity;
Lcom/android/internal/telephony/DctConstants$Activity;->DATAOUT:Lcom/android/internal/telephony/DctConstants$Activity;
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/ActivityView.java b/core/java/android/app/ActivityView.java
index 0b5776e..57132a7 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -37,6 +37,7 @@
import android.view.SurfaceHolder;
import android.view.SurfaceSession;
import android.view.SurfaceView;
+import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
@@ -320,7 +321,7 @@
public void surfaceCreated(SurfaceHolder surfaceHolder) {
mTmpSurface = new Surface();
if (mVirtualDisplay == null) {
- initVirtualDisplay(new SurfaceSession(surfaceHolder.getSurface()));
+ initVirtualDisplay(new SurfaceSession());
if (mVirtualDisplay != null && mActivityViewCallback != null) {
mActivityViewCallback.onActivityViewReady(ActivityView.this);
}
@@ -354,6 +355,12 @@
}
}
+ @Override
+ protected void onVisibilityChanged(View changedView, int visibility) {
+ super.onVisibilityChanged(changedView, visibility);
+ mSurfaceView.setVisibility(visibility);
+ }
+
private void initVirtualDisplay(SurfaceSession surfaceSession) {
if (mVirtualDisplay != null) {
throw new IllegalStateException("Trying to initialize for the second time.");
@@ -382,6 +389,7 @@
mRootSurfaceControl = new SurfaceControl.Builder(surfaceSession)
.setContainerLayer(true)
+ .setParent(mSurfaceView.getSurfaceControl())
.setName(DISPLAY_NAME)
.build();
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/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl
index 3a2038d..666f721 100644
--- a/core/java/android/app/IWallpaperManager.aidl
+++ b/core/java/android/app/IWallpaperManager.aidl
@@ -142,19 +142,20 @@
*
* @param which either {@link WallpaperManager#FLAG_LOCK}
* or {@link WallpaperManager#FLAG_SYSTEM}
+ * @param displayId Which display is interested
* @return colors of chosen wallpaper
*/
- WallpaperColors getWallpaperColors(int which, int userId);
+ WallpaperColors getWallpaperColors(int which, int userId, int displayId);
/**
- * Register a callback to receive color updates
+ * Register a callback to receive color updates from a display
*/
- void registerWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId);
+ void registerWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId, int displayId);
/**
- * Unregister a callback that was receiving color updates
+ * Unregister a callback that was receiving color updates from a display
*/
- void unregisterWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId);
+ void unregisterWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId, int displayId);
/**
* Called from SystemUI when it shows the AoD UI.
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 27ae0b0..a929fe0 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -307,13 +307,14 @@
* @param callback Listener
* @param handler Thread to call it from. Main thread if null.
* @param userId Owner of the wallpaper or UserHandle.USER_ALL
+ * @param displayId Caller comes from which display
*/
public void addOnColorsChangedListener(@NonNull OnColorsChangedListener callback,
- @Nullable Handler handler, int userId) {
+ @Nullable Handler handler, int userId, int displayId) {
synchronized (this) {
if (!mColorCallbackRegistered) {
try {
- mService.registerWallpaperColorsCallback(this, userId);
+ mService.registerWallpaperColorsCallback(this, userId, displayId);
mColorCallbackRegistered = true;
} catch (RemoteException e) {
// Failed, service is gone
@@ -329,16 +330,17 @@
*
* @param callback listener
* @param userId Owner of the wallpaper or UserHandle.USER_ALL
+ * @param displayId Which display is interested
*/
public void removeOnColorsChangedListener(@NonNull OnColorsChangedListener callback,
- int userId) {
+ int userId, int displayId) {
synchronized (this) {
mColorListeners.removeIf(pair -> pair.first == callback);
if (mColorListeners.size() == 0 && mColorCallbackRegistered) {
mColorCallbackRegistered = false;
try {
- mService.unregisterWallpaperColorsCallback(this, userId);
+ mService.unregisterWallpaperColorsCallback(this, userId, displayId);
} catch (RemoteException e) {
// Failed, service is gone
Log.w(TAG, "Can't unregister color updates", e);
@@ -370,14 +372,14 @@
}
}
- WallpaperColors getWallpaperColors(int which, int userId) {
+ WallpaperColors getWallpaperColors(int which, int userId, int displayId) {
if (which != FLAG_LOCK && which != FLAG_SYSTEM) {
throw new IllegalArgumentException(
"Must request colors for exactly one kind of wallpaper");
}
try {
- return mService.getWallpaperColors(which, userId);
+ return mService.getWallpaperColors(which, userId, displayId);
} catch (RemoteException e) {
// Can't get colors, connection lost.
}
@@ -894,7 +896,7 @@
@UnsupportedAppUsage
public void addOnColorsChangedListener(@NonNull OnColorsChangedListener listener,
@NonNull Handler handler, int userId) {
- sGlobals.addOnColorsChangedListener(listener, handler, userId);
+ sGlobals.addOnColorsChangedListener(listener, handler, userId, mContext.getDisplayId());
}
/**
@@ -913,7 +915,7 @@
*/
public void removeOnColorsChangedListener(@NonNull OnColorsChangedListener callback,
int userId) {
- sGlobals.removeOnColorsChangedListener(callback, userId);
+ sGlobals.removeOnColorsChangedListener(callback, userId, mContext.getDisplayId());
}
/**
@@ -947,7 +949,7 @@
*/
@UnsupportedAppUsage
public @Nullable WallpaperColors getWallpaperColors(int which, int userId) {
- return sGlobals.getWallpaperColors(which, userId);
+ return sGlobals.getWallpaperColors(which, userId, mContext.getDisplayId());
}
/**
diff --git a/core/java/android/app/usage/IUsageStatsManager.aidl b/core/java/android/app/usage/IUsageStatsManager.aidl
index 4d52263..bbae7d3 100644
--- a/core/java/android/app/usage/IUsageStatsManager.aidl
+++ b/core/java/android/app/usage/IUsageStatsManager.aidl
@@ -55,4 +55,8 @@
long sessionThresholdTimeMs, in PendingIntent limitReachedCallbackIntent,
in PendingIntent sessionEndCallbackIntent, String callingPackage);
void unregisterUsageSessionObserver(int sessionObserverId, String callingPackage);
+ void reportUsageStart(in IBinder activity, String token, String callingPackage);
+ void reportPastUsageStart(in IBinder activity, String token, long timeAgoMs,
+ String callingPackage);
+ void reportUsageStop(in IBinder activity, String token, String callingPackage);
}
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index 26beb45..605deac 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -23,6 +23,7 @@
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.UnsupportedAppUsage;
+import android.app.Activity;
import android.app.PendingIntent;
import android.content.Context;
import android.content.pm.ParceledListSlice;
@@ -579,15 +580,18 @@
/**
* @hide
* Register an app usage limit observer that receives a callback on the provided intent when
- * the sum of usages of apps in the packages array exceeds the {@code timeLimit} specified. The
- * observer will automatically be unregistered when the time limit is reached and the intent
- * is delivered. Registering an {@code observerId} that was already registered will override
- * the previous one. No more than 1000 unique {@code observerId} may be registered by a single
- * uid at any one time.
+ * the sum of usages of apps and tokens in the {@code observed} array exceeds the
+ * {@code timeLimit} specified. The structure of a token is a String with the reporting
+ * package's name and a token the reporting app will use, separated by the forward slash
+ * character. Example: com.reporting.package/5OM3*0P4QU3-7OK3N
+ * The observer will automatically be unregistered when the time limit is reached and the
+ * intent is delivered. Registering an {@code observerId} that was already registered will
+ * override the previous one. No more than 1000 unique {@code observerId} may be registered by
+ * a single uid at any one time.
* @param observerId A unique id associated with the group of apps to be monitored. There can
* be multiple groups with common packages and different time limits.
- * @param packages The list of packages to observe for foreground activity time. Cannot be null
- * and must include at least one package.
+ * @param observedEntities The list of packages and token to observe for usage time. Cannot be
+ * null and must include at least one package or token.
* @param timeLimit The total time the set of apps can be in the foreground before the
* callbackIntent is delivered. Must be at least one minute.
* @param timeUnit The unit for time specified in {@code timeLimit}. Cannot be null.
@@ -600,11 +604,11 @@
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.OBSERVE_APP_USAGE)
- public void registerAppUsageObserver(int observerId, @NonNull String[] packages, long timeLimit,
- @NonNull TimeUnit timeUnit, @NonNull PendingIntent callbackIntent) {
+ public void registerAppUsageObserver(int observerId, @NonNull String[] observedEntities,
+ long timeLimit, @NonNull TimeUnit timeUnit, @NonNull PendingIntent callbackIntent) {
try {
- mService.registerAppUsageObserver(observerId, packages, timeUnit.toMillis(timeLimit),
- callbackIntent, mContext.getOpPackageName());
+ mService.registerAppUsageObserver(observerId, observedEntities,
+ timeUnit.toMillis(timeLimit), callbackIntent, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -631,18 +635,21 @@
/**
* Register a usage session observer that receives a callback on the provided {@code
- * limitReachedCallbackIntent} when the sum of usages of apps in the packages array exceeds
- * the {@code timeLimit} specified within a usage session. After the {@code timeLimit} has
- * been reached, the usage session observer will receive a callback on the provided {@code
- * sessionEndCallbackIntent} when the usage session ends. Registering another session
- * observer against a {@code sessionObserverId} that has already been registered will
- * override the previous session observer.
+ * limitReachedCallbackIntent} when the sum of usages of apps and tokens in the {@code
+ * observed} array exceeds the {@code timeLimit} specified within a usage session. The
+ * structure of a token is a String with the reporting packages' name and a token the
+ * reporting app will use, separated by the forward slash character.
+ * Example: com.reporting.package/5OM3*0P4QU3-7OK3N
+ * After the {@code timeLimit} has been reached, the usage session observer will receive a
+ * callback on the provided {@code sessionEndCallbackIntent} when the usage session ends.
+ * Registering another session observer against a {@code sessionObserverId} that has already
+ * been registered will override the previous session observer.
*
* @param sessionObserverId A unique id associated with the group of apps to be
* monitored. There can be multiple groups with common
* packages and different time limits.
- * @param packages The list of packages to observe for foreground activity time. Cannot be null
- * and must include at least one package.
+ * @param observedEntities The list of packages and token to observe for usage time. Cannot be
+ * null and must include at least one package or token.
* @param timeLimit The total time the set of apps can be used continuously before the {@code
* limitReachedCallbackIntent} is delivered. Must be at least one minute.
* @param timeUnit The unit for time specified in {@code timeLimit}. Cannot be null.
@@ -668,13 +675,13 @@
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.OBSERVE_APP_USAGE)
- public void registerUsageSessionObserver(int sessionObserverId, @NonNull String[] packages,
- long timeLimit, @NonNull TimeUnit timeUnit, long sessionThresholdTime,
- @NonNull TimeUnit sessionThresholdTimeUnit,
+ public void registerUsageSessionObserver(int sessionObserverId,
+ @NonNull String[] observedEntities, long timeLimit, @NonNull TimeUnit timeUnit,
+ long sessionThresholdTime, @NonNull TimeUnit sessionThresholdTimeUnit,
@NonNull PendingIntent limitReachedCallbackIntent,
@Nullable PendingIntent sessionEndCallbackIntent) {
try {
- mService.registerUsageSessionObserver(sessionObserverId, packages,
+ mService.registerUsageSessionObserver(sessionObserverId, observedEntities,
timeUnit.toMillis(timeLimit),
sessionThresholdTimeUnit.toMillis(sessionThresholdTime),
limitReachedCallbackIntent, sessionEndCallbackIntent,
@@ -704,6 +711,71 @@
}
}
+ /**
+ * Report usage associated with a particular {@code token} has started. Tokens are app defined
+ * strings used to represent usage of in-app features. Apps with the {@link
+ * android.Manifest.permission#OBSERVE_APP_USAGE} permission can register time limit observers
+ * to monitor the usage of a token. In app usage can only associated with an {@code activity}
+ * and usage will be considered stopped if the activity stops or crashes.
+ * @see #registerAppUsageObserver
+ * @see #registerUsageSessionObserver
+ *
+ * @param activity The activity {@code token} is associated with.
+ * @param token The token to report usage against.
+ * @hide
+ */
+ @SystemApi
+ public void reportUsageStart(@NonNull Activity activity, @NonNull String token) {
+ try {
+ mService.reportUsageStart(activity.getActivityToken(), token,
+ mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Report usage associated with a particular {@code token} had started some amount of time in
+ * the past. Tokens are app defined strings used to represent usage of in-app features. Apps
+ * with the {@link android.Manifest.permission#OBSERVE_APP_USAGE} permission can register time
+ * limit observers to monitor the usage of a token. In app usage can only associated with an
+ * {@code activity} and usage will be considered stopped if the activity stops or crashes.
+ * @see #registerAppUsageObserver
+ * @see #registerUsageSessionObserver
+ *
+ * @param activity The activity {@code token} is associated with.
+ * @param token The token to report usage against.
+ * @param timeAgoMs How long ago the start of usage took place
+ * @hide
+ */
+ @SystemApi
+ public void reportUsageStart(@NonNull Activity activity, @NonNull String token,
+ long timeAgoMs) {
+ try {
+ mService.reportPastUsageStart(activity.getActivityToken(), token, timeAgoMs,
+ mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Report the usage associated with a particular {@code token} has stopped.
+ *
+ * @param activity The activity {@code token} is associated with.
+ * @param token The token to report usage against.
+ * @hide
+ */
+ @SystemApi
+ public void reportUsageStop(@NonNull Activity activity, @NonNull String token) {
+ try {
+ mService.reportUsageStop(activity.getActivityToken(), token,
+ mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/** @hide */
public static String reasonToString(int standbyReason) {
StringBuilder sb = new StringBuilder();
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/content/Intent.java b/core/java/android/content/Intent.java
index 7b3497b..de810e8 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -816,6 +816,28 @@
= "android.intent.action.SHOW_APP_INFO";
/**
+ * Activity Action: Start an activity to show the app's detailed usage information for
+ * permission protected data.
+ *
+ * The Intent contains an extra {@link #EXTRA_PERMISSION_USAGE_PERMISSIONS} that is of
+ * type {@code String[]} and contains the specific permissions to show information for.
+ *
+ * Apps should handle this intent if they want to provide more information about permission
+ * usage to users beyond the information provided in the manifest.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_PERMISSION_USAGE_DETAILS =
+ "android.intent.action.PERMISSION_USAGE_DETAILS";
+
+ /**
+ * The name of the extra used to contain the permissions in
+ * {@link #ACTION_PERMISSION_USAGE_DETAILS}.
+ * @see #ACTION_PERMISSION_USAGE_DETAILS
+ */
+ public static final String EXTRA_PERMISSION_USAGE_PERMISSIONS =
+ "android.intent.extra.PERMISSION_USAGE_PERMISSIONS";
+
+ /**
* Represents a shortcut/live folder icon resource.
*
* @see Intent#ACTION_CREATE_SHORTCUT
@@ -1811,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/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/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 61d5a91..436b4a1 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -16,6 +16,7 @@
package android.net;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
@@ -909,6 +910,7 @@
*/
@Deprecated
@RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+ @Nullable
public NetworkInfo getActiveNetworkInfo() {
try {
return mService.getActiveNetworkInfo();
@@ -928,6 +930,7 @@
* {@code null} if no default network is currently active
*/
@RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+ @Nullable
public Network getActiveNetwork() {
try {
return mService.getActiveNetwork();
@@ -949,6 +952,7 @@
* @hide
*/
@RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL)
+ @Nullable
public Network getActiveNetworkForUid(int uid) {
return getActiveNetworkForUid(uid, false);
}
@@ -1074,6 +1078,7 @@
*/
@Deprecated
@RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+ @Nullable
public NetworkInfo getNetworkInfo(int networkType) {
try {
return mService.getNetworkInfo(networkType);
@@ -1095,7 +1100,8 @@
*/
@Deprecated
@RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
- public NetworkInfo getNetworkInfo(Network network) {
+ @Nullable
+ public NetworkInfo getNetworkInfo(@Nullable Network network) {
return getNetworkInfoForUid(network, Process.myUid(), false);
}
@@ -1121,6 +1127,7 @@
*/
@Deprecated
@RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+ @NonNull
public NetworkInfo[] getAllNetworkInfo() {
try {
return mService.getAllNetworkInfo();
@@ -1156,6 +1163,7 @@
* @return an array of {@link Network} objects.
*/
@RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+ @NonNull
public Network[] getAllNetworks() {
try {
return mService.getAllNetworks();
@@ -1230,7 +1238,8 @@
* @return The {@link LinkProperties} for the network, or {@code null}.
*/
@RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
- public LinkProperties getLinkProperties(Network network) {
+ @Nullable
+ public LinkProperties getLinkProperties(@Nullable Network network) {
try {
return mService.getLinkProperties(network);
} catch (RemoteException e) {
@@ -1246,7 +1255,8 @@
* @return The {@link android.net.NetworkCapabilities} for the network, or {@code null}.
*/
@RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
- public NetworkCapabilities getNetworkCapabilities(Network network) {
+ @Nullable
+ public NetworkCapabilities getNetworkCapabilities(@Nullable Network network) {
try {
return mService.getNetworkCapabilities(network);
} catch (RemoteException e) {
@@ -2000,7 +2010,7 @@
*
* @param l Previously registered listener.
*/
- public void removeDefaultNetworkActiveListener(OnNetworkActiveListener l) {
+ public void removeDefaultNetworkActiveListener(@NonNull OnNetworkActiveListener l) {
INetworkActivityListener rl = mNetworkActivityListeners.get(l);
Preconditions.checkArgument(rl != null, "Listener was not registered.");
try {
@@ -2475,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
@@ -2528,7 +2540,7 @@
* working and non-working connectivity.
*/
@Deprecated
- public void reportBadNetwork(Network network) {
+ public void reportBadNetwork(@Nullable Network network) {
printStackTrace();
try {
// One of these will be ignored because it matches system's current state.
@@ -2551,7 +2563,7 @@
* @param hasConnectivity {@code true} if the application was able to successfully access the
* Internet using {@code network} or {@code false} if not.
*/
- public void reportNetworkConnectivity(Network network, boolean hasConnectivity) {
+ public void reportNetworkConnectivity(@Nullable Network network, boolean hasConnectivity) {
printStackTrace();
try {
mService.reportNetworkConnectivity(network, hasConnectivity);
@@ -2625,6 +2637,7 @@
* @return the {@link ProxyInfo} for the current HTTP proxy, or {@code null} if no
* HTTP proxy is active.
*/
+ @Nullable
public ProxyInfo getDefaultProxy() {
return getProxyForNetwork(getBoundNetworkForProcess());
}
@@ -3156,8 +3169,9 @@
*
* @hide
*/
- public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback,
- int timeoutMs, int legacyType, Handler handler) {
+ public void requestNetwork(@NonNull NetworkRequest request,
+ @NonNull NetworkCallback networkCallback, int timeoutMs, int legacyType,
+ @NonNull Handler handler) {
CallbackHandler cbHandler = new CallbackHandler(handler);
NetworkCapabilities nc = request.networkCapabilities;
sendRequestForNetwork(nc, networkCallback, timeoutMs, REQUEST, legacyType, cbHandler);
@@ -3194,7 +3208,8 @@
* @throws IllegalArgumentException if {@code request} specifies any mutable
* {@code NetworkCapabilities}.
*/
- public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback) {
+ public void requestNetwork(@NonNull NetworkRequest request,
+ @NonNull NetworkCallback networkCallback) {
requestNetwork(request, networkCallback, getDefaultHandler());
}
@@ -3229,8 +3244,8 @@
* @throws IllegalArgumentException if {@code request} specifies any mutable
* {@code NetworkCapabilities}.
*/
- public void requestNetwork(
- NetworkRequest request, NetworkCallback networkCallback, Handler handler) {
+ public void requestNetwork(@NonNull NetworkRequest request,
+ @NonNull NetworkCallback networkCallback, @NonNull Handler handler) {
int legacyType = inferLegacyTypeForNetworkCapabilities(request.networkCapabilities);
CallbackHandler cbHandler = new CallbackHandler(handler);
requestNetwork(request, networkCallback, 0, legacyType, cbHandler);
@@ -3264,8 +3279,8 @@
* before {@link NetworkCallback#onUnavailable()} is called. The timeout must
* be a positive value (i.e. >0).
*/
- public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback,
- int timeoutMs) {
+ public void requestNetwork(@NonNull NetworkRequest request,
+ @NonNull NetworkCallback networkCallback, int timeoutMs) {
checkTimeout(timeoutMs);
int legacyType = inferLegacyTypeForNetworkCapabilities(request.networkCapabilities);
requestNetwork(request, networkCallback, timeoutMs, legacyType, getDefaultHandler());
@@ -3298,8 +3313,8 @@
* @param timeoutMs The time in milliseconds to attempt looking for a suitable network
* before {@link NetworkCallback#onUnavailable} is called.
*/
- public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback,
- Handler handler, int timeoutMs) {
+ public void requestNetwork(@NonNull NetworkRequest request,
+ @NonNull NetworkCallback networkCallback, @NonNull Handler handler, int timeoutMs) {
checkTimeout(timeoutMs);
int legacyType = inferLegacyTypeForNetworkCapabilities(request.networkCapabilities);
CallbackHandler cbHandler = new CallbackHandler(handler);
@@ -3371,7 +3386,8 @@
* {@link NetworkCapabilities#NET_CAPABILITY_VALIDATED} or
* {@link NetworkCapabilities#NET_CAPABILITY_CAPTIVE_PORTAL}.
*/
- public void requestNetwork(NetworkRequest request, PendingIntent operation) {
+ public void requestNetwork(@NonNull NetworkRequest request,
+ @NonNull PendingIntent operation) {
printStackTrace();
checkPendingIntentNotNull(operation);
try {
@@ -3395,7 +3411,7 @@
* {@link #requestNetwork(NetworkRequest, android.app.PendingIntent)} with the
* corresponding NetworkRequest you'd like to remove. Cannot be null.
*/
- public void releaseNetworkRequest(PendingIntent operation) {
+ public void releaseNetworkRequest(@NonNull PendingIntent operation) {
printStackTrace();
checkPendingIntentNotNull(operation);
try {
@@ -3428,7 +3444,8 @@
* The callback is invoked on the default internal Handler.
*/
@RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
- public void registerNetworkCallback(NetworkRequest request, NetworkCallback networkCallback) {
+ public void registerNetworkCallback(@NonNull NetworkRequest request,
+ @NonNull NetworkCallback networkCallback) {
registerNetworkCallback(request, networkCallback, getDefaultHandler());
}
@@ -3443,8 +3460,8 @@
* @param handler {@link Handler} to specify the thread upon which the callback will be invoked.
*/
@RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
- public void registerNetworkCallback(
- NetworkRequest request, NetworkCallback networkCallback, Handler handler) {
+ public void registerNetworkCallback(@NonNull NetworkRequest request,
+ @NonNull NetworkCallback networkCallback, @NonNull Handler handler) {
CallbackHandler cbHandler = new CallbackHandler(handler);
NetworkCapabilities nc = request.networkCapabilities;
sendRequestForNetwork(nc, networkCallback, 0, LISTEN, TYPE_NONE, cbHandler);
@@ -3480,7 +3497,8 @@
* comes from {@link PendingIntent#getBroadcast}. Cannot be null.
*/
@RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
- public void registerNetworkCallback(NetworkRequest request, PendingIntent operation) {
+ public void registerNetworkCallback(@NonNull NetworkRequest request,
+ @NonNull PendingIntent operation) {
printStackTrace();
checkPendingIntentNotNull(operation);
try {
@@ -3502,7 +3520,7 @@
* The callback is invoked on the default internal Handler.
*/
@RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
- public void registerDefaultNetworkCallback(NetworkCallback networkCallback) {
+ public void registerDefaultNetworkCallback(@NonNull NetworkCallback networkCallback) {
registerDefaultNetworkCallback(networkCallback, getDefaultHandler());
}
@@ -3516,7 +3534,8 @@
* @param handler {@link Handler} to specify the thread upon which the callback will be invoked.
*/
@RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
- public void registerDefaultNetworkCallback(NetworkCallback networkCallback, Handler handler) {
+ public void registerDefaultNetworkCallback(@NonNull NetworkCallback networkCallback,
+ @NonNull Handler handler) {
// This works because if the NetworkCapabilities are null,
// ConnectivityService takes them from the default request.
//
@@ -3541,7 +3560,7 @@
* @param network {@link Network} specifying which network you're interested.
* @return {@code true} on success, {@code false} if the {@link Network} is no longer valid.
*/
- public boolean requestBandwidthUpdate(Network network) {
+ public boolean requestBandwidthUpdate(@NonNull Network network) {
try {
return mService.requestBandwidthUpdate(network);
} catch (RemoteException e) {
@@ -3562,7 +3581,7 @@
*
* @param networkCallback The {@link NetworkCallback} used when making the request.
*/
- public void unregisterNetworkCallback(NetworkCallback networkCallback) {
+ public void unregisterNetworkCallback(@NonNull NetworkCallback networkCallback) {
printStackTrace();
checkCallbackNotNull(networkCallback);
final List<NetworkRequest> reqs = new ArrayList<>();
@@ -3601,7 +3620,7 @@
* {@link #registerNetworkCallback(NetworkRequest, android.app.PendingIntent)}.
* Cannot be null.
*/
- public void unregisterNetworkCallback(PendingIntent operation) {
+ public void unregisterNetworkCallback(@NonNull PendingIntent operation) {
checkPendingIntentNotNull(operation);
releaseNetworkRequest(operation);
}
@@ -3723,7 +3742,7 @@
* @return a bitwise OR of zero or more of the {@code MULTIPATH_PREFERENCE_*} constants.
*/
@RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
- public @MultipathPreference int getMultipathPreference(Network network) {
+ public @MultipathPreference int getMultipathPreference(@Nullable Network network) {
try {
return mService.getMultipathPreference(network);
} catch (RemoteException e) {
@@ -3761,7 +3780,7 @@
* the current binding.
* @return {@code true} on success, {@code false} if the {@link Network} is no longer valid.
*/
- public boolean bindProcessToNetwork(Network network) {
+ public boolean bindProcessToNetwork(@Nullable Network network) {
// Forcing callers to call through non-static function ensures ConnectivityManager
// instantiated.
return setProcessDefaultNetwork(network);
@@ -3789,7 +3808,7 @@
* is a direct replacement.
*/
@Deprecated
- public static boolean setProcessDefaultNetwork(Network network) {
+ public static boolean setProcessDefaultNetwork(@Nullable Network network) {
int netId = (network == null) ? NETID_UNSET : network.netId;
if (netId == NetworkUtils.getBoundNetworkForProcess()) {
return true;
@@ -3820,6 +3839,7 @@
*
* @return {@code Network} to which this process is bound, or {@code null}.
*/
+ @Nullable
public Network getBoundNetworkForProcess() {
// Forcing callers to call thru non-static function ensures ConnectivityManager
// instantiated.
@@ -3836,6 +3856,7 @@
* {@code getBoundNetworkForProcess} is a direct replacement.
*/
@Deprecated
+ @Nullable
public static Network getProcessDefaultNetwork() {
int netId = NetworkUtils.getBoundNetworkForProcess();
if (netId == NETID_UNSET) return null;
@@ -3962,6 +3983,7 @@
*
* @return Hash of network watchlist config file. Null if config does not exist.
*/
+ @Nullable
public byte[] getNetworkWatchlistConfigHash() {
try {
return mService.getNetworkWatchlistConfigHash();
@@ -3983,8 +4005,8 @@
* (e.g., if it is associated with the calling VPN app's tunnel) or
* {@link android.os.Process#INVALID_UID} if the connection is not found.
*/
- public int getConnectionOwnerUid(int protocol, InetSocketAddress local,
- InetSocketAddress remote) {
+ public int getConnectionOwnerUid(int protocol, @NonNull InetSocketAddress local,
+ @NonNull InetSocketAddress remote) {
ConnectionInfo connectionInfo = new ConnectionInfo(protocol, local, remote);
try {
return mService.getConnectionOwnerUid(connectionInfo);
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/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/DhcpServingParamsParcel.aidl b/core/java/android/net/dhcp/DhcpServingParamsParcel.aidl
new file mode 100644
index 0000000..7b8b9ee
--- /dev/null
+++ b/core/java/android/net/dhcp/DhcpServingParamsParcel.aidl
@@ -0,0 +1,30 @@
+/**
+ *
+ * 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;
+
+parcelable DhcpServingParamsParcel {
+ int serverAddr;
+ int serverAddrPrefixLength;
+ int[] defaultRouters;
+ int[] dnsServers;
+ int[] excludedAddrs;
+ long dhcpLeaseTimeSecs;
+ int linkMtu;
+ boolean metered;
+}
+
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/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index d979309..4c0ee6f 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -353,26 +353,6 @@
}
/**
- * 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.
@@ -450,12 +430,6 @@
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;
diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java
index 6ea155f..9801406 100644
--- a/core/java/android/os/Trace.java
+++ b/core/java/android/os/Trace.java
@@ -95,6 +95,8 @@
public static final long TRACE_TAG_AIDL = 1L << 24;
/** @hide */
public static final long TRACE_TAG_NNAPI = 1L << 25;
+ /** @hide */
+ public static final long TRACE_TAG_RRO = 1L << 26;
private static final long TRACE_TAG_NOT_READY = 1L << 63;
private static final int MAX_SECTION_NAME_LEN = 127;
diff --git a/core/java/android/os/UpdateEngine.java b/core/java/android/os/UpdateEngine.java
index 8f2826c..1df3dad 100644
--- a/core/java/android/os/UpdateEngine.java
+++ b/core/java/android/os/UpdateEngine.java
@@ -66,6 +66,7 @@
public static final int PAYLOAD_HASH_MISMATCH_ERROR = 10;
public static final int PAYLOAD_SIZE_MISMATCH_ERROR = 11;
public static final int DOWNLOAD_PAYLOAD_VERIFICATION_ERROR = 12;
+ public static final int PAYLOAD_TIMESTAMP_ERROR = 51;
public static final int UPDATED_BUT_NOT_ACTIVE = 52;
}
diff --git a/core/java/android/permission/IPermissionController.aidl b/core/java/android/permission/IPermissionController.aidl
index 38951d5..0e18b44 100644
--- a/core/java/android/permission/IPermissionController.aidl
+++ b/core/java/android/permission/IPermissionController.aidl
@@ -17,6 +17,7 @@
package android.permission;
import android.os.RemoteCallback;
+import android.os.Bundle;
/**
* Interface for system apps to communication with the permission controller.
@@ -24,6 +25,8 @@
* @hide
*/
oneway interface IPermissionController {
+ void revokeRuntimePermissions(in Bundle request, boolean doDryRun, int reason,
+ String callerPackageName, in RemoteCallback callback);
void getAppPermissions(String packageName, in RemoteCallback callback);
void revokeRuntimePermission(String packageName, String permissionName);
void countPermissionApps(in List<String> permissionNames, boolean countOnlyGranted,
diff --git a/core/java/android/permission/PermissionControllerManager.java b/core/java/android/permission/PermissionControllerManager.java
index 66e8666..e21a660 100644
--- a/core/java/android/permission/PermissionControllerManager.java
+++ b/core/java/android/permission/PermissionControllerManager.java
@@ -22,46 +22,97 @@
import static com.android.internal.util.Preconditions.checkNotNull;
import android.Manifest;
+import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
import android.annotation.SystemService;
+import android.annotation.TestApi;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.os.Binder;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.util.ArrayMap;
import android.util.Log;
import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService;
import com.android.internal.infra.AbstractRemoteService;
+import com.android.internal.util.Preconditions;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executor;
/**
- * Interface for communicating with the permission controller from system apps. All UI operations
- * regarding permissions and any changes to the permission state should flow through this
- * interface.
+ * Interface for communicating with the permission controller.
*
* @hide
*/
+@TestApi
+@SystemApi
@SystemService(Context.PERMISSION_CONTROLLER_SERVICE)
public final class PermissionControllerManager {
private static final String TAG = PermissionControllerManager.class.getSimpleName();
/**
* The key for retrieving the result from the returned bundle.
+ *
+ * @hide
*/
public static final String KEY_RESULT =
"android.permission.PermissionControllerManager.key.result";
+ /** @hide */
+ @IntDef(prefix = { "REASON_" }, value = {
+ REASON_MALWARE,
+ REASON_INSTALLER_POLICY_VIOLATION,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Reason {}
+
+ /** The permissions are revoked because the apps holding the permissions are malware */
+ public static final int REASON_MALWARE = 1;
+
+ /**
+ * The permissions are revoked because the apps holding the permissions violate a policy of the
+ * app that installed it.
+ *
+ * <p>If this reason is used only permissions of apps that are installed by the caller of the
+ * API can be revoked.
+ */
+ public static final int REASON_INSTALLER_POLICY_VIOLATION = 2;
+
+ /**
+ * Callback for delivering the result of {@link #revokeRuntimePermissions}.
+ */
+ public abstract static class OnRevokeRuntimePermissionsCallback {
+ /**
+ * The result for {@link #revokeRuntimePermissions}.
+ *
+ * @param revoked The actually revoked permissions as
+ * {@code Map<packageName, List<permission>>}
+ */
+ public abstract void onRevokeRuntimePermissions(@NonNull Map<String, List<String>> revoked);
+ }
+
/**
* Callback for delivering the result of {@link #getAppPermissions}.
+ *
+ * @hide
*/
public interface OnGetAppPermissionResultCallback {
/**
@@ -75,6 +126,8 @@
/**
* Callback for delivering the result of {@link #countPermissionApps}.
+ *
+ * @hide
*/
public interface OnCountPermissionAppsResultCallback {
/**
@@ -86,23 +139,61 @@
void onCountPermissionApps(int numApps);
}
+ private final @NonNull Context mContext;
private final RemoteService mRemoteService;
+ /** @hide */
public PermissionControllerManager(@NonNull Context context) {
Intent intent = new Intent(SERVICE_INTERFACE);
intent.setPackage(context.getPackageManager().getPermissionControllerPackageName());
ResolveInfo serviceInfo = context.getPackageManager().resolveService(intent, 0);
+ mContext = context;
mRemoteService = new RemoteService(context,
serviceInfo.getComponentInfo().getComponentName());
}
/**
+ * Revoke a set of runtime permissions for various apps.
+ *
+ * @param request The permissions to revoke as {@code Map<packageName, List<permission>>}
+ * @param doDryRun Compute the permissions that would be revoked, but not actually revoke them
+ * @param reason Why the permission should be revoked
+ * @param executor Executor on which to invoke the callback
+ * @param callback Callback to receive the result
+ */
+ @RequiresPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS)
+ public void revokeRuntimePermissions(@NonNull Map<String, List<String>> request,
+ boolean doDryRun, @Reason int reason, @NonNull @CallbackExecutor Executor executor,
+ @NonNull OnRevokeRuntimePermissionsCallback callback) {
+ // Check input to fail immediately instead of inside the async request
+ checkNotNull(executor);
+ checkNotNull(callback);
+ checkNotNull(request);
+ for (Map.Entry<String, List<String>> appRequest : request.entrySet()) {
+ checkNotNull(appRequest.getKey());
+ checkCollectionElementsNotNull(appRequest.getValue(), "permissions");
+ }
+
+ // Check required permission to fail immediately instead of inside the oneway binder call
+ if (mContext.checkSelfPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS
+ + " required");
+ }
+
+ mRemoteService.scheduleRequest(new PendingRevokeRuntimePermissionRequest(mRemoteService,
+ request, doDryRun, reason, mContext.getPackageName(), executor, callback));
+ }
+
+ /**
* Gets the runtime permissions for an app.
*
* @param packageName The package for which to query.
* @param callback Callback to receive the result.
* @param handler Handler on which to invoke the callback.
+ *
+ * @hide
*/
@RequiresPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS)
public void getAppPermissions(@NonNull String packageName,
@@ -119,6 +210,8 @@
*
* @param packageName The package for which to revoke
* @param permissionName The permission to revoke
+ *
+ * @hide
*/
@RequiresPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS)
public void revokeRuntimePermission(@NonNull String packageName,
@@ -138,6 +231,8 @@
* @param countSystem Also count system apps
* @param callback Callback to receive the result
* @param handler Handler on which to invoke the callback
+ *
+ * @hide
*/
@RequiresPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS)
public void countPermissionApps(@NonNull List<String> permissionNames,
@@ -206,6 +301,84 @@
}
/**
+ * Request for {@link #revokeRuntimePermissions}
+ */
+ private static final class PendingRevokeRuntimePermissionRequest extends
+ AbstractRemoteService.PendingRequest<RemoteService, IPermissionController> {
+ private final @NonNull Map<String, List<String>> mRequest;
+ private final boolean mDoDryRun;
+ private final int mReason;
+ private final @NonNull String mCallingPackage;
+ private final @NonNull OnRevokeRuntimePermissionsCallback mCallback;
+
+ private final @NonNull RemoteCallback mRemoteCallback;
+
+ private PendingRevokeRuntimePermissionRequest(@NonNull RemoteService service,
+ @NonNull Map<String, List<String>> request, boolean doDryRun,
+ @Reason int reason, @NonNull String callingPackage,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull OnRevokeRuntimePermissionsCallback callback) {
+ super(service);
+
+ mRequest = request;
+ mDoDryRun = doDryRun;
+ mReason = reason;
+ mCallingPackage = callingPackage;
+ mCallback = callback;
+
+ mRemoteCallback = new RemoteCallback(result -> executor.execute(() -> {
+ long token = Binder.clearCallingIdentity();
+ try {
+ Map<String, List<String>> revoked = new ArrayMap<>();
+ try {
+ Bundle bundleizedRevoked = result.getBundle(KEY_RESULT);
+
+ for (String packageName : bundleizedRevoked.keySet()) {
+ Preconditions.checkNotNull(packageName);
+
+ ArrayList<String> permissions =
+ bundleizedRevoked.getStringArrayList(packageName);
+ Preconditions.checkCollectionElementsNotNull(permissions,
+ "permissions");
+
+ revoked.put(packageName, permissions);
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Could not read result when revoking runtime permissions", e);
+ }
+
+ callback.onRevokeRuntimePermissions(revoked);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+
+ finish();
+ }
+ }), null);
+ }
+
+ @Override
+ protected void onTimeout(RemoteService remoteService) {
+ mCallback.onRevokeRuntimePermissions(Collections.emptyMap());
+ }
+
+ @Override
+ public void run() {
+ Bundle bundledizedRequest = new Bundle();
+ for (Map.Entry<String, List<String>> appRequest : mRequest.entrySet()) {
+ bundledizedRequest.putStringArrayList(appRequest.getKey(),
+ new ArrayList<>(appRequest.getValue()));
+ }
+
+ try {
+ getService().getServiceInterface().revokeRuntimePermissions(bundledizedRequest,
+ mDoDryRun, mReason, mCallingPackage, mRemoteCallback);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error revoking runtime permission", e);
+ }
+ }
+ }
+
+ /**
* Request for {@link #getAppPermissions}
*/
private static final class PendingGetAppPermissionRequest extends
diff --git a/core/java/android/permission/PermissionControllerService.java b/core/java/android/permission/PermissionControllerService.java
index 5dad071..f621737 100644
--- a/core/java/android/permission/PermissionControllerService.java
+++ b/core/java/android/permission/PermissionControllerService.java
@@ -16,6 +16,7 @@
package android.permission;
+import static com.android.internal.util.Preconditions.checkArgument;
import static com.android.internal.util.Preconditions.checkCollectionElementsNotNull;
import static com.android.internal.util.Preconditions.checkNotNull;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
@@ -26,12 +27,19 @@
import android.app.Service;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteCallback;
+import android.util.ArrayMap;
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
/**
* This service is meant to be implemented by the app controlling permissions.
@@ -60,6 +68,20 @@
}
/**
+ * Revoke a set of runtime permissions for various apps.
+ *
+ * @param requests The permissions to revoke as {@code Map<packageName, List<permission>>}
+ * @param doDryRun Compute the permissions that would be revoked, but not actually revoke them
+ * @param reason Why the permission should be revoked
+ * @param callerPackageName The package name of the calling app
+ *
+ * @return the actually removed permissions as {@code Map<packageName, List<permission>>}
+ */
+ public abstract @NonNull Map<String, List<String>> onRevokeRuntimePermissions(
+ @NonNull Map<String, List<String>> requests, boolean doDryRun,
+ @PermissionControllerManager.Reason int reason, @NonNull String callerPackageName);
+
+ /**
* Gets the runtime permissions for an app.
*
* @param packageName The package for which to query.
@@ -94,6 +116,41 @@
public final IBinder onBind(Intent intent) {
return new IPermissionController.Stub() {
@Override
+ public void revokeRuntimePermissions(
+ Bundle bundleizedRequest, boolean doDryRun, int reason,
+ String callerPackageName, RemoteCallback callback) {
+ checkNotNull(bundleizedRequest, "bundleizedRequest");
+ checkNotNull(callerPackageName);
+ checkNotNull(callback);
+
+ Map<String, List<String>> request = new ArrayMap<>();
+ for (String packageName : bundleizedRequest.keySet()) {
+ Preconditions.checkNotNull(packageName);
+
+ ArrayList<String> permissions =
+ bundleizedRequest.getStringArrayList(packageName);
+ Preconditions.checkCollectionElementsNotNull(permissions, "permissions");
+
+ request.put(packageName, permissions);
+ }
+
+ enforceCallingPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, null);
+
+ // Verify callerPackageName
+ try {
+ PackageInfo pkgInfo = getPackageManager().getPackageInfo(callerPackageName, 0);
+ checkArgument(getCallingUid() == pkgInfo.applicationInfo.uid);
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+
+ mHandler.sendMessage(obtainMessage(
+ PermissionControllerService::revokeRuntimePermissions,
+ PermissionControllerService.this, request, doDryRun, reason,
+ callerPackageName, callback));
+ }
+
+ @Override
public void getAppPermissions(String packageName, RemoteCallback callback) {
checkNotNull(packageName, "packageName");
checkNotNull(callback, "callback");
@@ -133,6 +190,27 @@
};
}
+ private void revokeRuntimePermissions(@NonNull Map<String, List<String>> requests,
+ boolean doDryRun, @PermissionControllerManager.Reason int reason,
+ @NonNull String callerPackageName, @NonNull RemoteCallback callback) {
+ Map<String, List<String>> revoked = onRevokeRuntimePermissions(requests,
+ doDryRun, reason, callerPackageName);
+
+ checkNotNull(revoked);
+ Bundle bundledizedRevoked = new Bundle();
+ for (Map.Entry<String, List<String>> appRevocation : revoked.entrySet()) {
+ checkNotNull(appRevocation.getKey());
+ checkCollectionElementsNotNull(appRevocation.getValue(), "permissions");
+
+ bundledizedRevoked.putStringArrayList(appRevocation.getKey(),
+ new ArrayList<>(appRevocation.getValue()));
+ }
+
+ Bundle result = new Bundle();
+ result.putBundle(PermissionControllerManager.KEY_RESULT, bundledizedRevoked);
+ callback.sendResult(result);
+ }
+
private void getAppPermissions(@NonNull String packageName, @NonNull RemoteCallback callback) {
List<RuntimePermissionPresentationInfo> permissions = onGetAppPermissions(packageName);
if (permissions != null && !permissions.isEmpty()) {
diff --git a/core/java/android/permission/TEST_MAPPING b/core/java/android/permission/TEST_MAPPING
new file mode 100644
index 0000000..ba9f36a
--- /dev/null
+++ b/core/java/android/permission/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsPermissionTestCases",
+ "options": [
+ {
+ "include-filter": "android.permission.cts.PermissionControllerTest"
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index bb1784a..5bb5ee8 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6207,7 +6207,7 @@
public static final String CHARGING_SOUNDS_ENABLED = "charging_sounds_enabled";
/**
- * Whether to vibrate for wireless charging events.
+ * Whether to vibrate for charging events.
* @hide
*/
public static final String CHARGING_VIBRATION_ENABLED = "charging_vibration_enabled";
@@ -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.
@@ -9415,7 +9423,8 @@
"hdmi_control_auto_wakeup_enabled";
/**
- * Whether TV will also turn off other CEC devices when it goes to standby mode.
+ * Whether TV or Audio System will also turn off other CEC devices when it goes to standby
+ * mode.
* (0 = false, 1 = true)
*
* @hide
@@ -9424,6 +9433,15 @@
"hdmi_control_auto_device_off_enabled";
/**
+ * Whether Audio System will also turn off TV when it goes to standby mode.
+ * (0 = false, 1 = true)
+ *
+ * @hide
+ */
+ public static final String HDMI_CONTROL_AUTO_TV_OFF_ENABLED =
+ "hdmi_control_auto_tv_off_enabled";
+
+ /**
* If <b>true</b>, enables out-of-the-box execution for priv apps.
* Default: false
* Values: 0 = false, 1 = true
diff --git a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
index 7683b8a..a9f4034 100644
--- a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
+++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
@@ -214,7 +214,7 @@
}
@Override
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ protected final void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mAutofillProxies != null) {
final int size = mAutofillProxies.size();
pw.print("Number proxies: "); pw.println(size);
@@ -225,6 +225,15 @@
proxy.dump(" ", pw);
}
}
+ dump(pw, args);
+ }
+
+ /**
+ * Implementation specific {@code dump}.
+ */
+ protected void dump(@NonNull PrintWriter pw,
+ @SuppressWarnings("unused") @NonNull String[] args) {
+ pw.print(getClass().getName()); pw.println(": nothing to dump");
}
/** @hide */
diff --git a/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java b/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java
index 784e719..db242a2 100644
--- a/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java
+++ b/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java
@@ -34,7 +34,7 @@
@SystemApi
@Deprecated
public final class ContentCaptureEventsRequest implements Parcelable {
-// TODO(b/121033016): remove .java and .aidl once service implementation doesn't use it anymore
+// TODO(b/121051220): remove .java and .aidl once service implementation doesn't use it anymore
private final ContentCaptureEvent mEvent;
diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java
index c9d46dd..26bf361 100644
--- a/core/java/android/service/contentcapture/ContentCaptureService.java
+++ b/core/java/android/service/contentcapture/ContentCaptureService.java
@@ -123,12 +123,12 @@
};
/**
- * List of sessions per UID.
+ * UIDs associated with each session.
*
* <p>This map is populated when an session is started, which is called by the system server
* and can be trusted. Then subsequent calls made by the app are verified against this map.
*/
- private final ArrayMap<String, Integer> mSessionsByUid = new ArrayMap<>();
+ private final ArrayMap<String, Integer> mSessionUids = new ArrayMap<>();
@CallSuper
@Override
@@ -285,13 +285,13 @@
@Override
@CallSuper
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- final int size = mSessionsByUid.size();
+ final int size = mSessionUids.size();
pw.print("Number sessions: "); pw.println(size);
if (size > 0) {
final String prefix = " ";
for (int i = 0; i < size; i++) {
- pw.print(prefix); pw.print(mSessionsByUid.keyAt(i));
- pw.print(": uid="); pw.println(mSessionsByUid.valueAt(i));
+ pw.print(prefix); pw.print(mSessionUids.keyAt(i));
+ pw.print(": uid="); pw.println(mSessionUids.valueAt(i));
}
}
}
@@ -309,7 +309,7 @@
private void handleOnCreateSession(@NonNull ContentCaptureContext context,
@NonNull String sessionId, int uid, IResultReceiver clientReceiver) {
- mSessionsByUid.put(sessionId, uid);
+ mSessionUids.put(sessionId, uid);
onCreateContentCaptureSession(context, new ContentCaptureSessionId(sessionId));
setClientState(clientReceiver, ContentCaptureSession.STATE_ACTIVE,
mClientInterface.asBinder());
@@ -336,11 +336,11 @@
case ContentCaptureEvent.TYPE_SESSION_STARTED:
final ContentCaptureContext clientContext = event.getClientContext();
clientContext.setParentSessionId(event.getParentSessionId());
- mSessionsByUid.put(sessionIdString, uid);
+ mSessionUids.put(sessionIdString, uid);
onCreateContentCaptureSession(clientContext, sessionId);
break;
case ContentCaptureEvent.TYPE_SESSION_FINISHED:
- mSessionsByUid.remove(sessionIdString);
+ mSessionUids.remove(sessionIdString);
onDestroyContentCaptureSession(sessionId);
break;
default:
@@ -355,7 +355,7 @@
}
private void handleFinishSession(@NonNull String sessionId) {
- mSessionsByUid.remove(sessionId);
+ mSessionUids.remove(sessionId);
onDestroyContentCaptureSession(new ContentCaptureSessionId(sessionId));
}
@@ -372,10 +372,13 @@
default:
sessionId = event.getSessionId();
}
- final Integer rightUid = mSessionsByUid.get(sessionId);
+ final Integer rightUid = mSessionUids.get(sessionId);
if (rightUid == null) {
- if (VERBOSE) Log.v(TAG, "No session for " + sessionId);
- // Just ignore, as the session could have finished
+ if (DEBUG) {
+ Log.d(TAG, "handleIsRightCallerFor(" + event + "): no session for " + sessionId
+ + ": " + mSessionUids);
+ }
+ // Just ignore, as the session could have been finished already
return false;
}
if (rightUid != uid) {
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/wallpaper/IWallpaperConnection.aidl b/core/java/android/service/wallpaper/IWallpaperConnection.aidl
index a976d0e..f334d9d 100644
--- a/core/java/android/service/wallpaper/IWallpaperConnection.aidl
+++ b/core/java/android/service/wallpaper/IWallpaperConnection.aidl
@@ -27,5 +27,5 @@
void attachEngine(IWallpaperEngine engine, int displayId);
void engineShown(IWallpaperEngine engine);
ParcelFileDescriptor setWallpaper(String name);
- void onWallpaperColorsChanged(in WallpaperColors colors);
+ void onWallpaperColorsChanged(in WallpaperColors colors, int displayId);
}
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index a011d67..0d7223d 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -56,6 +56,7 @@
import android.view.InputEventReceiver;
import android.view.InsetsState;
import android.view.MotionEvent;
+import android.view.SurfaceControl;
import android.view.SurfaceHolder;
import android.view.View;
import android.view.ViewGroup;
@@ -217,6 +218,8 @@
private Context mDisplayContext;
private int mDisplayState;
+ SurfaceControl mSurfaceControl = new SurfaceControl();
+
final BaseSurfaceHolder mSurfaceHolder = new BaseSurfaceHolder() {
{
mRequestedFormat = PixelFormat.RGBX_8888;
@@ -642,7 +645,7 @@
try {
final WallpaperColors newColors = onComputeColors();
if (mConnection != null) {
- mConnection.onWallpaperColorsChanged(newColors);
+ mConnection.onWallpaperColorsChanged(newColors, mDisplay.getDisplayId());
} else {
Log.w(TAG, "Can't notify system because wallpaper connection "
+ "was not established.");
@@ -843,8 +846,12 @@
mWindow, mWindow.mSeq, mLayout, mWidth, mHeight,
View.VISIBLE, 0, -1, mWinFrame, mOverscanInsets, mContentInsets,
mVisibleInsets, mStableInsets, mOutsets, mBackdropFrame,
- mDisplayCutout, mMergedConfiguration, mSurfaceHolder.mSurface,
+ mDisplayCutout, mMergedConfiguration, mSurfaceControl,
mInsetsState);
+ if (mSurfaceControl.isValid()) {
+ mSurfaceHolder.mSurface.copyFrom(mSurfaceControl);
+ mSurfaceControl.release();
+ }
if (DEBUG) Log.v(TAG, "New surface: " + mSurfaceHolder.mSurface
+ ", frame=" + mWinFrame);
@@ -963,7 +970,7 @@
mFinalSystemInsets.set(mDispatchedOverscanInsets);
mFinalStableInsets.set(mDispatchedStableInsets);
WindowInsets insets = new WindowInsets(mFinalSystemInsets,
- null, mFinalStableInsets,
+ mFinalStableInsets,
getResources().getConfiguration().isScreenRound(), false,
mDispatchedDisplayCutout);
if (DEBUG) {
@@ -1466,7 +1473,7 @@
break;
}
try {
- mConnection.onWallpaperColorsChanged(mEngine.onComputeColors());
+ mConnection.onWallpaperColorsChanged(mEngine.onComputeColors(), mDisplayId);
} catch (RemoteException e) {
// Connection went away, nothing to do in here.
}
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/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index d1115c7..658f06a 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -99,7 +99,7 @@
out Rect outContentInsets, out Rect outVisibleInsets, out Rect outStableInsets,
out Rect outOutsets, out Rect outBackdropFrame,
out DisplayCutout.ParcelableWrapper displayCutout,
- out MergedConfiguration outMergedConfiguration, out Surface outSurface,
+ out MergedConfiguration outMergedConfiguration, out SurfaceControl outSurfaceControl,
out InsetsState insetsState);
/*
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 1a708e8..0b6beba 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -67,6 +67,7 @@
int w, int h, int format, int flags, long parentObject, int windowType, int ownerUid)
throws OutOfResourcesException;
private static native long nativeReadFromParcel(Parcel in);
+ private static native long nativeCopyFromSurfaceControl(long nativeObject);
private static native void nativeWriteToParcel(long nativeObject, Parcel out);
private static native void nativeRelease(long nativeObject);
private static native void nativeDestroy(long nativeObject);
@@ -169,7 +170,7 @@
IBinder toToken);
private final CloseGuard mCloseGuard = CloseGuard.get();
- private final String mName;
+ private String mName;
long mNativeObject; // package visibility only for Surface.java access
// TODO: Move this to native.
@@ -359,6 +360,13 @@
*/
public static final int WINDOW_TYPE_DONT_SCREENSHOT = 441731;
+ public void copyFrom(SurfaceControl other) {
+ mName = other.mName;
+ mWidth = other.mWidth;
+ mHeight = other.mHeight;
+ mNativeObject = nativeCopyFromSurfaceControl(other.mNativeObject);
+ }
+
/**
* Builder class for {@link SurfaceControl} objects.
*/
@@ -660,14 +668,29 @@
}
private SurfaceControl(Parcel in) {
+ readFromParcel(in);
+ mCloseGuard.open("release");
+ }
+
+ public SurfaceControl() {
+ mCloseGuard.open("release");
+ }
+
+ public void readFromParcel(Parcel in) {
+ if (in == null) {
+ throw new IllegalArgumentException("source must not be null");
+ }
+
mName = in.readString();
mWidth = in.readInt();
mHeight = in.readInt();
- mNativeObject = nativeReadFromParcel(in);
- if (mNativeObject == 0) {
- throw new IllegalArgumentException("Couldn't read SurfaceControl from parcel=" + in);
+
+ release();
+ if (in.readInt() != 0) {
+ mNativeObject = nativeReadFromParcel(in);
+ } else {
+ mNativeObject = 0;
}
- mCloseGuard.open("release");
}
@Override
@@ -680,7 +703,16 @@
dest.writeString(mName);
dest.writeInt(mWidth);
dest.writeInt(mHeight);
+ if (mNativeObject == 0) {
+ dest.writeInt(0);
+ } else {
+ dest.writeInt(1);
+ }
nativeWriteToParcel(mNativeObject, dest);
+
+ if ((flags & Parcelable.PARCELABLE_WRITE_RETURN_VALUE) != 0) {
+ release();
+ }
}
/**
@@ -763,6 +795,10 @@
"mNativeObject is null. Have you called release() already?");
}
+ public boolean isValid() {
+ return mNativeObject != 0;
+ }
+
/*
* set surface parameters.
* needs to be inside open/closeTransaction block
diff --git a/core/java/android/view/SurfaceSession.java b/core/java/android/view/SurfaceSession.java
index a4fa12a..361ac93 100644
--- a/core/java/android/view/SurfaceSession.java
+++ b/core/java/android/view/SurfaceSession.java
@@ -30,7 +30,6 @@
private long mNativeClient; // SurfaceComposerClient*
private static native long nativeCreate();
- private static native long nativeCreateScoped(long surfacePtr);
private static native void nativeDestroy(long ptr);
private static native void nativeKill(long ptr);
@@ -40,15 +39,6 @@
mNativeClient = nativeCreate();
}
- public SurfaceSession(Surface root) {
- synchronized (root.mLock) {
- if (root.mNativeObject == 0) {
- throw new IllegalStateException("Surface is not initialized or has been released");
- }
- mNativeClient = nativeCreateScoped(root.mNativeObject);
- }
- }
-
/* no user serviceable parts here ... */
@Override
protected void finalize() throws Throwable {
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index a0af83d..61fb00d 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -547,7 +547,7 @@
if (creating) {
viewRoot.createBoundsSurface(mSubLayer);
- mSurfaceSession = new SurfaceSession(viewRoot.mBoundsSurface);
+ mSurfaceSession = new SurfaceSession();
mDeferredDestroySurfaceControl = mSurfaceControl;
updateOpaqueFlag();
@@ -559,6 +559,7 @@
new SurfaceControl.Builder(mSurfaceSession)
.setBufferSize(mSurfaceWidth, mSurfaceHeight)
.setFormat(mFormat)
+ .setParent(viewRoot.getSurfaceControl())
.setFlags(mSurfaceFlags));
} else if (mSurfaceControl == null) {
return;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 6b07efc..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;
}
}
@@ -7660,10 +7683,13 @@
/**
* Convenience method for sending a {@link AccessibilityEvent#TYPE_ANNOUNCEMENT}
- * {@link AccessibilityEvent} to make an announcement which is related to some
- * sort of a context change for which none of the events representing UI transitions
- * is a good fit. For example, announcing a new page in a book. If accessibility
- * is not enabled this method does nothing.
+ * {@link AccessibilityEvent} to suggest that an accessibility service announce the
+ * specified text to its users.
+ * <p>
+ * Note: The event generated with this API carries no semantic meaning, and is appropriate only
+ * in exceptional situations. Apps can generally achieve correct behavior for accessibility by
+ * accurately supplying the semantics of their UI.
+ * They should not need to specify what exactly is announced to users.
*
* @param text The announcement text.
*/
@@ -25053,9 +25079,10 @@
}
final ViewRootImpl root = mAttachInfo.mViewRootImpl;
- final SurfaceSession session = new SurfaceSession(root.mSurface);
+ final SurfaceSession session = new SurfaceSession();
final SurfaceControl surfaceControl = new SurfaceControl.Builder(session)
.setName("drag surface")
+ .setParent(root.getSurfaceControl())
.setBufferSize(shadowSize.x, shadowSize.y)
.setFormat(PixelFormat.TRANSLUCENT)
.build();
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 3f7a512..8e4dc67 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -432,6 +432,7 @@
// Surface can never be reassigned or cleared (use Surface.clear()).
@UnsupportedAppUsage
public final Surface mSurface = new Surface();
+ private final SurfaceControl mSurfaceControl = new SurfaceControl();
/**
* Child surface of {@code mSurface} with the same bounds as its parent, and crop bounds
@@ -1526,7 +1527,7 @@
*/
public void createBoundsSurface(int zOrderLayer) {
if (mSurfaceSession == null) {
- mSurfaceSession = new SurfaceSession(mSurface);
+ mSurfaceSession = new SurfaceSession();
}
if (mBoundsSurfaceControl != null && mBoundsSurface.isValid()) {
return; // surface control for bounds surface already exists.
@@ -1534,6 +1535,7 @@
mBoundsSurfaceControl = new SurfaceControl.Builder(mSurfaceSession)
.setName("Bounds for - " + getTitle().toString())
+ .setParent(mSurfaceControl)
.build();
setBoundsSurfaceCrop();
@@ -1567,6 +1569,8 @@
private void destroySurface() {
mSurface.release();
+ mSurfaceControl.release();
+
mSurfaceSession = null;
if (mBoundsSurfaceControl != null) {
@@ -1855,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);
}
@@ -6801,7 +6804,12 @@
insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
mTmpFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingDisplayCutout,
- mPendingMergedConfiguration, mSurface, mTempInsets);
+ mPendingMergedConfiguration, mSurfaceControl, mTempInsets);
+ if (mSurfaceControl.isValid()) {
+ mSurface.copyFrom(mSurfaceControl);
+ } else {
+ destroySurface();
+ }
mPendingAlwaysConsumeNavBar =
(relayoutResult & WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_NAV_BAR) != 0;
@@ -8483,6 +8491,10 @@
mActivityRelaunched = true;
}
+ public SurfaceControl getSurfaceControl() {
+ return mSurfaceControl;
+ }
+
/**
* Class for managing the accessibility interaction connection
* based on the global accessibility state.
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/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 56f973e..90ccc25 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -62,6 +62,7 @@
import com.android.internal.os.IResultReceiver;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.Preconditions;
+import com.android.internal.util.SyncResultReceiver;
import org.xmlpull.v1.XmlPullParserException;
@@ -75,8 +76,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Objects;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
+
//TODO: use java.lang.ref.Cleaner once Android supports Java 9
import sun.misc.Cleaner;
@@ -324,6 +324,11 @@
public static final int FC_SERVICE_TIMEOUT = 5000;
/**
+ * Timeout for calls to system_server.
+ */
+ private static final int SYNC_CALLS_TIMEOUT_MS = 5000;
+
+ /**
* Makes an authentication id from a request id and a dataset id.
*
* @param requestId The request id.
@@ -612,7 +617,8 @@
final AutofillClient client = getClient();
if (client != null) {
- final SyncResultReceiver receiver = new SyncResultReceiver();
+ final SyncResultReceiver receiver = new SyncResultReceiver(
+ SYNC_CALLS_TIMEOUT_MS);
try {
mService.restoreSession(mSessionId, client.autofillClientGetActivityToken(),
mServiceClient.asBinder(), receiver);
@@ -732,9 +738,9 @@
*/
@Nullable public FillEventHistory getFillEventHistory() {
try {
- final SyncResultReceiver receiver = new SyncResultReceiver();
+ final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
mService.getFillEventHistory(receiver);
- return receiver.getObjectResult(SyncResultReceiver.TYPE_PARCELABLE);
+ return receiver.getParcelableResult();
} catch (RemoteException e) {
e.rethrowFromSystemServer();
return null;
@@ -1287,7 +1293,7 @@
public boolean hasEnabledAutofillServices() {
if (mService == null) return false;
- final SyncResultReceiver receiver = new SyncResultReceiver();
+ final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
try {
mService.isServiceEnabled(mContext.getUserId(), mContext.getPackageName(), receiver);
return receiver.getIntResult() == 1;
@@ -1304,10 +1310,10 @@
public ComponentName getAutofillServiceComponentName() {
if (mService == null) return null;
- final SyncResultReceiver receiver = new SyncResultReceiver();
+ final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
try {
mService.getAutofillServiceComponentName(receiver);
- return receiver.getObjectResult(SyncResultReceiver.TYPE_PARCELABLE);
+ return receiver.getParcelableResult();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1330,9 +1336,9 @@
*/
@Nullable public String getUserDataId() {
try {
- final SyncResultReceiver receiver = new SyncResultReceiver();
+ final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
mService.getUserDataId(receiver);
- return receiver.getObjectResult(SyncResultReceiver.TYPE_STRING);
+ return receiver.getStringResult();
} catch (RemoteException e) {
e.rethrowFromSystemServer();
return null;
@@ -1352,9 +1358,9 @@
*/
@Nullable public UserData getUserData() {
try {
- final SyncResultReceiver receiver = new SyncResultReceiver();
+ final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
mService.getUserData(receiver);
- return receiver.getObjectResult(SyncResultReceiver.TYPE_PARCELABLE);
+ return receiver.getParcelableResult();
} catch (RemoteException e) {
e.rethrowFromSystemServer();
return null;
@@ -1390,7 +1396,7 @@
* the user.
*/
public boolean isFieldClassificationEnabled() {
- final SyncResultReceiver receiver = new SyncResultReceiver();
+ final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
try {
mService.isFieldClassificationEnabled(receiver);
return receiver.getIntResult() == 1;
@@ -1413,10 +1419,10 @@
*/
@Nullable
public String getDefaultFieldClassificationAlgorithm() {
- final SyncResultReceiver receiver = new SyncResultReceiver();
+ final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
try {
mService.getDefaultFieldClassificationAlgorithm(receiver);
- return receiver.getObjectResult(SyncResultReceiver.TYPE_STRING);
+ return receiver.getStringResult();
} catch (RemoteException e) {
e.rethrowFromSystemServer();
return null;
@@ -1433,11 +1439,10 @@
*/
@NonNull
public List<String> getAvailableFieldClassificationAlgorithms() {
- final SyncResultReceiver receiver = new SyncResultReceiver();
+ final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
try {
mService.getAvailableFieldClassificationAlgorithms(receiver);
- final String[] algorithms = receiver
- .getObjectResult(SyncResultReceiver.TYPE_STRING_ARRAY);
+ final String[] algorithms = receiver.getStringArrayResult();
return algorithms != null ? Arrays.asList(algorithms) : Collections.emptyList();
} catch (RemoteException e) {
e.rethrowFromSystemServer();
@@ -1458,7 +1463,7 @@
public boolean isAutofillSupported() {
if (mService == null) return false;
- final SyncResultReceiver receiver = new SyncResultReceiver();
+ final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
try {
mService.isServiceSupported(mContext.getUserId(), receiver);
return receiver.getIntResult() == 1;
@@ -1582,7 +1587,7 @@
final AutofillClient client = getClient();
if (client == null) return; // NOTE: getClient() already logged it..
- final SyncResultReceiver receiver = new SyncResultReceiver();
+ final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
mService.startSession(client.autofillClientGetActivityToken(),
mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
mCallback != null, flags, client.autofillClientGetComponentName(),
@@ -1665,7 +1670,7 @@
mServiceClient = new AutofillManagerClient(this);
try {
final int userId = mContext.getUserId();
- final SyncResultReceiver receiver = new SyncResultReceiver();
+ final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
mService.addClient(mServiceClient, userId, receiver);
final int flags = receiver.getIntResult();
mEnabled = (flags & FLAG_ADD_CLIENT_ENABLED) != 0;
@@ -2986,104 +2991,4 @@
}
}
}
-
- /**
- * @hide
- */
- public static final class SyncResultReceiver extends IResultReceiver.Stub {
-
- private static final String EXTRA = "EXTRA";
-
- /**
- * How long to block waiting for {@link IResultReceiver} callbacks when calling server.
- */
- private static final long BINDER_TIMEOUT_MS = 5000;
-
- private static final int TYPE_STRING = 0;
- private static final int TYPE_STRING_ARRAY = 1;
- private static final int TYPE_PARCELABLE = 2;
-
- private final CountDownLatch mLatch = new CountDownLatch(1);
- private int mResult;
- private Bundle mBundle;
-
- private void waitResult() {
- try {
- if (!mLatch.await(BINDER_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
- throw new IllegalStateException("Not called in " + BINDER_TIMEOUT_MS + "ms");
- }
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- }
- }
-
- /**
- * Gets the result from an operation that returns an {@code int}.
- */
- int getIntResult() {
- waitResult();
- return mResult;
- }
-
- /**
- * Gets the result from an operation that returns an {@code Object}.
- *
- * @param type type of expected object.
- */
- @Nullable
- @SuppressWarnings("unchecked")
- <T> T getObjectResult(int type) {
- waitResult();
- if (mBundle == null) {
- return null;
- }
- switch (type) {
- case TYPE_STRING:
- return (T) mBundle.getString(EXTRA);
- case TYPE_STRING_ARRAY:
- return (T) mBundle.getStringArray(EXTRA);
- case TYPE_PARCELABLE:
- return (T) mBundle.getParcelable(EXTRA);
- default:
- throw new IllegalArgumentException("unsupported type: " + type);
- }
- }
-
- @Override
- public void send(int resultCode, Bundle resultData) {
- mResult = resultCode;
- mBundle = resultData;
- mLatch.countDown();
- }
-
- /**
- * Creates a bundle for a {@code String} value.
- */
- @NonNull
- public static Bundle bundleFor(@Nullable String value) {
- final Bundle bundle = new Bundle();
- bundle.putString(EXTRA, value);
- return bundle;
- }
-
- /**
- * Creates a bundle for a {@code String[]} value.
- */
- @NonNull
- public static Bundle bundleFor(@Nullable String[] value) {
- final Bundle bundle = new Bundle();
- bundle.putStringArray(EXTRA, value);
- return bundle;
- }
-
- /**
- * Creates a bundle for a {@code Parcelable} value.
- */
- @NonNull
- public static Bundle bundleFor(@Nullable Parcelable value) {
- final Bundle bundle = new Bundle();
- bundle.putParcelable(EXTRA, value);
- return bundle;
- }
- }
}
diff --git a/core/java/android/view/contentcapture/ChildContentCaptureSession.java b/core/java/android/view/contentcapture/ChildContentCaptureSession.java
index 5166831..04e725e 100644
--- a/core/java/android/view/contentcapture/ChildContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ChildContentCaptureSession.java
@@ -33,7 +33,7 @@
final class ChildContentCaptureSession extends ContentCaptureSession {
@NonNull
- private final MainContentCaptureSession mParent;
+ private final ContentCaptureSession mParent;
/**
* {@link ContentCaptureContext} set by client, or {@code null} when it's the
@@ -46,16 +46,25 @@
private final ContentCaptureContext mClientContext;
/** @hide */
- protected ChildContentCaptureSession(@NonNull MainContentCaptureSession parent,
+ protected ChildContentCaptureSession(@NonNull ContentCaptureSession parent,
@NonNull ContentCaptureContext clientContext) {
mParent = parent;
mClientContext = Preconditions.checkNotNull(clientContext);
}
@Override
- ContentCaptureSession newChild(@NonNull ContentCaptureContext context) {
- // TODO(b/121033016): implement it
- throw new UnsupportedOperationException("grand-children not implemented yet");
+ MainContentCaptureSession getMainCaptureSession() {
+ if (mParent instanceof MainContentCaptureSession) {
+ return (MainContentCaptureSession) mParent;
+ }
+ return mParent.getMainCaptureSession();
+ }
+
+ @Override
+ ContentCaptureSession newChild(@NonNull ContentCaptureContext clientContext) {
+ final ContentCaptureSession child = new ChildContentCaptureSession(this, clientContext);
+ getMainCaptureSession().notifyChildSessionStarted(mId, child.mId, clientContext);
+ return child;
}
@Override
@@ -65,27 +74,27 @@
@Override
void onDestroy() {
- mParent.notifyChildSessionFinished(mParent.mId, mId);
+ getMainCaptureSession().notifyChildSessionFinished(mParent.mId, mId);
}
@Override
void internalNotifyViewAppeared(@NonNull ViewStructureImpl node) {
- mParent.notifyViewAppeared(mId, node);
+ getMainCaptureSession().notifyViewAppeared(mId, node);
}
@Override
void internalNotifyViewDisappeared(@NonNull AutofillId id) {
- mParent.notifyViewDisappeared(mId, id);
+ getMainCaptureSession().notifyViewDisappeared(mId, id);
}
@Override
void internalNotifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text,
int flags) {
- mParent.notifyViewTextChanged(mId, id, text, flags);
+ getMainCaptureSession().notifyViewTextChanged(mId, id, text, flags);
}
@Override
boolean isContentCaptureEnabled() {
- return mParent.isContentCaptureEnabled();
+ return getMainCaptureSession().isContentCaptureEnabled();
}
@Override
diff --git a/core/java/android/view/contentcapture/ContentCaptureEvent.java b/core/java/android/view/contentcapture/ContentCaptureEvent.java
index 9e3da92..0d06430 100644
--- a/core/java/android/view/contentcapture/ContentCaptureEvent.java
+++ b/core/java/android/view/contentcapture/ContentCaptureEvent.java
@@ -251,6 +251,10 @@
public String toString() {
final StringBuilder string = new StringBuilder("ContentCaptureEvent[type=")
.append(getTypeAsString(mType));
+ string.append(", session=").append(mSessionId);
+ if (mType == TYPE_SESSION_STARTED && mParentSessionId != null) {
+ string.append(", parent=").append(mParentSessionId);
+ }
if (mFlags > 0) {
string.append(", flags=").append(mFlags);
}
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index 9830790..e962e7c 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -117,13 +117,11 @@
/** @hide */
public void onActivityStarted(@NonNull IBinder applicationToken,
@NonNull ComponentName activityComponent) {
- // TODO(b/121033016): must start all sessions
getMainContentCaptureSession().start(applicationToken, activityComponent);
}
/** @hide */
public void onActivityStopped() {
- // TODO(b/121033016): must finish all sessions
getMainContentCaptureSession().destroy();
}
@@ -135,7 +133,6 @@
* @hide
*/
public void flush() {
- // TODO(b/121033016): must flush all sessions
getMainContentCaptureSession().flush();
}
diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java
index 46e6882..344b9973 100644
--- a/core/java/android/view/contentcapture/ContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ContentCaptureSession.java
@@ -27,6 +27,7 @@
import android.view.autofill.AutofillId;
import android.view.contentcapture.ViewNode.ViewStructureImpl;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
import dalvik.system.CloseGuard;
@@ -34,7 +35,6 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.UUID;
-import java.util.concurrent.atomic.AtomicBoolean;
/**
* Session used to notify a system-provided Content Capture service about events associated with
@@ -73,11 +73,11 @@
public static final int STATE_ACTIVE = 2;
/**
- * Session is disabled.
+ * Session is disabled because there is no service for this user.
*
* @hide
*/
- public static final int STATE_DISABLED = 3;
+ public static final int STATE_DISABLED_NO_SERVICE = 3;
/**
* Session is disabled because its id already existed on server.
@@ -90,11 +90,14 @@
private final CloseGuard mCloseGuard = CloseGuard.get();
+ private final Object mLock = new Object();
+
/**
* Guard use to ignore events after it's destroyed.
*/
@NonNull
- private final AtomicBoolean mDestroyed = new AtomicBoolean();
+ @GuardedBy("mLock")
+ private boolean mDestroyed;
/** @hide */
@Nullable
@@ -108,11 +111,8 @@
/**
* List of children session.
*/
- // TODO(b/121033016): need to synchonize access, either by changing on handler or UI thread
- // (for now there's no handler on this class, so we need to wait for the next refactoring),
- // most likely the former (as we have no guarantee that createContentCaptureSession()
- // it will be called in the UiThread; for example, WebView most likely won't call on it)
@Nullable
+ @GuardedBy("mLock")
private ArrayList<ContentCaptureSession> mChildren;
/** @hide */
@@ -120,6 +120,10 @@
mCloseGuard.open("destroy");
}
+ /** @hide */
+ @NonNull
+ abstract MainContentCaptureSession getMainCaptureSession();
+
/**
* Gets the id used to identify this session.
*/
@@ -143,10 +147,12 @@
Log.d(TAG, "createContentCaptureSession(" + context + ": parent=" + mId + ", child="
+ child.mId);
}
- if (mChildren == null) {
- mChildren = new ArrayList<>(INITIAL_CHILDREN_CAPACITY);
+ synchronized (mLock) {
+ if (mChildren == null) {
+ mChildren = new ArrayList<>(INITIAL_CHILDREN_CAPACITY);
+ }
+ mChildren.add(child);
}
- mChildren.add(child);
return child;
}
@@ -163,29 +169,31 @@
* <p>Once destroyed, any new notification will be dropped.
*/
public final void destroy() {
- if (!mDestroyed.compareAndSet(false, true)) {
- Log.e(TAG, "destroy(): already destroyed");
- return;
- }
+ synchronized (mLock) {
+ if (mDestroyed) {
+ Log.e(TAG, "destroy(" + mId + "): already destroyed");
+ return;
+ }
+ mDestroyed = true;
- mCloseGuard.close();
+ mCloseGuard.close();
- //TODO(b/111276913): check state (for example, how to handle if it's waiting for remote
- // id) and send it to the cache of batched commands
- if (VERBOSE) {
- Log.v(TAG, "destroy(): state=" + getStateAsString(mState) + ", mId=" + mId);
- }
-
- // Finish children first
- if (mChildren != null) {
- final int numberChildren = mChildren.size();
- if (VERBOSE) Log.v(TAG, "Destroying " + numberChildren + " children first");
- for (int i = 0; i < numberChildren; i++) {
- final ContentCaptureSession child = mChildren.get(i);
- try {
- child.destroy();
- } catch (Exception e) {
- Log.w(TAG, "exception destroying child session #" + i + ": " + e);
+ //TODO(b/111276913): check state (for example, how to handle if it's waiting for remote
+ // id) and send it to the cache of batched commands
+ if (VERBOSE) {
+ Log.v(TAG, "destroy(): state=" + getStateAsString(mState) + ", mId=" + mId);
+ }
+ // Finish children first
+ if (mChildren != null) {
+ final int numberChildren = mChildren.size();
+ if (VERBOSE) Log.v(TAG, "Destroying " + numberChildren + " children first");
+ for (int i = 0; i < numberChildren; i++) {
+ final ContentCaptureSession child = mChildren.get(i);
+ try {
+ child.destroy();
+ } catch (Exception e) {
+ Log.w(TAG, "exception destroying child session #" + i + ": " + e);
+ }
}
}
}
@@ -305,23 +313,26 @@
}
boolean isContentCaptureEnabled() {
- return !mDestroyed.get();
+ synchronized (mLock) {
+ return !mDestroyed;
+ }
}
@CallSuper
void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
pw.print(prefix); pw.print("id: "); pw.println(mId);
- pw.print(prefix); pw.print("destroyed: "); pw.println(mDestroyed.get());
- if (mChildren != null && !mChildren.isEmpty()) {
- final String prefix2 = prefix + " ";
- final int numberChildren = mChildren.size();
- pw.print(prefix); pw.print("number children: "); pw.println(numberChildren);
- for (int i = 0; i < numberChildren; i++) {
- final ContentCaptureSession child = mChildren.get(i);
- pw.print(prefix); pw.print(i); pw.println(": "); child.dump(prefix2, pw);
+ synchronized (mLock) {
+ pw.print(prefix); pw.print("destroyed: "); pw.println(mDestroyed);
+ if (mChildren != null && !mChildren.isEmpty()) {
+ final String prefix2 = prefix + " ";
+ final int numberChildren = mChildren.size();
+ pw.print(prefix); pw.print("number children: "); pw.println(numberChildren);
+ for (int i = 0; i < numberChildren; i++) {
+ final ContentCaptureSession child = mChildren.get(i);
+ pw.print(prefix); pw.print(i); pw.println(": "); child.dump(prefix2, pw);
+ }
}
}
-
}
@Override
@@ -341,8 +352,8 @@
return "WAITING_FOR_SERVER";
case STATE_ACTIVE:
return "ACTIVE";
- case STATE_DISABLED:
- return "DISABLED";
+ case STATE_DISABLED_NO_SERVICE:
+ return "DISABLED_NO_SERVICE";
case STATE_DISABLED_DUPLICATED_ID:
return "DISABLED_DUPLICATED_ID";
default:
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index baf4a35..44a381e 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -141,6 +141,11 @@
}
@Override
+ MainContentCaptureSession getMainCaptureSession() {
+ return this;
+ }
+
+ @Override
ContentCaptureSession newChild(@NonNull ContentCaptureContext clientContext) {
final ContentCaptureSession child = new ChildContentCaptureSession(this, clientContext);
notifyChildSessionStarted(mId, child.mId, clientContext);
@@ -171,6 +176,7 @@
@Override
void onDestroy() {
+ mHandler.removeMessages(MSG_FLUSH);
mHandler.sendMessage(
obtainMessage(MainContentCaptureSession::handleDestroySession, this));
}
@@ -237,7 +243,7 @@
Log.w(TAG, "Failed to link to death on " + binder + ": " + e);
}
}
- if (resultCode == STATE_DISABLED || resultCode == STATE_DISABLED_DUPLICATED_ID) {
+ if (resultCode == STATE_DISABLED_NO_SERVICE || resultCode == STATE_DISABLED_DUPLICATED_ID) {
mDisabled.set(true);
handleResetSession(/* resetState= */ false);
} else {
@@ -246,7 +252,7 @@
if (VERBOSE) {
Log.v(TAG, "handleSessionStarted() result: code=" + resultCode + ", id=" + mId
+ ", state=" + getStateAsString(mState) + ", disabled=" + mDisabled.get()
- + ", binder=" + binder);
+ + ", binder=" + binder + ", events=" + (mEvents == null ? 0 : mEvents.size()));
}
}
@@ -285,14 +291,14 @@
return;
}
- if (mState != STATE_ACTIVE) {
+ if (mState != STATE_ACTIVE && numberEvents >= MAX_BUFFER_SIZE) {
// Callback from startSession hasn't been called yet - typically happens on system
// apps that are started before the system service
// TODO(b/111276913): try to ignore session while system is not ready / boot
// not complete instead. Similarly, the manager service should return right away
// when the user does not have a service set
- if (VERBOSE) {
- Log.v(TAG, "Closing session for " + getActivityDebugName()
+ if (DEBUG) {
+ Log.d(TAG, "Closing session for " + getActivityDebugName()
+ " after " + numberEvents + " delayed events and state "
+ getStateAsString(mState));
}
@@ -331,7 +337,9 @@
if (mEvents == null) return;
if (mDirectServiceInterface == null) {
- if (DEBUG) Log.d(TAG, "handleForceFlush(): hold your horses, client not ready yet!");
+ if (VERBOSE) {
+ Log.v(TAG, "handleForceFlush(): hold your horses, client not ready: " + mEvents);
+ }
if (!mHandler.hasMessages(MSG_FLUSH)) {
handleScheduleFlush(/* checkExisting= */ false);
}
@@ -386,14 +394,14 @@
handleResetSession(/* resetState= */ true);
}
- // TODO(b/121033016): once we support multiple sessions, we might need to move some of these
+ // TODO(b/122454205): once we support multiple sessions, we might need to move some of these
// clearings out.
private void handleResetSession(boolean resetState) {
if (resetState) {
mState = STATE_UNKNOWN;
}
- // TODO(b/121033016): must reset children (which currently is owned by superclass)
+ // TODO(b/122454205): must reset children (which currently is owned by superclass)
mApplicationToken = null;
mComponentName = null;
mEvents = null;
@@ -426,7 +434,7 @@
&& !mDisabled.get();
}
- // TODO(b/121033016): refactor "notifyXXXX" methods below to a common "Buffer" object that is
+ // TODO(b/122454205): refactor "notifyXXXX" methods below to a common "Buffer" object that is
// shared between ActivityContentCaptureSession and ChildContentCaptureSession objects. Such
// change should also get get rid of the "internalNotifyXXXX" methods above
void notifyChildSessionStarted(@NonNull String parentSessionId,
@@ -435,14 +443,14 @@
new ContentCaptureEvent(childSessionId, TYPE_SESSION_STARTED)
.setParentSessionId(parentSessionId)
.setClientContext(clientContext),
- /* forceFlush= */ false));
+ /* forceFlush= */ true));
}
void notifyChildSessionFinished(@NonNull String parentSessionId,
@NonNull String childSessionId) {
mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleSendEvent, this,
new ContentCaptureEvent(childSessionId, TYPE_SESSION_FINISHED)
- .setParentSessionId(parentSessionId), /* forceFlush= */ false));
+ .setParentSessionId(parentSessionId), /* forceFlush= */ true));
}
void notifyViewAppeared(@NonNull String sessionId, @NonNull ViewStructureImpl node) {
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 7d02757..886718d 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -271,7 +271,7 @@
if (mWindow == null) {
synchronized (mLock) {
mWindow = new InternalPopupWindow(mView.getContext(), mView.getDisplay(),
- mParentSurface.mSurface, mWindowWidth, mWindowHeight,
+ mParentSurface.mSurfaceControl, mWindowWidth, mWindowHeight,
mWindowElevation, mWindowCornerRadius,
mOverlay != null ? mOverlay : new ColorDrawable(Color.TRANSPARENT),
Handler.getMain() /* draw the magnifier on the UI thread */, mLock,
@@ -528,17 +528,20 @@
final int surfaceHeight =
viewRootImpl.getHeight() + surfaceInsets.top + surfaceInsets.bottom;
validMainWindowSurface =
- new SurfaceInfo(mainWindowSurface, surfaceWidth, surfaceHeight, true);
+ new SurfaceInfo(viewRootImpl.getSurfaceControl(), mainWindowSurface,
+ surfaceWidth, surfaceHeight, true);
}
}
// Get the surface backing the magnified view, if it is a SurfaceView.
SurfaceInfo validSurfaceViewSurface = SurfaceInfo.NULL;
if (mView instanceof SurfaceView) {
+ final SurfaceControl sc = ((SurfaceView) mView).getSurfaceControl();
final SurfaceHolder surfaceHolder = ((SurfaceView) mView).getHolder();
final Surface surfaceViewSurface = surfaceHolder.getSurface();
- if (surfaceViewSurface != null && surfaceViewSurface.isValid()) {
+
+ if (sc != null && sc.isValid()) {
final Rect surfaceFrame = surfaceHolder.getSurfaceFrame();
- validSurfaceViewSurface = new SurfaceInfo(surfaceViewSurface,
+ validSurfaceViewSurface = new SurfaceInfo(sc, surfaceViewSurface,
surfaceFrame.right, surfaceFrame.bottom, false);
}
}
@@ -733,15 +736,18 @@
* Contains a surface and metadata corresponding to it.
*/
private static class SurfaceInfo {
- public static final SurfaceInfo NULL = new SurfaceInfo(null, 0, 0, false);
+ public static final SurfaceInfo NULL = new SurfaceInfo(null, null, 0, 0, false);
private Surface mSurface;
+ private SurfaceControl mSurfaceControl;
private int mWidth;
private int mHeight;
private boolean mIsMainWindowSurface;
- SurfaceInfo(final Surface surface, final int width, final int height,
+ SurfaceInfo(final SurfaceControl surfaceControl, final Surface surface,
+ final int width, final int height,
final boolean isMainWindowSurface) {
+ mSurfaceControl = surfaceControl;
mSurface = surface;
mWidth = width;
mHeight = height;
@@ -819,7 +825,7 @@
private Bitmap mCurrentContent;
InternalPopupWindow(final Context context, final Display display,
- final Surface parentSurface, final int width, final int height,
+ final SurfaceControl parentSurfaceControl, final int width, final int height,
final float elevation, final float cornerRadius, final Drawable overlay,
final Handler handler, final Object lock, final Callback callback) {
mDisplay = display;
@@ -834,12 +840,13 @@
// Setup the surface we will use for drawing the content and shadow.
mSurfaceWidth = mContentWidth + 2 * mOffsetX;
mSurfaceHeight = mContentHeight + 2 * mOffsetY;
- mSurfaceSession = new SurfaceSession(parentSurface);
+ mSurfaceSession = new SurfaceSession();
mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession)
.setFormat(PixelFormat.TRANSLUCENT)
.setBufferSize(mSurfaceWidth, mSurfaceHeight)
.setName("magnifier surface")
.setFlags(SurfaceControl.HIDDEN)
+ .setParent(parentSurfaceControl)
.build();
mSurface = new Surface();
mSurface.copyFrom(mSurfaceControl);
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/util/SyncResultReceiver.java b/core/java/com/android/internal/util/SyncResultReceiver.java
new file mode 100644
index 0000000..9a346ac
--- /dev/null
+++ b/core/java/com/android/internal/util/SyncResultReceiver.java
@@ -0,0 +1,145 @@
+/*
+ * 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.internal.util;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.os.RemoteException;
+
+import com.android.internal.os.IResultReceiver;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A {@code IResultReceiver} implementation that can be used to make "sync" Binder calls by blocking
+ * until it receives a result
+ *
+ * @hide
+ */
+public final class SyncResultReceiver extends IResultReceiver.Stub {
+
+ private static final String EXTRA = "EXTRA";
+
+ private final CountDownLatch mLatch = new CountDownLatch(1);
+ private final int mTimeoutMs;
+ private int mResult;
+ private Bundle mBundle;
+
+ /**
+ * Default constructor.
+ *
+ * @param timeoutMs how long to block waiting for {@link IResultReceiver} callbacks.
+ */
+ public SyncResultReceiver(int timeoutMs) {
+ mTimeoutMs = timeoutMs;
+ }
+
+ private void waitResult() throws TimeoutException {
+ try {
+ if (!mLatch.await(mTimeoutMs, TimeUnit.MILLISECONDS)) {
+ throw new TimeoutException("Not called in " + mTimeoutMs + "ms");
+ }
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new TimeoutException("Interrupted");
+ }
+ }
+
+ /**
+ * Gets the result from an operation that returns an {@code int}.
+ */
+ public int getIntResult() throws TimeoutException {
+ waitResult();
+ return mResult;
+ }
+
+ /**
+ * Gets the result from an operation that returns an {@code String}.
+ */
+ @Nullable
+ public String getStringResult() throws TimeoutException {
+ waitResult();
+ return mBundle == null ? null : mBundle.getString(EXTRA);
+ }
+
+ /**
+ * Gets the result from an operation that returns a {@code String[]}.
+ */
+ @Nullable
+ public String[] getStringArrayResult() throws TimeoutException {
+ waitResult();
+ return mBundle == null ? null : mBundle.getStringArray(EXTRA);
+ }
+
+ /**
+ * Gets the result from an operation that returns a {@code Parcelable}.
+ */
+ @Nullable
+ public <P extends Parcelable> P getParcelableResult() throws TimeoutException {
+ waitResult();
+ return mBundle == null ? null : mBundle.getParcelable(EXTRA);
+ }
+
+ @Override
+ public void send(int resultCode, Bundle resultData) {
+ mResult = resultCode;
+ mBundle = resultData;
+ mLatch.countDown();
+ }
+
+ /**
+ * Creates a bundle for a {@code String} value so it can be retrieved by
+ * {@link #getStringResult()}.
+ */
+ @NonNull
+ public static Bundle bundleFor(@Nullable String value) {
+ final Bundle bundle = new Bundle();
+ bundle.putString(EXTRA, value);
+ return bundle;
+ }
+
+ /**
+ * Creates a bundle for a {@code String[]} value so it can be retrieved by
+ * {@link #getStringArrayResult()}.
+ */
+ @NonNull
+ public static Bundle bundleFor(@Nullable String[] value) {
+ final Bundle bundle = new Bundle();
+ bundle.putStringArray(EXTRA, value);
+ return bundle;
+ }
+
+ /**
+ * Creates a bundle for a {@code Parcelable} value so it can be retrieved by
+ * {@link #getParcelableResult()}.
+ */
+ @NonNull
+ public static Bundle bundleFor(@Nullable Parcelable value) {
+ final Bundle bundle = new Bundle();
+ bundle.putParcelable(EXTRA, value);
+ return bundle;
+ }
+
+ /** @hide */
+ public static final class TimeoutException extends RemoteException {
+ private TimeoutException(String msg) {
+ super(msg);
+ }
+ }
+}
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 65765878..f292d25 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -438,8 +438,9 @@
static jboolean nativeSetDisplayedContentSamplingEnabled(JNIEnv* env, jclass clazz,
jobject tokenObj, jboolean enable, jint componentMask, jint maxFrames) {
sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
- return SurfaceComposerClient::setDisplayContentSamplingEnabled(
+ status_t rc = SurfaceComposerClient::setDisplayContentSamplingEnabled(
token, enable, componentMask, maxFrames);
+ return rc == OK;
}
static jobject nativeGetDisplayedContentSample(JNIEnv* env, jclass clazz, jobject tokenObj,
@@ -916,6 +917,17 @@
return reinterpret_cast<jlong>(surface.get());
}
+static jlong nativeCopyFromSurfaceControl(JNIEnv* env, jclass clazz, jlong surfaceControlNativeObj) {
+ sp<SurfaceControl> surface(reinterpret_cast<SurfaceControl *>(surfaceControlNativeObj));
+ if (surface == nullptr) {
+ return 0;
+ }
+
+ sp<SurfaceControl> newSurface = new SurfaceControl(surface);
+ newSurface->incStrong((void *)nativeCreate);
+ return reinterpret_cast<jlong>(newSurface.get());
+}
+
static void nativeWriteToParcel(JNIEnv* env, jclass clazz,
jlong nativeObject, jobject parcelObj) {
Parcel* parcel = parcelForJavaObject(env, parcelObj);
@@ -924,7 +936,9 @@
return;
}
SurfaceControl* const self = reinterpret_cast<SurfaceControl *>(nativeObject);
- self->writeToParcel(parcel);
+ if (self != nullptr) {
+ self->writeToParcel(parcel);
+ }
}
// ----------------------------------------------------------------------------
@@ -934,6 +948,8 @@
(void*)nativeCreate },
{"nativeReadFromParcel", "(Landroid/os/Parcel;)J",
(void*)nativeReadFromParcel },
+ {"nativeCopyFromSurfaceControl", "(J)J" ,
+ (void*)nativeCopyFromSurfaceControl },
{"nativeWriteToParcel", "(JLandroid/os/Parcel;)V",
(void*)nativeWriteToParcel },
{"nativeRelease", "(J)V",
diff --git a/core/jni/android_view_SurfaceSession.cpp b/core/jni/android_view_SurfaceSession.cpp
index 30c0030..191f748 100644
--- a/core/jni/android_view_SurfaceSession.cpp
+++ b/core/jni/android_view_SurfaceSession.cpp
@@ -46,13 +46,6 @@
return reinterpret_cast<jlong>(client);
}
-static jlong nativeCreateScoped(JNIEnv* env, jclass clazz, jlong surfaceObject) {
- Surface *parent = reinterpret_cast<Surface*>(surfaceObject);
- SurfaceComposerClient* client = new SurfaceComposerClient(parent->getIGraphicBufferProducer());
- client->incStrong((void*)nativeCreate);
- return reinterpret_cast<jlong>(client);
-}
-
static void nativeDestroy(JNIEnv* env, jclass clazz, jlong ptr) {
SurfaceComposerClient* client = reinterpret_cast<SurfaceComposerClient*>(ptr);
client->decStrong((void*)nativeCreate);
@@ -67,8 +60,6 @@
/* name, signature, funcPtr */
{ "nativeCreate", "()J",
(void*)nativeCreate },
- { "nativeCreateScoped", "(J)J",
- (void*)nativeCreateScoped },
{ "nativeDestroy", "(J)V",
(void*)nativeDestroy },
{ "nativeKill", "(J)V",
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index a914369..d817aa3 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -558,6 +558,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 +1019,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/drawable/ic_account_circle.xml b/core/res/res/drawable/ic_account_circle.xml
index f7317db..71691ad 100644
--- a/core/res/res/drawable/ic_account_circle.xml
+++ b/core/res/res/drawable/ic_account_circle.xml
@@ -14,18 +14,14 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48.0dp"
- android:height="48.0dp"
- android:viewportWidth="20.0"
- android:viewportHeight="20.0">
- <group
- android:translateX="-2"
- android:translateY="-2" >
- <path
- android:pathData="M12,2C6.48,2 2,6.48 2,12c0,5.52 4.48,10 10,10c5.52,0 10,-4.48 10,-10C22,6.48 17.52,2 12,2zM18.36,16.83c-1.43,-1.74 -4.9,-2.33 -6.36,-2.33s-4.93,0.59 -6.36,2.33C4.62,15.49 4,13.82 4,12c0,-4.41 3.59,-8 8,-8c4.41,0 8,3.59 8,8C20,13.82 19.38,15.49 18.36,16.83z"
- android:fillColor="#FFFFFFFF" />
- <path
- android:pathData="M12,6c-1.94,0 -3.5,1.56 -3.5,3.5S10.06,13 12,13c1.94,0 3.5,-1.56 3.5,-3.5S13.94,6 12,6z"
- android:fillColor="#FFFFFFFF" />
- </group>
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M12,2C6.48,2 2,6.48 2,12c0,5.52 4.48,10 10,10c5.52,0 10,-4.48 10,-10C22,6.48 17.52,2 12,2zM18.36,16.83c-1.43,-1.74 -4.9,-2.33 -6.36,-2.33s-4.93,0.59 -6.36,2.33C4.62,15.49 4,13.82 4,12c0,-4.41 3.59,-8 8,-8c4.41,0 8,3.59 8,8C20,13.82 19.38,15.49 18.36,16.83z"/>
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M12,6c-1.94,0 -3.5,1.56 -3.5,3.5S10.06,13 12,13c1.94,0 3.5,-1.56 3.5,-3.5S13.94,6 12,6z"/>
</vector>
diff --git a/core/res/res/drawable/ic_battery.xml b/core/res/res/drawable/ic_battery.xml
new file mode 100644
index 0000000..bd40f4d
--- /dev/null
+++ b/core/res/res/drawable/ic_battery.xml
@@ -0,0 +1,25 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="?android:attr/colorControlNormal">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M15.67,4H14V2h-4v2H8.33C7.6,4 7,4.6 7,5.33v15.33C7,21.4 7.6,22 8.33,22h7.33c0.74,0 1.34,-0.6 1.34,-1.33V5.33C17,4.6 16.4,4 15.67,4z"/>
+</vector>
diff --git a/core/res/res/drawable/ic_corp_badge.xml b/core/res/res/drawable/ic_corp_badge.xml
index 1dad977..5ab5045 100644
--- a/core/res/res/drawable/ic_corp_badge.xml
+++ b/core/res/res/drawable/ic_corp_badge.xml
@@ -15,22 +15,11 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="20dp"
- android:height="20dp"
- android:viewportWidth="48"
- android:viewportHeight="48">
-
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
<path
- android:fillType="evenOdd"
- android:strokeColor="#1A73E8"
- android:strokeWidth="4"
- android:pathData="M 24 2 C 36.1502644963 2 46 11.8497355037 46 24 C 46 36.1502644963 36.1502644963 46 24 46 C 11.8497355037 46 2 36.1502644963 2 24 C 2 11.8497355037 11.8497355037 2 24 2 Z" />
- <group
- android:translateX="10.400000"
- android:translateY="10.400000">
- <path
- android:fillColor="#1A73E8"
- android:strokeWidth="1"
- android:pathData="M24.2971429,5.38947368 L18.9485714,5.38947368 L18.9485714,2.80902256 C18.9485714,1.37687218 17.7585143,0.228571429 16.2742857,0.228571429 L10.9257143,0.228571429 C9.44148571,0.228571429 8.25142857,1.37687218 8.25142857,2.80902256 L8.25142857,5.38947368 L2.90285714,5.38947368 C1.41862857,5.38947368 0.241942857,6.53777444 0.241942857,7.96992481 L0.228571429,22.162406 C0.228571429,23.5945564 1.41862857,24.7428571 2.90285714,24.7428571 L24.2971429,24.7428571 C25.7813714,24.7428571 26.9714286,23.5945564 26.9714286,22.162406 L26.9714286,7.96992481 C26.9714286,6.53777444 25.7813714,5.38947368 24.2971429,5.38947368 Z M13.6,17.0015038 C12.1291429,17.0015038 10.9257143,15.8403008 10.9257143,14.4210526 C10.9257143,13.0018045 12.1291429,11.8406015 13.6,11.8406015 C15.0708571,11.8406015 16.2742857,13.0018045 16.2742857,14.4210526 C16.2742857,15.8403008 15.0708571,17.0015038 13.6,17.0015038 Z M16.2742857,5.38947368 L10.9257143,5.38947368 L10.9257143,2.80902256 L16.2742857,2.80902256 L16.2742857,5.38947368 Z" />
- </group>
+ android:fillColor="@*android:color/accent_device_default_light"
+ android:pathData="M20,6h-4V4c0,-1.11 -0.89,-2 -2,-2h-4C8.89,2 8,2.89 8,4v2H4C2.89,6 2.01,6.89 2.01,8L2,19c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2V8C22,6.89 21.11,6 20,6zM10,4h4v2h-4V4zM20,19H4V8h16V19z"/>
</vector>
\ No newline at end of file
diff --git a/core/res/res/layout/time_picker_header_material.xml b/core/res/res/layout/time_picker_header_material.xml
index adb2b62f..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/attrs.xml b/core/res/res/values/attrs.xml
index 183c2e8..fa3a549 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3660,7 +3660,12 @@
the settings for this service. This setting cannot be changed at runtime. -->
<attr name="settingsActivity" />
<!-- Attribute whether the accessibility service wants to be able to retrieve the
- active window content. This setting cannot be changed at runtime. -->
+ active window content. This setting cannot be changed at runtime.
+ <p>
+ Required to allow setting the {@link android.accessibilityservice
+ #AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS} flag.
+ </p>
+ -->
<attr name="canRetrieveWindowContent" format="boolean" />
<!-- Attribute whether the accessibility service wants to be able to request touch
exploration mode in which touched items are spoken aloud and the UI can be
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index e4abf8f..1c98c66 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1169,7 +1169,7 @@
<integer name="config_lowBatteryAutoTriggerDefaultLevel">15</integer>
<!-- The app which will handle routine based automatic battery saver, if empty the UI for
- routine based battery saver will be hidden -->
+ routine based battery saver will be hidden -->
<string name="config_batterySaverScheduleProvider"></string>
<!-- Close low battery warning when battery level reaches the lowBatteryWarningLevel
@@ -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.
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index a7bc57a..0878562 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -5238,6 +5238,15 @@
<!-- Content description of the overlay icon in the notification. [CHAR LIMIT=NONE] -->
<string name="notification_appops_overlay_active">displaying over other apps on your screen</string>
+ <!-- Dynamic mode battery saver strings -->
+ <!-- The user visible name of the notification channel for the routine mode battery saver fyi notification [CHAR_LIMIT=80]-->
+ <string name="dynamic_mode_notification_channel_name">Routine Mode info notification</string>
+ <!-- Title of notification letting users know why battery saver was turned on automatically [CHAR_LIMIT=NONE]-->
+ <string name="dynamic_mode_notification_title">Battery may run out before usual charge</string>
+ <!-- Summary of notification letting users know why battery saver was turned on automatically [CHAR_LIMIT=NONE]-->
+ <string name="dynamic_mode_notification_summary">Battery Saver activated to extend battery life</string>
+
+
<!-- Strings for car -->
<!-- String displayed when loading a user in the car [CHAR LIMIT=30] -->
<string name="car_loading_profile">Loading</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index e8cbf66..01fbf80f 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" />
@@ -3519,4 +3520,9 @@
<!-- For Secondary Launcher -->
<java-symbol type="string" name="config_secondaryHomeComponent" />
+
+ <java-symbol type="string" name="dynamic_mode_notification_channel_name" />
+ <java-symbol type="string" name="dynamic_mode_notification_title" />
+ <java-symbol type="string" name="dynamic_mode_notification_summary" />
+ <java-symbol type="drawable" name="ic_battery" />
</resources>
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index 0f4ca66..d60313a 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -1454,12 +1454,17 @@
<item name="panelMenuListTheme">@style/Theme.Material.Settings.CompactMenu</item>
<!-- action bar -->
+ <item name="actionBarStyle">@style/Widget.DeviceDefault.Light.ActionBar.Solid</item>
<item name="actionBarTheme">@style/ThemeOverlay.DeviceDefault.ActionBar</item>
<item name="popupTheme">@style/ThemeOverlay.DeviceDefault.Popup.Light</item>
<!-- Color palette -->
+ <item name="colorBackground">@color/background_device_default_light</item>
+ <item name="colorPrimary">@color/primary_device_default_settings_light</item>
<item name="colorPrimaryDark">@color/primary_dark_device_default_settings_light</item>
<item name="colorSecondary">@color/secondary_device_default_settings_light</item>
+ <item name="colorAccent">@color/accent_device_default_light</item>
+ <item name="colorError">@color/error_color_device_default_light</item>
<item name="colorEdgeEffect">@android:color/black</item>
<!-- Add white nav bar with divider that matches material -->
@@ -1467,9 +1472,16 @@
<item name="navigationBarColor">@android:color/white</item>
<item name="windowLightNavigationBar">true</item>
+ <!-- Dialog attributes -->
+ <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
+
<!-- Button styles -->
+ <item name="buttonCornerRadius">@dimen/config_buttonCornerRadius</item>
<item name="buttonBarButtonStyle">@style/Widget.DeviceDefault.Button.ButtonBar.AlertDialog</item>
+ <!-- Progress bar attributes -->
+ <item name="progressBarCornerRadius">@dimen/config_progressBarCornerRadius</item>
+
<item name="listDivider">@color/list_divider_color_light</item>
</style>
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index ac57d20..70267c7 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,
@@ -269,6 +270,7 @@
Settings.Global.GNSS_SATELLITE_BLACKLIST,
Settings.Global.GPRS_REGISTER_CHECK_PERIOD_MS,
Settings.Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED,
+ Settings.Global.HDMI_CONTROL_AUTO_TV_OFF_ENABLED,
Settings.Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED,
Settings.Global.HDMI_CONTROL_ENABLED,
Settings.Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED,
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/TextClassifierTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
index 81ec85e..82eaf88 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassifierTest.java
@@ -368,6 +368,7 @@
assertThat(textLanguage, isTextLanguage("ja"));
}
+ /* DISABLED: b/122467291
@Test
public void testSuggestConversationActions_textReplyOnly_maxThree() {
if (isTextClassifierDisabled()) return;
@@ -395,7 +396,7 @@
assertThat(conversationAction,
isConversationAction(ConversationActions.TYPE_TEXT_REPLY));
}
- }
+ }*/
@Test
public void testSuggestConversationActions_textReplyOnly_noMax() {
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/core/tests/hdmitests/Android.mk b/core/tests/hdmitests/Android.mk
index e0d2c09..2ca31a6 100644
--- a/core/tests/hdmitests/Android.mk
+++ b/core/tests/hdmitests/Android.mk
@@ -20,7 +20,7 @@
# Include all test java files
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test frameworks-base-testutils
+LOCAL_STATIC_JAVA_LIBRARIES := androidx.test.rules frameworks-base-testutils
LOCAL_JAVA_LIBRARIES := android.test.runner
LOCAL_PACKAGE_NAME := HdmiCecTests
diff --git a/core/tests/hdmitests/AndroidManifest.xml b/core/tests/hdmitests/AndroidManifest.xml
index 1460b41..f8ed118 100644
--- a/core/tests/hdmitests/AndroidManifest.xml
+++ b/core/tests/hdmitests/AndroidManifest.xml
@@ -22,7 +22,7 @@
<uses-library android:name="android.test.runner" />
</application>
- <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="android.hardware.hdmi"
android:label="HDMI CEC Tests"/>
diff --git a/core/tests/hdmitests/AndroidTest.xml b/core/tests/hdmitests/AndroidTest.xml
index 7ef672d..0c8da28 100644
--- a/core/tests/hdmitests/AndroidTest.xml
+++ b/core/tests/hdmitests/AndroidTest.xml
@@ -29,6 +29,6 @@
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.hardware.hdmi" />
<option name="hidden-api-checks" value="false"/>
- <option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/>
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner"/>
</test>
</configuration>
\ No newline at end of file
diff --git a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
index 7b76a08..0dd9d09 100644
--- a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
+++ b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
@@ -18,15 +18,18 @@
import android.os.Handler;
import android.os.test.TestLooper;
-import android.support.test.filters.SmallTest;
import android.util.Log;
-import java.util.List;
+
+import androidx.test.filters.SmallTest;
+
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import java.util.List;
+
/**
* Tests for {@link HdmiAudioSystemClient}
*/
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 6a40792..04bf804 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -312,9 +312,8 @@
<font weight="700" style="normal">NotoSansLaoUI-Bold.ttf</font>
</family>
<family lang="und-Mymr" variant="elegant">
- <font weight="400" style="normal">NotoSansMyanmar-Regular.otf</font>
- <font weight="500" style="normal">NotoSansMyanmar-Medium.otf</font>
- <font weight="700" style="normal">NotoSansMyanmar-Bold.otf</font>
+ <font weight="400" style="normal">NotoSansMyanmar-Regular-ZawDecode.ttf</font>
+ <font weight="700" style="normal">NotoSansMyanmar-Bold-ZawDecode.ttf</font>
<font weight="400" style="normal" fallbackFor="serif">NotoSerifMyanmar-Regular.otf</font>
<font weight="700" style="normal" fallbackFor="serif">NotoSerifMyanmar-Bold.otf</font>
</family>
diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java
index 2227cf5..f0efb58 100644
--- a/graphics/java/android/graphics/ColorSpace.java
+++ b/graphics/java/android/graphics/ColorSpace.java
@@ -1354,9 +1354,9 @@
*/
@NonNull
static ColorSpace get(@IntRange(from = MIN_ID, to = MAX_ID) int index) {
- if (index < 0 || index > Named.values().length) {
+ if (index < 0 || index >= Named.values().length) {
throw new IllegalArgumentException("Invalid ID, must be in the range [0.." +
- Named.values().length + "]");
+ Named.values().length + ")");
}
return sNamedColorSpaces[index];
}
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/WebViewFunctorManager.cpp b/libs/hwui/WebViewFunctorManager.cpp
index 5b7ae70..9170d6d 100644
--- a/libs/hwui/WebViewFunctorManager.cpp
+++ b/libs/hwui/WebViewFunctorManager.cpp
@@ -18,6 +18,7 @@
#include <private/hwui/WebViewFunctor.h>
#include "Properties.h"
+#include "renderthread/RenderThread.h"
#include <log/log.h>
#include <utils/Trace.h>
@@ -90,6 +91,10 @@
mHasContext = false;
ATRACE_NAME("WebViewFunctor::onContextDestroyed");
mCallbacks.onContextDestroyed(mFunctor, mData);
+
+ // grContext may be null in unit tests.
+ auto* grContext = renderthread::RenderThread::getInstance().getGrContext();
+ if (grContext) grContext->resetContext();
}
}
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/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp
index 7acc44c..6c04232 100644
--- a/libs/hwui/renderthread/CacheManager.cpp
+++ b/libs/hwui/renderthread/CacheManager.cpp
@@ -30,6 +30,7 @@
#include <gui/Surface.h>
#include <math.h>
#include <set>
+#include <SkMathPriv.h>
namespace android {
namespace uirenderer {
@@ -42,11 +43,6 @@
#define SURFACE_SIZE_MULTIPLIER (12.0f * 4.0f)
#define BACKGROUND_RETENTION_PERCENTAGE (0.5f)
-// for super large fonts we will draw them as paths so no need to keep linearly
-// increasing the font cache size.
-#define FONT_CACHE_MIN_MB (0.5f)
-#define FONT_CACHE_MAX_MB (4.0f)
-
CacheManager::CacheManager(const DisplayInfo& display) : mMaxSurfaceArea(display.w * display.h) {
mVectorDrawableAtlas = new skiapipeline::VectorDrawableAtlas(
mMaxSurfaceArea / 2,
@@ -106,25 +102,10 @@
void CacheManager::configureContext(GrContextOptions* contextOptions, const void* identity, ssize_t size) {
contextOptions->fAllowPathMaskCaching = true;
- float screenMP = mMaxSurfaceArea / 1024.0f / 1024.0f;
- float fontCacheMB = 0;
- float decimalVal = std::modf(screenMP, &fontCacheMB);
-
- // This is a basic heuristic to size the cache to a multiple of 512 KB
- if (decimalVal > 0.8f) {
- fontCacheMB += 1.0f;
- } else if (decimalVal > 0.5f) {
- fontCacheMB += 0.5f;
- }
-
- // set limits on min/max size of the cache
- fontCacheMB = std::max(FONT_CACHE_MIN_MB, std::min(FONT_CACHE_MAX_MB, fontCacheMB));
-
- // We must currently set the size of the text cache based on the size of the
- // display even though we like to be dynamicallysizing it to the size of the window.
- // Skia's implementation doesn't provide a mechanism to resize the font cache due to
- // the potential cost of recreating the glyphs.
- contextOptions->fGlyphCacheTextureMaximumBytes = fontCacheMB * 1024 * 1024;
+ // This sets the maximum size for a single texture atlas in the GPU font cache. If necessary,
+ // the cache can allocate additional textures that are counted against the total cache limits
+ // provided to Skia.
+ contextOptions->fGlyphCacheTextureMaximumBytes = GrNextSizePow2(mMaxSurfaceArea);
if (mTaskManager.canRunTasks()) {
if (!mTaskProcessor.get()) {
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index 12666b3..5272227 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -123,6 +123,7 @@
friend class RenderProxy;
friend class DummyVsyncSource;
friend class android::uirenderer::TestUtils;
+ friend class android::uirenderer::WebViewFunctor;
RenderThread();
virtual ~RenderThread();
diff --git a/libs/hwui/service/GraphicsStatsService.cpp b/libs/hwui/service/GraphicsStatsService.cpp
index 3d50d2d..29e4256 100644
--- a/libs/hwui/service/GraphicsStatsService.cpp
+++ b/libs/hwui/service/GraphicsStatsService.cpp
@@ -139,6 +139,7 @@
uint32_t file_version = *reinterpret_cast<uint32_t*>(addr);
if (file_version != sCurrentFileVersion) {
ALOGW("file_version mismatch! expected %d got %d", sCurrentFileVersion, file_version);
+ munmap(addr, sb.st_size);
return false;
}
@@ -150,6 +151,7 @@
ALOGW("Parse failed on '%s' error='%s'", path.c_str(),
output->InitializationErrorString().c_str());
}
+ munmap(addr, sb.st_size);
return success;
}
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/tests/common/scenes/BitmapFillrate.cpp b/libs/hwui/tests/common/scenes/BitmapFillrate.cpp
index 1d3d607..5af7d43 100644
--- a/libs/hwui/tests/common/scenes/BitmapFillrate.cpp
+++ b/libs/hwui/tests/common/scenes/BitmapFillrate.cpp
@@ -31,7 +31,7 @@
class BitmapFillrate : public TestScene {
public:
- BitmapFillrate(BitmapAllocationTestUtils::BitmapAllocator allocator)
+ explicit BitmapFillrate(BitmapAllocationTestUtils::BitmapAllocator allocator)
: TestScene(), mAllocator(allocator) {}
void createContent(int width, int height, Canvas& canvas) override {
@@ -70,4 +70,4 @@
BitmapAllocationTestUtils::BitmapAllocator mAllocator;
std::vector<sp<RenderNode> > mNodes;
-};
\ No newline at end of file
+};
diff --git a/libs/hwui/tests/common/scenes/BitmapShaders.cpp b/libs/hwui/tests/common/scenes/BitmapShaders.cpp
index ad11a1d..5107660 100644
--- a/libs/hwui/tests/common/scenes/BitmapShaders.cpp
+++ b/libs/hwui/tests/common/scenes/BitmapShaders.cpp
@@ -26,7 +26,7 @@
class BitmapShaders : public TestScene {
public:
- BitmapShaders(BitmapAllocationTestUtils::BitmapAllocator allocator)
+ explicit BitmapShaders(BitmapAllocationTestUtils::BitmapAllocator allocator)
: TestScene(), mAllocator(allocator) {}
sp<RenderNode> card;
diff --git a/libs/hwui/tests/common/scenes/TvApp.cpp b/libs/hwui/tests/common/scenes/TvApp.cpp
index a64e844..286f5f1 100644
--- a/libs/hwui/tests/common/scenes/TvApp.cpp
+++ b/libs/hwui/tests/common/scenes/TvApp.cpp
@@ -48,7 +48,7 @@
class TvApp : public TestScene {
public:
- TvApp(BitmapAllocationTestUtils::BitmapAllocator allocator)
+ explicit TvApp(BitmapAllocationTestUtils::BitmapAllocator allocator)
: TestScene(), mAllocator(allocator) {}
sp<RenderNode> mBg;
@@ -232,7 +232,7 @@
class TvAppNoRoundedCorner : public TvApp {
public:
- TvAppNoRoundedCorner(BitmapAllocationTestUtils::BitmapAllocator allocator) : TvApp(allocator) {}
+ explicit TvAppNoRoundedCorner(BitmapAllocationTestUtils::BitmapAllocator allocator) : TvApp(allocator) {}
private:
virtual float roundedCornerRadius() override { return dp(0); }
@@ -240,7 +240,7 @@
class TvAppColorFilter : public TvApp {
public:
- TvAppColorFilter(BitmapAllocationTestUtils::BitmapAllocator allocator) : TvApp(allocator) {}
+ explicit TvAppColorFilter(BitmapAllocationTestUtils::BitmapAllocator allocator) : TvApp(allocator) {}
private:
virtual bool useOverlay() override { return false; }
@@ -248,7 +248,7 @@
class TvAppNoRoundedCornerColorFilter : public TvApp {
public:
- TvAppNoRoundedCornerColorFilter(BitmapAllocationTestUtils::BitmapAllocator allocator)
+ explicit TvAppNoRoundedCornerColorFilter(BitmapAllocationTestUtils::BitmapAllocator allocator)
: TvApp(allocator) {}
private:
diff --git a/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp b/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp
index 479c462..635429d 100644
--- a/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp
+++ b/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp
@@ -44,7 +44,7 @@
static const int CANVAS_HEIGHT = 100;
class PropertyTestCanvas : public TestCanvasBase {
public:
- PropertyTestCanvas(std::function<void(const SkCanvas&)> callback)
+ explicit PropertyTestCanvas(std::function<void(const SkCanvas&)> callback)
: TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT), mCallback(callback) {}
void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
EXPECT_EQ(mDrawCounter++, 0);
diff --git a/libs/hwui/tests/unit/ThreadBaseTests.cpp b/libs/hwui/tests/unit/ThreadBaseTests.cpp
index 1168ff2..817c1f3 100644
--- a/libs/hwui/tests/unit/ThreadBaseTests.cpp
+++ b/libs/hwui/tests/unit/ThreadBaseTests.cpp
@@ -95,7 +95,7 @@
};
struct Counter {
- Counter(EventCount* count) : mCount(count) { mCount->construct++; }
+ explicit Counter(EventCount* count) : mCount(count) { mCount->construct++; }
Counter(const Counter& other) : mCount(other.mCount) {
if (mCount) mCount->copy++;
@@ -148,4 +148,4 @@
ASSERT_EQ(1, dummyObject->getStrongCount());
ASSERT_EQ(2, lifecycleTestHelper(dummyObject));
ASSERT_EQ(1, dummyObject->getStrongCount());
-}
\ No newline at end of file
+}
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index cef6614..f482f64 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
@@ -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 b160029..eeb1461 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -456,6 +456,9 @@
/** VP8/VORBIS data in a WEBM container */
public static final int WEBM = 9;
+ /** @hide HEIC data in a HEIF container */
+ public static final int HEIF = 10;
+
/** Opus data in a Ogg container */
public static final int OGG = 11;
};
diff --git a/media/java/android/media/session/ISessionController.aidl b/media/java/android/media/session/ISessionController.aidl
index e61bf5b..a843881 100644
--- a/media/java/android/media/session/ISessionController.aidl
+++ b/media/java/android/media/session/ISessionController.aidl
@@ -21,8 +21,8 @@
import android.media.MediaMetadata;
import android.media.Rating;
import android.media.session.ISessionControllerCallback;
+import android.media.session.MediaController;
import android.media.session.MediaSession;
-import android.media.session.ParcelableVolumeInfo;
import android.media.session.PlaybackState;
import android.net.Uri;
import android.os.Bundle;
@@ -47,7 +47,7 @@
String getTag();
PendingIntent getLaunchPendingIntent();
long getFlags();
- ParcelableVolumeInfo getVolumeAttributes();
+ MediaController.PlaybackInfo getVolumeAttributes();
void adjustVolume(String packageName, String opPackageName,
in ISessionControllerCallback caller, boolean asSystemService, int direction,
int flags);
diff --git a/media/java/android/media/session/ISessionControllerCallback.aidl b/media/java/android/media/session/ISessionControllerCallback.aidl
index cf31767..fac8897 100644
--- a/media/java/android/media/session/ISessionControllerCallback.aidl
+++ b/media/java/android/media/session/ISessionControllerCallback.aidl
@@ -17,8 +17,8 @@
import android.content.pm.ParceledListSlice;
import android.media.MediaMetadata;
-import android.media.session.ParcelableVolumeInfo;
import android.media.session.PlaybackState;
+import android.media.session.MediaController;
import android.media.session.MediaSession;
import android.os.Bundle;
@@ -35,5 +35,5 @@
void onQueueChanged(in ParceledListSlice queue);
void onQueueTitleChanged(CharSequence title);
void onExtrasChanged(in Bundle extras);
- void onVolumeInfoChanged(in ParcelableVolumeInfo info);
+ void onVolumeInfoChanged(in MediaController.PlaybackInfo info);
}
diff --git a/media/java/android/media/session/ParcelableVolumeInfo.aidl b/media/java/android/media/session/MediaController.aidl
similarity index 93%
rename from media/java/android/media/session/ParcelableVolumeInfo.aidl
rename to media/java/android/media/session/MediaController.aidl
index c4250f0..17167f4 100644
--- a/media/java/android/media/session/ParcelableVolumeInfo.aidl
+++ b/media/java/android/media/session/MediaController.aidl
@@ -15,4 +15,4 @@
package android.media.session;
-parcelable ParcelableVolumeInfo;
+parcelable MediaController.PlaybackInfo;
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index 181ee53..ef2df15 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -32,6 +32,8 @@
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.text.TextUtils;
@@ -327,10 +329,7 @@
*/
public @Nullable PlaybackInfo getPlaybackInfo() {
try {
- ParcelableVolumeInfo result = mSessionBinder.getVolumeAttributes();
- return new PlaybackInfo(result.volumeType, result.audioAttrs, result.controlType,
- result.maxVolume, result.currentVolume);
-
+ return mSessionBinder.getVolumeAttributes();
} catch (RemoteException e) {
Log.wtf(TAG, "Error calling getAudioInfo.", e);
}
@@ -986,15 +985,15 @@
* Holds information about the current playback and how audio is handled for
* this session.
*/
- public static final class PlaybackInfo {
- /**
- * The session uses remote playback.
- */
- public static final int PLAYBACK_TYPE_REMOTE = 2;
+ public static final class PlaybackInfo implements Parcelable {
/**
* The session uses local playback.
*/
public static final int PLAYBACK_TYPE_LOCAL = 1;
+ /**
+ * The session uses remote playback.
+ */
+ public static final int PLAYBACK_TYPE_REMOTE = 2;
private final int mVolumeType;
private final int mVolumeControl;
@@ -1005,12 +1004,20 @@
/**
* @hide
*/
- public PlaybackInfo(int type, AudioAttributes attrs, int control, int max, int current) {
+ public PlaybackInfo(int type, int control, int max, int current, AudioAttributes attrs) {
mVolumeType = type;
- mAudioAttrs = attrs;
mVolumeControl = control;
mMaxVolume = max;
mCurrentVolume = current;
+ mAudioAttrs = attrs;
+ }
+
+ PlaybackInfo(Parcel in) {
+ mVolumeType = in.readInt();
+ mVolumeControl = in.readInt();
+ mMaxVolume = in.readInt();
+ mCurrentVolume = in.readInt();
+ mAudioAttrs = in.readParcelable(null);
}
/**
@@ -1027,18 +1034,6 @@
}
/**
- * Get the audio attributes for this session. The attributes will affect
- * volume handling for the session. When the volume type is
- * {@link PlaybackInfo#PLAYBACK_TYPE_REMOTE} these may be ignored by the
- * remote volume handler.
- *
- * @return The attributes for this session.
- */
- public AudioAttributes getAudioAttributes() {
- return mAudioAttrs;
- }
-
- /**
* Get the type of volume control that can be used. One of:
* <ul>
* <li>{@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}</li>
@@ -1070,6 +1065,52 @@
public int getCurrentVolume() {
return mCurrentVolume;
}
+
+ /**
+ * Get the audio attributes for this session. The attributes will affect
+ * volume handling for the session. When the volume type is
+ * {@link PlaybackInfo#PLAYBACK_TYPE_REMOTE} these may be ignored by the
+ * remote volume handler.
+ *
+ * @return The attributes for this session.
+ */
+ public AudioAttributes getAudioAttributes() {
+ return mAudioAttrs;
+ }
+
+ @Override
+ public String toString() {
+ return "volumeType=" + mVolumeType + ", volumeControl=" + mVolumeControl
+ + ", maxVolume=" + mMaxVolume + ", currentVolume=" + mCurrentVolume
+ + ", audioAttrs=" + mAudioAttrs;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mVolumeType);
+ dest.writeInt(mVolumeControl);
+ dest.writeInt(mMaxVolume);
+ dest.writeInt(mCurrentVolume);
+ dest.writeParcelable(mAudioAttrs, flags);
+ }
+
+ public static final Parcelable.Creator<PlaybackInfo> CREATOR =
+ new Parcelable.Creator<PlaybackInfo>() {
+ @Override
+ public PlaybackInfo createFromParcel(Parcel in) {
+ return new PlaybackInfo(in);
+ }
+
+ @Override
+ public PlaybackInfo[] newArray(int size) {
+ return new PlaybackInfo[size];
+ }
+ };
}
private final static class CallbackStub extends ISessionControllerCallback.Stub {
@@ -1138,11 +1179,9 @@
}
@Override
- public void onVolumeInfoChanged(ParcelableVolumeInfo pvi) {
+ public void onVolumeInfoChanged(PlaybackInfo info) {
MediaController controller = mController.get();
if (controller != null) {
- PlaybackInfo info = new PlaybackInfo(pvi.volumeType, pvi.audioAttrs,
- pvi.controlType, pvi.maxVolume, pvi.currentVolume);
controller.postMessage(MSG_UPDATE_VOLUME, info, null);
}
}
@@ -1154,7 +1193,7 @@
private boolean mRegistered = false;
public MessageHandler(Looper looper, MediaController.Callback cb) {
- super(looper, null, true);
+ super(looper);
mCallback = cb;
}
@@ -1193,6 +1232,7 @@
public void post(int what, Object obj, Bundle data) {
Message msg = obtainMessage(what, obj);
+ msg.setAsynchronous(true);
msg.setData(data);
msg.sendToTarget();
}
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 8962bb7..df8cc35 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -1453,7 +1453,7 @@
private RemoteUserInfo mCurrentControllerInfo;
public CallbackMessageHandler(Looper looper, MediaSession.Callback callback) {
- super(looper, null, true);
+ super(looper);
mCallback = callback;
mCallback.mHandler = this;
}
@@ -1461,6 +1461,7 @@
public void post(RemoteUserInfo caller, int what, Object obj, Bundle data, long delayMs) {
Pair<RemoteUserInfo, Object> objWithCaller = Pair.create(caller, obj);
Message msg = obtainMessage(what, objWithCaller);
+ msg.setAsynchronous(true);
msg.setData(data);
if (delayMs > 0) {
sendMessageDelayed(msg, delayMs);
diff --git a/media/java/android/media/session/ParcelableVolumeInfo.java b/media/java/android/media/session/ParcelableVolumeInfo.java
deleted file mode 100644
index f59c975..0000000
--- a/media/java/android/media/session/ParcelableVolumeInfo.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/* Copyright 2014, 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.media.session;
-
-import android.media.AudioAttributes;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Convenience class for passing information about the audio configuration of a
- * session. The public implementation is {@link MediaController.PlaybackInfo}.
- *
- * @hide
- */
-public class ParcelableVolumeInfo implements Parcelable {
- public int volumeType;
- public AudioAttributes audioAttrs;
- public int controlType;
- public int maxVolume;
- public int currentVolume;
-
- public ParcelableVolumeInfo(int volumeType, AudioAttributes audioAttrs, int controlType,
- int maxVolume,
- int currentVolume) {
- this.volumeType = volumeType;
- this.audioAttrs = audioAttrs;
- this.controlType = controlType;
- this.maxVolume = maxVolume;
- this.currentVolume = currentVolume;
- }
-
- public ParcelableVolumeInfo(Parcel from) {
- volumeType = from.readInt();
- controlType = from.readInt();
- maxVolume = from.readInt();
- currentVolume = from.readInt();
- audioAttrs = AudioAttributes.CREATOR.createFromParcel(from);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(volumeType);
- dest.writeInt(controlType);
- dest.writeInt(maxVolume);
- dest.writeInt(currentVolume);
- audioAttrs.writeToParcel(dest, flags);
- }
-
-
- public static final Parcelable.Creator<ParcelableVolumeInfo> CREATOR
- = new Parcelable.Creator<ParcelableVolumeInfo>() {
- @Override
- public ParcelableVolumeInfo createFromParcel(Parcel in) {
- return new ParcelableVolumeInfo(in);
- }
-
- @Override
- public ParcelableVolumeInfo[] newArray(int size) {
- return new ParcelableVolumeInfo[size];
- }
- };
-}
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index f75f69b..fda0fc4 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -102,7 +102,7 @@
"libhidlbase",
"libhidlmemory",
- "libpowermanager", // Used by JWakeLock. Will be replace with public SDJ API.
+ "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.
@@ -111,6 +111,7 @@
// NDK or NDK-compliant
"libandroid",
+ "libbinder_ndk",
"libmediandk",
"libnativehelper_compat_libc++",
"liblog",
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/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java
index 62502ef..6d960d7 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/CarNotificationInterruptionStateProvider.java
@@ -18,8 +18,8 @@
import android.content.Context;
-import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
/** Auto-specific implementation of {@link NotificationInterruptionStateProvider}. */
public class CarNotificationInterruptionStateProvider extends
@@ -29,7 +29,7 @@
}
@Override
- public boolean shouldHeadsUp(NotificationData.Entry entry) {
+ public boolean shouldHeadsUp(NotificationEntry entry) {
// Because space is usually constrained in the auto use-case, there should not be a
// pinned notification when the shade has been expanded. Ensure this by not pinning any
// notification if the shade is already opened.
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 90%
rename from services/net/java/android/net/dhcp/DhcpLease.java
rename to packages/NetworkStack/src/android/net/dhcp/DhcpLease.java
index 6cdd2aa..6849cfa 100644
--- a/services/net/java/android/net/dhcp/DhcpLease.java
+++ b/packages/NetworkStack/src/android/net/dhcp/DhcpLease.java
@@ -58,6 +58,11 @@
mHostname = hostname;
}
+ /**
+ * Get the clientId associated with this lease, if any.
+ *
+ * <p>If the lease is not associated to a clientId, this returns null.
+ */
@Nullable
public byte[] getClientId() {
if (mClientId == null) {
@@ -97,6 +102,11 @@
(hostname == null ? mHostname : hostname));
}
+ /**
+ * Determine whether this lease matches a client with the specified parameters.
+ * @param clientId clientId of the client if any, or null otherwise.
+ * @param hwAddr Hardware address of the client.
+ */
public boolean matchesClient(@Nullable byte[] clientId, @NonNull MacAddress hwAddr) {
if (mClientId != null) {
return Arrays.equals(mClientId, clientId);
@@ -110,7 +120,7 @@
if (!(obj instanceof DhcpLease)) {
return false;
}
- final DhcpLease other = (DhcpLease)obj;
+ final DhcpLease other = (DhcpLease) obj;
return Arrays.equals(mClientId, other.mClientId)
&& mHwAddr.equals(other.mHwAddr)
&& mNetAddr.equals(other.mNetAddr)
diff --git a/services/net/java/android/net/dhcp/DhcpLeaseRepository.java b/packages/NetworkStack/src/android/net/dhcp/DhcpLeaseRepository.java
similarity index 98%
rename from services/net/java/android/net/dhcp/DhcpLeaseRepository.java
rename to packages/NetworkStack/src/android/net/dhcp/DhcpLeaseRepository.java
index 2dda421..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;
@@ -29,8 +30,8 @@
import android.annotation.Nullable;
import android.net.IpPrefix;
import android.net.MacAddress;
-import android.net.util.SharedLog;
import android.net.dhcp.DhcpServer.Clock;
+import android.net.util.SharedLog;
import android.util.ArrayMap;
import java.net.Inet4Address;
@@ -117,7 +118,7 @@
*/
private final LinkedHashMap<Inet4Address, Long> mDeclinedAddrs = new LinkedHashMap<>();
- public DhcpLeaseRepository(@NonNull IpPrefix prefix, @NonNull Set<Inet4Address> reservedAddrs,
+ DhcpLeaseRepository(@NonNull IpPrefix prefix, @NonNull Set<Inet4Address> reservedAddrs,
long leaseTimeMs, @NonNull SharedLog log, @NonNull Clock clock) {
updateParams(prefix, reservedAddrs, leaseTimeMs);
mLog = log;
@@ -250,8 +251,8 @@
// reqAddr null (RENEWING/REBINDING): client renewing its own lease for clientAddr.
// reqAddr set with sid not set (INIT-REBOOT): client verifying configuration.
// In both cases, throw if clientAddr or reqAddr does not match the known lease.
- throw new InvalidAddressException("Incorrect address for client in " +
- (reqAddr != null ? "INIT-REBOOT" : "RENEWING/REBINDING"));
+ throw new InvalidAddressException("Incorrect address for client in "
+ + (reqAddr != null ? "INIT-REBOOT" : "RENEWING/REBINDING"));
}
}
diff --git a/services/net/java/android/net/dhcp/DhcpPacketListener.java b/packages/NetworkStack/src/android/net/dhcp/DhcpPacketListener.java
similarity index 79%
rename from services/net/java/android/net/dhcp/DhcpPacketListener.java
rename to packages/NetworkStack/src/android/net/dhcp/DhcpPacketListener.java
index 6f620c5..dce8b61 100644
--- a/services/net/java/android/net/dhcp/DhcpPacketListener.java
+++ b/packages/NetworkStack/src/android/net/dhcp/DhcpPacketListener.java
@@ -32,32 +32,32 @@
*/
abstract class DhcpPacketListener extends FdEventsReader<DhcpPacketListener.Payload> {
static final class Payload {
- final byte[] bytes = new byte[DhcpPacket.MAX_LENGTH];
- Inet4Address srcAddr;
- int srcPort;
+ protected final byte[] mBytes = new byte[DhcpPacket.MAX_LENGTH];
+ protected Inet4Address mSrcAddr;
+ protected int mSrcPort;
}
- public DhcpPacketListener(@NonNull Handler handler) {
+ DhcpPacketListener(@NonNull Handler handler) {
super(handler, new Payload());
}
@Override
protected int recvBufSize(@NonNull Payload buffer) {
- return buffer.bytes.length;
+ return buffer.mBytes.length;
}
@Override
protected final void handlePacket(@NonNull Payload recvbuf, int length) {
- if (recvbuf.srcAddr == null) {
+ if (recvbuf.mSrcAddr == null) {
return;
}
try {
- final DhcpPacket packet = DhcpPacket.decodeFullPacket(recvbuf.bytes, length,
+ final DhcpPacket packet = DhcpPacket.decodeFullPacket(recvbuf.mBytes, length,
DhcpPacket.ENCAP_BOOTP);
- onReceive(packet, recvbuf.srcAddr, recvbuf.srcPort);
+ onReceive(packet, recvbuf.mSrcAddr, recvbuf.mSrcPort);
} catch (DhcpPacket.ParseException e) {
- logParseError(recvbuf.bytes, length, e);
+ logParseError(recvbuf.mBytes, length, e);
}
}
@@ -66,11 +66,11 @@
throws Exception {
final InetSocketAddress addr = new InetSocketAddress();
final int read = Os.recvfrom(
- fd, packetBuffer.bytes, 0, packetBuffer.bytes.length, 0 /* flags */, addr);
+ fd, packetBuffer.mBytes, 0, packetBuffer.mBytes.length, 0 /* flags */, addr);
// Buffers with null srcAddr will be dropped in handlePacket()
- packetBuffer.srcAddr = inet4AddrOrNull(addr);
- packetBuffer.srcPort = addr.getPort();
+ packetBuffer.mSrcAddr = inet4AddrOrNull(addr);
+ packetBuffer.mSrcPort = addr.getPort();
return read;
}
diff --git a/services/net/java/android/net/dhcp/DhcpServer.java b/packages/NetworkStack/src/android/net/dhcp/DhcpServer.java
similarity index 78%
rename from services/net/java/android/net/dhcp/DhcpServer.java
rename to packages/NetworkStack/src/android/net/dhcp/DhcpServer.java
index 35d29e7..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,14 +101,25 @@
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;
+ /**
+ * Clock to be used by DhcpServer to track time for lease expiration.
+ *
+ * <p>The clock should track time as may be measured by clients obtaining a lease. It does not
+ * need to be monotonous across restarts of the server as long as leases are cleared when the
+ * server is stopped.
+ */
public static class Clock {
/**
* @see SystemClock#elapsedRealtime()
@@ -110,15 +129,51 @@
}
}
+ /**
+ * Dependencies for the DhcpServer. Useful to be mocked in tests.
+ */
public interface Dependencies {
+ /**
+ * Send a packet to the specified datagram socket.
+ *
+ * @param fd File descriptor of the socket.
+ * @param buffer Data to be sent.
+ * @param dst Destination address of the packet.
+ */
void sendPacket(@NonNull FileDescriptor fd, @NonNull ByteBuffer buffer,
@NonNull InetAddress dst) throws ErrnoException, IOException;
+
+ /**
+ * Create a DhcpLeaseRepository for the server.
+ * @param servingParams Parameters used to serve DHCP requests.
+ * @param log Log to be used by the repository.
+ * @param clock Clock that the repository must use to track time.
+ */
DhcpLeaseRepository makeLeaseRepository(@NonNull DhcpServingParams servingParams,
@NonNull SharedLog log, @NonNull Clock clock);
+
+ /**
+ * Create a packet listener that will send packets to be processed.
+ */
DhcpPacketListener makePacketListener();
+
+ /**
+ * Create a clock that the server will use to track time.
+ */
Clock makeClock();
+
+ /**
+ * Add an entry to the ARP cache table.
+ * @param fd Datagram socket file descriptor that must use the new entry.
+ */
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 {
@@ -134,7 +189,7 @@
return new DhcpLeaseRepository(
DhcpServingParams.makeIpPrefix(servingParams.serverAddr),
servingParams.excludedAddrs,
- servingParams.dhcpLeaseTimeSecs*1000, log.forSubComponent(REPO_TAG), clock);
+ servingParams.dhcpLeaseTimeSecs * 1000, log.forSubComponent(REPO_TAG), clock);
}
@Override
@@ -152,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 {
@@ -160,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));
}
/**
@@ -203,38 +284,63 @@
* <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));
}
private class ServerHandler extends Handler {
- public ServerHandler(@NonNull Looper looper) {
+ ServerHandler(@NonNull Looper looper) {
super(looper);
}
@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);
+ }
}
}
}
@@ -496,22 +602,24 @@
}
private class PacketListener extends DhcpPacketListener {
- public PacketListener() {
+ PacketListener() {
super(mHandler);
}
@Override
- protected void onReceive(DhcpPacket packet, Inet4Address srcAddr, int srcPort) {
+ protected void onReceive(@NonNull DhcpPacket packet, @NonNull Inet4Address srcAddr,
+ int srcPort) {
processPacket(packet, srcPort);
}
@Override
- protected void logError(String msg, Exception e) {
+ protected void logError(@NonNull String msg, Exception e) {
mLog.e("Error receiving packet: " + msg, e);
}
@Override
- protected void logParseError(byte[] packet, int length, DhcpPacket.ParseException e) {
+ protected void logParseError(@NonNull byte[] packet, int length,
+ @NonNull DhcpPacket.ParseException e) {
mLog.e("Error parsing packet", e);
}
@@ -533,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 73%
rename from services/net/java/android/net/dhcp/DhcpServingParams.java
rename to packages/NetworkStack/src/android/net/dhcp/DhcpServingParams.java
index df15ba1..f38888a 100644
--- a/services/net/java/android/net/dhcp/DhcpServingParams.java
+++ b/packages/NetworkStack/src/android/net/dhcp/DhcpServingParams.java
@@ -17,13 +17,16 @@
package android.net.dhcp;
import static android.net.NetworkUtils.getPrefixMaskAsInet4Address;
-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 android.net.NetworkUtils.intToInet4AddressHTH;
+
+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;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.NetworkUtils;
@@ -103,6 +106,41 @@
this.metered = metered;
}
+ /**
+ * Create parameters from a stable AIDL-compatible parcel.
+ * @throws InvalidParameterException The parameters parcelable is null or invalid.
+ */
+ 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);
+ return new Builder()
+ .setServerAddr(serverAddr)
+ .setDefaultRouters(toInet4AddressSet(parcel.defaultRouters))
+ .setDnsServers(toInet4AddressSet(parcel.dnsServers))
+ .setExcludedAddrs(toInet4AddressSet(parcel.excludedAddrs))
+ .setDhcpLeaseTimeSecs(parcel.dhcpLeaseTimeSecs)
+ .setLinkMtu(parcel.linkMtu)
+ .setMetered(parcel.metered)
+ .build();
+ }
+
+ private static Set<Inet4Address> toInet4AddressSet(@Nullable int[] addrs) {
+ if (addrs == null) {
+ return new HashSet<>(0);
+ }
+
+ final HashSet<Inet4Address> res = new HashSet<>();
+ for (int addr : addrs) {
+ res.add(intToInet4AddressHTH(addr));
+ }
+ return res;
+ }
+
@NonNull
public Inet4Address getServerInet4Addr() {
return (Inet4Address) serverAddr.getAddress();
@@ -134,13 +172,13 @@
* of the parameters.
*/
public static class Builder {
- private LinkAddress serverAddr;
- private Set<Inet4Address> defaultRouters;
- private Set<Inet4Address> dnsServers;
- private Set<Inet4Address> excludedAddrs;
- private long dhcpLeaseTimeSecs;
- private int linkMtu = MTU_UNSET;
- private boolean metered;
+ private LinkAddress mServerAddr;
+ private Set<Inet4Address> mDefaultRouters;
+ private Set<Inet4Address> mDnsServers;
+ private Set<Inet4Address> mExcludedAddrs;
+ private long mDhcpLeaseTimeSecs;
+ private int mLinkMtu = MTU_UNSET;
+ private boolean mMetered;
/**
* Set the server address and served prefix for the DHCP server.
@@ -148,7 +186,7 @@
* <p>This parameter is required.
*/
public Builder setServerAddr(@NonNull LinkAddress serverAddr) {
- this.serverAddr = serverAddr;
+ this.mServerAddr = serverAddr;
return this;
}
@@ -159,7 +197,7 @@
* always be set explicitly before building the {@link DhcpServingParams}.
*/
public Builder setDefaultRouters(@NonNull Set<Inet4Address> defaultRouters) {
- this.defaultRouters = defaultRouters;
+ this.mDefaultRouters = defaultRouters;
return this;
}
@@ -189,7 +227,7 @@
* {@link DhcpServingParams}.
*/
public Builder setDnsServers(@NonNull Set<Inet4Address> dnsServers) {
- this.dnsServers = dnsServers;
+ this.mDnsServers = dnsServers;
return this;
}
@@ -219,7 +257,7 @@
* and do not need to be set here.
*/
public Builder setExcludedAddrs(@NonNull Set<Inet4Address> excludedAddrs) {
- this.excludedAddrs = excludedAddrs;
+ this.mExcludedAddrs = excludedAddrs;
return this;
}
@@ -239,7 +277,7 @@
* <p>This parameter is required.
*/
public Builder setDhcpLeaseTimeSecs(long dhcpLeaseTimeSecs) {
- this.dhcpLeaseTimeSecs = dhcpLeaseTimeSecs;
+ this.mDhcpLeaseTimeSecs = dhcpLeaseTimeSecs;
return this;
}
@@ -250,7 +288,7 @@
* is optional and defaults to {@link #MTU_UNSET}.
*/
public Builder setLinkMtu(int linkMtu) {
- this.linkMtu = linkMtu;
+ this.mLinkMtu = linkMtu;
return this;
}
@@ -260,7 +298,7 @@
* <p>If not set, the default value is false.
*/
public Builder setMetered(boolean metered) {
- this.metered = metered;
+ this.mMetered = metered;
return this;
}
@@ -274,54 +312,57 @@
*/
@NonNull
public DhcpServingParams build() throws InvalidParameterException {
- if (serverAddr == null) {
+ if (mServerAddr == null) {
throw new InvalidParameterException("Missing serverAddr");
}
- if (defaultRouters == null) {
+ if (mDefaultRouters == null) {
throw new InvalidParameterException("Missing defaultRouters");
}
- if (dnsServers == null) {
+ if (mDnsServers == null) {
// Empty set is OK, but enforce explicitly setting it
throw new InvalidParameterException("Missing dnsServers");
}
- if (dhcpLeaseTimeSecs <= 0 || dhcpLeaseTimeSecs > toUnsignedLong(INFINITE_LEASE)) {
- throw new InvalidParameterException("Invalid lease time: " + dhcpLeaseTimeSecs);
+ if (mDhcpLeaseTimeSecs <= 0 || mDhcpLeaseTimeSecs > toUnsignedLong(INFINITE_LEASE)) {
+ throw new InvalidParameterException("Invalid lease time: " + mDhcpLeaseTimeSecs);
}
- if (linkMtu != MTU_UNSET && (linkMtu < IPV4_MIN_MTU || linkMtu > IPV4_MAX_MTU)) {
- throw new InvalidParameterException("Invalid link MTU: " + linkMtu);
+ if (mLinkMtu != MTU_UNSET && (mLinkMtu < IPV4_MIN_MTU || mLinkMtu > IPV4_MAX_MTU)) {
+ throw new InvalidParameterException("Invalid link MTU: " + mLinkMtu);
}
- if (!serverAddr.isIPv4()) {
+ if (!mServerAddr.isIPv4()) {
throw new InvalidParameterException("serverAddr must be IPv4");
}
- if (serverAddr.getPrefixLength() < MIN_PREFIX_LENGTH
- || serverAddr.getPrefixLength() > MAX_PREFIX_LENGTH) {
+ if (mServerAddr.getPrefixLength() < MIN_PREFIX_LENGTH
+ || mServerAddr.getPrefixLength() > MAX_PREFIX_LENGTH) {
throw new InvalidParameterException("Prefix length is not in supported range");
}
- final IpPrefix prefix = makeIpPrefix(serverAddr);
- for (Inet4Address addr : defaultRouters) {
+ final IpPrefix prefix = makeIpPrefix(mServerAddr);
+ for (Inet4Address addr : mDefaultRouters) {
if (!prefix.contains(addr)) {
throw new InvalidParameterException(String.format(
- "Default router %s is not in server prefix %s", addr, serverAddr));
+ "Default router %s is not in server prefix %s", addr, mServerAddr));
}
}
final Set<Inet4Address> excl = new HashSet<>();
- if (excludedAddrs != null) {
- excl.addAll(excludedAddrs);
+ if (mExcludedAddrs != null) {
+ excl.addAll(mExcludedAddrs);
}
- excl.add((Inet4Address) serverAddr.getAddress());
- excl.addAll(defaultRouters);
- excl.addAll(dnsServers);
+ excl.add((Inet4Address) mServerAddr.getAddress());
+ excl.addAll(mDefaultRouters);
+ excl.addAll(mDnsServers);
- return new DhcpServingParams(serverAddr,
- Collections.unmodifiableSet(new HashSet<>(defaultRouters)),
- Collections.unmodifiableSet(new HashSet<>(dnsServers)),
+ return new DhcpServingParams(mServerAddr,
+ Collections.unmodifiableSet(new HashSet<>(mDefaultRouters)),
+ Collections.unmodifiableSet(new HashSet<>(mDnsServers)),
Collections.unmodifiableSet(excl),
- dhcpLeaseTimeSecs, linkMtu, metered);
+ mDhcpLeaseTimeSecs, mLinkMtu, mMetered);
}
}
+ /**
+ * Utility method to create an IpPrefix with the address and prefix length of a LinkAddress.
+ */
@NonNull
static IpPrefix makeIpPrefix(@NonNull LinkAddress addr) {
return new IpPrefix(addr.getAddress(), addr.getPrefixLength());
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 7f8e7b5..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,14 +30,13 @@
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;
import android.net.IpPrefix;
import android.net.MacAddress;
-import android.net.util.SharedLog;
import android.net.dhcp.DhcpServer.Clock;
+import android.net.util.SharedLog;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -126,7 +126,7 @@
mRepo.updateParams(new IpPrefix(TEST_SERVER_ADDR, 28), TEST_EXCL_SET, TEST_LEASE_TIME_MS);
// /28 should have 16 addresses, 14 w/o the first/last, 11 w/o excluded addresses
- requestAddresses((byte)11);
+ requestAddresses((byte) 11);
try {
mRepo.getOffer(null, TEST_MAC_2,
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 74%
rename from tests/net/java/android/net/dhcp/DhcpServingParamsTest.java
rename to packages/NetworkStack/tests/src/android/net/dhcp/DhcpServingParamsTest.java
index b6a4073..3ca0564 100644
--- a/tests/net/java/android/net/dhcp/DhcpServingParamsTest.java
+++ b/packages/NetworkStack/tests/src/android/net/dhcp/DhcpServingParamsTest.java
@@ -16,17 +16,18 @@
package android.net.dhcp;
+import static android.net.InetAddresses.parseNumericAddress;
+import static android.net.NetworkUtils.inet4AddressToIntHTH;
import static android.net.dhcp.DhcpServingParams.MTU_UNSET;
import static junit.framework.Assert.assertEquals;
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;
+import android.net.NetworkUtils;
import android.net.dhcp.DhcpServingParams.InvalidParameterException;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -35,8 +36,10 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.lang.reflect.Modifier;
import java.net.Inet4Address;
import java.util.Arrays;
+import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
@@ -56,6 +59,7 @@
private static final int TEST_MTU = 1500;
private static final Set<Inet4Address> TEST_EXCLUDED_ADDRS = new HashSet<>(
Arrays.asList(parseAddr("192.168.0.200"), parseAddr("192.168.0.201")));
+ private static final boolean TEST_METERED = true;
@Before
public void setUp() {
@@ -65,7 +69,8 @@
.setDnsServers(TEST_DNS_SERVERS)
.setServerAddr(TEST_LINKADDR)
.setLinkMtu(TEST_MTU)
- .setExcludedAddrs(TEST_EXCLUDED_ADDRS);
+ .setExcludedAddrs(TEST_EXCLUDED_ADDRS)
+ .setMetered(TEST_METERED);
}
@Test
@@ -91,6 +96,7 @@
assertEquals(TEST_DNS_SERVERS, params.dnsServers);
assertEquals(TEST_LINKADDR, params.serverAddr);
assertEquals(TEST_MTU, params.linkMtu);
+ assertEquals(TEST_METERED, params.metered);
assertContains(params.excludedAddrs, TEST_EXCLUDED_ADDRS);
assertContains(params.excludedAddrs, TEST_DEFAULT_ROUTERS);
@@ -159,6 +165,44 @@
mBuilder.setDefaultRouters(parseAddr("192.168.254.254")).build();
}
+ @Test
+ public void testFromParcelableObject() throws InvalidParameterException {
+ final DhcpServingParams params = mBuilder.build();
+ final DhcpServingParamsParcel parcel = new DhcpServingParamsParcel();
+ parcel.defaultRouters = toIntArray(TEST_DEFAULT_ROUTERS);
+ parcel.dhcpLeaseTimeSecs = TEST_LEASE_TIME_SECS;
+ parcel.dnsServers = toIntArray(TEST_DNS_SERVERS);
+ parcel.serverAddr = inet4AddressToIntHTH(TEST_SERVER_ADDR);
+ parcel.serverAddrPrefixLength = TEST_LINKADDR.getPrefixLength();
+ parcel.linkMtu = TEST_MTU;
+ parcel.excludedAddrs = toIntArray(TEST_EXCLUDED_ADDRS);
+ parcel.metered = TEST_METERED;
+ final DhcpServingParams parceled = DhcpServingParams.fromParcelableObject(parcel);
+
+ assertEquals(params.defaultRouters, parceled.defaultRouters);
+ assertEquals(params.dhcpLeaseTimeSecs, parceled.dhcpLeaseTimeSecs);
+ assertEquals(params.dnsServers, parceled.dnsServers);
+ assertEquals(params.serverAddr, parceled.serverAddr);
+ assertEquals(params.linkMtu, parceled.linkMtu);
+ assertEquals(params.excludedAddrs, parceled.excludedAddrs);
+ assertEquals(params.metered, parceled.metered);
+
+ // Ensure that we do not miss any field if added in the future
+ final long numFields = Arrays.stream(DhcpServingParams.class.getDeclaredFields())
+ .filter(f -> !Modifier.isStatic(f.getModifiers()))
+ .count();
+ 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();
+ }
+
private static <T> void assertContains(@NonNull Set<T> set, @NonNull Set<T> subset) {
for (final T elem : subset) {
assertContains(set, elem);
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/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java
index eed66e9..d332bac 100644
--- a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java
+++ b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java
@@ -77,7 +77,7 @@
*/
public class BarChartPreference extends Preference {
- static final int MAXIMUM_BAR_VIEWS = 4;
+ public static final int MAXIMUM_BAR_VIEWS = 4;
private static final String TAG = "BarChartPreference";
private static final int[] BAR_VIEWS = {
R.id.bar_view1,
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 570d351..27d624a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -16,6 +16,8 @@
import com.android.systemui.plugins.ClockPlugin;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.StatusBarStateController;
import java.util.Objects;
import java.util.TimeZone;
@@ -82,6 +84,24 @@
}
}
};
+ private final StatusBarStateController.StateListener mStateListener =
+ new StatusBarStateController.StateListener() {
+ @Override
+ public void onStateChanged(int newState) {
+ if (mBigClockContainer == null) {
+ return;
+ }
+ if (newState == StatusBarState.SHADE) {
+ if (mBigClockContainer.getVisibility() == View.VISIBLE) {
+ mBigClockContainer.setVisibility(View.INVISIBLE);
+ }
+ } else {
+ if (mBigClockContainer.getVisibility() == View.INVISIBLE) {
+ mBigClockContainer.setVisibility(View.VISIBLE);
+ }
+ }
+ }
+ };
public KeyguardClockSwitch(Context context) {
this(context, null);
@@ -104,12 +124,14 @@
super.onAttachedToWindow();
Dependency.get(PluginManager.class).addPluginListener(mClockPluginListener,
ClockPlugin.class);
+ Dependency.get(StatusBarStateController.class).addCallback(mStateListener);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
Dependency.get(PluginManager.class).removePluginListener(mClockPluginListener);
+ Dependency.get(StatusBarStateController.class).removeCallback(mStateListener);
}
/**
@@ -238,4 +260,9 @@
PluginListener getClockPluginListener() {
return mClockPluginListener;
}
+
+ @VisibleForTesting (otherwise = VisibleForTesting.NONE)
+ StatusBarStateController.StateListener getStateListener() {
+ return mStateListener;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 3cc9bb6..9d81064 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -56,12 +56,12 @@
import com.android.systemui.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.notification.NotificationAlertingManager;
-import com.android.systemui.statusbar.notification.NotificationData.KeyguardEnvironment;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationFilter;
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.NotificationRowBinder;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
index b0b7e6c..96f216e 100644
--- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
@@ -24,9 +24,9 @@
import android.util.Log;
import com.android.internal.statusbar.NotificationVisibility;
-import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -49,18 +49,18 @@
mForegroundServiceController = foregroundServiceController;
notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
@Override
- public void onPendingEntryAdded(NotificationData.Entry entry) {
+ public void onPendingEntryAdded(NotificationEntry entry) {
addNotification(entry.notification, entry.importance);
}
@Override
- public void onEntryUpdated(NotificationData.Entry entry) {
+ public void onEntryUpdated(NotificationEntry entry) {
updateNotification(entry.notification, entry.importance);
}
@Override
public void onEntryRemoved(
- NotificationData.Entry entry,
+ NotificationEntry entry,
NotificationVisibility visibility,
boolean removedByUser) {
removeNotification(entry.notification);
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 5347a5c..d0111cb 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -40,9 +40,9 @@
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl;
import com.android.systemui.statusbar.ScrimView;
-import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
+import com.android.systemui.statusbar.notification.collection.NotificationData;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.KeyguardBouncer;
import com.android.systemui.statusbar.phone.KeyguardEnvironmentImpl;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 6447233..7577609 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -35,9 +35,9 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.phone.StatusBarWindowController;
import java.util.ArrayList;
@@ -180,7 +180,7 @@
/**
* Adds a bubble associated with the provided notification entry or updates it if it exists.
*/
- public void addBubble(NotificationData.Entry notif) {
+ public void addBubble(NotificationEntry notif) {
if (mBubbles.containsKey(notif.key)) {
// It's an update
BubbleView bubble = mBubbles.get(notif.key);
@@ -226,7 +226,7 @@
bv.getEntry().setBubbleDismissed(true);
}
- NotificationData.Entry entry = mNotificationEntryManager.getNotificationData().get(key);
+ NotificationEntry entry = mNotificationEntryManager.getNotificationData().get(key);
if (entry != null) {
entry.setBubbleDismissed(true);
if (!DEBUG_DEMOTE_TO_NOTIF) {
@@ -241,7 +241,7 @@
@SuppressWarnings("FieldCanBeLocal")
private final NotificationEntryListener mEntryListener = new NotificationEntryListener() {
@Override
- public void onPendingEntryAdded(NotificationData.Entry entry) {
+ public void onPendingEntryAdded(NotificationEntry entry) {
if (shouldAutoBubble(mContext, entry)) {
entry.setIsBubble(true);
}
@@ -275,7 +275,7 @@
}
ArrayList<BubbleView> viewsToRemove = new ArrayList<>();
for (BubbleView bv : mBubbles.values()) {
- NotificationData.Entry entry = bv.getEntry();
+ NotificationEntry entry = bv.getEntry();
if (entry != null) {
if (entry.isRowRemoved() || entry.isBubbleDismissed() || entry.isRowDismissed()) {
viewsToRemove.add(bv);
@@ -332,7 +332,7 @@
/**
* Whether the notification should bubble or not.
*/
- private static boolean shouldAutoBubble(Context context, NotificationData.Entry entry) {
+ private static boolean shouldAutoBubble(Context context, NotificationEntry entry) {
if (entry.isBubbleDismissed()) {
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index dfd18b2..d69e7b3 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -40,7 +40,7 @@
import com.android.internal.widget.ViewClippingUtil;
import com.android.systemui.R;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
import com.android.systemui.statusbar.notification.stack.ViewState;
@@ -247,7 +247,7 @@
* @param bubbleView the view to update in the stack.
* @param entry the entry to update it with.
*/
- public void updateBubble(BubbleView bubbleView, NotificationData.Entry entry) {
+ public void updateBubble(BubbleView bubbleView, NotificationEntry entry) {
// TODO - move to top of bubble stack, make it show its update if it makes sense
bubbleView.update(entry);
if (bubbleView.equals(mExpandedBubble)) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
index 6c47aac..3307992 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
@@ -31,7 +31,7 @@
import com.android.internal.util.ContrastColorUtil;
import com.android.systemui.R;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
/**
@@ -43,7 +43,7 @@
private Context mContext;
private View mIconView;
- private NotificationData.Entry mEntry;
+ private NotificationEntry mEntry;
private int mBubbleSize;
private int mIconSize;
@@ -72,7 +72,7 @@
*
* @param entry the notification to display as a bubble.
*/
- public void setNotif(NotificationData.Entry entry) {
+ public void setNotif(NotificationEntry entry) {
removeAllViews();
// TODO: migrate to inflater
mIconView = new ImageView(mContext);
@@ -89,7 +89,7 @@
/**
* Updates the UI based on the entry.
*/
- public void update(NotificationData.Entry entry) {
+ public void update(NotificationEntry entry) {
mEntry = entry;
Notification n = entry.notification.getNotification();
Icon ic = n.getLargeIcon() != null ? n.getLargeIcon() : n.getSmallIcon();
@@ -112,7 +112,7 @@
/**
* @return the notification entry associated with this bubble.
*/
- public NotificationData.Entry getEntry() {
+ public NotificationEntry getEntry() {
return mEntry;
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
index 6a0e8ad..c4c8bc7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
@@ -50,9 +50,9 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.statusbar.NotificationMediaManager;
+import com.android.systemui.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.policy.NextAlarmController;
import com.android.systemui.statusbar.policy.NextAlarmControllerImpl;
import com.android.systemui.statusbar.policy.ZenModeController;
@@ -68,7 +68,7 @@
*/
public class KeyguardSliceProvider extends SliceProvider implements
NextAlarmController.NextAlarmChangeCallback, ZenModeController.Callback,
- NotificationMediaManager.MediaListener {
+ NotificationMediaManager.MediaListener, StatusBarStateController.StateListener {
private static final StyleSpan BOLD_STYLE = new StyleSpan(Typeface.BOLD);
public static final String KEYGUARD_SLICE_URI = "content://com.android.systemui.keyguard/main";
@@ -109,7 +109,9 @@
private AlarmManager.AlarmClockInfo mNextAlarmInfo;
private PendingIntent mPendingIntent;
protected NotificationMediaManager mMediaManager;
+ private StatusBarStateController mStatusBarStateController;
protected MediaMetadata mMediaMetaData;
+ protected boolean mDozing;
/**
* Receiver responsible for time ticking and updating the date format.
@@ -167,9 +169,20 @@
mMediaUri = Uri.parse(KEYGUARD_MEDIA_URI);
}
- public void initDependencies() {
- mMediaManager = Dependency.get(NotificationMediaManager.class);
+ /**
+ * Initialize dependencies that don't exist during {@link android.content.ContentProvider}
+ * instantiation.
+ *
+ * @param mediaManager {@link NotificationMediaManager} singleton.
+ * @param statusBarStateController {@link StatusBarStateController} singleton.
+ */
+ public void initDependencies(
+ NotificationMediaManager mediaManager,
+ StatusBarStateController statusBarStateController) {
+ mMediaManager = mediaManager;
mMediaManager.addCallback(this);
+ mStatusBarStateController = statusBarStateController;
+ mStatusBarStateController.addCallback(this);
}
@AnyThread
@@ -179,7 +192,7 @@
Slice slice;
synchronized (this) {
ListBuilder builder = new ListBuilder(getContext(), mSliceUri, ListBuilder.INFINITY);
- if (mMediaMetaData != null) {
+ if (needsMediaLocked()) {
addMediaLocked(builder);
} else {
builder.addRow(new RowBuilder(mDateUri).setTitle(mLastText));
@@ -193,6 +206,10 @@
return slice;
}
+ protected boolean needsMediaLocked() {
+ return mMediaMetaData != null && mDozing;
+ }
+
protected void addMediaLocked(ListBuilder listBuilder) {
if (mMediaMetaData != null) {
SpannableStringBuilder builder = new SpannableStringBuilder();
@@ -209,7 +226,7 @@
}
RowBuilder mediaBuilder = new RowBuilder(mMediaUri).setTitle(builder);
- Icon notificationIcon = mMediaManager.getMediaIcon();
+ Icon notificationIcon = mMediaManager == null ? null : mMediaManager.getMediaIcon();
if (notificationIcon != null) {
IconCompat icon = IconCompat.createFromIcon(notificationIcon);
mediaBuilder.addEndItem(icon, ListBuilder.ICON_IMAGE);
@@ -389,13 +406,35 @@
@Override
public void onMetadataChanged(MediaMetadata metadata) {
+ final boolean notify;
synchronized (this) {
+ boolean neededMedia = needsMediaLocked();
mMediaMetaData = metadata;
+ notify = neededMedia != needsMediaLocked();
}
- notifyChange();
+ if (notify) {
+ notifyChange();
+ }
}
protected void notifyChange() {
mContentResolver.notifyChange(mSliceUri, null /* observer */);
}
+
+ @Override
+ public void onDozingChanged(boolean isDozing) {
+ final boolean notify;
+ synchronized (this) {
+ boolean neededMedia = needsMediaLocked();
+ mDozing = isDozing;
+ notify = neededMedia != needsMediaLocked();
+ }
+ if (notify) {
+ notifyChange();
+ }
+ }
+
+ @Override
+ public void onStateChanged(int newState) {
+ }
}
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/statusbar/AlertingNotificationManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
index bc38169..a776d0f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
@@ -16,8 +16,6 @@
package com.android.systemui.statusbar;
-import static com.android.systemui.statusbar.notification.NotificationData.Entry;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Handler;
@@ -29,6 +27,7 @@
import android.view.accessibility.AccessibilityEvent;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
import java.util.stream.Stream;
@@ -48,7 +47,7 @@
* NotificationManagerService side, but we keep it to prevent the UI from looking weird and
* will remove when possible. See {@link NotificationLifetimeExtender}
*/
- protected final ArraySet<Entry> mExtendedLifetimeAlertEntries = new ArraySet<>();
+ protected final ArraySet<NotificationEntry> mExtendedLifetimeAlertEntries = new ArraySet<>();
protected NotificationSafeToRemoveCallback mNotificationLifetimeFinishedCallback;
protected int mMinimumDisplayTime;
@@ -61,7 +60,7 @@
* Adds the notification to be managed.
* @param entry entry to show
*/
- public void showNotification(@NonNull Entry entry) {
+ public void showNotification(@NonNull NotificationEntry entry) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "showNotification");
}
@@ -139,7 +138,7 @@
* @return the entry
*/
@Nullable
- public Entry getEntry(@NonNull String key) {
+ public NotificationEntry getEntry(@NonNull String key) {
AlertEntry entry = mAlertEntries.get(key);
return entry != null ? entry.mEntry : null;
}
@@ -149,7 +148,7 @@
* @return all entries
*/
@NonNull
- public Stream<Entry> getAllEntries() {
+ public Stream<NotificationEntry> getAllEntries() {
return mAlertEntries.values().stream().map(headsUpEntry -> headsUpEntry.mEntry);
}
@@ -180,7 +179,7 @@
* Add a new entry and begin managing it.
* @param entry the entry to add
*/
- protected final void addAlertEntry(@NonNull Entry entry) {
+ protected final void addAlertEntry(@NonNull NotificationEntry entry) {
AlertEntry alertEntry = createAlertEntry();
alertEntry.setEntry(entry);
mAlertEntries.put(entry.key, alertEntry);
@@ -203,7 +202,7 @@
if (alertEntry == null) {
return;
}
- Entry entry = alertEntry.mEntry;
+ NotificationEntry entry = alertEntry.mEntry;
mAlertEntries.remove(key);
onAlertEntryRemoved(alertEntry);
entry.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
@@ -250,12 +249,12 @@
}
@Override
- public boolean shouldExtendLifetime(Entry entry) {
+ public boolean shouldExtendLifetime(NotificationEntry entry) {
return !canRemoveImmediately(entry.key);
}
@Override
- public void setShouldManageLifetime(Entry entry, boolean shouldExtend) {
+ public void setShouldManageLifetime(NotificationEntry entry, boolean shouldExtend) {
if (shouldExtend) {
mExtendedLifetimeAlertEntries.add(entry);
} else {
@@ -265,17 +264,17 @@
///////////////////////////////////////////////////////////////////////////////////////////////
protected class AlertEntry implements Comparable<AlertEntry> {
- @Nullable public Entry mEntry;
+ @Nullable public NotificationEntry mEntry;
public long mPostTime;
public long mEarliestRemovaltime;
@Nullable protected Runnable mRemoveAlertRunnable;
- public void setEntry(@NonNull final Entry entry) {
+ public void setEntry(@NonNull final NotificationEntry entry) {
setEntry(entry, () -> removeAlertEntry(entry.key));
}
- public void setEntry(@NonNull final Entry entry,
+ public void setEntry(@NonNull final NotificationEntry entry,
@Nullable Runnable removeAlertRunnable) {
mEntry = entry;
mRemoveAlertRunnable = removeAlertRunnable;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
index 9bfd4ee..a3beb96 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
@@ -25,7 +25,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.R;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
import javax.inject.Inject;
@@ -83,7 +83,7 @@
@Override
protected void onAlertEntryAdded(AlertEntry alertEntry) {
- NotificationData.Entry entry = alertEntry.mEntry;
+ NotificationEntry entry = alertEntry.mEntry;
entry.setAmbientPulsing(true);
for (OnAmbientChangedListener listener : mListeners) {
listener.onAmbientStateChanged(entry, true);
@@ -92,7 +92,7 @@
@Override
protected void onAlertEntryRemoved(AlertEntry alertEntry) {
- NotificationData.Entry entry = alertEntry.mEntry;
+ NotificationEntry entry = alertEntry.mEntry;
entry.setAmbientPulsing(false);
for (OnAmbientChangedListener listener : mListeners) {
listener.onAmbientStateChanged(entry, false);
@@ -131,7 +131,7 @@
* @param entry the entry that changed
* @param isPulsing true if the entry is now pulsing, false otherwise
*/
- void onAmbientStateChanged(NotificationData.Entry entry, boolean isPulsing);
+ void onAmbientStateChanged(NotificationEntry entry, boolean isPulsing);
}
private final class AmbientEntry extends AlertEntry {
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/HeadsUpStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
index e217777..3f1ff33 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
@@ -32,7 +32,7 @@
import com.android.keyguard.AlphaOptimizedLinearLayout;
import com.android.systemui.R;
import com.android.systemui.plugins.DarkIconDispatcher;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import java.util.List;
@@ -50,7 +50,7 @@
private int mEndMargin;
private View mIconPlaceholder;
private TextView mTextView;
- private NotificationData.Entry mShowingEntry;
+ private NotificationEntry mShowingEntry;
private Rect mLayoutedIconRect = new Rect();
private int[] mTmpPosition = new int[2];
private boolean mFirstLayout = true;
@@ -162,7 +162,7 @@
mTextView = findViewById(R.id.text);
}
- public void setEntry(NotificationData.Entry entry) {
+ public void setEntry(NotificationEntry entry) {
if (entry != null) {
mShowingEntry = entry;
CharSequence text = entry.headsUpStatusBarText;
@@ -261,7 +261,7 @@
return super.fitSystemWindows(insets);
}
- public NotificationData.Entry getShowingEntry() {
+ public NotificationEntry getShowingEntry() {
return mShowingEntry;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLifetimeExtender.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLifetimeExtender.java
index ecd9814..0f295ba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLifetimeExtender.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLifetimeExtender.java
@@ -2,7 +2,7 @@
import androidx.annotation.NonNull;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
/**
* Interface for anything that may need to keep notifications managed even after
@@ -24,7 +24,7 @@
* @param entry the entry containing the notification to check
* @return true if the notification lifetime should be extended
*/
- boolean shouldExtendLifetime(@NonNull NotificationData.Entry entry);
+ boolean shouldExtendLifetime(@NonNull NotificationEntry entry);
/**
* Sets whether or not the lifetime should be managed by the extender. In practice, if
@@ -37,7 +37,7 @@
* @param entry the entry that needs an extended lifetime
* @param shouldManage true if the extender should manage the entry now, false otherwise
*/
- void setShouldManageLifetime(@NonNull NotificationData.Entry entry, boolean shouldManage);
+ void setShouldManageLifetime(@NonNull NotificationEntry entry, boolean shouldManage);
/**
* The callback for when the notification is now safe to remove (i.e. its lifetime has ended).
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
index bc662e3..f46ded4d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
@@ -18,7 +18,7 @@
import android.service.notification.StatusBarNotification;
import android.util.SparseArray;
-import com.android.systemui.statusbar.notification.NotificationData.Entry;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
public interface NotificationLockscreenUserManager {
String PERMISSION_SELF = "com.android.systemui.permission.SELF";
@@ -55,7 +55,7 @@
void updatePublicMode();
- boolean needsRedaction(Entry entry);
+ boolean needsRedaction(NotificationEntry entry);
boolean userAllowsPrivateNotificationsInPublic(int currentUserId);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index bba4369..d5f4d04 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -46,9 +46,9 @@
import com.android.systemui.Dumpable;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.statusbar.StatusBarStateController.StateListener;
-import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationUtils;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.KeyguardMonitor;
@@ -407,7 +407,7 @@
}
/** @return true if the entry needs redaction when on the lockscreen. */
- public boolean needsRedaction(NotificationData.Entry ent) {
+ public boolean needsRedaction(NotificationEntry ent) {
int userId = ent.notification.getUserId();
boolean currentUserWantsRedaction = !userAllowsPrivateNotificationsInPublic(mCurrentUserId);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index e59bc2a..7412702 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -46,9 +46,9 @@
import com.android.systemui.Dumpable;
import com.android.systemui.Interpolators;
import com.android.systemui.colorextraction.SysuiColorExtractor;
-import com.android.systemui.statusbar.notification.NotificationData.Entry;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.phone.BiometricUnlockController;
import com.android.systemui.statusbar.phone.LockscreenWallpaper;
import com.android.systemui.statusbar.phone.ScrimController;
@@ -156,7 +156,7 @@
notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
@Override
public void onEntryRemoved(
- Entry entry,
+ NotificationEntry entry,
NotificationVisibility visibility,
boolean removedByUser) {
onNotificationRemoved(entry.key);
@@ -188,7 +188,7 @@
return null;
}
synchronized (mEntryManager.getNotificationData()) {
- Entry entry = mEntryManager.getNotificationData().get(mMediaNotificationKey);
+ NotificationEntry entry = mEntryManager.getNotificationData().get(mMediaNotificationKey);
if (entry == null || entry.expandedIcon == null) {
return null;
}
@@ -210,15 +210,15 @@
boolean metaDataChanged = false;
synchronized (mEntryManager.getNotificationData()) {
- ArrayList<Entry> activeNotifications =
+ ArrayList<NotificationEntry> activeNotifications =
mEntryManager.getNotificationData().getActiveNotifications();
final int N = activeNotifications.size();
// Promote the media notification with a controller in 'playing' state, if any.
- Entry mediaNotification = null;
+ NotificationEntry mediaNotification = null;
MediaController controller = null;
for (int i = 0; i < N; i++) {
- final Entry entry = activeNotifications.get(i);
+ final NotificationEntry entry = activeNotifications.get(i);
if (entry.isMediaNotification()) {
final MediaSession.Token token =
@@ -258,7 +258,7 @@
final String pkg = aController.getPackageName();
for (int i = 0; i < N; i++) {
- final Entry entry = activeNotifications.get(i);
+ final NotificationEntry entry = activeNotifications.get(i);
if (entry.notification.getPackageName().equals(pkg)) {
if (DEBUG_MEDIA) {
Log.v(TAG, "DEBUG_MEDIA: found controller matching "
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 1ab9c5c..7d6231f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -51,9 +51,9 @@
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.Dumpable;
-import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.policy.RemoteInputView;
@@ -103,7 +103,7 @@
* Notifications that are already removed but are kept around because the remote input is
* actively being used (i.e. user is typing in it). See {@link RemoteInputActiveExtender}.
*/
- protected final ArraySet<NotificationData.Entry> mEntriesKeptForRemoteInputActive =
+ protected final ArraySet<NotificationEntry> mEntriesKeptForRemoteInputActive =
new ArraySet<>();
// Dependencies:
@@ -253,7 +253,7 @@
notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
@Override
public void onEntryRemoved(
- @Nullable NotificationData.Entry entry,
+ @Nullable NotificationEntry entry,
NotificationVisibility visibility,
boolean removedByUser) {
if (removedByUser && entry != null) {
@@ -269,7 +269,7 @@
mRemoteInputController = new RemoteInputController(delegate);
mRemoteInputController.addCallback(new RemoteInputController.Callback() {
@Override
- public void onRemoteInputSent(NotificationData.Entry entry) {
+ public void onRemoteInputSent(NotificationEntry entry) {
if (FORCE_REMOTE_INPUT_HISTORY
&& isNotificationKeptForRemoteInputHistory(entry.key)) {
mNotificationLifetimeFinishedCallback.onSafeToRemove(entry.key);
@@ -413,7 +413,7 @@
}
@VisibleForTesting
- void onPerformRemoveNotification(NotificationData.Entry entry, final String key) {
+ void onPerformRemoveNotification(NotificationEntry entry, final String key) {
if (mKeysKeptForRemoteInputHistory.contains(key)) {
mKeysKeptForRemoteInputHistory.remove(key);
}
@@ -424,7 +424,7 @@
public void onPanelCollapsed() {
for (int i = 0; i < mEntriesKeptForRemoteInputActive.size(); i++) {
- NotificationData.Entry entry = mEntriesKeptForRemoteInputActive.valueAt(i);
+ NotificationEntry entry = mEntriesKeptForRemoteInputActive.valueAt(i);
mRemoteInputController.removeRemoteInput(entry, null);
if (mNotificationLifetimeFinishedCallback != null) {
mNotificationLifetimeFinishedCallback.onSafeToRemove(entry.key);
@@ -437,7 +437,7 @@
return mKeysKeptForRemoteInputHistory.contains(key);
}
- public boolean shouldKeepForRemoteInputHistory(NotificationData.Entry entry) {
+ public boolean shouldKeepForRemoteInputHistory(NotificationEntry entry) {
if (entry.isDismissed()) {
return false;
}
@@ -447,7 +447,7 @@
return (mRemoteInputController.isSpinning(entry.key) || entry.hasJustSentRemoteInput());
}
- public boolean shouldKeepForSmartReplyHistory(NotificationData.Entry entry) {
+ public boolean shouldKeepForSmartReplyHistory(NotificationEntry entry) {
if (entry.isDismissed()) {
return false;
}
@@ -467,13 +467,13 @@
@VisibleForTesting
StatusBarNotification rebuildNotificationForCanceledSmartReplies(
- NotificationData.Entry entry) {
+ NotificationEntry entry) {
return rebuildNotificationWithRemoteInput(entry, null /* remoteInputTest */,
false /* showSpinner */);
}
@VisibleForTesting
- StatusBarNotification rebuildNotificationWithRemoteInput(NotificationData.Entry entry,
+ StatusBarNotification rebuildNotificationWithRemoteInput(NotificationEntry entry,
CharSequence remoteInputText, boolean showSpinner) {
StatusBarNotification sbn = entry.notification;
@@ -530,7 +530,7 @@
}
@VisibleForTesting
- public Set<NotificationData.Entry> getEntriesKeptForRemoteInputActive() {
+ public Set<NotificationEntry> getEntriesKeptForRemoteInputActive() {
return mEntriesKeptForRemoteInputActive;
}
@@ -553,12 +553,12 @@
*/
protected class RemoteInputHistoryExtender extends RemoteInputExtender {
@Override
- public boolean shouldExtendLifetime(@NonNull NotificationData.Entry entry) {
+ public boolean shouldExtendLifetime(@NonNull NotificationEntry entry) {
return shouldKeepForRemoteInputHistory(entry);
}
@Override
- public void setShouldManageLifetime(NotificationData.Entry entry,
+ public void setShouldManageLifetime(NotificationEntry entry,
boolean shouldExtend) {
if (shouldExtend) {
CharSequence remoteInputText = entry.remoteInputText;
@@ -599,12 +599,12 @@
*/
protected class SmartReplyHistoryExtender extends RemoteInputExtender {
@Override
- public boolean shouldExtendLifetime(@NonNull NotificationData.Entry entry) {
+ public boolean shouldExtendLifetime(@NonNull NotificationEntry entry) {
return shouldKeepForSmartReplyHistory(entry);
}
@Override
- public void setShouldManageLifetime(NotificationData.Entry entry,
+ public void setShouldManageLifetime(NotificationEntry entry,
boolean shouldExtend) {
if (shouldExtend) {
StatusBarNotification newSbn = rebuildNotificationForCanceledSmartReplies(entry);
@@ -637,7 +637,7 @@
*/
protected class RemoteInputActiveExtender extends RemoteInputExtender {
@Override
- public boolean shouldExtendLifetime(@NonNull NotificationData.Entry entry) {
+ public boolean shouldExtendLifetime(@NonNull NotificationEntry entry) {
if (entry.isDismissed()) {
return false;
}
@@ -645,7 +645,7 @@
}
@Override
- public void setShouldManageLifetime(NotificationData.Entry entry,
+ public void setShouldManageLifetime(NotificationEntry entry,
boolean shouldExtend) {
if (shouldExtend) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java
index f23ae3f..f0d804d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java
@@ -24,7 +24,7 @@
import androidx.annotation.VisibleForTesting;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import java.util.ArrayList;
import java.util.Arrays;
@@ -52,7 +52,7 @@
}
public static NotificationUiAdjustment extractFromNotificationEntry(
- NotificationData.Entry entry) {
+ NotificationEntry entry) {
return new NotificationUiAdjustment(
entry.key, entry.systemGeneratedSmartActions, entry.smartReplies);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 017a9c3..bf6caa0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -27,9 +27,9 @@
import com.android.systemui.R;
import com.android.systemui.bubbles.BubbleController;
-import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
@@ -150,13 +150,13 @@
*/
//TODO: Rewrite this to focus on Entries, or some other data object instead of views
public void updateNotificationViews() {
- ArrayList<NotificationData.Entry> activeNotifications = mEntryManager.getNotificationData()
+ ArrayList<NotificationEntry> activeNotifications = mEntryManager.getNotificationData()
.getActiveNotifications();
ArrayList<ExpandableNotificationRow> toShow = new ArrayList<>(activeNotifications.size());
- ArrayList<NotificationData.Entry> toBubble = new ArrayList<>();
+ ArrayList<NotificationEntry> toBubble = new ArrayList<>();
final int N = activeNotifications.size();
for (int i = 0; i < N; i++) {
- NotificationData.Entry ent = activeNotifications.get(i);
+ NotificationEntry ent = activeNotifications.get(i);
if (ent.isRowDismissed() || ent.isRowRemoved()) {
// we don't want to update removed notifications because they could
// temporarily become children if they were isolated before.
@@ -187,7 +187,7 @@
ent.getRow().setSensitive(sensitive, deviceSensitive);
ent.getRow().setNeedsRedaction(needsRedaction);
if (mGroupManager.isChildInGroupWithSummary(ent.notification)) {
- NotificationData.Entry summary = mGroupManager.getGroupSummary(ent.notification);
+ NotificationEntry summary = mGroupManager.getGroupSummary(ent.notification);
List<ExpandableNotificationRow> orderedChildren =
mTmpChildOrderMap.get(summary.getRow());
if (orderedChildren == null) {
@@ -271,7 +271,7 @@
for (int i = 0; i < toBubble.size(); i++) {
// TODO: might make sense to leave them in the shade and just reposition them
- NotificationData.Entry ent = toBubble.get(i);
+ NotificationEntry ent = toBubble.get(i);
mBubbleController.addBubble(ent);
}
@@ -385,7 +385,7 @@
}
while(!stack.isEmpty()) {
ExpandableNotificationRow row = stack.pop();
- NotificationData.Entry entry = row.getEntry();
+ NotificationEntry entry = row.getEntry();
boolean isChildNotification =
mGroupManager.isChildInGroupWithSummary(entry.notification);
@@ -408,7 +408,7 @@
if (!showOnKeyguard) {
// min priority notifications should show if their summary is showing
if (mGroupManager.isChildInGroupWithSummary(entry.notification)) {
- NotificationData.Entry summary = mGroupManager.getLogicalGroupSummary(
+ NotificationEntry summary = mGroupManager.getLogicalGroupSummary(
entry.notification);
if (summary != null && mLockscreenUserManager.shouldShowOnKeyguard(
summary.notification)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
index e8abcc2..998cf52 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
@@ -24,7 +24,7 @@
import android.util.Pair;
import com.android.internal.util.Preconditions;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.policy.RemoteInputView;
import java.lang.ref.WeakReference;
@@ -38,7 +38,7 @@
private static final boolean ENABLE_REMOTE_INPUT =
SystemProperties.getBoolean("debug.enable_remote_input", true);
- private final ArrayList<Pair<WeakReference<NotificationData.Entry>, Object>> mOpen
+ private final ArrayList<Pair<WeakReference<NotificationEntry>, Object>> mOpen
= new ArrayList<>();
private final ArrayMap<String, Object> mSpinning = new ArrayMap<>();
private final ArrayList<Callback> mCallbacks = new ArrayList<>(3);
@@ -101,7 +101,7 @@
* @param entry the entry for which a remote input is now active.
* @param token a token identifying the view that is managing the remote input
*/
- public void addRemoteInput(NotificationData.Entry entry, Object token) {
+ public void addRemoteInput(NotificationEntry entry, Object token) {
Preconditions.checkNotNull(entry);
Preconditions.checkNotNull(token);
@@ -122,7 +122,7 @@
* the entry is only removed if the token matches the last added token for this
* entry. If null, the entry is removed regardless.
*/
- public void removeRemoteInput(NotificationData.Entry entry, Object token) {
+ public void removeRemoteInput(NotificationEntry entry, Object token) {
Preconditions.checkNotNull(entry);
pruneWeakThenRemoveAndContains(null /* contains */, entry /* remove */, token);
@@ -173,7 +173,7 @@
return mSpinning.get(key) == token;
}
- private void apply(NotificationData.Entry entry) {
+ private void apply(NotificationEntry entry) {
mDelegate.setRemoteInputActive(entry, isRemoteInputActive(entry));
boolean remoteInputActive = isRemoteInputActive();
int N = mCallbacks.size();
@@ -185,7 +185,7 @@
/**
* @return true if {@param entry} has an active RemoteInput
*/
- public boolean isRemoteInputActive(NotificationData.Entry entry) {
+ public boolean isRemoteInputActive(NotificationEntry entry) {
return pruneWeakThenRemoveAndContains(entry /* contains */, null /* remove */,
null /* removeToken */);
}
@@ -208,10 +208,10 @@
* @return true if {@param contains} is in the set of active remote inputs
*/
private boolean pruneWeakThenRemoveAndContains(
- NotificationData.Entry contains, NotificationData.Entry remove, Object removeToken) {
+ NotificationEntry contains, NotificationEntry remove, Object removeToken) {
boolean found = false;
for (int i = mOpen.size() - 1; i >= 0; i--) {
- NotificationData.Entry item = mOpen.get(i).first.get();
+ NotificationEntry item = mOpen.get(i).first.get();
Object itemToken = mOpen.get(i).second;
boolean removeTokenMatches = (removeToken == null || itemToken == removeToken);
@@ -235,7 +235,7 @@
mCallbacks.add(callback);
}
- public void remoteInputSent(NotificationData.Entry entry) {
+ public void remoteInputSent(NotificationEntry entry) {
int N = mCallbacks.size();
for (int i = 0; i < N; i++) {
mCallbacks.get(i).onRemoteInputSent(entry);
@@ -248,16 +248,16 @@
}
// Make a copy because closing the remote inputs will modify mOpen.
- ArrayList<NotificationData.Entry> list = new ArrayList<>(mOpen.size());
+ ArrayList<NotificationEntry> list = new ArrayList<>(mOpen.size());
for (int i = mOpen.size() - 1; i >= 0; i--) {
- NotificationData.Entry entry = mOpen.get(i).first.get();
+ NotificationEntry entry = mOpen.get(i).first.get();
if (entry != null && entry.rowExists()) {
list.add(entry);
}
}
for (int i = list.size() - 1; i >= 0; i--) {
- NotificationData.Entry entry = list.get(i);
+ NotificationEntry entry = list.get(i);
if (entry.rowExists()) {
entry.closeRemoteInput();
}
@@ -268,31 +268,31 @@
mDelegate.requestDisallowLongPressAndDismiss();
}
- public void lockScrollTo(NotificationData.Entry entry) {
+ public void lockScrollTo(NotificationEntry entry) {
mDelegate.lockScrollTo(entry);
}
public interface Callback {
default void onRemoteInputActive(boolean active) {}
- default void onRemoteInputSent(NotificationData.Entry entry) {}
+ default void onRemoteInputSent(NotificationEntry entry) {}
}
public interface Delegate {
/**
* Activate remote input if necessary.
*/
- void setRemoteInputActive(NotificationData.Entry entry, boolean remoteInputActive);
+ void setRemoteInputActive(NotificationEntry entry, boolean remoteInputActive);
- /**
- * Request that the view does not dismiss nor perform long press for the current touch.
- */
- void requestDisallowLongPressAndDismiss();
+ /**
+ * Request that the view does not dismiss nor perform long press for the current touch.
+ */
+ void requestDisallowLongPressAndDismiss();
- /**
- * Request that the view is made visible by scrolling to it, and keep the scroll locked until
- * the user scrolls, or {@param v} loses focus or is detached.
- */
- void lockScrollTo(NotificationData.Entry entry);
+ /**
+ * Request that the view is made visible by scrolling to it, and keep the scroll locked until
+ * the user scrolls, or {@param entry} loses focus or is detached.
+ */
+ void lockScrollTo(NotificationEntry entry);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
index 9e91133..573c1f8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
@@ -21,8 +21,8 @@
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
-import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import java.util.Set;
@@ -54,7 +54,7 @@
/**
* Notifies StatusBarService a smart reply is sent.
*/
- public void smartReplySent(NotificationData.Entry entry, int replyIndex, CharSequence reply,
+ public void smartReplySent(NotificationEntry entry, int replyIndex, CharSequence reply,
boolean generatedByAssistant) {
mCallback.onSmartReplySent(entry, reply);
mSendingKeys.add(entry.key);
@@ -70,7 +70,7 @@
* Notifies StatusBarService a smart action is clicked.
*/
public void smartActionClicked(
- NotificationData.Entry entry, int actionIndex, Notification.Action action,
+ NotificationEntry entry, int actionIndex, Notification.Action action,
boolean generatedByAssistant) {
final int count = mEntryManager.getNotificationData().getActiveNotifications().size();
final int rank = mEntryManager.getNotificationData().getRank(entry.key);
@@ -95,7 +95,7 @@
/**
* Smart Replies and Actions have been added to the UI.
*/
- public void smartSuggestionsAdded(final NotificationData.Entry entry, int replyCount,
+ public void smartSuggestionsAdded(final NotificationEntry entry, int replyCount,
int actionCount, boolean generatedByAssistant) {
try {
mBarService.onNotificationSmartSuggestionsAdded(
@@ -105,7 +105,7 @@
}
}
- public void stopSending(final NotificationData.Entry entry) {
+ public void stopSending(final NotificationEntry entry) {
if (entry != null) {
mSendingKeys.remove(entry.notification.getKey());
}
@@ -121,6 +121,6 @@
* @param entry the entry for the notification
* @param reply the reply that was sent
*/
- void onSmartReplySent(NotificationData.Entry entry, CharSequence reply);
+ void onSmartReplySent(NotificationEntry entry, CharSequence reply);
}
}
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 2bb0d5c..c24698d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
@@ -29,6 +29,7 @@
import com.android.systemui.statusbar.AmbientPulseManager;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.NotificationInflater;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -71,18 +72,18 @@
notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
@Override
- public void onEntryInflated(NotificationData.Entry entry, int inflatedFlags) {
+ public void onEntryInflated(NotificationEntry entry, int inflatedFlags) {
showAlertingView(entry, inflatedFlags);
}
@Override
- public void onEntryUpdated(NotificationData.Entry entry) {
+ public void onEntryUpdated(NotificationEntry entry) {
updateAlertState(entry);
}
@Override
public void onEntryRemoved(
- NotificationData.Entry entry,
+ NotificationEntry entry,
NotificationVisibility visibility,
boolean removedByUser) {
stopAlerting(entry.key);
@@ -101,7 +102,7 @@
* @param entry entry to add
* @param inflatedFlags flags representing content views that were inflated
*/
- private void showAlertingView(NotificationData.Entry entry,
+ private void showAlertingView(NotificationEntry entry,
@NotificationInflater.InflationFlag int inflatedFlags) {
if ((inflatedFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0) {
// Possible for shouldHeadsUp to change between the inflation starting and ending.
@@ -123,7 +124,7 @@
}
}
- private void updateAlertState(NotificationData.Entry entry) {
+ private void updateAlertState(NotificationEntry entry) {
boolean alertAgain = alertAgain(entry, entry.notification.getNotification());
AlertingNotificationManager alertManager;
boolean shouldAlert;
@@ -150,7 +151,7 @@
}
private static boolean alertAgain(
- NotificationData.Entry oldEntry, Notification newNotification) {
+ NotificationEntry oldEntry, Notification newNotification) {
return oldEntry == null || !oldEntry.hasInterrupted()
|| (newNotification.flags & Notification.FLAG_ONLY_ALERT_ONCE) == 0;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
deleted file mode 100644
index a51896e..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
+++ /dev/null
@@ -1,1072 +0,0 @@
-/*
- * Copyright (C) 2008 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.systemui.statusbar.notification;
-
-import static android.app.Notification.CATEGORY_ALARM;
-import static android.app.Notification.CATEGORY_CALL;
-import static android.app.Notification.CATEGORY_EVENT;
-import static android.app.Notification.CATEGORY_MESSAGE;
-import static android.app.Notification.CATEGORY_REMINDER;
-import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
-import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
-import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST;
-import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
-import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
-
-import android.annotation.NonNull;
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
-import android.app.Person;
-import android.content.Context;
-import android.graphics.drawable.Icon;
-import android.os.Bundle;
-import android.os.Parcelable;
-import android.os.SystemClock;
-import android.service.notification.NotificationListenerService.Ranking;
-import android.service.notification.NotificationListenerService.RankingMap;
-import android.service.notification.SnoozeCriterion;
-import android.service.notification.StatusBarNotification;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.view.View;
-import android.widget.ImageView;
-
-import androidx.annotation.Nullable;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.statusbar.StatusBarIcon;
-import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.ContrastColorUtil;
-import com.android.systemui.Dependency;
-import com.android.systemui.statusbar.InflationTask;
-import com.android.systemui.statusbar.NotificationMediaManager;
-import com.android.systemui.statusbar.StatusBarIconView;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.row.NotificationGuts;
-import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * The list of currently displaying notifications.
- */
-public class NotificationData {
-
- private final NotificationFilter mNotificationFilter = Dependency.get(NotificationFilter.class);
-
- /**
- * These dependencies are late init-ed
- */
- private KeyguardEnvironment mEnvironment;
- private NotificationMediaManager mMediaManager;
-
- private HeadsUpManager mHeadsUpManager;
-
- public static final class Entry {
- private static final long LAUNCH_COOLDOWN = 2000;
- private static final long REMOTE_INPUT_COOLDOWN = 500;
- private static final long INITIALIZATION_DELAY = 400;
- private static final long NOT_LAUNCHED_YET = -LAUNCH_COOLDOWN;
- private static final int COLOR_INVALID = 1;
- public final String key;
- public StatusBarNotification notification;
- public NotificationChannel channel;
- public long lastAudiblyAlertedMs;
- public boolean noisy;
- public boolean ambient;
- public int importance;
- public StatusBarIconView icon;
- public StatusBarIconView expandedIcon;
- private boolean interruption;
- public boolean autoRedacted; // whether the redacted notification was generated by us
- public int targetSdk;
- private long lastFullScreenIntentLaunchTime = NOT_LAUNCHED_YET;
- public CharSequence remoteInputText;
- public List<SnoozeCriterion> snoozeCriteria;
- public int userSentiment = Ranking.USER_SENTIMENT_NEUTRAL;
- /** Smart Actions provided by the NotificationAssistantService. */
- @NonNull
- public List<Notification.Action> systemGeneratedSmartActions = Collections.emptyList();
- public CharSequence[] smartReplies = new CharSequence[0];
- @VisibleForTesting
- public int suppressedVisualEffects;
- public boolean suspended;
-
- private Entry parent; // our parent (if we're in a group)
- private ArrayList<Entry> children = new ArrayList<Entry>();
- private ExpandableNotificationRow row; // the outer expanded view
-
- private int mCachedContrastColor = COLOR_INVALID;
- private int mCachedContrastColorIsFor = COLOR_INVALID;
- private InflationTask mRunningTask = null;
- private Throwable mDebugThrowable;
- public CharSequence remoteInputTextWhenReset;
- public long lastRemoteInputSent = NOT_LAUNCHED_YET;
- public ArraySet<Integer> mActiveAppOps = new ArraySet<>(3);
- public CharSequence headsUpStatusBarText;
- public CharSequence headsUpStatusBarTextPublic;
-
- private long initializationTime = -1;
-
- /**
- * Whether or not this row represents a system notification. Note that if this is
- * {@code null}, that means we were either unable to retrieve the info or have yet to
- * retrieve the info.
- */
- public Boolean mIsSystemNotification;
-
- /**
- * Has the user sent a reply through this Notification.
- */
- private boolean hasSentReply;
-
- /**
- * Whether this notification should be displayed as a bubble.
- */
- private boolean mIsBubble;
-
- /**
- * Whether the user has dismissed this notification when it was in bubble form.
- */
- private boolean mUserDismissedBubble;
-
- public Entry(StatusBarNotification n) {
- this(n, null);
- }
-
- public Entry(StatusBarNotification n, @Nullable Ranking ranking) {
- this.key = n.getKey();
- this.notification = n;
- if (ranking != null) {
- populateFromRanking(ranking);
- }
- }
-
- public void populateFromRanking(@NonNull Ranking ranking) {
- channel = ranking.getChannel();
- lastAudiblyAlertedMs = ranking.getLastAudiblyAlertedMillis();
- importance = ranking.getImportance();
- ambient = ranking.isAmbient();
- snoozeCriteria = ranking.getSnoozeCriteria();
- userSentiment = ranking.getUserSentiment();
- systemGeneratedSmartActions = ranking.getSmartActions() == null
- ? Collections.emptyList() : ranking.getSmartActions();
- smartReplies = ranking.getSmartReplies() == null
- ? new CharSequence[0]
- : ranking.getSmartReplies().toArray(new CharSequence[0]);
- suppressedVisualEffects = ranking.getSuppressedVisualEffects();
- suspended = ranking.isSuspended();
- }
-
- public void setInterruption() {
- interruption = true;
- }
-
- public boolean hasInterrupted() {
- return interruption;
- }
-
- public void setIsBubble(boolean bubbleable) {
- mIsBubble = bubbleable;
- }
-
- public boolean isBubble() {
- return mIsBubble;
- }
-
- public void setBubbleDismissed(boolean userDismissed) {
- mUserDismissedBubble = userDismissed;
- }
-
- public boolean isBubbleDismissed() {
- return mUserDismissedBubble;
- }
-
- /**
- * Resets the notification entry to be re-used.
- */
- public void reset() {
- if (row != null) {
- row.reset();
- }
- }
-
- public ExpandableNotificationRow getRow() {
- return row;
- }
-
- //TODO: This will go away when we have a way to bind an entry to a row
- public void setRow(ExpandableNotificationRow row) {
- this.row = row;
- }
-
- @Nullable
- public List<Entry> getChildren() {
- if (children.size() <= 0) {
- return null;
- }
-
- return children;
- }
-
- public void notifyFullScreenIntentLaunched() {
- setInterruption();
- lastFullScreenIntentLaunchTime = SystemClock.elapsedRealtime();
- }
-
- public boolean hasJustLaunchedFullScreenIntent() {
- return SystemClock.elapsedRealtime() < lastFullScreenIntentLaunchTime + LAUNCH_COOLDOWN;
- }
-
- public boolean hasJustSentRemoteInput() {
- return SystemClock.elapsedRealtime() < lastRemoteInputSent + REMOTE_INPUT_COOLDOWN;
- }
-
- public boolean hasFinishedInitialization() {
- return initializationTime == -1 ||
- SystemClock.elapsedRealtime() > initializationTime + INITIALIZATION_DELAY;
- }
-
- /**
- * Create the icons for a notification
- * @param context the context to create the icons with
- * @param sbn the notification
- * @throws InflationException
- */
- public void createIcons(Context context, StatusBarNotification sbn)
- throws InflationException {
- Notification n = sbn.getNotification();
- final Icon smallIcon = n.getSmallIcon();
- if (smallIcon == null) {
- throw new InflationException("No small icon in notification from "
- + sbn.getPackageName());
- }
-
- // Construct the icon.
- icon = new StatusBarIconView(context,
- sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), sbn);
- icon.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
-
- // Construct the expanded icon.
- expandedIcon = new StatusBarIconView(context,
- sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), sbn);
- expandedIcon.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
- final StatusBarIcon ic = new StatusBarIcon(
- sbn.getUser(),
- sbn.getPackageName(),
- smallIcon,
- n.iconLevel,
- n.number,
- StatusBarIconView.contentDescForNotification(context, n));
- if (!icon.set(ic) || !expandedIcon.set(ic)) {
- icon = null;
- expandedIcon = null;
- throw new InflationException("Couldn't create icon: " + ic);
- }
- expandedIcon.setVisibility(View.INVISIBLE);
- expandedIcon.setOnVisibilityChangedListener(
- newVisibility -> {
- if (row != null) {
- row.setIconsVisible(newVisibility != View.VISIBLE);
- }
- });
- }
-
- public void setIconTag(int key, Object tag) {
- if (icon != null) {
- icon.setTag(key, tag);
- expandedIcon.setTag(key, tag);
- }
- }
-
- /**
- * Update the notification icons.
- *
- * @param context the context to create the icons with.
- * @param sbn the notification to read the icon from.
- * @throws InflationException
- */
- public void updateIcons(Context context, StatusBarNotification sbn)
- throws InflationException {
- if (icon != null) {
- // Update the icon
- Notification n = sbn.getNotification();
- final StatusBarIcon ic = new StatusBarIcon(
- notification.getUser(),
- notification.getPackageName(),
- n.getSmallIcon(),
- n.iconLevel,
- n.number,
- StatusBarIconView.contentDescForNotification(context, n));
- icon.setNotification(sbn);
- expandedIcon.setNotification(sbn);
- if (!icon.set(ic) || !expandedIcon.set(ic)) {
- throw new InflationException("Couldn't update icon: " + ic);
- }
- }
- }
-
- public int getContrastedColor(Context context, boolean isLowPriority,
- int backgroundColor) {
- int rawColor = isLowPriority ? Notification.COLOR_DEFAULT :
- notification.getNotification().color;
- if (mCachedContrastColorIsFor == rawColor && mCachedContrastColor != COLOR_INVALID) {
- return mCachedContrastColor;
- }
- final int contrasted = ContrastColorUtil.resolveContrastColor(context, rawColor,
- backgroundColor);
- mCachedContrastColorIsFor = rawColor;
- mCachedContrastColor = contrasted;
- return mCachedContrastColor;
- }
-
- /**
- * Abort all existing inflation tasks
- */
- public void abortTask() {
- if (mRunningTask != null) {
- mRunningTask.abort();
- mRunningTask = null;
- }
- }
-
- public void setInflationTask(InflationTask abortableTask) {
- // abort any existing inflation
- InflationTask existing = mRunningTask;
- abortTask();
- mRunningTask = abortableTask;
- if (existing != null && mRunningTask != null) {
- mRunningTask.supersedeTask(existing);
- }
- }
-
- public void onInflationTaskFinished() {
- mRunningTask = null;
- }
-
- @VisibleForTesting
- public InflationTask getRunningTask() {
- return mRunningTask;
- }
-
- /**
- * Set a throwable that is used for debugging
- *
- * @param debugThrowable the throwable to save
- */
- public void setDebugThrowable(Throwable debugThrowable) {
- mDebugThrowable = debugThrowable;
- }
-
- public Throwable getDebugThrowable() {
- return mDebugThrowable;
- }
-
- public void onRemoteInputInserted() {
- lastRemoteInputSent = NOT_LAUNCHED_YET;
- remoteInputTextWhenReset = null;
- }
-
- public void setHasSentReply() {
- hasSentReply = true;
- }
-
- public boolean isLastMessageFromReply() {
- if (!hasSentReply) {
- return false;
- }
- Bundle extras = notification.getNotification().extras;
- CharSequence[] replyTexts = extras.getCharSequenceArray(
- Notification.EXTRA_REMOTE_INPUT_HISTORY);
- if (!ArrayUtils.isEmpty(replyTexts)) {
- return true;
- }
- Parcelable[] messages = extras.getParcelableArray(Notification.EXTRA_MESSAGES);
- if (messages != null && messages.length > 0) {
- Parcelable message = messages[messages.length - 1];
- if (message instanceof Bundle) {
- Notification.MessagingStyle.Message lastMessage =
- Notification.MessagingStyle.Message.getMessageFromBundle(
- (Bundle) message);
- if (lastMessage != null) {
- Person senderPerson = lastMessage.getSenderPerson();
- if (senderPerson == null) {
- return true;
- }
- Person user = extras.getParcelable(Notification.EXTRA_MESSAGING_PERSON);
- return Objects.equals(user, senderPerson);
- }
- }
- }
- return false;
- }
-
- public void setInitializationTime(long time) {
- if (initializationTime == -1) {
- initializationTime = time;
- }
- }
-
- public void sendAccessibilityEvent(int eventType) {
- if (row != null) {
- row.sendAccessibilityEvent(eventType);
- }
- }
-
- /**
- * Used by NotificationMediaManager to determine... things
- * @return {@code true} if we are a media notification
- */
- public boolean isMediaNotification() {
- if (row == null) return false;
-
- return row.isMediaRow();
- }
-
- /**
- * We are a top level child if our parent is the list of notifications duh
- * @return {@code true} if we're a top level notification
- */
- public boolean isTopLevelChild() {
- return row != null && row.isTopLevelChild();
- }
-
- public void resetUserExpansion() {
- if (row != null) row.resetUserExpansion();
- }
-
- public void freeContentViewWhenSafe(@InflationFlag int inflationFlag) {
- if (row != null) row.freeContentViewWhenSafe(inflationFlag);
- }
-
- public void setAmbientPulsing(boolean pulsing) {
- if (row != null) row.setAmbientPulsing(pulsing);
- }
-
- public boolean rowExists() {
- return row != null;
- }
-
- public boolean isRowDismissed() {
- return row != null && row.isDismissed();
- }
-
- public boolean isRowRemoved() {
- return row != null && row.isRemoved();
- }
-
- /**
- * @return {@code true} if the row is null or removed
- */
- public boolean isRemoved() {
- //TODO: recycling invalidates this
- return row == null || row.isRemoved();
- }
-
- /**
- * @return {@code true} if the row is null or dismissed
- */
- public boolean isDismissed() {
- //TODO: recycling
- return row == null || row.isDismissed();
- }
-
- public boolean isRowPinned() {
- return row != null && row.isPinned();
- }
-
- public void setRowPinned(boolean pinned) {
- if (row != null) row.setPinned(pinned);
- }
-
- public boolean isRowAnimatingAway() {
- return row != null && row.isHeadsUpAnimatingAway();
- }
-
- public boolean isRowHeadsUp() {
- return row != null && row.isHeadsUp();
- }
-
- public void setHeadsUp(boolean shouldHeadsUp) {
- if (row != null) row.setHeadsUp(shouldHeadsUp);
- }
-
- public boolean mustStayOnScreen() {
- return row != null && row.mustStayOnScreen();
- }
-
- public void setHeadsUpIsVisible() {
- if (row != null) row.setHeadsUpIsVisible();
- }
-
- //TODO: i'm imagining a world where this isn't just the row, but I could be rwong
- public ExpandableNotificationRow getHeadsUpAnimationView() {
- return row;
- }
-
- public void setUserLocked(boolean userLocked) {
- if (row != null) row.setUserLocked(userLocked);
- }
-
- public void setUserExpanded(boolean userExpanded, boolean allowChildExpansion) {
- if (row != null) row.setUserExpanded(userExpanded, allowChildExpansion);
- }
-
- public void setGroupExpansionChanging(boolean changing) {
- if (row != null) row.setGroupExpansionChanging(changing);
- }
-
- public void notifyHeightChanged(boolean needsAnimation) {
- if (row != null) row.notifyHeightChanged(needsAnimation);
- }
-
- public void closeRemoteInput() {
- if (row != null) row.closeRemoteInput();
- }
-
- public boolean areChildrenExpanded() {
- return row != null && row.areChildrenExpanded();
- }
-
- public boolean keepInParent() {
- return row != null && row.keepInParent();
- }
-
- //TODO: probably less confusing to say "is group fully visible"
- public boolean isGroupNotFullyVisible() {
- return row == null || row.isGroupNotFullyVisible();
- }
-
- public NotificationGuts getGuts() {
- if (row != null) return row.getGuts();
- return null;
- }
-
- public boolean hasLowPriorityStateUpdated() {
- return row != null && row.hasLowPriorityStateUpdated();
- }
-
- public void removeRow() {
- if (row != null) row.setRemoved();
- }
-
- public boolean isSummaryWithChildren() {
- return row != null && row.isSummaryWithChildren();
- }
-
- public void setKeepInParent(boolean keep) {
- if (row != null) row.setKeepInParent(keep);
- }
-
- public void onDensityOrFontScaleChanged() {
- if (row != null) row.onDensityOrFontScaleChanged();
- }
-
- public boolean areGutsExposed() {
- return row != null && row.getGuts() != null && row.getGuts().isExposed();
- }
-
- public boolean isChildInGroup() {
- return parent == null;
- }
-
- public void setLowPriorityStateUpdated(boolean updated) {
- if (row != null) row.setLowPriorityStateUpdated(updated);
- }
-
- /**
- * @return Can the underlying notification be cleared? This can be different from whether the
- * notification can be dismissed in case notifications are sensitive on the lockscreen.
- * @see #canViewBeDismissed()
- */
- public boolean isClearable() {
- if (notification == null || !notification.isClearable()) {
- return false;
- }
- if (children.size() > 0) {
- for (int i = 0; i < children.size(); i++) {
- Entry child = children.get(i);
- if (!child.isClearable()) {
- return false;
- }
- }
- }
- return true;
- }
-
- public boolean canViewBeDismissed() {
- if (row == null) return true;
- return row.canViewBeDismissed();
- }
-
- boolean isExemptFromDndVisualSuppression() {
- if (isNotificationBlockedByPolicy(notification.getNotification())) {
- return false;
- }
-
- if ((notification.getNotification().flags
- & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
- return true;
- }
- if (notification.getNotification().isMediaNotification()) {
- return true;
- }
- if (mIsSystemNotification != null && mIsSystemNotification) {
- return true;
- }
- return false;
- }
-
- private boolean shouldSuppressVisualEffect(int effect) {
- if (isExemptFromDndVisualSuppression()) {
- return false;
- }
- return (suppressedVisualEffects & effect) != 0;
- }
-
- /**
- * Returns whether {@link NotificationManager.Policy#SUPPRESSED_EFFECT_FULL_SCREEN_INTENT}
- * is set for this entry.
- */
- public boolean shouldSuppressFullScreenIntent() {
- return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_FULL_SCREEN_INTENT);
- }
-
- /**
- * Returns whether {@link NotificationManager.Policy#SUPPRESSED_EFFECT_PEEK}
- * is set for this entry.
- */
- public boolean shouldSuppressPeek() {
- return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_PEEK);
- }
-
- /**
- * Returns whether {@link NotificationManager.Policy#SUPPRESSED_EFFECT_STATUS_BAR}
- * is set for this entry.
- */
- public boolean shouldSuppressStatusBar() {
- return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_STATUS_BAR);
- }
-
- /**
- * Returns whether {@link NotificationManager.Policy#SUPPRESSED_EFFECT_AMBIENT}
- * is set for this entry.
- */
- public boolean shouldSuppressAmbient() {
- return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_AMBIENT);
- }
-
- /**
- * Returns whether {@link NotificationManager.Policy#SUPPRESSED_EFFECT_NOTIFICATION_LIST}
- * is set for this entry.
- */
- public boolean shouldSuppressNotificationList() {
- return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_NOTIFICATION_LIST);
- }
- }
-
- private final ArrayMap<String, Entry> mEntries = new ArrayMap<>();
- private final ArrayList<Entry> mSortedAndFiltered = new ArrayList<>();
- private final ArrayList<Entry> mFilteredForUser = new ArrayList<>();
-
- private final NotificationGroupManager mGroupManager
- = Dependency.get(NotificationGroupManager.class);
-
- private RankingMap mRankingMap;
- private final Ranking mTmpRanking = new Ranking();
-
- public void setHeadsUpManager(HeadsUpManager headsUpManager) {
- mHeadsUpManager = headsUpManager;
- }
-
- private final Comparator<Entry> mRankingComparator = new Comparator<Entry>() {
- private final Ranking mRankingA = new Ranking();
- private final Ranking mRankingB = new Ranking();
-
- @Override
- public int compare(Entry a, Entry b) {
- final StatusBarNotification na = a.notification;
- final StatusBarNotification nb = b.notification;
- int aImportance = NotificationManager.IMPORTANCE_DEFAULT;
- int bImportance = NotificationManager.IMPORTANCE_DEFAULT;
- int aRank = 0;
- int bRank = 0;
-
- if (mRankingMap != null) {
- // RankingMap as received from NoMan
- getRanking(a.key, mRankingA);
- getRanking(b.key, mRankingB);
- aImportance = mRankingA.getImportance();
- bImportance = mRankingB.getImportance();
- aRank = mRankingA.getRank();
- bRank = mRankingB.getRank();
- }
-
- String mediaNotification = getMediaManager().getMediaNotificationKey();
-
- // IMPORTANCE_MIN media streams are allowed to drift to the bottom
- final boolean aMedia = a.key.equals(mediaNotification)
- && aImportance > NotificationManager.IMPORTANCE_MIN;
- final boolean bMedia = b.key.equals(mediaNotification)
- && bImportance > NotificationManager.IMPORTANCE_MIN;
-
- boolean aSystemMax = aImportance >= NotificationManager.IMPORTANCE_HIGH &&
- isSystemNotification(na);
- boolean bSystemMax = bImportance >= NotificationManager.IMPORTANCE_HIGH &&
- isSystemNotification(nb);
-
- boolean isHeadsUp = a.row.isHeadsUp();
- if (isHeadsUp != b.row.isHeadsUp()) {
- return isHeadsUp ? -1 : 1;
- } else if (isHeadsUp) {
- // Provide consistent ranking with headsUpManager
- return mHeadsUpManager.compare(a, b);
- } else if (a.row.isAmbientPulsing() != b.row.isAmbientPulsing()) {
- return a.row.isAmbientPulsing() ? -1 : 1;
- } else if (aMedia != bMedia) {
- // Upsort current media notification.
- return aMedia ? -1 : 1;
- } else if (aSystemMax != bSystemMax) {
- // Upsort PRIORITY_MAX system notifications
- return aSystemMax ? -1 : 1;
- } else if (aRank != bRank) {
- return aRank - bRank;
- } else {
- return Long.compare(nb.getNotification().when, na.getNotification().when);
- }
- }
- };
-
- private KeyguardEnvironment getEnvironment() {
- if (mEnvironment == null) {
- mEnvironment = Dependency.get(KeyguardEnvironment.class);
- }
- return mEnvironment;
- }
-
- private NotificationMediaManager getMediaManager() {
- if (mMediaManager == null) {
- mMediaManager = Dependency.get(NotificationMediaManager.class);
- }
- return mMediaManager;
- }
-
- /**
- * Returns the sorted list of active notifications (depending on {@link KeyguardEnvironment}
- *
- * <p>
- * This call doesn't update the list of active notifications. Call {@link #filterAndSort()}
- * when the environment changes.
- * <p>
- * Don't hold on to or modify the returned list.
- */
- public ArrayList<Entry> getActiveNotifications() {
- return mSortedAndFiltered;
- }
-
- public ArrayList<Entry> getNotificationsForCurrentUser() {
- mFilteredForUser.clear();
-
- synchronized (mEntries) {
- final int N = mEntries.size();
- for (int i = 0; i < N; i++) {
- Entry entry = mEntries.valueAt(i);
- final StatusBarNotification sbn = entry.notification;
- if (!getEnvironment().isNotificationForCurrentProfiles(sbn)) {
- continue;
- }
- mFilteredForUser.add(entry);
- }
- }
- return mFilteredForUser;
- }
-
- public Entry get(String key) {
- return mEntries.get(key);
- }
-
- public void add(Entry entry) {
- synchronized (mEntries) {
- mEntries.put(entry.notification.getKey(), entry);
- }
- mGroupManager.onEntryAdded(entry);
-
- updateRankingAndSort(mRankingMap);
- }
-
- public Entry remove(String key, RankingMap ranking) {
- Entry removed;
- synchronized (mEntries) {
- removed = mEntries.remove(key);
- }
- if (removed == null) return null;
- mGroupManager.onEntryRemoved(removed);
- updateRankingAndSort(ranking);
- return removed;
- }
-
- /** Updates the given notification entry with the provided ranking. */
- public void update(Entry entry, RankingMap ranking, StatusBarNotification notification) {
- updateRanking(ranking);
- final StatusBarNotification oldNotification = entry.notification;
- entry.notification = notification;
- mGroupManager.onEntryUpdated(entry, oldNotification);
- }
-
- public void updateRanking(RankingMap ranking) {
- updateRankingAndSort(ranking);
- }
-
- public void updateAppOp(int appOp, int uid, String pkg, String key, boolean showIcon) {
- synchronized (mEntries) {
- final int N = mEntries.size();
- for (int i = 0; i < N; i++) {
- Entry entry = mEntries.valueAt(i);
- if (uid == entry.notification.getUid()
- && pkg.equals(entry.notification.getPackageName())
- && key.equals(entry.key)) {
- if (showIcon) {
- entry.mActiveAppOps.add(appOp);
- } else {
- entry.mActiveAppOps.remove(appOp);
- }
- }
- }
- }
- }
-
- /**
- * Returns true if this notification should be displayed in the high-priority notifications
- * section (and on the lockscreen and status bar).
- */
- public boolean isHighPriority(StatusBarNotification statusBarNotification) {
- if (mRankingMap != null) {
- getRanking(statusBarNotification.getKey(), mTmpRanking);
- if (mTmpRanking.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT
- || statusBarNotification.getNotification().isForegroundService()
- || statusBarNotification.getNotification().hasMediaSession()) {
- return true;
- }
- if (mGroupManager.isSummaryOfGroup(statusBarNotification)) {
- for (Entry child : mGroupManager.getLogicalChildren(statusBarNotification)) {
- if (isHighPriority(child.notification)) {
- return true;
- }
- }
- }
- }
- return false;
- }
-
- public boolean isAmbient(String key) {
- if (mRankingMap != null) {
- getRanking(key, mTmpRanking);
- return mTmpRanking.isAmbient();
- }
- return false;
- }
-
- public int getVisibilityOverride(String key) {
- if (mRankingMap != null) {
- getRanking(key, mTmpRanking);
- return mTmpRanking.getVisibilityOverride();
- }
- return Ranking.VISIBILITY_NO_OVERRIDE;
- }
-
- /**
- * Categories that are explicitly called out on DND settings screens are always blocked, if
- * DND has flagged them, even if they are foreground or system notifications that might
- * otherwise visually bypass DND.
- */
- private static boolean isNotificationBlockedByPolicy(Notification n) {
- if (isCategory(CATEGORY_CALL, n)
- || isCategory(CATEGORY_MESSAGE, n)
- || isCategory(CATEGORY_ALARM, n)
- || isCategory(CATEGORY_EVENT, n)
- || isCategory(CATEGORY_REMINDER, n)) {
- return true;
- }
- return false;
- }
-
- private static boolean isCategory(String category, Notification n) {
- return Objects.equals(n.category, category);
- }
-
- public int getImportance(String key) {
- if (mRankingMap != null) {
- getRanking(key, mTmpRanking);
- return mTmpRanking.getImportance();
- }
- return NotificationManager.IMPORTANCE_UNSPECIFIED;
- }
-
- public String getOverrideGroupKey(String key) {
- if (mRankingMap != null) {
- getRanking(key, mTmpRanking);
- return mTmpRanking.getOverrideGroupKey();
- }
- return null;
- }
-
- public List<SnoozeCriterion> getSnoozeCriteria(String key) {
- if (mRankingMap != null) {
- getRanking(key, mTmpRanking);
- return mTmpRanking.getSnoozeCriteria();
- }
- return null;
- }
-
- public NotificationChannel getChannel(String key) {
- if (mRankingMap != null) {
- getRanking(key, mTmpRanking);
- return mTmpRanking.getChannel();
- }
- return null;
- }
-
- public int getRank(String key) {
- if (mRankingMap != null) {
- getRanking(key, mTmpRanking);
- return mTmpRanking.getRank();
- }
- return 0;
- }
-
- public boolean shouldHide(String key) {
- if (mRankingMap != null) {
- getRanking(key, mTmpRanking);
- return mTmpRanking.isSuspended();
- }
- return false;
- }
-
- private void updateRankingAndSort(RankingMap ranking) {
- if (ranking != null) {
- mRankingMap = ranking;
- synchronized (mEntries) {
- final int N = mEntries.size();
- for (int i = 0; i < N; i++) {
- Entry entry = mEntries.valueAt(i);
- if (!getRanking(entry.key, mTmpRanking)) {
- continue;
- }
- final StatusBarNotification oldSbn = entry.notification.cloneLight();
- final String overrideGroupKey = getOverrideGroupKey(entry.key);
- if (!Objects.equals(oldSbn.getOverrideGroupKey(), overrideGroupKey)) {
- entry.notification.setOverrideGroupKey(overrideGroupKey);
- mGroupManager.onEntryUpdated(entry, oldSbn);
- }
- entry.populateFromRanking(mTmpRanking);
- }
- }
- }
- filterAndSort();
- }
-
- /**
- * Get the ranking from the current ranking map.
- *
- * @param key the key to look up
- * @param outRanking the ranking to populate
- *
- * @return {@code true} if the ranking was properly obtained.
- */
- @VisibleForTesting
- protected boolean getRanking(String key, Ranking outRanking) {
- return mRankingMap.getRanking(key, outRanking);
- }
-
- // TODO: This should not be public. Instead the Environment should notify this class when
- // anything changed, and this class should call back the UI so it updates itself.
- public void filterAndSort() {
- mSortedAndFiltered.clear();
-
- synchronized (mEntries) {
- final int N = mEntries.size();
- for (int i = 0; i < N; i++) {
- Entry entry = mEntries.valueAt(i);
-
- if (mNotificationFilter.shouldFilterOut(entry)) {
- continue;
- }
-
- mSortedAndFiltered.add(entry);
- }
- }
-
- Collections.sort(mSortedAndFiltered, mRankingComparator);
- }
-
- public void dump(PrintWriter pw, String indent) {
- int N = mSortedAndFiltered.size();
- pw.print(indent);
- pw.println("active notifications: " + N);
- int active;
- for (active = 0; active < N; active++) {
- NotificationData.Entry e = mSortedAndFiltered.get(active);
- dumpEntry(pw, indent, active, e);
- }
- synchronized (mEntries) {
- int M = mEntries.size();
- pw.print(indent);
- pw.println("inactive notifications: " + (M - active));
- int inactiveCount = 0;
- for (int i = 0; i < M; i++) {
- Entry entry = mEntries.valueAt(i);
- if (!mSortedAndFiltered.contains(entry)) {
- dumpEntry(pw, indent, inactiveCount, entry);
- inactiveCount++;
- }
- }
- }
- }
-
- private void dumpEntry(PrintWriter pw, String indent, int i, Entry e) {
- getRanking(e.key, mTmpRanking);
- pw.print(indent);
- pw.println(" [" + i + "] key=" + e.key + " icon=" + e.icon);
- StatusBarNotification n = e.notification;
- pw.print(indent);
- pw.println(" pkg=" + n.getPackageName() + " id=" + n.getId() + " importance=" +
- mTmpRanking.getImportance());
- pw.print(indent);
- pw.println(" notification=" + n.getNotification());
- }
-
- private static boolean isSystemNotification(StatusBarNotification sbn) {
- String sbnPackage = sbn.getPackageName();
- return "android".equals(sbnPackage) || "com.android.systemui".equals(sbnPackage);
- }
-
- /**
- * Provides access to keyguard state and user settings dependent data.
- */
- public interface KeyguardEnvironment {
- boolean isDeviceProvisioned();
- boolean isNotificationForCurrentProfiles(StatusBarNotification sbn);
- }
-}
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 2f60f11..c640760 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryListener.java
@@ -19,6 +19,7 @@
import android.service.notification.StatusBarNotification;
import com.android.internal.statusbar.NotificationVisibility;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.NotificationInflater;
/**
@@ -29,25 +30,25 @@
* Called when a new notification is posted. At this point, the notification is "pending": its
* views haven't been inflated yet and most of the system pretends like it doesn't exist yet.
*/
- default void onPendingEntryAdded(NotificationData.Entry entry) {
+ default void onPendingEntryAdded(NotificationEntry entry) {
}
/**
* Called when a new entry is created.
*/
- default void onNotificationAdded(NotificationData.Entry entry) {
+ default void onNotificationAdded(NotificationEntry entry) {
}
/**
* Called when a notification was updated.
*/
- default void onEntryUpdated(NotificationData.Entry entry) {
+ default void onEntryUpdated(NotificationEntry entry) {
}
/**
* Called when a notification's views are inflated for the first time.
*/
- default void onEntryInflated(NotificationData.Entry entry,
+ default void onEntryInflated(NotificationEntry entry,
@NotificationInflater.InflationFlag int inflatedFlags) {
}
@@ -57,7 +58,7 @@
*
* @param entry notification data entry that was reinflated.
*/
- default void onEntryReinflated(NotificationData.Entry entry) {
+ default void onEntryReinflated(NotificationEntry entry) {
}
/**
@@ -77,7 +78,7 @@
* @param removedByUser true if the notification was removed by a user action
*/
default void onEntryRemoved(
- NotificationData.Entry entry,
+ NotificationEntry entry,
@Nullable NotificationVisibility visibility,
boolean removedByUser) {
}
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 5d6f60e..038efdd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -36,8 +36,9 @@
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationUiAdjustment;
import com.android.systemui.statusbar.NotificationUpdateHandler;
-import com.android.systemui.statusbar.notification.NotificationData.KeyguardEnvironment;
-import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+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.NotificationInflater;
import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
@@ -68,10 +69,8 @@
public static final long RECENTLY_ALERTED_THRESHOLD_MS = TimeUnit.SECONDS.toMillis(30);
protected final Context mContext;
- protected final HashMap<String, NotificationData.Entry> mPendingNotifications = new HashMap<>();
+ 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 =
@@ -113,7 +112,7 @@
if (mPendingNotifications.size() == 0) {
pw.println("null");
} else {
- for (NotificationData.Entry entry : mPendingNotifications.values()) {
+ for (NotificationEntry entry : mPendingNotifications.values()) {
pw.println(entry.notification);
}
}
@@ -205,11 +204,11 @@
private void abortExistingInflation(String key) {
if (mPendingNotifications.containsKey(key)) {
- NotificationData.Entry entry = mPendingNotifications.get(key);
+ NotificationEntry entry = mPendingNotifications.get(key);
entry.abortTask();
mPendingNotifications.remove(key);
}
- NotificationData.Entry addedEntry = mNotificationData.get(key);
+ NotificationEntry addedEntry = mNotificationData.get(key);
if (addedEntry != null) {
addedEntry.abortTask();
}
@@ -230,7 +229,7 @@
}
}
- private void maybeScheduleUpdateNotificationViews(NotificationData.Entry entry) {
+ private void maybeScheduleUpdateNotificationViews(NotificationEntry entry) {
long audibleAlertTimeout = RECENTLY_ALERTED_THRESHOLD_MS
- (System.currentTimeMillis() - entry.lastAudiblyAlertedMs);
if (audibleAlertTimeout > 0) {
@@ -240,7 +239,7 @@
}
@Override
- public void onAsyncInflationFinished(NotificationData.Entry entry,
+ public void onAsyncInflationFinished(NotificationEntry entry,
@InflationFlag int inflatedFlags) {
mPendingNotifications.remove(entry.key);
// If there was an async task started after the removal, we don't want to add it back to
@@ -279,7 +278,7 @@
@Nullable NotificationVisibility visibility,
boolean forceRemove,
boolean removedByUser) {
- final NotificationData.Entry entry = mNotificationData.get(key);
+ final NotificationEntry entry = mNotificationData.get(key);
abortExistingInflation(key);
@@ -337,19 +336,19 @@
*
*/
private void handleGroupSummaryRemoved(String key) {
- NotificationData.Entry entry = mNotificationData.get(key);
+ NotificationEntry entry = mNotificationData.get(key);
if (entry != null && entry.rowExists() && entry.isSummaryWithChildren()) {
if (entry.notification.getOverrideGroupKey() != null && !entry.isRowDismissed()) {
// We don't want to remove children for autobundled notifications as they are not
// always cancelled. We only remove them if they were dismissed by the user.
return;
}
- List<NotificationData.Entry> childEntries = entry.getChildren();
+ List<NotificationEntry> childEntries = entry.getChildren();
if (childEntries == null) {
return;
}
for (int i = 0; i < childEntries.size(); i++) {
- NotificationData.Entry childEntry = childEntries.get(i);
+ NotificationEntry childEntry = childEntries.get(i);
boolean isForeground = (entry.notification.getNotification().flags
& Notification.FLAG_FOREGROUND_SERVICE) != 0;
boolean keepForReply =
@@ -368,19 +367,6 @@
}
}
- public void updateNotificationsOnDensityOrFontScaleChanged() {
- ArrayList<NotificationData.Entry> userNotifications =
- mNotificationData.getNotificationsForCurrentUser();
- for (int i = 0; i < userNotifications.size(); i++) {
- NotificationData.Entry 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();
@@ -392,7 +378,7 @@
NotificationListenerService.Ranking ranking = new NotificationListenerService.Ranking();
rankingMap.getRanking(key, ranking);
- NotificationData.Entry entry = new NotificationData.Entry(notification, ranking);
+ NotificationEntry entry = new NotificationEntry(notification, ranking);
Dependency.get(LeakDetector.class).trackInstance(entry);
// Construct the expanded view.
@@ -445,7 +431,7 @@
final String key = notification.getKey();
abortExistingInflation(key);
- NotificationData.Entry entry = mNotificationData.get(key);
+ NotificationEntry entry = mNotificationData.get(key);
if (entry == null) {
return;
}
@@ -463,12 +449,6 @@
updateNotifications();
- if (!notification.isClearable()) {
- // The user may have performed a dismiss action on the notification, since it's
- // not clearable we should snap it back.
- mListContainer.snapViewIfNeeded(entry);
- }
-
if (DEBUG) {
// Is this for you?
boolean isForCurrentUser = Dependency.get(KeyguardEnvironment.class)
@@ -500,14 +480,14 @@
}
public void updateNotificationRanking(NotificationListenerService.RankingMap rankingMap) {
- List<NotificationData.Entry> entries = new ArrayList<>();
+ List<NotificationEntry> entries = new ArrayList<>();
entries.addAll(mNotificationData.getActiveNotifications());
entries.addAll(mPendingNotifications.values());
// Has a copy of the current UI adjustments.
ArrayMap<String, NotificationUiAdjustment> oldAdjustments = new ArrayMap<>();
ArrayMap<String, Integer> oldImportances = new ArrayMap<>();
- for (NotificationData.Entry entry : entries) {
+ for (NotificationEntry entry : entries) {
NotificationUiAdjustment adjustment =
NotificationUiAdjustment.extractFromNotificationEntry(entry);
oldAdjustments.put(entry.key, adjustment);
@@ -519,7 +499,7 @@
updateRankingOfPendingNotifications(rankingMap);
// By comparing the old and new UI adjustments, reinflate the view accordingly.
- for (NotificationData.Entry entry : entries) {
+ for (NotificationEntry entry : entries) {
getRowBinder().onNotificationRankingUpdated(
entry,
oldImportances.get(entry.key),
@@ -537,7 +517,7 @@
return;
}
NotificationListenerService.Ranking tmpRanking = new NotificationListenerService.Ranking();
- for (NotificationData.Entry pendingNotification : mPendingNotifications.values()) {
+ for (NotificationEntry pendingNotification : mPendingNotifications.values()) {
rankingMap.getRanking(pendingNotification.key, tmpRanking);
pendingNotification.populateFromRanking(tmpRanking);
}
@@ -548,7 +528,7 @@
* notifications whose views have not yet been inflated. In general, the system pretends like
* these don't exist, although there are a couple exceptions.
*/
- public Iterable<NotificationData.Entry> getPendingNotificationsIterator() {
+ public Iterable<NotificationEntry> getPendingNotificationsIterator() {
return mPendingNotifications.values();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
index 700382a..e199ead 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
@@ -28,6 +28,8 @@
import com.android.systemui.Dependency;
import com.android.systemui.ForegroundServiceController;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.notification.collection.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -82,7 +84,7 @@
/**
* @return true if the provided notification should NOT be shown right now.
*/
- public boolean shouldFilterOut(NotificationData.Entry entry) {
+ public boolean shouldFilterOut(NotificationEntry entry) {
final StatusBarNotification sbn = entry.notification;
if (!(getEnvironment().isDeviceProvisioned()
|| showNotificationEvenIfUnprovisioned(sbn))) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java
index 8bd0e9a..fc7a2b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInterruptionStateProvider.java
@@ -37,6 +37,7 @@
import com.android.systemui.Dependency;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -139,7 +140,7 @@
* @param entry the entry to check
* @return true if the entry should heads up, false otherwise
*/
- public boolean shouldHeadsUp(NotificationData.Entry entry) {
+ public boolean shouldHeadsUp(NotificationEntry entry) {
StatusBarNotification sbn = entry.notification;
if (getShadeController().isDozing()) {
@@ -227,7 +228,7 @@
* @param entry the entry to check
* @return true if the entry should ambient pulse, false otherwise
*/
- public boolean shouldPulse(NotificationData.Entry entry) {
+ public boolean shouldPulse(NotificationEntry entry) {
StatusBarNotification sbn = entry.notification;
if (!getShadeController().isDozing()) {
@@ -273,14 +274,14 @@
/**
* Common checks between heads up alerting and ambient pulse alerting. See
- * {@link #shouldHeadsUp(NotificationData.Entry)} and
- * {@link #shouldPulse(NotificationData.Entry)}. Notifications that fail any of these checks
+ * {@link #shouldHeadsUp(NotificationEntry)} and
+ * {@link #shouldPulse(NotificationEntry)}. Notifications that fail any of these checks
* should not alert at all.
*
* @param entry the entry to check
* @return true if these checks pass, false if the notification should not alert
*/
- protected boolean canAlertCommon(NotificationData.Entry entry) {
+ protected boolean canAlertCommon(NotificationEntry entry) {
StatusBarNotification sbn = entry.notification;
if (mNotificationFilter.shouldFilterOut(entry)) {
@@ -325,7 +326,7 @@
* @param sbn notification that might be heads upped
* @return false if the notification can not be heads upped
*/
- boolean canHeadsUp(NotificationData.Entry entry, StatusBarNotification sbn);
+ boolean canHeadsUp(NotificationEntry entry, StatusBarNotification sbn);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java
index 058efca..cc302b1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java
@@ -42,6 +42,7 @@
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationUiAdjustment;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.row.NotificationInflater;
@@ -126,7 +127,7 @@
* Inflates the views for the given entry (possibly asynchronously).
*/
public void inflateViews(
- NotificationData.Entry entry,
+ NotificationEntry entry,
Runnable onDismissRunnable,
boolean isUpdate)
throws InflationException {
@@ -149,7 +150,7 @@
}
}
- private void bindRow(NotificationData.Entry entry, PackageManager pmUser,
+ private void bindRow(NotificationEntry entry, PackageManager pmUser,
StatusBarNotification sbn, ExpandableNotificationRow row,
Runnable onDismissRunnable) {
row.setExpansionLogger(mExpansionLogger, entry.notification.getKey());
@@ -197,7 +198,7 @@
* reinflating them.
*/
public void onNotificationRankingUpdated(
- NotificationData.Entry entry,
+ NotificationEntry entry,
@Nullable Integer oldImportance,
NotificationUiAdjustment oldAdjustment,
NotificationUiAdjustment newAdjustment,
@@ -224,7 +225,7 @@
//TODO: This method associates a row with an entry, but eventually needs to not do that
private void updateNotification(
- NotificationData.Entry entry,
+ NotificationEntry entry,
PackageManager pmUser,
StatusBarNotification sbn,
ExpandableNotificationRow row,
@@ -292,7 +293,7 @@
* @param sbn notification
* @param row row for the notification
*/
- void onBindRow(NotificationData.Entry entry, PackageManager pmUser,
+ void onBindRow(NotificationEntry entry, PackageManager pmUser,
StatusBarNotification sbn, ExpandableNotificationRow row);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java
index a194eef..f09c57d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisibilityLocationProvider.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+
/**
* An object that can determine the visibility of a Notification.
*/
@@ -27,5 +29,5 @@
* @param entry
* @return true if row is in a visible location
*/
- boolean isInVisibleLocation(NotificationData.Entry entry);
+ boolean isInVisibleLocation(NotificationEntry entry);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
index 8e6a93d..c886685 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
@@ -21,6 +21,7 @@
import androidx.collection.ArraySet;
import com.android.systemui.statusbar.NotificationPresenter;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
@@ -52,7 +53,7 @@
public VisualStabilityManager(NotificationEntryManager notificationEntryManager) {
notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
@Override
- public void onEntryReinflated(NotificationData.Entry entry) {
+ public void onEntryReinflated(NotificationEntry entry) {
if (entry.hasLowPriorityStateUpdated()) {
onLowPriorityUpdated(entry);
if (mPresenter != null) {
@@ -163,7 +164,7 @@
}
@Override
- public void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) {
+ public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
if (isHeadsUp) {
// Heads up notifications should in general be allowed to reorder if they are out of
// view and stay at the current location if they aren't.
@@ -171,7 +172,7 @@
}
}
- private void onLowPriorityUpdated(NotificationData.Entry entry) {
+ private void onLowPriorityUpdated(NotificationEntry entry) {
mLowPriorityReorderingViews.add(entry.getRow());
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java
new file mode 100644
index 0000000..8c29fb5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java
@@ -0,0 +1,424 @@
+/*
+ * 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.systemui.statusbar.notification.collection;
+
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.service.notification.NotificationListenerService.Ranking;
+import android.service.notification.NotificationListenerService.RankingMap;
+import android.service.notification.SnoozeCriterion;
+import android.service.notification.StatusBarNotification;
+import android.util.ArrayMap;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.Dependency;
+import com.android.systemui.statusbar.NotificationMediaManager;
+import com.android.systemui.statusbar.notification.NotificationFilter;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * The list of currently displaying notifications.
+ */
+public class NotificationData {
+
+ private final NotificationFilter mNotificationFilter = Dependency.get(NotificationFilter.class);
+
+ /**
+ * These dependencies are late init-ed
+ */
+ private KeyguardEnvironment mEnvironment;
+ private NotificationMediaManager mMediaManager;
+
+ private HeadsUpManager mHeadsUpManager;
+
+ private final ArrayMap<String, NotificationEntry> mEntries = new ArrayMap<>();
+ private final ArrayList<NotificationEntry> mSortedAndFiltered = new ArrayList<>();
+ private final ArrayList<NotificationEntry> mFilteredForUser = new ArrayList<>();
+
+ private final NotificationGroupManager mGroupManager =
+ Dependency.get(NotificationGroupManager.class);
+
+ private RankingMap mRankingMap;
+ private final Ranking mTmpRanking = new Ranking();
+
+ public void setHeadsUpManager(HeadsUpManager headsUpManager) {
+ mHeadsUpManager = headsUpManager;
+ }
+
+ private final Comparator<NotificationEntry> mRankingComparator =
+ new Comparator<NotificationEntry>() {
+ private final Ranking mRankingA = new Ranking();
+ private final Ranking mRankingB = new Ranking();
+
+ @Override
+ public int compare(NotificationEntry a, NotificationEntry b) {
+ final StatusBarNotification na = a.notification;
+ final StatusBarNotification nb = b.notification;
+ int aImportance = NotificationManager.IMPORTANCE_DEFAULT;
+ int bImportance = NotificationManager.IMPORTANCE_DEFAULT;
+ int aRank = 0;
+ int bRank = 0;
+
+ if (mRankingMap != null) {
+ // RankingMap as received from NoMan
+ getRanking(a.key, mRankingA);
+ getRanking(b.key, mRankingB);
+ aImportance = mRankingA.getImportance();
+ bImportance = mRankingB.getImportance();
+ aRank = mRankingA.getRank();
+ bRank = mRankingB.getRank();
+ }
+
+ String mediaNotification = getMediaManager().getMediaNotificationKey();
+
+ // IMPORTANCE_MIN media streams are allowed to drift to the bottom
+ final boolean aMedia = a.key.equals(mediaNotification)
+ && aImportance > NotificationManager.IMPORTANCE_MIN;
+ final boolean bMedia = b.key.equals(mediaNotification)
+ && bImportance > NotificationManager.IMPORTANCE_MIN;
+
+ boolean aSystemMax = aImportance >= NotificationManager.IMPORTANCE_HIGH
+ && isSystemNotification(na);
+ boolean bSystemMax = bImportance >= NotificationManager.IMPORTANCE_HIGH
+ && isSystemNotification(nb);
+
+ boolean isHeadsUp = a.getRow().isHeadsUp();
+ if (isHeadsUp != b.getRow().isHeadsUp()) {
+ return isHeadsUp ? -1 : 1;
+ } else if (isHeadsUp) {
+ // Provide consistent ranking with headsUpManager
+ return mHeadsUpManager.compare(a, b);
+ } else if (a.getRow().isAmbientPulsing() != b.getRow().isAmbientPulsing()) {
+ return a.getRow().isAmbientPulsing() ? -1 : 1;
+ } else if (aMedia != bMedia) {
+ // Upsort current media notification.
+ return aMedia ? -1 : 1;
+ } else if (aSystemMax != bSystemMax) {
+ // Upsort PRIORITY_MAX system notifications
+ return aSystemMax ? -1 : 1;
+ } else if (aRank != bRank) {
+ return aRank - bRank;
+ } else {
+ return Long.compare(nb.getNotification().when, na.getNotification().when);
+ }
+ }
+ };
+
+ private KeyguardEnvironment getEnvironment() {
+ if (mEnvironment == null) {
+ mEnvironment = Dependency.get(KeyguardEnvironment.class);
+ }
+ return mEnvironment;
+ }
+
+ private NotificationMediaManager getMediaManager() {
+ if (mMediaManager == null) {
+ mMediaManager = Dependency.get(NotificationMediaManager.class);
+ }
+ return mMediaManager;
+ }
+
+ /**
+ * Returns the sorted list of active notifications (depending on {@link KeyguardEnvironment}
+ *
+ * <p>
+ * This call doesn't update the list of active notifications. Call {@link #filterAndSort()}
+ * when the environment changes.
+ * <p>
+ * Don't hold on to or modify the returned list.
+ */
+ public ArrayList<NotificationEntry> getActiveNotifications() {
+ return mSortedAndFiltered;
+ }
+
+ public ArrayList<NotificationEntry> getNotificationsForCurrentUser() {
+ mFilteredForUser.clear();
+
+ synchronized (mEntries) {
+ final int len = mEntries.size();
+ for (int i = 0; i < len; i++) {
+ NotificationEntry entry = mEntries.valueAt(i);
+ final StatusBarNotification sbn = entry.notification;
+ if (!getEnvironment().isNotificationForCurrentProfiles(sbn)) {
+ continue;
+ }
+ mFilteredForUser.add(entry);
+ }
+ }
+ return mFilteredForUser;
+ }
+
+ public NotificationEntry get(String key) {
+ return mEntries.get(key);
+ }
+
+ public void add(NotificationEntry entry) {
+ synchronized (mEntries) {
+ mEntries.put(entry.notification.getKey(), entry);
+ }
+ mGroupManager.onEntryAdded(entry);
+
+ updateRankingAndSort(mRankingMap);
+ }
+
+ public NotificationEntry remove(String key, RankingMap ranking) {
+ NotificationEntry removed;
+ synchronized (mEntries) {
+ removed = mEntries.remove(key);
+ }
+ if (removed == null) return null;
+ mGroupManager.onEntryRemoved(removed);
+ updateRankingAndSort(ranking);
+ return removed;
+ }
+
+ /** Updates the given notification entry with the provided ranking. */
+ public void update(
+ NotificationEntry entry,
+ RankingMap ranking,
+ StatusBarNotification notification) {
+ updateRanking(ranking);
+ final StatusBarNotification oldNotification = entry.notification;
+ entry.notification = notification;
+ mGroupManager.onEntryUpdated(entry, oldNotification);
+ }
+
+ public void updateRanking(RankingMap ranking) {
+ updateRankingAndSort(ranking);
+ }
+
+ public void updateAppOp(int appOp, int uid, String pkg, String key, boolean showIcon) {
+ synchronized (mEntries) {
+ final int len = mEntries.size();
+ for (int i = 0; i < len; i++) {
+ NotificationEntry entry = mEntries.valueAt(i);
+ if (uid == entry.notification.getUid()
+ && pkg.equals(entry.notification.getPackageName())
+ && key.equals(entry.key)) {
+ if (showIcon) {
+ entry.mActiveAppOps.add(appOp);
+ } else {
+ entry.mActiveAppOps.remove(appOp);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns true if this notification should be displayed in the high-priority notifications
+ * section (and on the lockscreen and status bar).
+ */
+ public boolean isHighPriority(StatusBarNotification statusBarNotification) {
+ if (mRankingMap != null) {
+ getRanking(statusBarNotification.getKey(), mTmpRanking);
+ if (mTmpRanking.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT
+ || statusBarNotification.getNotification().isForegroundService()
+ || statusBarNotification.getNotification().hasMediaSession()) {
+ return true;
+ }
+ if (mGroupManager.isSummaryOfGroup(statusBarNotification)) {
+ final ArrayList<NotificationEntry> logicalChildren =
+ mGroupManager.getLogicalChildren(statusBarNotification);
+ for (NotificationEntry child : logicalChildren) {
+ if (isHighPriority(child.notification)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ public boolean isAmbient(String key) {
+ if (mRankingMap != null) {
+ getRanking(key, mTmpRanking);
+ return mTmpRanking.isAmbient();
+ }
+ return false;
+ }
+
+ public int getVisibilityOverride(String key) {
+ if (mRankingMap != null) {
+ getRanking(key, mTmpRanking);
+ return mTmpRanking.getVisibilityOverride();
+ }
+ return Ranking.VISIBILITY_NO_OVERRIDE;
+ }
+
+ public int getImportance(String key) {
+ if (mRankingMap != null) {
+ getRanking(key, mTmpRanking);
+ return mTmpRanking.getImportance();
+ }
+ return NotificationManager.IMPORTANCE_UNSPECIFIED;
+ }
+
+ public String getOverrideGroupKey(String key) {
+ if (mRankingMap != null) {
+ getRanking(key, mTmpRanking);
+ return mTmpRanking.getOverrideGroupKey();
+ }
+ return null;
+ }
+
+ public List<SnoozeCriterion> getSnoozeCriteria(String key) {
+ if (mRankingMap != null) {
+ getRanking(key, mTmpRanking);
+ return mTmpRanking.getSnoozeCriteria();
+ }
+ return null;
+ }
+
+ public NotificationChannel getChannel(String key) {
+ if (mRankingMap != null) {
+ getRanking(key, mTmpRanking);
+ return mTmpRanking.getChannel();
+ }
+ return null;
+ }
+
+ public int getRank(String key) {
+ if (mRankingMap != null) {
+ getRanking(key, mTmpRanking);
+ return mTmpRanking.getRank();
+ }
+ return 0;
+ }
+
+ public boolean shouldHide(String key) {
+ if (mRankingMap != null) {
+ getRanking(key, mTmpRanking);
+ return mTmpRanking.isSuspended();
+ }
+ return false;
+ }
+
+ private void updateRankingAndSort(RankingMap ranking) {
+ if (ranking != null) {
+ mRankingMap = ranking;
+ synchronized (mEntries) {
+ final int len = mEntries.size();
+ for (int i = 0; i < len; i++) {
+ NotificationEntry entry = mEntries.valueAt(i);
+ if (!getRanking(entry.key, mTmpRanking)) {
+ continue;
+ }
+ final StatusBarNotification oldSbn = entry.notification.cloneLight();
+ final String overrideGroupKey = getOverrideGroupKey(entry.key);
+ if (!Objects.equals(oldSbn.getOverrideGroupKey(), overrideGroupKey)) {
+ entry.notification.setOverrideGroupKey(overrideGroupKey);
+ mGroupManager.onEntryUpdated(entry, oldSbn);
+ }
+ entry.populateFromRanking(mTmpRanking);
+ }
+ }
+ }
+ filterAndSort();
+ }
+
+ /**
+ * Get the ranking from the current ranking map.
+ *
+ * @param key the key to look up
+ * @param outRanking the ranking to populate
+ *
+ * @return {@code true} if the ranking was properly obtained.
+ */
+ @VisibleForTesting
+ protected boolean getRanking(String key, Ranking outRanking) {
+ return mRankingMap.getRanking(key, outRanking);
+ }
+
+ // TODO: This should not be public. Instead the Environment should notify this class when
+ // anything changed, and this class should call back the UI so it updates itself.
+ public void filterAndSort() {
+ mSortedAndFiltered.clear();
+
+ synchronized (mEntries) {
+ final int len = mEntries.size();
+ for (int i = 0; i < len; i++) {
+ NotificationEntry entry = mEntries.valueAt(i);
+
+ if (mNotificationFilter.shouldFilterOut(entry)) {
+ continue;
+ }
+
+ mSortedAndFiltered.add(entry);
+ }
+ }
+
+ Collections.sort(mSortedAndFiltered, mRankingComparator);
+ }
+
+ public void dump(PrintWriter pw, String indent) {
+ int filteredLen = mSortedAndFiltered.size();
+ pw.print(indent);
+ pw.println("active notifications: " + filteredLen);
+ int active;
+ for (active = 0; active < filteredLen; active++) {
+ NotificationEntry e = mSortedAndFiltered.get(active);
+ dumpEntry(pw, indent, active, e);
+ }
+ synchronized (mEntries) {
+ int totalLen = mEntries.size();
+ pw.print(indent);
+ pw.println("inactive notifications: " + (totalLen - active));
+ int inactiveCount = 0;
+ for (int i = 0; i < totalLen; i++) {
+ NotificationEntry entry = mEntries.valueAt(i);
+ if (!mSortedAndFiltered.contains(entry)) {
+ dumpEntry(pw, indent, inactiveCount, entry);
+ inactiveCount++;
+ }
+ }
+ }
+ }
+
+ private void dumpEntry(PrintWriter pw, String indent, int i, NotificationEntry e) {
+ getRanking(e.key, mTmpRanking);
+ pw.print(indent);
+ pw.println(" [" + i + "] key=" + e.key + " icon=" + e.icon);
+ StatusBarNotification n = e.notification;
+ pw.print(indent);
+ pw.println(" pkg=" + n.getPackageName() + " id=" + n.getId() + " importance="
+ + mTmpRanking.getImportance());
+ pw.print(indent);
+ pw.println(" notification=" + n.getNotification());
+ }
+
+ private static boolean isSystemNotification(StatusBarNotification sbn) {
+ String sbnPackage = sbn.getPackageName();
+ return "android".equals(sbnPackage) || "com.android.systemui".equals(sbnPackage);
+ }
+
+ /**
+ * Provides access to keyguard state and user settings dependent data.
+ */
+ public interface KeyguardEnvironment {
+ boolean isDeviceProvisioned();
+ boolean isNotificationForCurrentProfiles(StatusBarNotification sbn);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
new file mode 100644
index 0000000..58aa02c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -0,0 +1,701 @@
+/*
+ * 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.systemui.statusbar.notification.collection;
+
+import static android.app.Notification.CATEGORY_ALARM;
+import static android.app.Notification.CATEGORY_CALL;
+import static android.app.Notification.CATEGORY_EVENT;
+import static android.app.Notification.CATEGORY_MESSAGE;
+import static android.app.Notification.CATEGORY_REMINDER;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
+import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
+
+import android.annotation.NonNull;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager.Policy;
+import android.app.Person;
+import android.content.Context;
+import android.graphics.drawable.Icon;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.os.SystemClock;
+import android.service.notification.NotificationListenerService;
+import android.service.notification.SnoozeCriterion;
+import android.service.notification.StatusBarNotification;
+import android.util.ArraySet;
+import android.view.View;
+import android.widget.ImageView;
+
+import androidx.annotation.Nullable;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.statusbar.StatusBarIcon;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.ContrastColorUtil;
+import com.android.systemui.statusbar.InflationTask;
+import com.android.systemui.statusbar.StatusBarIconView;
+import com.android.systemui.statusbar.notification.InflationException;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotificationGuts;
+import com.android.systemui.statusbar.notification.row.NotificationInflater;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Represents a notification that the system UI knows about
+ *
+ * Whenever the NotificationManager tells us about the existence of a new notification, we wrap it
+ * in a NotificationEntry. Thus, every notification has an associated NotificationEntry, even if
+ * that notification is never displayed to the user (for example, if it's filtered out for some
+ * reason).
+ *
+ * Entries store information about the current state of the notification. Essentially:
+ * anything that needs to persist or be modifiable even when the notification's views don't
+ * exist. Any other state should be stored on the views/view controllers themselves.
+ *
+ * At the moment, there are many things here that shouldn't be and vice-versa. Hopefully we can
+ * clean this up in the future.
+ */
+public final class NotificationEntry {
+ private static final long LAUNCH_COOLDOWN = 2000;
+ private static final long REMOTE_INPUT_COOLDOWN = 500;
+ private static final long INITIALIZATION_DELAY = 400;
+ private static final long NOT_LAUNCHED_YET = -LAUNCH_COOLDOWN;
+ private static final int COLOR_INVALID = 1;
+ public final String key;
+ public StatusBarNotification notification;
+ public NotificationChannel channel;
+ public long lastAudiblyAlertedMs;
+ public boolean noisy;
+ public boolean ambient;
+ public int importance;
+ public StatusBarIconView icon;
+ public StatusBarIconView expandedIcon;
+ private boolean interruption;
+ public boolean autoRedacted; // whether the redacted notification was generated by us
+ public int targetSdk;
+ private long lastFullScreenIntentLaunchTime = NOT_LAUNCHED_YET;
+ public CharSequence remoteInputText;
+ public List<SnoozeCriterion> snoozeCriteria;
+ public int userSentiment = NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL;
+ /** Smart Actions provided by the NotificationAssistantService. */
+ @NonNull
+ public List<Notification.Action> systemGeneratedSmartActions = Collections.emptyList();
+ public CharSequence[] smartReplies = new CharSequence[0];
+ @VisibleForTesting
+ public int suppressedVisualEffects;
+ public boolean suspended;
+
+ private NotificationEntry parent; // our parent (if we're in a group)
+ private ArrayList<NotificationEntry> children = new ArrayList<NotificationEntry>();
+ private ExpandableNotificationRow row; // the outer expanded view
+
+ private int mCachedContrastColor = COLOR_INVALID;
+ private int mCachedContrastColorIsFor = COLOR_INVALID;
+ private InflationTask mRunningTask = null;
+ private Throwable mDebugThrowable;
+ public CharSequence remoteInputTextWhenReset;
+ public long lastRemoteInputSent = NOT_LAUNCHED_YET;
+ public ArraySet<Integer> mActiveAppOps = new ArraySet<>(3);
+ public CharSequence headsUpStatusBarText;
+ public CharSequence headsUpStatusBarTextPublic;
+
+ private long initializationTime = -1;
+
+ /**
+ * Whether or not this row represents a system notification. Note that if this is
+ * {@code null}, that means we were either unable to retrieve the info or have yet to
+ * retrieve the info.
+ */
+ public Boolean mIsSystemNotification;
+
+ /**
+ * Has the user sent a reply through this Notification.
+ */
+ private boolean hasSentReply;
+
+ /**
+ * Whether this notification should be displayed as a bubble.
+ */
+ private boolean mIsBubble;
+
+ /**
+ * Whether the user has dismissed this notification when it was in bubble form.
+ */
+ private boolean mUserDismissedBubble;
+
+ public NotificationEntry(StatusBarNotification n) {
+ this(n, null);
+ }
+
+ public NotificationEntry(
+ StatusBarNotification n,
+ @Nullable NotificationListenerService.Ranking ranking) {
+ this.key = n.getKey();
+ this.notification = n;
+ if (ranking != null) {
+ populateFromRanking(ranking);
+ }
+ }
+
+ public void populateFromRanking(@NonNull NotificationListenerService.Ranking ranking) {
+ channel = ranking.getChannel();
+ lastAudiblyAlertedMs = ranking.getLastAudiblyAlertedMillis();
+ importance = ranking.getImportance();
+ ambient = ranking.isAmbient();
+ snoozeCriteria = ranking.getSnoozeCriteria();
+ userSentiment = ranking.getUserSentiment();
+ systemGeneratedSmartActions = ranking.getSmartActions() == null
+ ? Collections.emptyList() : ranking.getSmartActions();
+ smartReplies = ranking.getSmartReplies() == null
+ ? new CharSequence[0]
+ : ranking.getSmartReplies().toArray(new CharSequence[0]);
+ suppressedVisualEffects = ranking.getSuppressedVisualEffects();
+ suspended = ranking.isSuspended();
+ }
+
+ public void setInterruption() {
+ interruption = true;
+ }
+
+ public boolean hasInterrupted() {
+ return interruption;
+ }
+
+ public void setIsBubble(boolean bubbleable) {
+ mIsBubble = bubbleable;
+ }
+
+ public boolean isBubble() {
+ return mIsBubble;
+ }
+
+ public void setBubbleDismissed(boolean userDismissed) {
+ mUserDismissedBubble = userDismissed;
+ }
+
+ public boolean isBubbleDismissed() {
+ return mUserDismissedBubble;
+ }
+
+ /**
+ * Resets the notification entry to be re-used.
+ */
+ public void reset() {
+ if (row != null) {
+ row.reset();
+ }
+ }
+
+ public ExpandableNotificationRow getRow() {
+ return row;
+ }
+
+ //TODO: This will go away when we have a way to bind an entry to a row
+ public void setRow(ExpandableNotificationRow row) {
+ this.row = row;
+ }
+
+ @Nullable
+ public List<NotificationEntry> getChildren() {
+ if (children.size() <= 0) {
+ return null;
+ }
+
+ return children;
+ }
+
+ public void notifyFullScreenIntentLaunched() {
+ setInterruption();
+ lastFullScreenIntentLaunchTime = SystemClock.elapsedRealtime();
+ }
+
+ public boolean hasJustLaunchedFullScreenIntent() {
+ return SystemClock.elapsedRealtime() < lastFullScreenIntentLaunchTime + LAUNCH_COOLDOWN;
+ }
+
+ public boolean hasJustSentRemoteInput() {
+ return SystemClock.elapsedRealtime() < lastRemoteInputSent + REMOTE_INPUT_COOLDOWN;
+ }
+
+ public boolean hasFinishedInitialization() {
+ return initializationTime == -1
+ || SystemClock.elapsedRealtime() > initializationTime + INITIALIZATION_DELAY;
+ }
+
+ /**
+ * Create the icons for a notification
+ * @param context the context to create the icons with
+ * @param sbn the notification
+ * @throws InflationException Exception if required icons are not valid or specified
+ */
+ public void createIcons(Context context, StatusBarNotification sbn)
+ throws InflationException {
+ Notification n = sbn.getNotification();
+ final Icon smallIcon = n.getSmallIcon();
+ if (smallIcon == null) {
+ throw new InflationException("No small icon in notification from "
+ + sbn.getPackageName());
+ }
+
+ // Construct the icon.
+ icon = new StatusBarIconView(context,
+ sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), sbn);
+ icon.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
+
+ // Construct the expanded icon.
+ expandedIcon = new StatusBarIconView(context,
+ sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), sbn);
+ expandedIcon.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
+ final StatusBarIcon ic = new StatusBarIcon(
+ sbn.getUser(),
+ sbn.getPackageName(),
+ smallIcon,
+ n.iconLevel,
+ n.number,
+ StatusBarIconView.contentDescForNotification(context, n));
+ if (!icon.set(ic) || !expandedIcon.set(ic)) {
+ icon = null;
+ expandedIcon = null;
+ throw new InflationException("Couldn't create icon: " + ic);
+ }
+ expandedIcon.setVisibility(View.INVISIBLE);
+ expandedIcon.setOnVisibilityChangedListener(
+ newVisibility -> {
+ if (row != null) {
+ row.setIconsVisible(newVisibility != View.VISIBLE);
+ }
+ });
+ }
+
+ public void setIconTag(int key, Object tag) {
+ if (icon != null) {
+ icon.setTag(key, tag);
+ expandedIcon.setTag(key, tag);
+ }
+ }
+
+ /**
+ * Update the notification icons.
+ *
+ * @param context the context to create the icons with.
+ * @param sbn the notification to read the icon from.
+ * @throws InflationException Exception if required icons are not valid or specified
+ */
+ public void updateIcons(Context context, StatusBarNotification sbn)
+ throws InflationException {
+ if (icon != null) {
+ // Update the icon
+ Notification n = sbn.getNotification();
+ final StatusBarIcon ic = new StatusBarIcon(
+ notification.getUser(),
+ notification.getPackageName(),
+ n.getSmallIcon(),
+ n.iconLevel,
+ n.number,
+ StatusBarIconView.contentDescForNotification(context, n));
+ icon.setNotification(sbn);
+ expandedIcon.setNotification(sbn);
+ if (!icon.set(ic) || !expandedIcon.set(ic)) {
+ throw new InflationException("Couldn't update icon: " + ic);
+ }
+ }
+ }
+
+ public int getContrastedColor(Context context, boolean isLowPriority,
+ int backgroundColor) {
+ int rawColor = isLowPriority ? Notification.COLOR_DEFAULT :
+ notification.getNotification().color;
+ if (mCachedContrastColorIsFor == rawColor && mCachedContrastColor != COLOR_INVALID) {
+ return mCachedContrastColor;
+ }
+ final int contrasted = ContrastColorUtil.resolveContrastColor(context, rawColor,
+ backgroundColor);
+ mCachedContrastColorIsFor = rawColor;
+ mCachedContrastColor = contrasted;
+ return mCachedContrastColor;
+ }
+
+ /**
+ * Abort all existing inflation tasks
+ */
+ public void abortTask() {
+ if (mRunningTask != null) {
+ mRunningTask.abort();
+ mRunningTask = null;
+ }
+ }
+
+ public void setInflationTask(InflationTask abortableTask) {
+ // abort any existing inflation
+ InflationTask existing = mRunningTask;
+ abortTask();
+ mRunningTask = abortableTask;
+ if (existing != null && mRunningTask != null) {
+ mRunningTask.supersedeTask(existing);
+ }
+ }
+
+ public void onInflationTaskFinished() {
+ mRunningTask = null;
+ }
+
+ @VisibleForTesting
+ public InflationTask getRunningTask() {
+ return mRunningTask;
+ }
+
+ /**
+ * Set a throwable that is used for debugging
+ *
+ * @param debugThrowable the throwable to save
+ */
+ public void setDebugThrowable(Throwable debugThrowable) {
+ mDebugThrowable = debugThrowable;
+ }
+
+ public Throwable getDebugThrowable() {
+ return mDebugThrowable;
+ }
+
+ public void onRemoteInputInserted() {
+ lastRemoteInputSent = NOT_LAUNCHED_YET;
+ remoteInputTextWhenReset = null;
+ }
+
+ public void setHasSentReply() {
+ hasSentReply = true;
+ }
+
+ public boolean isLastMessageFromReply() {
+ if (!hasSentReply) {
+ return false;
+ }
+ Bundle extras = notification.getNotification().extras;
+ CharSequence[] replyTexts = extras.getCharSequenceArray(
+ Notification.EXTRA_REMOTE_INPUT_HISTORY);
+ if (!ArrayUtils.isEmpty(replyTexts)) {
+ return true;
+ }
+ Parcelable[] messages = extras.getParcelableArray(Notification.EXTRA_MESSAGES);
+ if (messages != null && messages.length > 0) {
+ Parcelable message = messages[messages.length - 1];
+ if (message instanceof Bundle) {
+ Notification.MessagingStyle.Message lastMessage =
+ Notification.MessagingStyle.Message.getMessageFromBundle(
+ (Bundle) message);
+ if (lastMessage != null) {
+ Person senderPerson = lastMessage.getSenderPerson();
+ if (senderPerson == null) {
+ return true;
+ }
+ Person user = extras.getParcelable(Notification.EXTRA_MESSAGING_PERSON);
+ return Objects.equals(user, senderPerson);
+ }
+ }
+ }
+ return false;
+ }
+
+ public void setInitializationTime(long time) {
+ if (initializationTime == -1) {
+ initializationTime = time;
+ }
+ }
+
+ public void sendAccessibilityEvent(int eventType) {
+ if (row != null) {
+ row.sendAccessibilityEvent(eventType);
+ }
+ }
+
+ /**
+ * Used by NotificationMediaManager to determine... things
+ * @return {@code true} if we are a media notification
+ */
+ public boolean isMediaNotification() {
+ if (row == null) return false;
+
+ return row.isMediaRow();
+ }
+
+ /**
+ * We are a top level child if our parent is the list of notifications duh
+ * @return {@code true} if we're a top level notification
+ */
+ public boolean isTopLevelChild() {
+ return row != null && row.isTopLevelChild();
+ }
+
+ public void resetUserExpansion() {
+ if (row != null) row.resetUserExpansion();
+ }
+
+ public void freeContentViewWhenSafe(@NotificationInflater.InflationFlag int inflationFlag) {
+ if (row != null) row.freeContentViewWhenSafe(inflationFlag);
+ }
+
+ public void setAmbientPulsing(boolean pulsing) {
+ if (row != null) row.setAmbientPulsing(pulsing);
+ }
+
+ public boolean rowExists() {
+ return row != null;
+ }
+
+ public boolean isRowDismissed() {
+ return row != null && row.isDismissed();
+ }
+
+ public boolean isRowRemoved() {
+ return row != null && row.isRemoved();
+ }
+
+ /**
+ * @return {@code true} if the row is null or removed
+ */
+ public boolean isRemoved() {
+ //TODO: recycling invalidates this
+ return row == null || row.isRemoved();
+ }
+
+ /**
+ * @return {@code true} if the row is null or dismissed
+ */
+ public boolean isDismissed() {
+ //TODO: recycling
+ return row == null || row.isDismissed();
+ }
+
+ public boolean isRowPinned() {
+ return row != null && row.isPinned();
+ }
+
+ public void setRowPinned(boolean pinned) {
+ if (row != null) row.setPinned(pinned);
+ }
+
+ public boolean isRowAnimatingAway() {
+ return row != null && row.isHeadsUpAnimatingAway();
+ }
+
+ public boolean isRowHeadsUp() {
+ return row != null && row.isHeadsUp();
+ }
+
+ public void setHeadsUp(boolean shouldHeadsUp) {
+ if (row != null) row.setHeadsUp(shouldHeadsUp);
+ }
+
+ public boolean mustStayOnScreen() {
+ return row != null && row.mustStayOnScreen();
+ }
+
+ public void setHeadsUpIsVisible() {
+ if (row != null) row.setHeadsUpIsVisible();
+ }
+
+ //TODO: i'm imagining a world where this isn't just the row, but I could be rwong
+ public ExpandableNotificationRow getHeadsUpAnimationView() {
+ return row;
+ }
+
+ public void setUserLocked(boolean userLocked) {
+ if (row != null) row.setUserLocked(userLocked);
+ }
+
+ public void setUserExpanded(boolean userExpanded, boolean allowChildExpansion) {
+ if (row != null) row.setUserExpanded(userExpanded, allowChildExpansion);
+ }
+
+ public void setGroupExpansionChanging(boolean changing) {
+ if (row != null) row.setGroupExpansionChanging(changing);
+ }
+
+ public void notifyHeightChanged(boolean needsAnimation) {
+ if (row != null) row.notifyHeightChanged(needsAnimation);
+ }
+
+ public void closeRemoteInput() {
+ if (row != null) row.closeRemoteInput();
+ }
+
+ public boolean areChildrenExpanded() {
+ return row != null && row.areChildrenExpanded();
+ }
+
+ public boolean keepInParent() {
+ return row != null && row.keepInParent();
+ }
+
+ //TODO: probably less confusing to say "is group fully visible"
+ public boolean isGroupNotFullyVisible() {
+ return row == null || row.isGroupNotFullyVisible();
+ }
+
+ public NotificationGuts getGuts() {
+ if (row != null) return row.getGuts();
+ return null;
+ }
+
+ public boolean hasLowPriorityStateUpdated() {
+ return row != null && row.hasLowPriorityStateUpdated();
+ }
+
+ public void removeRow() {
+ if (row != null) row.setRemoved();
+ }
+
+ public boolean isSummaryWithChildren() {
+ return row != null && row.isSummaryWithChildren();
+ }
+
+ public void setKeepInParent(boolean keep) {
+ if (row != null) row.setKeepInParent(keep);
+ }
+
+ public void onDensityOrFontScaleChanged() {
+ if (row != null) row.onDensityOrFontScaleChanged();
+ }
+
+ public boolean areGutsExposed() {
+ return row != null && row.getGuts() != null && row.getGuts().isExposed();
+ }
+
+ public boolean isChildInGroup() {
+ return parent == null;
+ }
+
+ public void setLowPriorityStateUpdated(boolean updated) {
+ if (row != null) row.setLowPriorityStateUpdated(updated);
+ }
+
+ /**
+ * @return Can the underlying notification be cleared? This can be different from whether the
+ * notification can be dismissed in case notifications are sensitive on the lockscreen.
+ * @see #canViewBeDismissed()
+ */
+ public boolean isClearable() {
+ if (notification == null || !notification.isClearable()) {
+ return false;
+ }
+ if (children.size() > 0) {
+ for (int i = 0; i < children.size(); i++) {
+ NotificationEntry child = children.get(i);
+ if (!child.isClearable()) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ public boolean canViewBeDismissed() {
+ if (row == null) return true;
+ return row.canViewBeDismissed();
+ }
+
+ @VisibleForTesting
+ boolean isExemptFromDndVisualSuppression() {
+ if (isNotificationBlockedByPolicy(notification.getNotification())) {
+ return false;
+ }
+
+ if ((notification.getNotification().flags
+ & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
+ return true;
+ }
+ if (notification.getNotification().isMediaNotification()) {
+ return true;
+ }
+ if (mIsSystemNotification != null && mIsSystemNotification) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean shouldSuppressVisualEffect(int effect) {
+ if (isExemptFromDndVisualSuppression()) {
+ return false;
+ }
+ return (suppressedVisualEffects & effect) != 0;
+ }
+
+ /**
+ * Returns whether {@link Policy#SUPPRESSED_EFFECT_FULL_SCREEN_INTENT}
+ * is set for this entry.
+ */
+ public boolean shouldSuppressFullScreenIntent() {
+ return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_FULL_SCREEN_INTENT);
+ }
+
+ /**
+ * Returns whether {@link Policy#SUPPRESSED_EFFECT_PEEK}
+ * is set for this entry.
+ */
+ public boolean shouldSuppressPeek() {
+ return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_PEEK);
+ }
+
+ /**
+ * Returns whether {@link Policy#SUPPRESSED_EFFECT_STATUS_BAR}
+ * is set for this entry.
+ */
+ public boolean shouldSuppressStatusBar() {
+ return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_STATUS_BAR);
+ }
+
+ /**
+ * Returns whether {@link Policy#SUPPRESSED_EFFECT_AMBIENT}
+ * is set for this entry.
+ */
+ public boolean shouldSuppressAmbient() {
+ return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_AMBIENT);
+ }
+
+ /**
+ * Returns whether {@link Policy#SUPPRESSED_EFFECT_NOTIFICATION_LIST}
+ * is set for this entry.
+ */
+ public boolean shouldSuppressNotificationList() {
+ return shouldSuppressVisualEffect(SUPPRESSED_EFFECT_NOTIFICATION_LIST);
+ }
+
+ /**
+ * Categories that are explicitly called out on DND settings screens are always blocked, if
+ * DND has flagged them, even if they are foreground or system notifications that might
+ * otherwise visually bypass DND.
+ */
+ private static boolean isNotificationBlockedByPolicy(Notification n) {
+ return isCategory(CATEGORY_CALL, n)
+ || isCategory(CATEGORY_MESSAGE, n)
+ || isCategory(CATEGORY_ALARM, n)
+ || isCategory(CATEGORY_EVENT, n)
+ || isCategory(CATEGORY_REMINDER, n);
+ }
+
+ private static boolean isCategory(String category, Notification n) {
+ return Objects.equals(n.category, category);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index 43048a2..3eec38e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -33,9 +33,9 @@
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.StatusBarStateController.StateListener;
-import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -115,11 +115,11 @@
// notifications.
// 3. Report newly visible and no-longer visible notifications.
// 4. Keep currently visible notifications for next report.
- ArrayList<NotificationData.Entry> activeNotifications = mEntryManager
+ ArrayList<NotificationEntry> activeNotifications = mEntryManager
.getNotificationData().getActiveNotifications();
int N = activeNotifications.size();
for (int i = 0; i < N; i++) {
- NotificationData.Entry entry = activeNotifications.get(i);
+ NotificationEntry entry = activeNotifications.get(i);
String key = entry.notification.getKey();
boolean isVisible = mListContainer.isInVisibleLocation(entry);
NotificationVisibility visObj = NotificationVisibility.obtain(key, i, N, isVisible);
@@ -167,7 +167,7 @@
entryManager.addNotificationEntryListener(new NotificationEntryListener() {
@Override
public void onEntryRemoved(
- NotificationData.Entry entry,
+ NotificationEntry entry,
NotificationVisibility visibility,
boolean removedByUser) {
if (removedByUser && visibility != null) {
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 a58c7cde..9e0dd84 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,10 +86,10 @@
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.NotificationData;
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;
import com.android.systemui.statusbar.notification.logging.NotificationCounters;
import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
@@ -199,7 +199,7 @@
private ExpansionLogger mLogger;
private String mLoggingKey;
private NotificationGuts mGuts;
- private NotificationData.Entry mEntry;
+ private NotificationEntry mEntry;
private StatusBarNotification mStatusBarNotification;
private String mAppName;
@@ -451,7 +451,7 @@
*
* @param entry the entry this row is tied to
*/
- public void setEntry(@NonNull NotificationData.Entry entry) {
+ public void setEntry(@NonNull NotificationEntry entry) {
mEntry = entry;
mStatusBarNotification = entry.notification;
cacheIsSystemNotification();
@@ -685,7 +685,7 @@
return mStatusBarNotification;
}
- public NotificationData.Entry getEntry() {
+ public NotificationEntry getEntry() {
return mEntry;
}
@@ -1422,7 +1422,7 @@
public void performDismiss(boolean fromAccessibility) {
if (isOnlyChildInGroup()) {
- NotificationData.Entry groupSummary =
+ NotificationEntry groupSummary =
mGroupManager.getLogicalGroupSummary(getStatusBarNotification());
if (groupSummary.isClearable()) {
// If this is the only child in the group, dismiss the group, but don't try to show
@@ -2538,7 +2538,7 @@
/**
* @return Whether this view is allowed to be dismissed. Only valid for visible notifications as
* otherwise some state might not be updated. To request about the general clearability
- * see {@link NotificationData.Entry#isClearable()}.
+ * see {@link NotificationEntry#isClearable()}.
*/
public boolean canViewBeDismissed() {
return mEntry.isClearable() && (!shouldShowPublic() || !mSensitiveHiddenInGeneral);
@@ -2969,7 +2969,7 @@
}
public interface OnExpandClickListener {
- void onExpandClicked(NotificationData.Entry clickedEntry, boolean nowExpanded);
+ void onExpandClicked(NotificationEntry clickedEntry, boolean nowExpanded);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index 02a310c..bf30cf9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -46,8 +46,8 @@
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.SmartReplyController;
import com.android.systemui.statusbar.TransformableView;
-import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.NotificationUtils;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationCustomViewWrapper;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
@@ -1231,7 +1231,7 @@
updateAllSingleLineViews();
}
- public void onNotificationUpdated(NotificationData.Entry entry) {
+ public void onNotificationUpdated(NotificationEntry entry) {
mStatusBarNotification = entry.notification;
mOnContentViewInactiveListeners.clear();
mBeforeN = entry.targetSdk < Build.VERSION_CODES.N;
@@ -1292,7 +1292,7 @@
}
}
- private void applyRemoteInputAndSmartReply(final NotificationData.Entry entry) {
+ private void applyRemoteInputAndSmartReply(final NotificationEntry entry) {
if (mRemoteInputController == null) {
return;
}
@@ -1313,7 +1313,7 @@
@VisibleForTesting
static SmartRepliesAndActions chooseSmartRepliesAndActions(
SmartReplyConstants smartReplyConstants,
- final NotificationData.Entry entry) {
+ final NotificationEntry entry) {
boolean enableAppGeneratedSmartReplies = (smartReplyConstants.isEnabled()
&& (!smartReplyConstants.requiresTargetingP()
|| entry.targetSdk >= Build.VERSION_CODES.P));
@@ -1370,7 +1370,7 @@
smartReplies, smartActions, freeformRemoteInputActionPair != null);
}
- private void applyRemoteInput(NotificationData.Entry entry, boolean hasFreeformRemoteInput) {
+ private void applyRemoteInput(NotificationEntry entry, boolean hasFreeformRemoteInput) {
View bigContentView = mExpandedChild;
if (bigContentView != null) {
mExpandedRemoteInput = applyRemoteInput(bigContentView, entry, hasFreeformRemoteInput,
@@ -1402,7 +1402,7 @@
mCachedHeadsUpRemoteInput = null;
}
- private RemoteInputView applyRemoteInput(View view, NotificationData.Entry entry,
+ private RemoteInputView applyRemoteInput(View view, NotificationEntry entry,
boolean hasRemoteInput, PendingIntent existingPendingIntent,
RemoteInputView cachedView, NotificationViewWrapper wrapper) {
View actionContainerCandidate = view.findViewById(
@@ -1470,7 +1470,7 @@
}
private void applySmartReplyView(SmartRepliesAndActions smartRepliesAndActions,
- NotificationData.Entry entry) {
+ NotificationEntry entry) {
if (mExpandedChild != null) {
mExpandedSmartReplyView =
applySmartReplyView(mExpandedChild, smartRepliesAndActions, entry);
@@ -1496,7 +1496,7 @@
}
private SmartReplyView applySmartReplyView(View view,
- SmartRepliesAndActions smartRepliesAndActions, NotificationData.Entry entry) {
+ SmartRepliesAndActions smartRepliesAndActions, NotificationEntry entry) {
View smartReplyContainerCandidate = view.findViewById(
com.android.internal.R.id.smart_reply_container);
if (!(smartReplyContainerCandidate instanceof LinearLayout)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index ac4e583..bd1dfb1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -48,7 +48,7 @@
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSaveListener;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -116,7 +116,7 @@
mNotificationActivityStarter = notificationActivityStarter;
}
- public void onDensityOrFontScaleChanged(NotificationData.Entry entry) {
+ public void onDensityOrFontScaleChanged(NotificationEntry entry) {
setExposedGuts(entry.getGuts());
bindGuts(entry.getRow());
}
@@ -429,7 +429,7 @@
}
@Override
- public boolean shouldExtendLifetime(NotificationData.Entry entry) {
+ public boolean shouldExtendLifetime(NotificationEntry entry) {
return entry != null
&&(mNotificationGutsExposed != null
&& entry.getGuts() != null
@@ -438,7 +438,7 @@
}
@Override
- public void setShouldManageLifetime(NotificationData.Entry entry, boolean shouldExtend) {
+ public void setShouldManageLifetime(NotificationEntry entry, boolean shouldExtend) {
if (shouldExtend) {
mKeyToRemoveOnGutsClosed = entry.key;
if (Log.isLoggable(TAG, Log.DEBUG)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java
index 9908049..42ebfce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java
@@ -37,7 +37,7 @@
import com.android.systemui.statusbar.InflationTask;
import com.android.systemui.statusbar.notification.InflationException;
import com.android.systemui.statusbar.notification.MediaNotificationProcessor;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.util.Assert;
@@ -614,7 +614,7 @@
@Nullable InflationCallback endListener, ExpandableNotificationRow row,
boolean redactAmbient) {
Assert.isMainThread();
- NotificationData.Entry entry = row.getEntry();
+ NotificationEntry entry = row.getEntry();
NotificationContentView privateLayout = row.getPrivateLayout();
NotificationContentView publicLayout = row.getPublicLayout();
if (runningInflations.isEmpty()) {
@@ -724,7 +724,7 @@
* @param entry the entry with the content views set
* @param inflatedFlags the flags associated with the content views that were inflated
*/
- void onAsyncInflationFinished(NotificationData.Entry entry,
+ void onAsyncInflationFinished(NotificationEntry entry,
@InflationFlag int inflatedFlags);
/**
@@ -782,7 +782,7 @@
mRedactAmbient = redactAmbient;
mRemoteViewClickHandler = remoteViewClickHandler;
mCallback = callback;
- NotificationData.Entry entry = row.getEntry();
+ NotificationEntry entry = row.getEntry();
entry.setInflationTask(this);
}
@@ -857,7 +857,7 @@
}
@Override
- public void onAsyncInflationFinished(NotificationData.Entry entry,
+ public void onAsyncInflationFinished(NotificationEntry entry,
@InflationFlag int inflatedFlags) {
mRow.getEntry().onInflationTaskFinished();
mRow.onNotificationUpdated();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java
index 1741a0b..0160c547 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java
@@ -25,7 +25,7 @@
import com.android.systemui.R;
import com.android.systemui.statusbar.InflationTask;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
/**
* An inflater task that asynchronously inflates a ExpandableNotificationRow
@@ -36,14 +36,14 @@
private static final boolean TRACE_ORIGIN = true;
private RowInflationFinishedListener mListener;
- private NotificationData.Entry mEntry;
+ private NotificationEntry mEntry;
private boolean mCancelled;
private Throwable mInflateOrigin;
/**
* Inflates a new notificationView. This should not be called twice on this object
*/
- public void inflate(Context context, ViewGroup parent, NotificationData.Entry entry,
+ public void inflate(Context context, ViewGroup parent, NotificationEntry entry,
RowInflationFinishedListener listener) {
if (TRACE_ORIGIN) {
mInflateOrigin = new Throwable("inflate requested here");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index 670908f..cbec37e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -25,7 +25,7 @@
import com.android.systemui.statusbar.AmbientPulseManager;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
@@ -358,7 +358,7 @@
mPulsing = hasPulsing;
}
- public boolean isPulsing(NotificationData.Entry entry) {
+ public boolean isPulsing(NotificationEntry entry) {
if (!mPulsing || mAmbientPulseManager == null) {
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
index f0a2653..f771be0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java
@@ -16,15 +16,14 @@
package com.android.systemui.statusbar.notification.stack;
-import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator
- .ExpandAnimationParameters;
+import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
import android.view.View;
import android.view.ViewGroup;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
-import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
@@ -113,19 +112,12 @@
void setMaxDisplayedNotifications(int maxNotifications);
/**
- * Handle snapping a non-dismissable row back if the user tried to dismiss it.
- *
- * @param entry the entry whose row needs to snap back
- */
- void snapViewIfNeeded(NotificationData.Entry entry);
-
- /**
* Get the view parent for a notification entry. For example, NotificationStackScrollLayout.
*
* @param entry entry to get the view parent for
* @return the view parent for entry
*/
- ViewGroup getViewParentForNotification(NotificationData.Entry entry);
+ ViewGroup getViewParentForNotification(NotificationEntry entry);
/**
* Resets the currently exposed menu view.
@@ -148,7 +140,7 @@
*
* @param entry the entry whose view's view state needs to be cleaned up (say that 5 times fast)
*/
- void cleanUpViewStateForEntry(NotificationData.Entry entry);
+ void cleanUpViewStateForEntry(NotificationEntry entry);
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
index 4f0831f1..9418601 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
@@ -18,7 +18,7 @@
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.NUM_SECTIONS;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
@@ -49,12 +49,12 @@
}
@Override
- public void onHeadsUpPinned(NotificationData.Entry headsUp) {
+ public void onHeadsUpPinned(NotificationEntry headsUp) {
updateView(headsUp.getRow(), false /* animate */);
}
@Override
- public void onHeadsUpUnPinned(NotificationData.Entry headsUp) {
+ public void onHeadsUpUnPinned(NotificationEntry headsUp) {
updateView(headsUp.getRow(), true /* animate */);
}
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 8deb7d5..e4b00dd 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
@@ -96,12 +96,13 @@
import com.android.systemui.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.statusbar.notification.FakeShadowView;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.ShadeViewRefactor;
import com.android.systemui.statusbar.notification.ShadeViewRefactor.RefactorComponent;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -517,6 +518,17 @@
mLowPriorityBeforeSpeedBump = "1".equals(newValue);
}
}, LOW_PRIORITY);
+
+ mEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
+ @Override
+ public void onEntryUpdated(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.
+ snapViewIfNeeded(entry);
+ }
+ }
+ });
}
@Override
@@ -590,14 +602,14 @@
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public RemoteInputController.Delegate createDelegate() {
return new RemoteInputController.Delegate() {
- public void setRemoteInputActive(NotificationData.Entry entry,
+ public void setRemoteInputActive(NotificationEntry entry,
boolean remoteInputActive) {
mHeadsUpManager.setRemoteInputActive(entry, remoteInputActive);
entry.notifyHeightChanged(true /* needsAnimation */);
updateFooter();
}
- public void lockScrollTo(NotificationData.Entry entry) {
+ public void lockScrollTo(NotificationEntry entry) {
NotificationStackScrollLayout.this.lockScrollTo(entry.getRow());
}
@@ -910,7 +922,7 @@
@Override
@ShadeViewRefactor(RefactorComponent.LAYOUT_ALGORITHM)
- public boolean isInVisibleLocation(NotificationData.Entry entry) {
+ public boolean isInVisibleLocation(NotificationEntry entry) {
ExpandableNotificationRow row = entry.getRow();
ExpandableViewState childViewState = row.getViewState();
@@ -1224,13 +1236,13 @@
*/
@ShadeViewRefactor(RefactorComponent.COORDINATOR)
private int getTopHeadsUpPinnedHeight() {
- NotificationData.Entry topEntry = mHeadsUpManager.getTopEntry();
+ NotificationEntry topEntry = mHeadsUpManager.getTopEntry();
if (topEntry == null) {
return 0;
}
ExpandableNotificationRow row = topEntry.getRow();
if (row.isChildInGroup()) {
- final NotificationData.Entry groupSummary
+ final NotificationEntry groupSummary
= mGroupManager.getGroupSummary(row.getStatusBarNotification());
if (groupSummary != null) {
row = groupSummary.getRow();
@@ -1405,7 +1417,7 @@
&& touchY >= top && touchY <= bottom && touchX >= left && touchX <= right) {
if (slidingChild instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) slidingChild;
- NotificationData.Entry entry = row.getEntry();
+ NotificationEntry entry = row.getEntry();
if (!mIsExpanded && row.isHeadsUp() && row.isPinned()
&& mHeadsUpManager.getTopEntry().getRow() != row
&& mGroupManager.getGroupSummary(
@@ -1538,9 +1550,8 @@
true /* isDismissAll */);
}
- @Override
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
- public void snapViewIfNeeded(NotificationData.Entry entry) {
+ private void snapViewIfNeeded(NotificationEntry entry) {
ExpandableNotificationRow child = entry.getRow();
boolean animate = mIsExpanded || isPinnedHeadsUp(child);
// If the child is showing the notification menu snap to that
@@ -1550,7 +1561,7 @@
@Override
@ShadeViewRefactor(RefactorComponent.ADAPTER)
- public ViewGroup getViewParentForNotification(NotificationData.Entry entry) {
+ public ViewGroup getViewParentForNotification(NotificationEntry entry) {
return this;
}
@@ -2053,7 +2064,7 @@
}
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- private boolean isPulsing(NotificationData.Entry entry) {
+ private boolean isPulsing(NotificationEntry entry) {
return mAmbientState.isPulsing(entry);
}
@@ -2558,7 +2569,7 @@
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
@Override
- public void cleanUpViewStateForEntry(NotificationData.Entry entry) {
+ public void cleanUpViewStateForEntry(NotificationEntry entry) {
View child = entry.getRow();
if (child == mSwipeHelper.getTranslatingParentView()) {
mSwipeHelper.clearTranslatingParentView();
@@ -2686,7 +2697,7 @@
private boolean isChildInInvisibleGroup(View child) {
if (child instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
- NotificationData.Entry groupSummary =
+ NotificationEntry groupSummary =
mGroupManager.getGroupSummary(row.getStatusBarNotification());
if (groupSummary != null && groupSummary.getRow() != row) {
return row.getVisibility() == View.INVISIBLE;
@@ -4705,7 +4716,7 @@
mHeadsUpManager.setAnimationStateHandler(this::setHeadsUpGoingAwayAnimationsAllowed);
}
- public void generateHeadsUpAnimation(NotificationData.Entry entry, boolean isHeadsUp) {
+ public void generateHeadsUpAnimation(NotificationEntry entry, boolean isHeadsUp) {
ExpandableNotificationRow row = entry.getHeadsUpAnimationView();
generateHeadsUpAnimation(row, isHeadsUp);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
index d1e488a..876b902 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java
@@ -28,7 +28,7 @@
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.HeadsUpStatusBarView;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
@@ -143,7 +143,7 @@
}
@Override
- public void onHeadsUpPinned(NotificationData.Entry entry) {
+ public void onHeadsUpPinned(NotificationEntry entry) {
updateTopEntry();
updateHeader(entry);
}
@@ -206,11 +206,11 @@
}
private void updateTopEntry() {
- NotificationData.Entry newEntry = null;
+ NotificationEntry newEntry = null;
if (!mIsExpanded && mHeadsUpManager.hasPinnedHeadsUp()) {
newEntry = mHeadsUpManager.getTopEntry();
}
- NotificationData.Entry previousEntry = mHeadsUpStatusBarView.getShowingEntry();
+ NotificationEntry previousEntry = mHeadsUpStatusBarView.getShowingEntry();
mHeadsUpStatusBarView.setEntry(newEntry);
if (newEntry != previousEntry) {
boolean animateIsolation = false;
@@ -298,7 +298,7 @@
}
@Override
- public void onHeadsUpUnPinned(NotificationData.Entry entry) {
+ public void onHeadsUpUnPinned(NotificationEntry entry) {
updateTopEntry();
updateHeader(entry);
}
@@ -338,7 +338,7 @@
});
}
- public void updateHeader(NotificationData.Entry entry) {
+ public void updateHeader(NotificationEntry entry) {
ExpandableNotificationRow row = entry.getRow();
float headerVisibleAmount = 1.0f;
if (row.isPinned() || row.isHeadsUpAnimatingAway() || row == mTrackedChild) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index f4cfd41..0fada66 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -41,8 +41,8 @@
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.StatusBarStateController.StateListener;
-import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -72,8 +72,8 @@
private int mDisplayCutoutTouchableRegionSize;
private boolean mTrackingHeadsUp;
private HashSet<String> mSwipedOutKeys = new HashSet<>();
- private HashSet<NotificationData.Entry> mEntriesToRemoveAfterExpand = new HashSet<>();
- private ArraySet<NotificationData.Entry> mEntriesToRemoveWhenReorderingAllowed
+ private HashSet<NotificationEntry> mEntriesToRemoveAfterExpand = new HashSet<>();
+ private ArraySet<NotificationEntry> mEntriesToRemoveWhenReorderingAllowed
= new ArraySet<>();
private boolean mIsExpanded;
private int[] mTmpTwoArray = new int[2];
@@ -187,7 +187,7 @@
releaseAllImmediately();
mReleaseOnExpandFinish = false;
} else {
- for (NotificationData.Entry entry : mEntriesToRemoveAfterExpand) {
+ for (NotificationEntry entry : mEntriesToRemoveAfterExpand) {
if (isAlerting(entry.key)) {
// Maybe the heads-up was removed already
removeAlertEntry(entry.key);
@@ -252,7 +252,7 @@
* @param remoteInputActive True to notify active, False to notify inactive.
*/
public void setRemoteInputActive(
- @NonNull NotificationData.Entry entry, boolean remoteInputActive) {
+ @NonNull NotificationEntry entry, boolean remoteInputActive) {
HeadsUpEntryPhone headsUpEntry = getHeadsUpEntryPhone(entry.key);
if (headsUpEntry != null && headsUpEntry.remoteInputActive != remoteInputActive) {
headsUpEntry.remoteInputActive = remoteInputActive;
@@ -268,7 +268,7 @@
* Sets whether an entry's menu row is exposed and therefore it should stick in the heads up
* area if it's pinned until it's hidden again.
*/
- public void setMenuShown(@NonNull NotificationData.Entry entry, boolean menuShown) {
+ public void setMenuShown(@NonNull NotificationEntry entry, boolean menuShown) {
HeadsUpEntry headsUpEntry = getHeadsUpEntry(entry.key);
if (headsUpEntry instanceof HeadsUpEntryPhone && entry.isRowPinned()) {
((HeadsUpEntryPhone) headsUpEntry).setMenuShownPinned(menuShown);
@@ -315,9 +315,9 @@
return;
}
if (hasPinnedHeadsUp()) {
- NotificationData.Entry topEntry = getTopEntry();
+ NotificationEntry topEntry = getTopEntry();
if (topEntry.isChildInGroup()) {
- final NotificationData.Entry groupSummary
+ final NotificationEntry groupSummary
= mGroupManager.getGroupSummary(topEntry.notification);
if (groupSummary != null) {
topEntry = groupSummary;
@@ -374,7 +374,7 @@
@Override
public void onReorderingAllowed() {
mAnimationStateHandler.setHeadsUpGoingAwayAnimationsAllowed(false);
- for (NotificationData.Entry entry : mEntriesToRemoveWhenReorderingAllowed) {
+ for (NotificationEntry entry : mEntriesToRemoveWhenReorderingAllowed) {
if (isAlerting(entry.key)) {
// Maybe the heads-up was removed already
removeAlertEntry(entry.key);
@@ -399,7 +399,7 @@
}
@Override
- protected boolean shouldHeadsUpBecomePinned(NotificationData.Entry entry) {
+ protected boolean shouldHeadsUpBecomePinned(NotificationEntry entry) {
return mStatusBarState != StatusBarState.KEYGUARD && !mIsExpanded
|| super.shouldHeadsUpBecomePinned(entry);
}
@@ -488,7 +488,7 @@
return super.isSticky() || mMenuShownPinned;
}
- public void setEntry(@NonNull final NotificationData.Entry entry) {
+ public void setEntry(@NonNull final NotificationEntry entry) {
Runnable removeHeadsUpRunnable = () -> {
if (!mVisualStabilityManager.isReorderingAllowed()) {
mEntriesToRemoveWhenReorderingAllowed.add(entry);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
index 9c1c71a..dd200da 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
@@ -21,7 +21,7 @@
import android.view.ViewConfiguration;
import com.android.systemui.Gefingerpoken;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
@@ -83,7 +83,7 @@
} else if (child == null && !mCallback.isExpanded()) {
// We might touch above the visible heads up child, but then we still would
// like to capture it.
- NotificationData.Entry topEntry = mHeadsUpManager.getTopEntry();
+ NotificationEntry topEntry = mHeadsUpManager.getTopEntry();
if (topEntry != null && topEntry.isRowPinned()) {
mPickedChild = topEntry.getRow();
mTouchingHeadsUpView = true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardEnvironmentImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardEnvironmentImpl.java
index 927228e..925a19d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardEnvironmentImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardEnvironmentImpl.java
@@ -22,7 +22,7 @@
import com.android.systemui.Dependency;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.notification.NotificationData.KeyguardEnvironment;
+import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
public class KeyguardEnvironmentImpl implements KeyguardEnvironment {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
index af3257a..d364356 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
@@ -31,9 +31,9 @@
import com.android.systemui.statusbar.InflationTask;
import com.android.systemui.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.StatusBarStateController.StateListener;
-import com.android.systemui.statusbar.notification.NotificationData.Entry;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.NotificationInflater.AsyncInflationTask;
import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
import com.android.systemui.statusbar.phone.NotificationGroupManager.NotificationGroup;
@@ -108,7 +108,7 @@
* @param entry notification to check
* @return true if the entry was transferred to and should inflate + alert
*/
- public boolean isAlertTransferPending(@NonNull Entry entry) {
+ public boolean isAlertTransferPending(@NonNull NotificationEntry entry) {
PendingAlertInfo alertInfo = mPendingAlerts.get(entry.key);
return alertInfo != null && alertInfo.isStillValid();
}
@@ -172,16 +172,16 @@
};
@Override
- public void onAmbientStateChanged(Entry entry, boolean isAmbient) {
+ public void onAmbientStateChanged(NotificationEntry entry, boolean isAmbient) {
onAlertStateChanged(entry, isAmbient, mAmbientPulseManager);
}
@Override
- public void onHeadsUpStateChanged(Entry entry, boolean isHeadsUp) {
+ public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
onAlertStateChanged(entry, isHeadsUp, mHeadsUpManager);
}
- private void onAlertStateChanged(Entry entry, boolean isAlerting,
+ private void onAlertStateChanged(NotificationEntry entry, boolean isAlerting,
AlertingNotificationManager alertManager) {
if (isAlerting && mGroupManager.isSummaryOfSuppressedGroup(entry.notification)) {
handleSuppressedSummaryAlerted(entry, alertManager);
@@ -193,7 +193,7 @@
// Called when a new notification has been posted but is not inflated yet. We use this to
// see as early as we can if we need to abort a transfer.
@Override
- public void onPendingEntryAdded(Entry entry) {
+ public void onPendingEntryAdded(NotificationEntry entry) {
String groupKey = mGroupManager.getGroupKey(entry.notification);
GroupAlertEntry groupAlertEntry = mGroupAlertEntries.get(groupKey);
if (groupAlertEntry != null) {
@@ -204,7 +204,7 @@
// Called when the entry's reinflation has finished. If there is an alert pending, we
// then show the alert.
@Override
- public void onEntryReinflated(Entry entry) {
+ public void onEntryReinflated(NotificationEntry entry) {
PendingAlertInfo alertInfo = mPendingAlerts.remove(entry.key);
if (alertInfo != null) {
if (alertInfo.isStillValid()) {
@@ -219,7 +219,7 @@
@Override
public void onEntryRemoved(
- @Nullable Entry entry,
+ @Nullable NotificationEntry entry,
NotificationVisibility visibility,
boolean removedByUser) {
// Removes any alerts pending on this entry. Note that this will not stop any inflation
@@ -241,8 +241,8 @@
return 0;
}
int number = 0;
- Iterable<Entry> values = mEntryManager.getPendingNotificationsIterator();
- for (Entry entry : values) {
+ Iterable<NotificationEntry> values = mEntryManager.getPendingNotificationsIterator();
+ for (NotificationEntry entry : values) {
if (isPendingNotificationInGroup(entry, group) && onlySummaryAlerts(entry)) {
number++;
}
@@ -260,8 +260,8 @@
if (mEntryManager == null) {
return false;
}
- Iterable<Entry> values = mEntryManager.getPendingNotificationsIterator();
- for (Entry entry : values) {
+ Iterable<NotificationEntry> values = mEntryManager.getPendingNotificationsIterator();
+ for (NotificationEntry entry : values) {
if (isPendingNotificationInGroup(entry, group)) {
return true;
}
@@ -276,7 +276,7 @@
* @param group group to check
* @return true if the notification will add to the group, false o/w
*/
- private boolean isPendingNotificationInGroup(@NonNull Entry entry,
+ private boolean isPendingNotificationInGroup(@NonNull NotificationEntry entry,
@NonNull NotificationGroup group) {
String groupKey = mGroupManager.getGroupKey(group.summary.notification);
return mGroupManager.isGroupChild(entry.notification)
@@ -293,7 +293,7 @@
* @param summary the summary that is suppressed and alerting
* @param alertManager the alert manager that manages the alerting summary
*/
- private void handleSuppressedSummaryAlerted(@NonNull Entry summary,
+ private void handleSuppressedSummaryAlerted(@NonNull NotificationEntry summary,
@NonNull AlertingNotificationManager alertManager) {
StatusBarNotification sbn = summary.notification;
GroupAlertEntry groupAlertEntry =
@@ -309,7 +309,7 @@
return;
}
- Entry child = mGroupManager.getLogicalChildren(summary.notification).iterator().next();
+ NotificationEntry child = mGroupManager.getLogicalChildren(summary.notification).iterator().next();
if (child != null) {
if (child.getRow().keepInParent()
|| child.isRowRemoved()
@@ -333,7 +333,7 @@
* @param toEntry entry to transfer to
* @param alertManager alert manager for the alert type
*/
- private void transferAlertState(@NonNull Entry fromEntry, @NonNull Entry toEntry,
+ private void transferAlertState(@NonNull NotificationEntry fromEntry, @NonNull NotificationEntry toEntry,
@NonNull AlertingNotificationManager alertManager) {
alertManager.removeNotification(fromEntry.key, true /* releaseImmediately */);
alertNotificationWhenPossible(toEntry, alertManager);
@@ -353,13 +353,13 @@
private void checkShouldTransferBack(@NonNull GroupAlertEntry groupAlertEntry) {
if (SystemClock.elapsedRealtime() - groupAlertEntry.mLastAlertTransferTime
< ALERT_TRANSFER_TIMEOUT) {
- Entry summary = groupAlertEntry.mGroup.summary;
+ NotificationEntry summary = groupAlertEntry.mGroup.summary;
AlertingNotificationManager alertManager = getActiveAlertManager();
if (!onlySummaryAlerts(summary)) {
return;
}
- ArrayList<Entry> children = mGroupManager.getLogicalChildren(summary.notification);
+ ArrayList<NotificationEntry> children = mGroupManager.getLogicalChildren(summary.notification);
int numChildren = children.size();
int numPendingChildren = getPendingChildrenNotAlerting(groupAlertEntry.mGroup);
numChildren += numPendingChildren;
@@ -368,7 +368,7 @@
}
boolean releasedChild = false;
for (int i = 0; i < children.size(); i++) {
- Entry entry = children.get(i);
+ NotificationEntry entry = children.get(i);
if (onlySummaryAlerts(entry) && alertManager.isAlerting(entry.key)) {
releasedChild = true;
alertManager.removeNotification(entry.key, true /* releaseImmediately */);
@@ -399,7 +399,7 @@
* @param entry entry to show
* @param alertManager alert manager for the alert type
*/
- private void alertNotificationWhenPossible(@NonNull Entry entry,
+ private void alertNotificationWhenPossible(@NonNull NotificationEntry entry,
@NonNull AlertingNotificationManager alertManager) {
@InflationFlag int contentFlag = alertManager.getContentFlag();
if (!entry.getRow().isInflationFlagSet(contentFlag)) {
@@ -419,7 +419,7 @@
return mIsDozing ? mAmbientPulseManager : mHeadsUpManager;
}
- private boolean onlySummaryAlerts(Entry entry) {
+ private boolean onlySummaryAlerts(NotificationEntry entry) {
return entry.notification.getNotification().getGroupAlertBehavior()
== Notification.GROUP_ALERT_SUMMARY;
}
@@ -439,7 +439,7 @@
* the transfer is still valid if the notification is updated.
*/
final StatusBarNotification mOriginalNotification;
- final Entry mEntry;
+ final NotificationEntry mEntry;
/**
* The notification is still pending inflation but we've decided that we no longer need
@@ -450,7 +450,7 @@
*/
boolean mAbortOnInflation;
- PendingAlertInfo(Entry entry, AlertingNotificationManager alertManager) {
+ PendingAlertInfo(NotificationEntry entry, AlertingNotificationManager alertManager) {
mOriginalNotification = entry.notification;
mEntry = entry;
mAlertManager = alertManager;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
index 3c1c076..bb9e418 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
@@ -27,7 +27,7 @@
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.StatusBarStateController.StateListener;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
@@ -97,7 +97,7 @@
}
}
- public void onEntryRemoved(NotificationData.Entry removed) {
+ public void onEntryRemoved(NotificationEntry removed) {
onEntryRemovedInternal(removed, removed.notification);
mIsolatedEntries.remove(removed.key);
}
@@ -109,7 +109,7 @@
* @param sbn the notification the entry has, which doesn't need to be the same as it's internal
* notification
*/
- private void onEntryRemovedInternal(NotificationData.Entry removed,
+ private void onEntryRemovedInternal(NotificationEntry removed,
final StatusBarNotification sbn) {
String groupKey = getGroupKey(sbn);
final NotificationGroup group = mGroupMap.get(groupKey);
@@ -136,7 +136,7 @@
}
}
- public void onEntryAdded(final NotificationData.Entry added) {
+ public void onEntryAdded(final NotificationEntry added) {
if (added.isRowRemoved()) {
added.setDebugThrowable(new Throwable());
}
@@ -152,7 +152,7 @@
}
}
if (isGroupChild) {
- NotificationData.Entry existing = group.children.get(added.key);
+ NotificationEntry existing = group.children.get(added.key);
if (existing != null && existing != added) {
Throwable existingThrowable = existing.getDebugThrowable();
Log.wtf(TAG, "Inconsistent entries found with the same key " + added.key
@@ -169,9 +169,9 @@
group.expanded = added.areChildrenExpanded();
updateSuppression(group);
if (!group.children.isEmpty()) {
- ArrayList<NotificationData.Entry> childrenCopy
+ ArrayList<NotificationEntry> childrenCopy
= new ArrayList<>(group.children.values());
- for (NotificationData.Entry child : childrenCopy) {
+ for (NotificationEntry child : childrenCopy) {
onEntryBecomingChild(child);
}
for (OnGroupChangeListener listener : mListeners) {
@@ -181,7 +181,7 @@
}
}
- private void onEntryBecomingChild(NotificationData.Entry entry) {
+ private void onEntryBecomingChild(NotificationEntry entry) {
if (shouldIsolate(entry)) {
isolateNotification(entry);
}
@@ -221,7 +221,7 @@
return count;
}
- private NotificationData.Entry getIsolatedChild(String groupKey) {
+ private NotificationEntry getIsolatedChild(String groupKey) {
for (StatusBarNotification sbn : mIsolatedEntries.values()) {
if (sbn.getGroupKey().equals(groupKey) && isIsolated(sbn)) {
return mGroupMap.get(sbn.getKey()).summary;
@@ -230,7 +230,7 @@
return null;
}
- public void onEntryUpdated(NotificationData.Entry entry,
+ public void onEntryUpdated(NotificationEntry entry,
StatusBarNotification oldNotification) {
String oldKey = oldNotification.getGroupKey();
String newKey = entry.notification.getGroupKey();
@@ -267,7 +267,7 @@
if (!isOnlyChild(sbn)) {
return false;
}
- NotificationData.Entry logicalGroupSummary = getLogicalGroupSummary(sbn);
+ NotificationEntry logicalGroupSummary = getLogicalGroupSummary(sbn);
return logicalGroupSummary != null
&& !logicalGroupSummary.notification.equals(sbn);
}
@@ -343,7 +343,7 @@
* Get the summary of a specified status bar notification. For isolated notification this return
* itself.
*/
- public NotificationData.Entry getGroupSummary(StatusBarNotification sbn) {
+ public NotificationEntry getGroupSummary(StatusBarNotification sbn) {
return getGroupSummary(getGroupKey(sbn));
}
@@ -352,12 +352,12 @@
* but the logical summary, i.e when a child is isolated, it still returns the summary as if
* it wasn't isolated.
*/
- public NotificationData.Entry getLogicalGroupSummary(StatusBarNotification sbn) {
+ public NotificationEntry getLogicalGroupSummary(StatusBarNotification sbn) {
return getGroupSummary(sbn.getGroupKey());
}
@Nullable
- private NotificationData.Entry getGroupSummary(String groupKey) {
+ private NotificationEntry getGroupSummary(String groupKey) {
NotificationGroup group = mGroupMap.get(groupKey);
//TODO: see if this can become an Entry
return group == null ? null
@@ -371,13 +371,13 @@
* @param summary summary of a group
* @return list of the children
*/
- public ArrayList<NotificationData.Entry> getLogicalChildren(StatusBarNotification summary) {
+ public ArrayList<NotificationEntry> getLogicalChildren(StatusBarNotification summary) {
NotificationGroup group = mGroupMap.get(summary.getGroupKey());
if (group == null) {
return null;
}
- ArrayList<NotificationData.Entry> children = new ArrayList<>(group.children.values());
- NotificationData.Entry isolatedChild = getIsolatedChild(summary.getGroupKey());
+ ArrayList<NotificationEntry> children = new ArrayList<>(group.children.values());
+ NotificationEntry isolatedChild = getIsolatedChild(summary.getGroupKey());
if (isolatedChild != null) {
children.add(isolatedChild);
}
@@ -443,24 +443,24 @@
}
@Override
- public void onHeadsUpPinned(NotificationData.Entry entry) {
+ public void onHeadsUpPinned(NotificationEntry entry) {
}
@Override
- public void onHeadsUpUnPinned(NotificationData.Entry entry) {
+ public void onHeadsUpUnPinned(NotificationEntry entry) {
}
@Override
- public void onAmbientStateChanged(NotificationData.Entry entry, boolean isAmbient) {
+ public void onAmbientStateChanged(NotificationEntry entry, boolean isAmbient) {
onAlertStateChanged(entry, isAmbient);
}
@Override
- public void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) {
+ public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
onAlertStateChanged(entry, isHeadsUp);
}
- private void onAlertStateChanged(NotificationData.Entry entry, boolean isAlerting) {
+ private void onAlertStateChanged(NotificationEntry entry, boolean isAlerting) {
if (isAlerting) {
if (shouldIsolate(entry)) {
isolateNotification(entry);
@@ -479,7 +479,7 @@
* @return true if the entry should be isolated
*/
- private boolean shouldIsolate(NotificationData.Entry entry) {
+ private boolean shouldIsolate(NotificationEntry entry) {
StatusBarNotification sbn = entry.notification;
NotificationGroup notificationGroup = mGroupMap.get(sbn.getGroupKey());
if (!sbn.isGroup() || sbn.getNotification().isGroupSummary()) {
@@ -499,7 +499,7 @@
*
* @param entry the notification to isolate
*/
- private void isolateNotification(NotificationData.Entry entry) {
+ private void isolateNotification(NotificationEntry entry) {
StatusBarNotification sbn = entry.notification;
// We will be isolated now, so lets update the groups
@@ -523,7 +523,7 @@
*
* @param entry the notification to un-isolate
*/
- private void stopIsolatingNotification(NotificationData.Entry entry) {
+ private void stopIsolatingNotification(NotificationEntry entry) {
StatusBarNotification sbn = entry.notification;
if (mIsolatedEntries.containsKey(sbn.getKey())) {
// not isolated anymore, we need to update the groups
@@ -564,8 +564,8 @@
}
public static class NotificationGroup {
- public final HashMap<String, NotificationData.Entry> children = new HashMap<>();
- public NotificationData.Entry summary;
+ public final HashMap<String, NotificationEntry> children = new HashMap<>();
+ public NotificationEntry summary;
public boolean expanded;
/**
* Is this notification group suppressed, i.e its summary is hidden
@@ -580,7 +580,7 @@
? Log.getStackTraceString(summary.getDebugThrowable())
: "");
result += "\n children size: " + children.size();
- for (NotificationData.Entry child : children.values()) {
+ for (NotificationEntry child : children.values()) {
result += "\n " + child.notification
+ (child.getDebugThrowable() != null
? Log.getStackTraceString(child.getDebugThrowable())
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index 056c8a7..1fb5484 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -23,9 +23,9 @@
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.StatusBarIconView;
-import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationUtils;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.tuner.TunerService;
@@ -182,7 +182,7 @@
return mStatusBar.getStatusBarHeight();
}
- protected boolean shouldShowNotificationIcon(NotificationData.Entry entry,
+ protected boolean shouldShowNotificationIcon(NotificationEntry entry,
boolean showAmbient, boolean showLowPriority, boolean hideDismissed,
boolean hideRepliedMessages) {
if (mEntryManager.getNotificationData().isAmbient(entry.key) && !showAmbient) {
@@ -247,7 +247,7 @@
* @param hideDismissed should dismissed icons be hidden
* @param hideRepliedMessages should messages that have been replied to be hidden
*/
- private void updateIconsForLayout(Function<NotificationData.Entry, StatusBarIconView> function,
+ private void updateIconsForLayout(Function<NotificationEntry, StatusBarIconView> function,
NotificationIconContainer hostLayout, boolean showAmbient, boolean showLowPriority,
boolean hideDismissed, boolean hideRepliedMessages) {
ArrayList<StatusBarIconView> toShow = new ArrayList<>(
@@ -257,7 +257,7 @@
for (int i = 0; i < mNotificationScrollLayout.getChildCount(); i++) {
View view = mNotificationScrollLayout.getChildAt(i);
if (view instanceof ExpandableNotificationRow) {
- NotificationData.Entry ent = ((ExpandableNotificationRow) view).getEntry();
+ NotificationEntry ent = ((ExpandableNotificationRow) view).getEntry();
if (shouldShowNotificationIcon(ent, showAmbient, showLowPriority, hideDismissed,
hideRepliedMessages)) {
toShow.add(function.apply(ent));
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 16576ad..d873b0c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -76,9 +76,9 @@
import com.android.systemui.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
import com.android.systemui.statusbar.notification.AnimatableProperty;
-import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.PropertyAnimator;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
@@ -2489,12 +2489,12 @@
}
@Override
- public void onHeadsUpPinned(NotificationData.Entry entry) {
+ public void onHeadsUpPinned(NotificationEntry entry) {
mNotificationStackScroller.generateHeadsUpAnimation(entry.getHeadsUpAnimationView(), true);
}
@Override
- public void onHeadsUpUnPinned(NotificationData.Entry entry) {
+ public void onHeadsUpUnPinned(NotificationEntry entry) {
// When we're unpinning the notification via active edge they remain heads-upped,
// we need to make sure that an animation happens in this case, otherwise the notification
@@ -2507,7 +2507,7 @@
}
@Override
- public void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) {
+ public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
mNotificationStackScroller.generateHeadsUpAnimation(entry, isHeadsUp);
}
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 977e336..6d78f72 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -192,12 +192,11 @@
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationAlertingManager;
import com.android.systemui.statusbar.notification.NotificationClicker;
-import com.android.systemui.statusbar.notification.NotificationData;
-import com.android.systemui.statusbar.notification.NotificationData.Entry;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.NotificationRowBinder;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -489,7 +488,7 @@
private Runnable mLaunchTransitionEndRunnable;
protected boolean mLaunchTransitionFadingAway;
- private NotificationData.Entry mDraggedDownEntry;
+ private NotificationEntry mDraggedDownEntry;
private boolean mLaunchCameraOnScreenTurningOn;
private boolean mLaunchCameraOnFinishedGoingToSleep;
private int mLastCameraLaunchSource;
@@ -633,7 +632,7 @@
mBubbleController.setExpandListener(mBubbleExpandListener);
KeyguardSliceProvider sliceProvider = KeyguardSliceProvider.getAttachedInstance();
if (sliceProvider != null) {
- sliceProvider.initDependencies();
+ sliceProvider.initDependencies(mMediaManager, mStatusBarStateController);
} else {
Log.w(TAG, "Cannot init KeyguardSliceProvider dependencies");
}
@@ -1510,21 +1509,21 @@
}
@Override
- public void onHeadsUpPinned(NotificationData.Entry entry) {
+ public void onHeadsUpPinned(NotificationEntry entry) {
dismissVolumeDialog();
}
@Override
- public void onHeadsUpUnPinned(NotificationData.Entry entry) {
+ public void onHeadsUpUnPinned(NotificationEntry entry) {
}
@Override
- public void onHeadsUpStateChanged(Entry entry, boolean isHeadsUp) {
+ public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
mEntryManager.updateNotificationRanking(null /* rankingMap */);
}
@Override
- public void onAmbientStateChanged(Entry entry, boolean isAmbient) {
+ public void onAmbientStateChanged(NotificationEntry entry, boolean isAmbient) {
mEntryManager.updateNotificationRanking(null);
if (isAmbient) {
mDozeServiceHost.fireNotificationPulse();
@@ -2551,11 +2550,11 @@
};
public void resetUserExpandedStates() {
- ArrayList<Entry> activeNotifications = mEntryManager.getNotificationData()
+ ArrayList<NotificationEntry> activeNotifications = mEntryManager.getNotificationData()
.getActiveNotifications();
final int notificationCount = activeNotifications.size();
for (int i = 0; i < notificationCount; i++) {
- NotificationData.Entry entry = activeNotifications.get(i);
+ NotificationEntry entry = activeNotifications.get(i);
entry.resetUserExpansion();
}
}
@@ -3506,7 +3505,7 @@
int userId = mLockscreenUserManager.getCurrentUserId();
ExpandableNotificationRow row = null;
- NotificationData.Entry entry = null;
+ NotificationEntry entry = null;
if (expandView instanceof ExpandableNotificationRow) {
entry = ((ExpandableNotificationRow) expandView).getEntry();
entry.setUserExpanded(true /* userExpanded */, true /* allowChildExpansion */);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index 8d1b911..4f61009 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -60,10 +60,10 @@
import com.android.systemui.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
-import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.policy.HeadsUpUtil;
import com.android.systemui.statusbar.policy.KeyguardMonitor;
@@ -131,7 +131,7 @@
mEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
@Override
- public void onPendingEntryAdded(NotificationData.Entry entry) {
+ public void onPendingEntryAdded(NotificationEntry entry) {
handleFullScreenIntent(entry);
}
});
@@ -267,7 +267,7 @@
}
}
Intent fillInIntent = null;
- NotificationData.Entry entry = row.getEntry();
+ NotificationEntry entry = row.getEntry();
CharSequence remoteInputText = null;
if (!TextUtils.isEmpty(entry.remoteInputText)) {
remoteInputText = entry.remoteInputText;
@@ -345,7 +345,7 @@
}, null, false /* afterKeyguardGone */);
}
- private void handleFullScreenIntent(NotificationData.Entry entry) {
+ private void handleFullScreenIntent(NotificationEntry entry) {
boolean isHeadsUped = mNotificationInterruptionStateProvider.shouldHeadsUp(entry);
if (!isHeadsUped && entry.notification.getNotification().fullScreenIntent != null) {
if (shouldSuppressFullScreenIntent(entry)) {
@@ -413,7 +413,7 @@
|| !mActivityLaunchAnimator.isAnimationPending();
}
- private boolean shouldSuppressFullScreenIntent(NotificationData.Entry entry) {
+ private boolean shouldSuppressFullScreenIntent(NotificationEntry entry) {
if (mPresenter.isDeviceInVrMode()) {
return true;
}
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 fb3157a..2c9def24 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -60,12 +60,12 @@
import com.android.systemui.statusbar.notification.AboveShelfObserver;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
import com.android.systemui.statusbar.notification.NotificationAlertingManager;
-import com.android.systemui.statusbar.notification.NotificationData.Entry;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.NotificationRowBinder;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -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;
@@ -183,19 +185,19 @@
Dependency.get(InitController.class).addPostInitTask(() -> {
NotificationEntryListener notificationEntryListener = new NotificationEntryListener() {
@Override
- public void onNotificationAdded(Entry entry) {
+ public void onNotificationAdded(NotificationEntry entry) {
// Recalculate the position of the sliding windows and the titles.
mShadeController.updateAreThereNotifications();
}
@Override
- public void onEntryUpdated(Entry entry) {
+ public void onEntryUpdated(NotificationEntry entry) {
mShadeController.updateAreThereNotifications();
}
@Override
public void onEntryRemoved(
- @Nullable Entry entry,
+ @Nullable NotificationEntry entry,
NotificationVisibility visibility,
boolean removedByUser) {
StatusBarNotificationPresenter.this.onNotificationRemoved(
@@ -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;
}
@@ -258,10 +258,10 @@
}
private void updateNotificationOnUiModeChanged() {
- ArrayList<Entry> userNotifications
+ ArrayList<NotificationEntry> userNotifications
= mEntryManager.getNotificationData().getNotificationsForCurrentUser();
for (int i = 0; i < userNotifications.size(); i++) {
- Entry entry = userNotifications.get(i);
+ NotificationEntry entry = userNotifications.get(i);
ExpandableNotificationRow row = entry.getRow();
if (row != null) {
row.onUiModeChanged();
@@ -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()
@@ -323,7 +336,7 @@
return !mEntryManager.getNotificationData().getActiveNotifications().isEmpty();
}
- public boolean canHeadsUp(Entry entry, StatusBarNotification sbn) {
+ public boolean canHeadsUp(NotificationEntry entry, StatusBarNotification sbn) {
if (mShadeController.isDozing()) {
return false;
}
@@ -367,7 +380,7 @@
if (MULTIUSER_DEBUG) mNotificationPanelDebugText.setText("USER " + newUserId);
mCommandQueue.animateCollapsePanels();
if (mReinflateNotificationsOnUserSwitched) {
- mEntryManager.updateNotificationsOnDensityOrFontScaleChanged();
+ updateNotificationsOnDensityOrFontScaleChanged();
mReinflateNotificationsOnUserSwitched = false;
}
if (mDispatchUiModeChangeOnUserSwitched) {
@@ -381,7 +394,7 @@
}
@Override
- public void onBindRow(Entry entry, PackageManager pmUser,
+ public void onBindRow(NotificationEntry entry, PackageManager pmUser,
StatusBarNotification sbn, ExpandableNotificationRow row) {
row.setAboveShelfChangedListener(mAboveShelfObserver);
row.setSecureStateProvider(mUnlockMethodCache::canSkipBouncer);
@@ -439,7 +452,7 @@
}
@Override
- public void onExpandClicked(Entry clickedEntry, boolean nowExpanded) {
+ public void onExpandClicked(NotificationEntry clickedEntry, boolean nowExpanded) {
mHeadsUpManager.setExpanded(clickedEntry, nowExpanded);
if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD && nowExpanded) {
mShadeController.goToLockedShade(clickedEntry.getRow());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index a02c9d5..fd3f680 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -30,7 +30,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.R;
import com.android.systemui.statusbar.AlertingNotificationManager;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
import java.io.FileDescriptor;
@@ -108,11 +108,11 @@
}
}
- protected boolean shouldHeadsUpBecomePinned(@NonNull NotificationData.Entry entry) {
+ protected boolean shouldHeadsUpBecomePinned(@NonNull NotificationEntry entry) {
return hasFullScreenIntent(entry);
}
- protected boolean hasFullScreenIntent(@NonNull NotificationData.Entry entry) {
+ protected boolean hasFullScreenIntent(@NonNull NotificationEntry entry) {
return entry.notification.getNotification().fullScreenIntent != null;
}
@@ -121,7 +121,7 @@
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "setEntryPinned: " + isPinned);
}
- NotificationData.Entry entry = headsUpEntry.mEntry;
+ NotificationEntry entry = headsUpEntry.mEntry;
if (entry.isRowPinned() != isPinned) {
entry.setRowPinned(isPinned);
updatePinnedMode();
@@ -141,7 +141,7 @@
@Override
protected void onAlertEntryAdded(AlertEntry alertEntry) {
- NotificationData.Entry entry = alertEntry.mEntry;
+ NotificationEntry entry = alertEntry.mEntry;
entry.setHeadsUp(true);
setEntryPinned((HeadsUpEntry) alertEntry, shouldHeadsUpBecomePinned(entry));
for (OnHeadsUpChangedListener listener : mListeners) {
@@ -151,7 +151,7 @@
@Override
protected void onAlertEntryRemoved(AlertEntry alertEntry) {
- NotificationData.Entry entry = alertEntry.mEntry;
+ NotificationEntry entry = alertEntry.mEntry;
entry.setHeadsUp(false);
setEntryPinned((HeadsUpEntry) alertEntry, false /* isPinned */);
for (OnHeadsUpChangedListener listener : mListeners) {
@@ -222,7 +222,7 @@
* Returns the top Heads Up Notification, which appears to show at first.
*/
@Nullable
- public NotificationData.Entry getTopEntry() {
+ public NotificationEntry getTopEntry() {
HeadsUpEntry topEntry = getTopHeadsUpEntry();
return (topEntry != null) ? topEntry.mEntry : null;
}
@@ -323,7 +323,7 @@
* @return -1 if the first argument should be ranked higher than the second, 1 if the second
* one should be ranked higher and 0 if they are equal.
*/
- public int compare(@NonNull NotificationData.Entry a, @NonNull NotificationData.Entry b) {
+ public int compare(@NonNull NotificationEntry a, @NonNull NotificationEntry b) {
AlertEntry aEntry = getHeadsUpEntry(a.key);
AlertEntry bEntry = getHeadsUpEntry(b.key);
if (aEntry == null || bEntry == null) {
@@ -336,7 +336,7 @@
* Set an entry to be expanded and therefore stick in the heads up area if it's pinned
* until it's collapsed again.
*/
- public void setExpanded(@NonNull NotificationData.Entry entry, boolean expanded) {
+ public void setExpanded(@NonNull NotificationEntry entry, boolean expanded) {
HeadsUpEntry headsUpEntry = getHeadsUpEntry(entry.key);
if (headsUpEntry != null && entry.isRowPinned()) {
headsUpEntry.setExpanded(expanded);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java
index 7ad547a..438226a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/OnHeadsUpChangedListener.java
@@ -16,8 +16,7 @@
package com.android.systemui.statusbar.policy;
-import com.android.systemui.statusbar.notification.NotificationData;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
/**
* A listener to heads up changes
@@ -33,12 +32,12 @@
/**
* A notification was just pinned to the top.
*/
- default void onHeadsUpPinned(NotificationData.Entry entry) {}
+ default void onHeadsUpPinned(NotificationEntry entry) {}
/**
* A notification was just unpinned from the top.
*/
- default void onHeadsUpUnPinned(NotificationData.Entry entry) {}
+ default void onHeadsUpUnPinned(NotificationEntry entry) {}
/**
* A notification just became a heads up or turned back to its normal state.
@@ -46,5 +45,5 @@
* @param entry the entry of the changed notification
* @param isHeadsUp whether the notification is now a headsUp notification
*/
- default void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) {}
+ default void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 866015e..dfb02cf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -56,7 +56,7 @@
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.statusbar.RemoteInputController;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
@@ -83,7 +83,7 @@
private RemoteInputController mController;
private RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler;
- private NotificationData.Entry mEntry;
+ private NotificationEntry mEntry;
private boolean mRemoved;
@@ -180,7 +180,7 @@
}
public static RemoteInputView inflate(Context context, ViewGroup root,
- NotificationData.Entry entry,
+ NotificationEntry entry,
RemoteInputController controller) {
RemoteInputView v = (RemoteInputView)
LayoutInflater.from(context).inflate(R.layout.remote_input, root, false);
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 f85d803..68b172d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
@@ -36,8 +36,8 @@
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.SmartReplyController;
-import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.NotificationUtils;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
import java.text.BreakIterator;
@@ -194,7 +194,7 @@
*/
public void addRepliesFromRemoteInput(
SmartReplies smartReplies,
- SmartReplyController smartReplyController, NotificationData.Entry entry) {
+ SmartReplyController smartReplyController, NotificationEntry entry) {
if (smartReplies.remoteInput != null && smartReplies.pendingIntent != null) {
if (smartReplies.choices != null) {
for (int i = 0; i < smartReplies.choices.length; ++i) {
@@ -212,7 +212,7 @@
* notification are shown.
*/
public void addSmartActions(SmartActions smartActions,
- SmartReplyController smartReplyController, NotificationData.Entry entry,
+ SmartReplyController smartReplyController, NotificationEntry entry,
HeadsUpManager headsUpManager) {
int numSmartActions = smartActions.actions.size();
for (int n = 0; n < numSmartActions; n++) {
@@ -235,7 +235,7 @@
@VisibleForTesting
Button inflateReplyButton(Context context, ViewGroup root, int replyIndex,
SmartReplies smartReplies, SmartReplyController smartReplyController,
- NotificationData.Entry entry) {
+ NotificationEntry entry) {
Button b = (Button) LayoutInflater.from(context).inflate(
R.layout.smart_reply_button, root, false);
CharSequence choice = smartReplies.choices[replyIndex];
@@ -289,7 +289,7 @@
@VisibleForTesting
Button inflateActionButton(Context context, ViewGroup root, int actionIndex,
SmartActions smartActions, SmartReplyController smartReplyController,
- NotificationData.Entry entry, HeadsUpManager headsUpManager) {
+ NotificationEntry entry, HeadsUpManager headsUpManager) {
Notification.Action action = smartActions.actions.get(actionIndex);
Button button = (Button) LayoutInflater.from(context).inflate(
R.layout.smart_action_button, root, false);
@@ -428,9 +428,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/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
index 4150602..1844df5 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
@@ -35,6 +35,8 @@
import android.testing.TestableLooper.RunWithLooper;
import android.text.TextPaint;
import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.TextClock;
@@ -42,6 +44,8 @@
import com.android.systemui.plugins.ClockPlugin;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.StatusBarStateController;
import org.junit.Before;
import org.junit.Test;
@@ -60,6 +64,7 @@
public class KeyguardClockSwitchTest extends SysuiTestCase {
private PluginManager mPluginManager;
private FrameLayout mClockContainer;
+ private StatusBarStateController.StateListener mStateListener;
@Mock
TextClock mClockView;
@@ -75,6 +80,7 @@
mClockContainer = mKeyguardClockSwitch.findViewById(R.id.clock_view);
MockitoAnnotations.initMocks(this);
when(mClockView.getPaint()).thenReturn(mock(TextPaint.class));
+ mStateListener = mKeyguardClockSwitch.getStateListener();
}
@Test
@@ -272,4 +278,28 @@
verify(plugin).setStyle(style);
}
+
+ @Test
+ public void onStateChanged_InvisibleInShade() {
+ // GIVEN that the big clock container is visible
+ ViewGroup container = mock(ViewGroup.class);
+ when(container.getVisibility()).thenReturn(View.VISIBLE);
+ mKeyguardClockSwitch.setBigClockContainer(container);
+ // WHEN transitioned to SHADE state
+ mStateListener.onStateChanged(StatusBarState.SHADE);
+ // THEN the container is invisible.
+ verify(container).setVisibility(View.INVISIBLE);
+ }
+
+ @Test
+ public void onStateChanged_VisibleInKeyguard() {
+ // GIVEN that the big clock container is invisible
+ ViewGroup container = mock(ViewGroup.class);
+ when(container.getVisibility()).thenReturn(View.INVISIBLE);
+ mKeyguardClockSwitch.setBigClockContainer(container);
+ // WHEN transitioned to KEYGUARD state
+ mStateListener.onStateChanged(StatusBarState.KEYGUARD);
+ // THEN the container is visible.
+ verify(container).setVisibility(View.VISIBLE);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
index f8912bc..9400d6d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
@@ -36,9 +36,9 @@
import android.widget.RemoteViews;
import com.android.internal.messages.nano.SystemMessageProto;
-import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import org.junit.Before;
import org.junit.Test;
@@ -391,18 +391,18 @@
}
private void entryRemoved(StatusBarNotification notification) {
- mEntryListener.onEntryRemoved(new NotificationData.Entry(notification),
+ mEntryListener.onEntryRemoved(new NotificationEntry(notification),
null, false);
}
private void entryAdded(StatusBarNotification notification, int importance) {
- NotificationData.Entry entry = new NotificationData.Entry(notification);
+ NotificationEntry entry = new NotificationEntry(notification);
entry.importance = importance;
mEntryListener.onPendingEntryAdded(entry);
}
private void entryUpdated(StatusBarNotification notification, int importance) {
- NotificationData.Entry entry = new NotificationData.Entry(notification);
+ NotificationEntry entry = new NotificationEntry(notification);
entry.importance = importance;
mEntryListener.onEntryUpdated(entry);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 31df4a3..21d3652 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -33,9 +33,9 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.NotificationTestHelper;
-import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationData;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.StatusBarWindowController;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
index a8ff0b2..cfc19ef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
@@ -21,6 +21,7 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -44,6 +45,7 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.NotificationMediaManager;
+import com.android.systemui.statusbar.StatusBarStateController;
import org.junit.Assert;
import org.junit.Before;
@@ -67,6 +69,8 @@
private AlarmManager mAlarmManager;
@Mock
private NotificationMediaManager mNotificationMediaManager;
+ @Mock
+ private StatusBarStateController mStatusBarStateController;
private TestableKeyguardSliceProvider mProvider;
private boolean mIsZenMode;
@@ -76,7 +80,7 @@
mIsZenMode = false;
mProvider = new TestableKeyguardSliceProvider();
mProvider.attachInfo(getContext(), null);
- mProvider.initDependencies();
+ mProvider.initDependencies(mNotificationMediaManager, mStatusBarStateController);
SliceProvider.setSpecs(new HashSet<>(Arrays.asList(SliceSpecs.LIST)));
}
@@ -98,6 +102,7 @@
@Test
public void onBindSlice_readsMedia() {
MediaMetadata metadata = mock(MediaMetadata.class);
+ mProvider.onDozingChanged(true);
mProvider.onMetadataChanged(metadata);
mProvider.onBindSlice(mProvider.getUri());
verify(metadata).getText(eq(MediaMetadata.METADATA_KEY_TITLE));
@@ -162,7 +167,31 @@
@Test
public void onMetadataChanged_updatesSlice() {
mProvider.onMetadataChanged(mock(MediaMetadata.class));
+ mProvider.onDozingChanged(true);
verify(mContentResolver).notifyChange(eq(mProvider.getUri()), eq(null));
+
+ // Hides after waking up
+ reset(mContentResolver);
+ mProvider.onDozingChanged(false);
+ verify(mContentResolver).notifyChange(eq(mProvider.getUri()), eq(null));
+
+ // And won't update slice if device is awake
+ reset(mContentResolver);
+ mProvider.onMetadataChanged(mock(MediaMetadata.class));
+ verify(mContentResolver, never()).notifyChange(eq(mProvider.getUri()), eq(null));
+ }
+
+ @Test
+ public void onDozingChanged_updatesSliceIfMedia() {
+ // Show media when dozing
+ mProvider.onMetadataChanged(mock(MediaMetadata.class));
+ mProvider.onDozingChanged(true);
+ verify(mContentResolver).notifyChange(eq(mProvider.getUri()), eq(null));
+
+ // Do not notify again if nothing changed
+ reset(mContentResolver);
+ mProvider.onDozingChanged(true);
+ verify(mContentResolver, never()).notifyChange(eq(mProvider.getUri()), eq(null));
}
private class TestableKeyguardSliceProvider extends KeyguardSliceProvider {
@@ -201,11 +230,6 @@
protected String getFormattedDateLocked() {
return super.getFormattedDateLocked() + mCounter++;
}
-
- @Override
- public void initDependencies() {
- mMediaManager = mNotificationMediaManager;
- }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
index 9d0556f..f8957b2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
@@ -36,7 +36,7 @@
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -67,7 +67,7 @@
private AlertingNotificationManager mAlertingNotificationManager;
- protected NotificationData.Entry mEntry;
+ protected NotificationEntry mEntry;
protected Handler mTestHandler;
private StatusBarNotification mSbn;
protected boolean mTimedOut = false;
@@ -119,7 +119,7 @@
public void setUp() {
mTestHandler = Handler.createAsync(Looper.myLooper());
mSbn = createNewNotification(0 /* id */);
- mEntry = new NotificationData.Entry(mSbn);
+ mEntry = new NotificationEntry(mSbn);
mEntry.setRow(mRow);
mAlertingNotificationManager = createAlertingNotificationManager();
@@ -170,7 +170,7 @@
public void testReleaseAllImmediately() {
for (int i = 0; i < TEST_NUM_NOTIFICATIONS; i++) {
StatusBarNotification sbn = createNewNotification(i);
- NotificationData.Entry entry = new NotificationData.Entry(sbn);
+ NotificationEntry entry = new NotificationEntry(sbn);
entry.setRow(mRow);
mAlertingNotificationManager.showNotification(entry);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
index 65c04fe..c880172 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
@@ -31,8 +31,9 @@
import com.android.systemui.Dependency;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import org.junit.Before;
import org.junit.Test;
@@ -85,7 +86,7 @@
@Test
public void testNotificationUpdateCallsUpdateNotification() {
- when(mNotificationData.get(mSbn.getKey())).thenReturn(new NotificationData.Entry(mSbn));
+ when(mNotificationData.get(mSbn.getKey())).thenReturn(new NotificationEntry(mSbn));
mListener.onNotificationPosted(mSbn, mRanking);
TestableLooper.get(this).processAllMessages();
verify(mEntryManager).updateNotification(mSbn, mRanking);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
index f168a49..d7ca5b4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar;
-import static android.content.Intent.ACTION_DEVICE_LOCKED_CHANGED;
import static android.content.Intent.ACTION_USER_SWITCHED;
import static junit.framework.Assert.assertFalse;
@@ -42,8 +41,8 @@
import com.android.systemui.Dependency;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationData;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
index 150dcb9..c159516 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
@@ -24,8 +24,8 @@
import com.android.systemui.statusbar.NotificationRemoteInputManager.RemoteInputActiveExtender;
import com.android.systemui.statusbar.NotificationRemoteInputManager.RemoteInputHistoryExtender;
import com.android.systemui.statusbar.NotificationRemoteInputManager.SmartReplyHistoryExtender;
-import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.phone.ShadeController;
@@ -60,7 +60,7 @@
private TestableNotificationRemoteInputManager mRemoteInputManager;
private StatusBarNotification mSbn;
- private NotificationData.Entry mEntry;
+ private NotificationEntry mEntry;
private RemoteInputHistoryExtender mRemoteInputHistoryExtender;
private SmartReplyHistoryExtender mSmartReplyHistoryExtender;
private RemoteInputActiveExtender mRemoteInputActiveExtender;
@@ -75,7 +75,7 @@
Handler.createAsync(Looper.myLooper()));
mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID,
0, new Notification(), UserHandle.CURRENT, null, 0);
- mEntry = new NotificationData.Entry(mSbn);
+ mEntry = new NotificationEntry(mSbn);
mEntry.setRow(mRow);
mRemoteInputManager.setUpWithPresenterForTest(mCallback,
@@ -170,7 +170,7 @@
// Setup a notification entry with 1 remote input.
StatusBarNotification newSbn =
mRemoteInputManager.rebuildNotificationWithRemoteInput(mEntry, "A Reply", false);
- NotificationData.Entry entry = new NotificationData.Entry(newSbn);
+ NotificationEntry entry = new NotificationEntry(newSbn);
// Try rebuilding to add another reply.
newSbn = mRemoteInputManager.rebuildNotificationWithRemoteInput(entry, "Reply 2", true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
index fb5e875..529da82 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
@@ -32,7 +32,7 @@
import android.widget.RemoteViews;
import com.android.systemui.R;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
import com.android.systemui.statusbar.notification.row.NotificationInflaterTest;
@@ -238,7 +238,7 @@
mUser,
null /* overrideGroupKey */,
System.currentTimeMillis());
- NotificationData.Entry entry = new NotificationData.Entry(sbn);
+ NotificationEntry entry = new NotificationEntry(sbn);
entry.setRow(row);
entry.createIcons(mContext, sbn);
entry.channel = new NotificationChannel(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
index e716421..bf91305 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -38,10 +38,10 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
-import com.android.systemui.statusbar.notification.NotificationData;
-import com.android.systemui.statusbar.notification.NotificationData.Entry;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
@@ -102,9 +102,9 @@
mViewHierarchyManager.setUpWithPresenter(mPresenter, mListContainer);
}
- private NotificationData.Entry createEntry() throws Exception {
+ private NotificationEntry createEntry() throws Exception {
ExpandableNotificationRow row = mHelper.createRow();
- NotificationData.Entry entry = new NotificationData.Entry(row.getStatusBarNotification());
+ NotificationEntry entry = new NotificationEntry(row.getStatusBarNotification());
entry.setRow(row);
return entry;
}
@@ -113,9 +113,9 @@
public void testNotificationsBecomingBundled() throws Exception {
// Tests 3 top level notifications becoming a single bundled notification with |entry0| as
// the summary.
- NotificationData.Entry entry0 = createEntry();
- NotificationData.Entry entry1 = createEntry();
- NotificationData.Entry entry2 = createEntry();
+ NotificationEntry entry0 = createEntry();
+ NotificationEntry entry1 = createEntry();
+ NotificationEntry entry2 = createEntry();
// Set up the prior state to look like three top level notifications.
mListContainer.addContainerView(entry0.getRow());
@@ -142,9 +142,9 @@
@Test
public void testNotificationsBecomingUnbundled() throws Exception {
// Tests a bundled notification becoming three top level notifications.
- NotificationData.Entry entry0 = createEntry();
- NotificationData.Entry entry1 = createEntry();
- NotificationData.Entry entry2 = createEntry();
+ NotificationEntry entry0 = createEntry();
+ NotificationEntry entry1 = createEntry();
+ NotificationEntry entry2 = createEntry();
entry0.getRow().addChildNotification(entry1.getRow());
entry0.getRow().addChildNotification(entry2.getRow());
@@ -173,8 +173,8 @@
@Test
public void testNotificationsBecomingSuppressed() throws Exception {
// Tests two top level notifications becoming a suppressed summary and a child.
- NotificationData.Entry entry0 = createEntry();
- NotificationData.Entry entry1 = createEntry();
+ NotificationEntry entry0 = createEntry();
+ NotificationEntry entry1 = createEntry();
entry0.getRow().addChildNotification(entry1.getRow());
// Set up the prior state to look like a top level notification.
@@ -199,7 +199,7 @@
@Test
public void testUpdateNotificationViews_appOps() throws Exception {
- NotificationData.Entry entry0 = createEntry();
+ NotificationEntry entry0 = createEntry();
entry0.setRow(spy(entry0.getRow()));
when(mNotificationData.getActiveNotifications()).thenReturn(
Lists.newArrayList(entry0));
@@ -264,10 +264,7 @@
public void setMaxDisplayedNotifications(int maxNotifications) {}
@Override
- public void snapViewIfNeeded(Entry entry) {}
-
- @Override
- public ViewGroup getViewParentForNotification(NotificationData.Entry entry) {
+ public ViewGroup getViewParentForNotification(NotificationEntry entry) {
return null;
}
@@ -283,10 +280,10 @@
}
@Override
- public void cleanUpViewStateForEntry(Entry entry) { }
+ public void cleanUpViewStateForEntry(NotificationEntry entry) { }
@Override
- public boolean isInVisibleLocation(Entry entry) {
+ public boolean isInVisibleLocation(NotificationEntry entry) {
return true;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
index e7a1f05..6cca434 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
@@ -36,8 +36,8 @@
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.phone.ShadeController;
import org.junit.Before;
@@ -58,7 +58,7 @@
private static final int TEST_ACTION_COUNT = 3;
private Notification mNotification;
- private NotificationData.Entry mEntry;
+ private NotificationEntry mEntry;
private SmartReplyController mSmartReplyController;
private NotificationRemoteInputManager mRemoteInputManager;
@@ -92,7 +92,7 @@
mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID,
0, mNotification, new UserHandle(ActivityManager.getCurrentUser()), null, 0);
- mEntry = new NotificationData.Entry(mSbn);
+ mEntry = new NotificationEntry(mSbn);
}
@Test
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 6197341..4d22536 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
@@ -65,7 +65,8 @@
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.SmartReplyController;
import com.android.systemui.statusbar.StatusBarIconView;
-import com.android.systemui.statusbar.notification.NotificationData.KeyguardEnvironment;
+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;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.row.NotificationInflater;
@@ -123,7 +124,7 @@
@Mock private RowInflaterTask mAsyncInflationTask;
@Mock private NotificationRowBinder mMockedRowBinder;
- private NotificationData.Entry mEntry;
+ private NotificationEntry mEntry;
private StatusBarNotification mSbn;
private TestableNotificationEntryManager mEntryManager;
private CountDownLatch mCountDownLatch;
@@ -137,7 +138,7 @@
}
@Override
- public void onAsyncInflationFinished(NotificationData.Entry entry,
+ public void onAsyncInflationFinished(NotificationEntry entry,
@NotificationInflater.InflationFlag int inflatedFlags) {
super.onAsyncInflationFinished(entry, inflatedFlags);
@@ -222,7 +223,7 @@
.setContentText("Text");
mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID,
0, n.build(), new UserHandle(ActivityManager.getCurrentUser()), null, 0);
- mEntry = new NotificationData.Entry(mSbn);
+ mEntry = new NotificationEntry(mSbn);
mEntry.expandedIcon = mock(StatusBarIconView.class);
mEntryManager = new TestableNotificationEntryManager(mContext);
@@ -259,10 +260,10 @@
verify(mEntryListener, never()).onInflationError(any(), any());
// Row inflation:
- ArgumentCaptor<NotificationData.Entry> entryCaptor = ArgumentCaptor.forClass(
- NotificationData.Entry.class);
+ ArgumentCaptor<NotificationEntry> entryCaptor = ArgumentCaptor.forClass(
+ NotificationEntry.class);
verify(mBindCallback).onBindRow(entryCaptor.capture(), any(), eq(mSbn), any());
- NotificationData.Entry entry = entryCaptor.getValue();
+ NotificationEntry entry = entryCaptor.getValue();
verify(mRemoteInputManager).bindRow(entry.getRow());
// Row content inflation:
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
index da8bc01d..387475f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java
@@ -40,6 +40,8 @@
import com.android.systemui.ForegroundServiceController;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.collection.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.ShadeController;
@@ -191,12 +193,12 @@
// test should filter out hidden notifications:
// hidden
- NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification);
+ NotificationEntry entry = new NotificationEntry(mMockStatusBarNotification);
entry.suspended = true;
assertTrue(mNotificationFilter.shouldFilterOut(entry));
// not hidden
- entry = new NotificationData.Entry(mMockStatusBarNotification);
+ entry = new NotificationEntry(mMockStatusBarNotification);
entry.suspended = false;
assertFalse(mNotificationFilter.shouldFilterOut(entry));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java
index 6fbd8c7..813fc9d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/VisualStabilityManagerTest.java
@@ -29,6 +29,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.NotificationPresenter;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import org.junit.Before;
@@ -44,13 +45,13 @@
private VisualStabilityManager.Callback mCallback = mock(VisualStabilityManager.Callback.class);
private VisibilityLocationProvider mLocationProvider = mock(VisibilityLocationProvider.class);
private ExpandableNotificationRow mRow = mock(ExpandableNotificationRow.class);
- private NotificationData.Entry mEntry;
+ private NotificationEntry mEntry;
@Before
public void setUp() {
mVisualStabilityManager.setUpWithPresenter(mock(NotificationPresenter.class));
mVisualStabilityManager.setVisibilityLocationProvider(mLocationProvider);
- mEntry = new NotificationData.Entry(mock(StatusBarNotification.class));
+ mEntry = new NotificationEntry(mock(StatusBarNotification.class));
mEntry.setRow(mRow);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java
similarity index 95%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java
index 6f4de1c..193f483 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java
@@ -14,7 +14,7 @@
* limitations under the License
*/
-package com.android.systemui.statusbar.notification;
+package com.android.systemui.statusbar.notification.collection;
import static android.app.AppOpsManager.OP_ACCEPT_HANDOVER;
import static android.app.AppOpsManager.OP_CAMERA;
@@ -59,7 +59,9 @@
import com.android.systemui.InitController;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.NotificationTestHelper;
-import com.android.systemui.statusbar.notification.NotificationData.KeyguardEnvironment;
+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;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.ShadeController;
@@ -203,7 +205,7 @@
mRow.getEntry().notification)).thenReturn(false);
when(mEnvironment.isNotificationForCurrentProfiles(
row2.getEntry().notification)).thenReturn(true);
- ArrayList<NotificationData.Entry> result =
+ ArrayList<NotificationEntry> result =
mNotificationData.getNotificationsForCurrentUser();
assertEquals(result.size(), 1);
@@ -217,7 +219,7 @@
TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY);
Notification n = mMockStatusBarNotification.getNotification();
n.flags = Notification.FLAG_FOREGROUND_SERVICE;
- NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification);
+ NotificationEntry entry = new NotificationEntry(mMockStatusBarNotification);
mNotificationData.add(entry);
assertTrue(entry.isExemptFromDndVisualSuppression());
@@ -234,7 +236,7 @@
nb.setStyle(new Notification.MediaStyle().setMediaSession(mock(MediaSession.Token.class)));
n = nb.build();
when(mMockStatusBarNotification.getNotification()).thenReturn(n);
- NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification);
+ NotificationEntry entry = new NotificationEntry(mMockStatusBarNotification);
mNotificationData.add(entry);
assertTrue(entry.isExemptFromDndVisualSuppression());
@@ -246,7 +248,7 @@
initStatusBarNotification(false);
when(mMockStatusBarNotification.getKey()).thenReturn(
TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY);
- NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification);
+ NotificationEntry entry = new NotificationEntry(mMockStatusBarNotification);
entry.mIsSystemNotification = true;
mNotificationData.add(entry);
@@ -259,7 +261,7 @@
initStatusBarNotification(false);
when(mMockStatusBarNotification.getKey()).thenReturn(
TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY);
- NotificationData.Entry entry = new NotificationData.Entry(mMockStatusBarNotification);
+ NotificationEntry entry = new NotificationEntry(mMockStatusBarNotification);
entry.mIsSystemNotification = true;
mNotificationData.add(entry);
@@ -313,8 +315,8 @@
snoozeCriterions.add(snoozeCriterion);
when(ranking.getSnoozeCriteria()).thenReturn(snoozeCriterions);
- NotificationData.Entry entry =
- new NotificationData.Entry(mMockStatusBarNotification, ranking);
+ NotificationEntry entry =
+ new NotificationEntry(mMockStatusBarNotification, ranking);
assertEquals(systemGeneratedSmartActions, entry.systemGeneratedSmartActions);
assertEquals(NOTIFICATION_CHANNEL, entry.channel);
@@ -343,7 +345,7 @@
StatusBarNotification sbn = new StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0,
notification, mContext.getUser(), "", 0);
- NotificationData.Entry entry = new NotificationData.Entry(sbn);
+ NotificationEntry entry = new NotificationEntry(sbn);
entry.setHasSentReply();
assertTrue(entry.isLastMessageFromReply());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
index afdeb62..6472589 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java
@@ -41,9 +41,10 @@
import com.android.systemui.UiOffloadThread;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
@@ -77,7 +78,7 @@
@Mock private NotificationListener mListener;
@Captor private ArgumentCaptor<NotificationEntryListener> mEntryListenerCaptor;
- private NotificationData.Entry mEntry;
+ private NotificationEntry mEntry;
private TestableNotificationLogger mLogger;
private NotificationEntryListener mNotificationEntryListener;
private ConcurrentLinkedQueue<AssertionError> mErrorQueue = new ConcurrentLinkedQueue<>();
@@ -93,7 +94,7 @@
StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME,
0, null, TEST_UID,
0, new Notification(), UserHandle.CURRENT, null, 0);
- mEntry = new NotificationData.Entry(sbn);
+ mEntry = new NotificationEntry(sbn);
mEntry.setRow(mRow);
mLogger = new TestableNotificationLogger(mListener, Dependency.get(UiOffloadThread.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
index 82b42c5..d94bf84 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java
@@ -47,7 +47,7 @@
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.policy.SmartReplyConstants;
import org.junit.Before;
@@ -73,7 +73,7 @@
StatusBarNotification mStatusBarNotification;
@Mock
Notification mNotification;
- NotificationData.Entry mEntry;
+ NotificationEntry mEntry;
@Mock
RemoteInput mRemoteInput;
@Mock
@@ -107,7 +107,7 @@
// Smart replies and actions
when(mNotification.getAllowSystemGeneratedContextualActions()).thenReturn(true);
when(mStatusBarNotification.getNotification()).thenReturn(mNotification);
- mEntry = new NotificationData.Entry(mStatusBarNotification);
+ mEntry = new NotificationEntry(mStatusBarNotification);
when(mSmartReplyConstants.isEnabled()).thenReturn(true);
mActionIcon = Icon.createWithResource(mContext, R.drawable.ic_person);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
index ad43bea..0899c73 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -60,7 +60,7 @@
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationTestHelper;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -183,8 +183,8 @@
when(row.getGuts()).thenReturn(guts);
doNothing().when(row).inflateGuts();
- NotificationData.Entry realEntry = realRow.getEntry();
- NotificationData.Entry entry = spy(realEntry);
+ NotificationEntry realEntry = realRow.getEntry();
+ NotificationEntry entry = spy(realEntry);
when(entry.getRow()).thenReturn(row);
when(entry.getGuts()).thenReturn(guts);
@@ -443,7 +443,7 @@
NotificationGuts guts = new NotificationGuts(mContext);
ExpandableNotificationRow row = spy(createTestNotificationRow());
doReturn(guts).when(row).getGuts();
- NotificationData.Entry entry = row.getEntry();
+ NotificationEntry entry = row.getEntry();
entry.setRow(row);
mGutsManager.setExposedGuts(guts);
@@ -452,7 +452,7 @@
@Test
public void testSetShouldManageLifetime_setShouldManage() {
- NotificationData.Entry entry = createTestNotificationRow().getEntry();
+ NotificationEntry entry = createTestNotificationRow().getEntry();
mGutsManager.setShouldManageLifetime(entry, true /* shouldManage */);
assertTrue(entry.key.equals(mGutsManager.mKeyToRemoveOnGutsClosed));
@@ -460,7 +460,7 @@
@Test
public void testSetShouldManageLifetime_setShouldNotManage() {
- NotificationData.Entry entry = createTestNotificationRow().getEntry();
+ NotificationEntry entry = createTestNotificationRow().getEntry();
mGutsManager.mKeyToRemoveOnGutsClosed = entry.key;
mGutsManager.setShouldManageLifetime(entry, false /* shouldManage */);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInflaterTest.java
index d6b706d..648df3c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInflaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInflaterTest.java
@@ -48,7 +48,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.InflationTask;
import com.android.systemui.statusbar.NotificationTestHelper;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import org.junit.Assert;
import org.junit.Before;
@@ -88,7 +88,7 @@
}
@Override
- public void onAsyncInflationFinished(NotificationData.Entry entry,
+ public void onAsyncInflationFinished(NotificationEntry entry,
@NotificationInflater.InflationFlag int inflatedFlags) {
}
});
@@ -174,7 +174,7 @@
}
@Override
- public void onAsyncInflationFinished(NotificationData.Entry entry,
+ public void onAsyncInflationFinished(NotificationEntry entry,
@NotificationInflater.InflationFlag int inflatedFlags) {
countDownLatch.countDown();
}
@@ -256,7 +256,7 @@
}
@Override
- public void onAsyncInflationFinished(NotificationData.Entry entry,
+ public void onAsyncInflationFinished(NotificationEntry entry,
@NotificationInflater.InflationFlag int inflatedFlags) {
if (expectingException) {
exceptionHolder.setException(new RuntimeException(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
index e4d0196..04d7509 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
@@ -35,7 +35,7 @@
import android.view.ViewGroup;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.utils.leaks.LeakCheckedTest;
import org.junit.Before;
@@ -54,7 +54,7 @@
public void setup() {
injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
mRow = mock(ExpandableNotificationRow.class);
- NotificationData.Entry entry = new NotificationData.Entry(
+ NotificationEntry entry = new NotificationEntry(
mock(StatusBarNotification.class));
entry.channel = mock(NotificationChannel.class);
when(mRow.getEntry()).thenReturn(entry);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index b66d0ab78..214404c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -53,9 +53,9 @@
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.notification.NotificationData;
-import com.android.systemui.statusbar.notification.NotificationData.Entry;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.FooterView;
import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
@@ -265,8 +265,8 @@
@Test
public void testUpdateFooter_remoteInput() {
setBarStateForTest(StatusBarState.SHADE);
- ArrayList<Entry> entries = new ArrayList<>();
- entries.add(mock(Entry.class));
+ ArrayList<NotificationEntry> entries = new ArrayList<>();
+ entries.add(mock(NotificationEntry.class));
when(mNotificationData.getActiveNotifications()).thenReturn(entries);
ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
@@ -282,8 +282,8 @@
@Test
public void testUpdateFooter_oneClearableNotification() {
setBarStateForTest(StatusBarState.SHADE);
- ArrayList<Entry> entries = new ArrayList<>();
- entries.add(mock(Entry.class));
+ ArrayList<NotificationEntry> entries = new ArrayList<>();
+ entries.add(mock(NotificationEntry.class));
when(mNotificationData.getActiveNotifications()).thenReturn(entries);
ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
@@ -298,8 +298,8 @@
@Test
public void testUpdateFooter_oneNonClearableNotification() {
setBarStateForTest(StatusBarState.SHADE);
- ArrayList<Entry> entries = new ArrayList<>();
- entries.add(mock(Entry.class));
+ ArrayList<NotificationEntry> entries = new ArrayList<>();
+ entries.add(mock(NotificationEntry.class));
when(mEntryManager.getNotificationData().getActiveNotifications()).thenReturn(entries);
assertTrue(mEntryManager.getNotificationData().getActiveNotifications().size() != 0);
@@ -314,7 +314,7 @@
// add notification
ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
- NotificationData.Entry entry = mock(NotificationData.Entry.class);
+ NotificationEntry entry = mock(NotificationEntry.class);
when(row.getEntry()).thenReturn(entry);
when(entry.isClearable()).thenReturn(true);
mStackScroller.addContainerView(row);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
index 44deb10..9ceb325 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
@@ -16,15 +16,20 @@
package com.android.systemui.statusbar.phone;
-import android.view.View;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Mockito.when;
+
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.view.View;
import com.android.systemui.statusbar.AlertingNotificationManager;
import com.android.systemui.statusbar.AlertingNotificationManagerTest;
-import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import org.junit.Before;
import org.junit.Rule;
@@ -34,11 +39,6 @@
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
-import static junit.framework.Assert.assertTrue;
-import static junit.framework.Assert.assertFalse;
-
-import static org.mockito.Mockito.when;
-
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@@ -96,7 +96,7 @@
@Test
public void testCanRemoveImmediately_notTopEntry() {
- NotificationData.Entry laterEntry = new NotificationData.Entry(createNewNotification(1));
+ NotificationEntry laterEntry = new NotificationEntry(createNewNotification(1));
laterEntry.setRow(mRow);
mHeadsUpManager.showNotification(mEntry);
mHeadsUpManager.showNotification(laterEntry);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
index 79695fd..cefd4ac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
@@ -32,10 +32,9 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.AmbientPulseManager;
-import com.android.systemui.statusbar.notification.NotificationData;
-import com.android.systemui.statusbar.notification.NotificationData.Entry;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import org.junit.Before;
@@ -64,7 +63,7 @@
@Captor
private ArgumentCaptor<NotificationEntryListener> mListenerCaptor;
private NotificationEntryListener mNotificationEntryListener;
- private final HashMap<String, Entry> mPendingEntries = new HashMap<>();
+ private final HashMap<String, NotificationEntry> mPendingEntries = new HashMap<>();
private final NotificationGroupTestHelper mGroupTestHelper =
new NotificationGroupTestHelper(mContext);
@@ -94,9 +93,9 @@
@Test
public void testSuppressedSummaryHeadsUpTransfersToChild() {
- Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
+ NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
mHeadsUpManager.showNotification(summaryEntry);
- Entry childEntry = mGroupTestHelper.createChildNotification();
+ NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
// Summary will be suppressed because there is only one child.
mGroupManager.onEntryAdded(summaryEntry);
@@ -109,11 +108,11 @@
@Test
public void testSuppressedSummaryHeadsUpTransfersToChildButBackAgain() {
- NotificationData.Entry summaryEntry =
+ NotificationEntry summaryEntry =
mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
- NotificationData.Entry childEntry =
+ NotificationEntry childEntry =
mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
- NotificationData.Entry childEntry2 =
+ NotificationEntry childEntry2 =
mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
mHeadsUpManager.showNotification(summaryEntry);
// Trigger a transfer of alert state from summary to child.
@@ -133,11 +132,11 @@
@Test
public void testSuppressedSummaryHeadsUpDoesntTransferBackOnDozingChanged() {
- NotificationData.Entry summaryEntry =
+ NotificationEntry summaryEntry =
mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
- NotificationData.Entry childEntry =
+ NotificationEntry childEntry =
mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
- NotificationData.Entry childEntry2 =
+ NotificationEntry childEntry2 =
mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
mHeadsUpManager.showNotification(summaryEntry);
// Trigger a transfer of alert state from summary to child.
@@ -158,9 +157,9 @@
@Test
public void testSuppressedSummaryHeadsUpTransferDoesNotAlertChildIfUninflated() {
- Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
+ NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
mHeadsUpManager.showNotification(summaryEntry);
- Entry childEntry = mGroupTestHelper.createChildNotification();
+ NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
.thenReturn(false);
@@ -176,9 +175,9 @@
@Test
public void testSuppressedSummaryHeadsUpTransferAlertsChildOnInflation() {
- Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
+ NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
mHeadsUpManager.showNotification(summaryEntry);
- Entry childEntry = mGroupTestHelper.createChildNotification();
+ NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
.thenReturn(false);
@@ -196,13 +195,13 @@
@Test
public void testSuppressedSummaryHeadsUpTransferBackAbortsChildInflation() {
- NotificationData.Entry summaryEntry =
+ NotificationEntry summaryEntry =
mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
- NotificationData.Entry childEntry =
+ NotificationEntry childEntry =
mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
.thenReturn(false);
- NotificationData.Entry childEntry2 =
+ NotificationEntry childEntry2 =
mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
mHeadsUpManager.showNotification(summaryEntry);
// Trigger a transfer of alert state from summary to child.
@@ -226,9 +225,9 @@
@Test
public void testCleanUpPendingAlertInfo() {
- NotificationData.Entry summaryEntry =
+ NotificationEntry summaryEntry =
mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
- NotificationData.Entry childEntry =
+ NotificationEntry childEntry =
mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
.thenReturn(false);
@@ -244,9 +243,9 @@
@Test
public void testUpdateGroupChangeDoesNotTransfer() {
- NotificationData.Entry summaryEntry =
+ NotificationEntry summaryEntry =
mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
- NotificationData.Entry childEntry =
+ NotificationEntry childEntry =
mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
.thenReturn(false);
@@ -267,9 +266,9 @@
@Test
public void testUpdateChildToSummaryDoesNotTransfer() {
- NotificationData.Entry summaryEntry =
+ NotificationEntry summaryEntry =
mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
- NotificationData.Entry childEntry =
+ NotificationEntry childEntry =
mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
when(childEntry.getRow().isInflationFlagSet(mHeadsUpManager.getContentFlag()))
.thenReturn(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java
index b0bd1fb..ecb3e4d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java
@@ -29,7 +29,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.AmbientPulseManager;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import org.junit.Before;
@@ -69,8 +69,8 @@
@Test
public void testIsOnlyChildInGroup() {
- NotificationData.Entry childEntry = mGroupTestHelper.createChildNotification();
- NotificationData.Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
+ NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
+ NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
mGroupManager.onEntryAdded(summaryEntry);
mGroupManager.onEntryAdded(childEntry);
@@ -80,8 +80,8 @@
@Test
public void testIsChildInGroupWithSummary() {
- NotificationData.Entry childEntry = mGroupTestHelper.createChildNotification();
- NotificationData.Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
+ NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
+ NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
mGroupManager.onEntryAdded(summaryEntry);
mGroupManager.onEntryAdded(childEntry);
@@ -92,8 +92,8 @@
@Test
public void testIsSummaryOfGroupWithChildren() {
- NotificationData.Entry childEntry = mGroupTestHelper.createChildNotification();
- NotificationData.Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
+ NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
+ NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
mGroupManager.onEntryAdded(summaryEntry);
mGroupManager.onEntryAdded(childEntry);
@@ -105,8 +105,8 @@
@Test
public void testRemoveChildFromGroupWithSummary() {
- NotificationData.Entry childEntry = mGroupTestHelper.createChildNotification();
- NotificationData.Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
+ NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
+ NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
mGroupManager.onEntryAdded(summaryEntry);
mGroupManager.onEntryAdded(childEntry);
mGroupManager.onEntryAdded(mGroupTestHelper.createChildNotification());
@@ -118,8 +118,8 @@
@Test
public void testRemoveSummaryFromGroupWithSummary() {
- NotificationData.Entry childEntry = mGroupTestHelper.createChildNotification();
- NotificationData.Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
+ NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
+ NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
mGroupManager.onEntryAdded(summaryEntry);
mGroupManager.onEntryAdded(childEntry);
mGroupManager.onEntryAdded(mGroupTestHelper.createChildNotification());
@@ -132,8 +132,8 @@
@Test
public void testHeadsUpEntryIsIsolated() {
- NotificationData.Entry childEntry = mGroupTestHelper.createChildNotification();
- NotificationData.Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
+ NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
+ NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
mGroupManager.onEntryAdded(summaryEntry);
mGroupManager.onEntryAdded(childEntry);
mGroupManager.onEntryAdded(mGroupTestHelper.createChildNotification());
@@ -149,8 +149,8 @@
@Test
public void testAmbientPulseEntryIsIsolated() {
- NotificationData.Entry childEntry = mGroupTestHelper.createChildNotification();
- NotificationData.Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
+ NotificationEntry childEntry = mGroupTestHelper.createChildNotification();
+ NotificationEntry summaryEntry = mGroupTestHelper.createSummaryNotification();
mGroupManager.onEntryAdded(summaryEntry);
mGroupManager.onEntryAdded(childEntry);
mGroupManager.onEntryAdded(mGroupTestHelper.createChildNotification());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
index 7ad68eb..e6b9778 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
@@ -27,7 +27,7 @@
import android.service.notification.StatusBarNotification;
import com.android.systemui.R;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
/**
@@ -44,23 +44,23 @@
mContext = context;
}
- public NotificationData.Entry createSummaryNotification() {
+ public NotificationEntry createSummaryNotification() {
return createSummaryNotification(Notification.GROUP_ALERT_ALL);
}
- public NotificationData.Entry createSummaryNotification(int groupAlertBehavior) {
+ public NotificationEntry createSummaryNotification(int groupAlertBehavior) {
return createEntry(true, groupAlertBehavior);
}
- public NotificationData.Entry createChildNotification() {
+ public NotificationEntry createChildNotification() {
return createChildNotification(Notification.GROUP_ALERT_ALL);
}
- public NotificationData.Entry createChildNotification(int groupAlertBehavior) {
+ public NotificationEntry createChildNotification(int groupAlertBehavior) {
return createEntry(false, groupAlertBehavior);
}
- public NotificationData.Entry createEntry(boolean isSummary, int groupAlertBehavior) {
+ public NotificationEntry createEntry(boolean isSummary, int groupAlertBehavior) {
Notification notif = new Notification.Builder(mContext, TEST_CHANNEL_ID)
.setContentTitle("Title")
.setSmallIcon(R.drawable.ic_person)
@@ -79,7 +79,7 @@
new UserHandle(ActivityManager.getCurrentUser()),
null /* overrideGroupKey */,
0 /* postTime */);
- NotificationData.Entry entry = new NotificationData.Entry(sbn);
+ NotificationEntry entry = new NotificationEntry(sbn);
ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
entry.setRow(row);
when(row.getEntry()).thenReturn(entry);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index 2bbfd7d..12cb9957 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -40,7 +40,7 @@
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
import com.android.systemui.statusbar.notification.NotificationAlertingManager;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
@@ -82,7 +82,7 @@
Notification n = new Notification.Builder(getContext(), "a").build();
StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
UserHandle.of(0), null, 0);
- NotificationData.Entry entry = new NotificationData.Entry(sbn);
+ NotificationEntry entry = new NotificationEntry(sbn);
mCommandQueue.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_EXPAND, 0,
false /* animate */);
TestableLooper.get(this).processAllMessages();
@@ -96,7 +96,7 @@
Notification n = new Notification.Builder(getContext(), "a").build();
StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
UserHandle.of(0), null, 0);
- NotificationData.Entry entry = new NotificationData.Entry(sbn);
+ NotificationEntry entry = new NotificationEntry(sbn);
mCommandQueue.disable(DEFAULT_DISPLAY, 0, StatusBarManager.DISABLE2_NOTIFICATION_SHADE,
false /* animate */);
TestableLooper.get(this).processAllMessages();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index a45a5c4c..0d4046b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -95,13 +95,13 @@
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.notification.NotificationAlertingManager;
-import com.android.systemui.statusbar.notification.NotificationData;
-import com.android.systemui.statusbar.notification.NotificationData.Entry;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationFilter;
import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
@@ -135,7 +135,7 @@
@Mock private IDreamManager mDreamManager;
@Mock private ScrimController mScrimController;
@Mock private DozeScrimController mDozeScrimController;
- @Mock private ArrayList<Entry> mNotificationList;
+ @Mock private ArrayList<NotificationEntry> mNotificationList;
@Mock private BiometricUnlockController mBiometricUnlockController;
@Mock private NotificationData mNotificationData;
@Mock
@@ -409,7 +409,7 @@
.build();
StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
UserHandle.of(0), null, 0);
- NotificationData.Entry entry = new NotificationData.Entry(sbn);
+ NotificationEntry entry = new NotificationEntry(sbn);
entry.importance = IMPORTANCE_HIGH;
assertTrue(mNotificationInterruptionStateProvider.shouldHeadsUp(entry));
@@ -430,7 +430,7 @@
.build();
StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
UserHandle.of(0), null, 0);
- NotificationData.Entry entry = new NotificationData.Entry(sbn);
+ NotificationEntry entry = new NotificationEntry(sbn);
entry.importance = IMPORTANCE_HIGH;
assertFalse(mNotificationInterruptionStateProvider.shouldHeadsUp(entry));
@@ -447,7 +447,7 @@
Notification n = new Notification.Builder(getContext(), "a").build();
StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
UserHandle.of(0), null, 0);
- NotificationData.Entry entry = new NotificationData.Entry(sbn);
+ NotificationEntry entry = new NotificationEntry(sbn);
entry.suppressedVisualEffects = SUPPRESSED_EFFECT_PEEK;
entry.importance = IMPORTANCE_HIGH;
@@ -465,7 +465,7 @@
Notification n = new Notification.Builder(getContext(), "a").build();
StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, "a", 0, 0, n,
UserHandle.of(0), null, 0);
- NotificationData.Entry entry = new NotificationData.Entry(sbn);
+ NotificationEntry entry = new NotificationEntry(sbn);
entry.importance = IMPORTANCE_HIGH;
assertTrue(mNotificationInterruptionStateProvider.shouldHeadsUp(entry));
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 c5bac92..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
@@ -50,7 +50,7 @@
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import com.android.systemui.statusbar.SmartReplyController;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
import com.android.systemui.statusbar.phone.ShadeController;
@@ -94,7 +94,7 @@
private int mSpacing;
@Mock private SmartReplyController mLogger;
- private NotificationData.Entry mEntry;
+ private NotificationEntry mEntry;
private Notification mNotification;
@Mock ActivityStarter mActivityStarter;
@@ -127,7 +127,7 @@
StatusBarNotification sbn = mock(StatusBarNotification.class);
when(sbn.getNotification()).thenReturn(mNotification);
when(sbn.getKey()).thenReturn(TEST_NOTIFICATION_KEY);
- mEntry = new NotificationData.Entry(sbn);
+ mEntry = new NotificationEntry(sbn);
mActionIcon = Icon.createWithResource(mContext, R.drawable.ic_person);
}
@@ -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/services/PacProcessor/jni/com_android_pacprocessor_PacNative.cpp b/packages/services/PacProcessor/jni/com_android_pacprocessor_PacNative.cpp
index 846ff25..a40cf1c 100644
--- a/packages/services/PacProcessor/jni/com_android_pacprocessor_PacNative.cpp
+++ b/packages/services/PacProcessor/jni/com_android_pacprocessor_PacNative.cpp
@@ -27,23 +27,7 @@
namespace android {
-class ProxyErrorLogger : public net::ProxyErrorListener {
-public:
- ~ProxyErrorLogger() {
-
- }
- void AlertMessage(String16 message) {
- String8 str(message);
- ALOGD("Alert: %s", str.string());
- }
- void ErrorMessage(String16 message) {
- String8 str(message);
- ALOGE("Error: %s", str.string());
- }
-};
-
net::ProxyResolverV8* proxyResolver = NULL;
-ProxyErrorLogger* logger = NULL;
bool pacSet = false;
String16 jstringToString16(JNIEnv* env, jstring jstr) {
@@ -64,9 +48,7 @@
static jboolean com_android_pacprocessor_PacNative_createV8ParserNativeLocked(JNIEnv* /* env */,
jobject) {
if (proxyResolver == NULL) {
- logger = new ProxyErrorLogger();
- proxyResolver = new net::ProxyResolverV8(net::ProxyResolverJSBindings::CreateDefault(),
- logger);
+ proxyResolver = new net::ProxyResolverV8(net::ProxyResolverJSBindings::CreateDefault());
pacSet = false;
return JNI_FALSE;
}
@@ -76,9 +58,7 @@
static jboolean com_android_pacprocessor_PacNative_destroyV8ParserNativeLocked(JNIEnv* /* env */,
jobject) {
if (proxyResolver != NULL) {
- delete logger;
delete proxyResolver;
- logger = NULL;
proxyResolver = NULL;
return JNI_FALSE;
}
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index 3379b1b..665773c 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -3684,7 +3684,7 @@
ACTION_SETTINGS_TILE_CLICK = 830;
// OPEN: Notification unsnoozed. CLOSE: Notification snoozed. UPDATE: snoozed notification
- // updated
+ // updated. TYPE_DISMISS: snoozing canceled due to data being cleared on package
// CATEGORY: NOTIFICATION
// OS: O
NOTIFICATION_SNOOZED = 831;
@@ -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/Android.bp b/services/Android.bp
index 58a0997..01734f4 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -28,6 +28,7 @@
"services.net",
"services.print",
"services.restrictions",
+ "services.startop",
"services.usage",
"services.usb",
"services.voiceinteraction",
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 1a6bee9..6cccd62 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -70,6 +70,7 @@
import com.android.internal.os.IResultReceiver;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.Preconditions;
+import com.android.internal.util.SyncResultReceiver;
import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.autofill.ui.AutoFillUI;
@@ -606,15 +607,15 @@
}
private void send(@NonNull IResultReceiver receiver, @Nullable String value) {
- send(receiver, AutofillManager.SyncResultReceiver.bundleFor(value));
+ send(receiver, SyncResultReceiver.bundleFor(value));
}
private void send(@NonNull IResultReceiver receiver, @Nullable String[] value) {
- send(receiver, AutofillManager.SyncResultReceiver.bundleFor(value));
+ send(receiver, SyncResultReceiver.bundleFor(value));
}
private void send(@NonNull IResultReceiver receiver, @Nullable Parcelable value) {
- send(receiver, AutofillManager.SyncResultReceiver.bundleFor(value));
+ send(receiver, SyncResultReceiver.bundleFor(value));
}
private void send(@NonNull IResultReceiver receiver, boolean value) {
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 4b7efe1..a6bb049 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -870,8 +870,11 @@
}
pw.print(prefix); pw.print("Default component: "); pw.println(getContext()
.getString(R.string.config_defaultAutofillService));
- pw.print(prefix); pw.print("mAugmentedAutofillNamer: ");
- mAugmentedAutofillResolver.dumpShort(pw); pw.println();
+
+ pw.print(prefix); pw.println("mAugmentedAutofillNamer: ");
+ pw.print(prefix2); mAugmentedAutofillResolver.dumpShort(pw); pw.println();
+ pw.print(prefix2); mAugmentedAutofillResolver.dumpShort(pw, mUserId); pw.println();
+
if (mRemoteAugmentedAutofillService != null) {
pw.print(prefix); pw.println("RemoteAugmentedAutofillService: ");
mRemoteAugmentedAutofillService.dump(prefix2, pw);
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 6bd3990..12db4f3 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -39,7 +39,6 @@
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
import android.os.Trace;
import android.os.UserHandle;
import android.util.Slog;
@@ -139,13 +138,7 @@
mServiceUsers.put(userId, userBackupManagerService);
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup enable");
- try {
- // TODO(b/121198604): Make enable file per-user and clean up indirection.
- mTrampoline.setBackupEnabledForUser(
- userId, UserBackupManagerFilePersistedSettings.readBackupEnableState(userId));
- } catch (RemoteException e) {
- // Can't happen, it's a local object.
- }
+ userBackupManagerService.initializeBackupEnableState();
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
diff --git a/services/backup/java/com/android/server/backup/FullBackupJob.java b/services/backup/java/com/android/server/backup/FullBackupJob.java
index 82638b4..5708b1c 100644
--- a/services/backup/java/com/android/server/backup/FullBackupJob.java
+++ b/services/backup/java/com/android/server/backup/FullBackupJob.java
@@ -24,15 +24,12 @@
import android.content.Context;
public class FullBackupJob extends JobService {
- private static final String TAG = "FullBackupJob";
- private static final boolean DEBUG = true;
-
private static ComponentName sIdleService =
new ComponentName("android", FullBackupJob.class.getName());
private static final int JOB_ID = 0x5038;
- JobParameters mParams;
+ private JobParameters mParams;
public static void schedule(Context ctx, long minDelay, BackupManagerConstants constants) {
JobScheduler js = (JobScheduler) ctx.getSystemService(Context.JOB_SCHEDULER_SERVICE);
diff --git a/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java b/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java
index bed520e..3184bd8 100644
--- a/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java
@@ -13,8 +13,6 @@
import android.os.RemoteException;
import android.util.Slog;
-import com.android.server.backup.restore.PerformAdbRestoreTask;
-
import libcore.io.IoUtils;
import java.io.File;
@@ -42,11 +40,10 @@
private final UserBackupManagerService mBackupManagerService;
private final File mDataDir;
- FileMetadata mInfo;
- PerformAdbRestoreTask mRestoreTask;
- ParcelFileDescriptor mInFD;
- IBackupAgent mAgent;
- int mToken;
+ private final FileMetadata mInfo;
+ private final ParcelFileDescriptor mInFD;
+ private final IBackupAgent mAgent;
+ private final int mToken;
public KeyValueAdbRestoreEngine(UserBackupManagerService backupManagerService,
File dataDir, FileMetadata info, ParcelFileDescriptor inFD, IBackupAgent agent,
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerFiles.java b/services/backup/java/com/android/server/backup/UserBackupManagerFiles.java
index a0feaf9..aabd41a6 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerFiles.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerFiles.java
@@ -17,29 +17,35 @@
package com.android.server.backup;
import android.os.Environment;
+import android.os.UserHandle;
import java.io.File;
/** Directories used for user specific backup/restore persistent state and book-keeping. */
-public final class UserBackupManagerFiles {
+final class UserBackupManagerFiles {
// Name of the directories the service stores bookkeeping data under.
private static final String BACKUP_PERSISTENT_DIR = "backup";
private static final String BACKUP_STAGING_DIR = "backup_stage";
+ private static File getBaseDir(int userId) {
+ return Environment.getDataSystemCeDirectory(userId);
+ }
+
static File getBaseStateDir(int userId) {
- // TODO (b/120424138) this should be per user
+ if (userId != UserHandle.USER_SYSTEM) {
+ return new File(getBaseDir(userId), BACKUP_PERSISTENT_DIR);
+ }
+ // TODO (b/120424138) remove if clause above and use same logic for system user.
+ // simultaneously, copy below dir to new system user dir
return new File(Environment.getDataDirectory(), BACKUP_PERSISTENT_DIR);
}
static File getDataDir(int userId) {
- // TODO (b/120424138) this should be per user
- // This dir on /cache is managed directly in init.rc
+ if (userId != UserHandle.USER_SYSTEM) {
+ return new File(getBaseDir(userId), BACKUP_STAGING_DIR);
+ }
+ // TODO (b/120424138) remove if clause above and use same logic for system user. Since this
+ // is a staging dir, we dont need to copy below dir to new system user dir
return new File(Environment.getDownloadCacheDirectory(), BACKUP_STAGING_DIR);
}
-
- /** Directory used by full backup engine to store state. */
- public static File getFullBackupEngineFilesDir(int userId) {
- // TODO (b/120424138) this should be per user
- return new File("/data/system");
- }
}
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 198a258..d7a2365 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;
@@ -208,8 +210,8 @@
public static final String RUN_BACKUP_ACTION = "android.app.backup.intent.RUN";
public static final String RUN_INITIALIZE_ACTION = "android.app.backup.intent.INIT";
- public static final String BACKUP_FINISHED_ACTION = "android.intent.action.BACKUP_FINISHED";
- public static final String BACKUP_FINISHED_PACKAGE_EXTRA = "packageName";
+ private static final String BACKUP_FINISHED_ACTION = "android.intent.action.BACKUP_FINISHED";
+ private static final String BACKUP_FINISHED_PACKAGE_EXTRA = "packageName";
// Bookkeeping of in-flight operations. The operation token is the index of the entry in the
// pending operations list.
@@ -251,21 +253,22 @@
private PackageManager mPackageManager;
private IPackageManager mPackageManagerBinder;
private IActivityManager mActivityManager;
+ private ActivityManagerInternal mActivityManagerInternal;
private PowerManager mPowerManager;
- private AlarmManager mAlarmManager;
- private IStorageManager mStorageManager;
- private BackupManagerConstants mConstants;
- private PowerManager.WakeLock mWakelock;
- private BackupHandler mBackupHandler;
+ private final AlarmManager mAlarmManager;
+ private final IStorageManager mStorageManager;
+ private final BackupManagerConstants mConstants;
+ private final PowerManager.WakeLock mWakelock;
+ private final BackupHandler mBackupHandler;
- private IBackupManager mBackupManagerBinder;
+ private final IBackupManager mBackupManagerBinder;
private boolean mEnabled; // access to this is synchronized on 'this'
private boolean mSetupComplete;
private boolean mAutoRestore;
- private PendingIntent mRunBackupIntent;
- private PendingIntent mRunInitIntent;
+ private final PendingIntent mRunBackupIntent;
+ private final PendingIntent mRunInitIntent;
private final ArraySet<String> mPendingInits = new ArraySet<>(); // transport names
@@ -273,7 +276,7 @@
private final SparseArray<HashSet<String>> mBackupParticipants = new SparseArray<>();
// Backups that we haven't started yet. Keys are package names.
- private HashMap<String, BackupRequest> mPendingBackups = new HashMap<>();
+ private final HashMap<String, BackupRequest> mPendingBackups = new HashMap<>();
// locking around the pending-backup management
private final Object mQueueLock = new Object();
@@ -340,7 +343,7 @@
private final SparseArray<Operation> mCurrentOperations = new SparseArray<>();
private final Object mCurrentOpLock = new Object();
private final Random mTokenGenerator = new Random();
- final AtomicInteger mNextToken = new AtomicInteger();
+ private final AtomicInteger mNextToken = new AtomicInteger();
// Where we keep our journal files and other bookkeeping.
private final File mBaseStateDir;
@@ -348,7 +351,7 @@
private final File mJournalDir;
@Nullable
private DataChangedJournal mJournal;
- private File mFullBackupScheduleFile;
+ private final File mFullBackupScheduleFile;
// Keep a log of all the apps we've ever backed up.
private ProcessedPackagesJournal mProcessedPackagesJournal;
@@ -371,8 +374,8 @@
Trampoline trampoline,
Set<ComponentName> transportWhitelist) {
String currentTransport =
- Settings.Secure.getString(
- context.getContentResolver(), Settings.Secure.BACKUP_TRANSPORT);
+ Settings.Secure.getStringForUser(
+ context.getContentResolver(), Settings.Secure.BACKUP_TRANSPORT, userId);
if (TextUtils.isEmpty(currentTransport)) {
currentTransport = null;
}
@@ -434,6 +437,19 @@
transportManager);
}
+ /**
+ * Returns the value of {@link Settings.Secure#USER_SETUP_COMPLETE} for the specified user
+ * {@code userId} as a {@code boolean}.
+ */
+ public static boolean getSetupCompleteSettingForUser(Context context, int userId) {
+ return Settings.Secure.getIntForUser(
+ context.getContentResolver(),
+ Settings.Secure.USER_SETUP_COMPLETE,
+ 0,
+ userId)
+ != 0;
+ }
+
private UserBackupManagerService(
@UserIdInt int userId,
Context context,
@@ -447,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);
@@ -465,12 +482,9 @@
// Set up our bookkeeping
final ContentResolver resolver = context.getContentResolver();
- mSetupComplete =
- Settings.Secure.getIntForUser(
- resolver, Settings.Secure.USER_SETUP_COMPLETE, 0, mUserId)
- != 0;
- mAutoRestore = Settings.Secure.getInt(resolver,
- Settings.Secure.BACKUP_AUTO_RESTORE, 1) != 0;
+ mSetupComplete = getSetupCompleteSettingForUser(context, userId);
+ mAutoRestore = Settings.Secure.getIntForUser(resolver,
+ Settings.Secure.BACKUP_AUTO_RESTORE, 1, userId) != 0;
ContentObserver setupObserver = new SetupObserver(this, mBackupHandler);
resolver.registerContentObserver(
@@ -489,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");
@@ -546,12 +580,17 @@
mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*backup*");
}
+ void initializeBackupEnableState() {
+ boolean isEnabled = UserBackupManagerFilePersistedSettings.readBackupEnableState(mUserId);
+ setBackupEnabled(isEnabled);
+ }
+
/** Cleans up state when the user of this service is stopped. */
void tearDownService() {
mUserBackupThread.quit();
}
- public int getUserId() {
+ public @UserIdInt int getUserId() {
return mUserId;
}
@@ -567,51 +606,27 @@
return mContext;
}
- public void setContext(Context context) {
- mContext = context;
- }
-
public PackageManager getPackageManager() {
return mPackageManager;
}
- public void setPackageManager(PackageManager packageManager) {
- mPackageManager = packageManager;
- }
-
public IPackageManager getPackageManagerBinder() {
return mPackageManagerBinder;
}
- public void setPackageManagerBinder(IPackageManager packageManagerBinder) {
- mPackageManagerBinder = packageManagerBinder;
- }
-
public IActivityManager getActivityManager() {
return mActivityManager;
}
- public void setActivityManager(IActivityManager activityManager) {
- mActivityManager = activityManager;
- }
-
public AlarmManager getAlarmManager() {
return mAlarmManager;
}
- public void setAlarmManager(AlarmManager alarmManager) {
- mAlarmManager = alarmManager;
- }
-
@VisibleForTesting
void setPowerManager(PowerManager powerManager) {
mPowerManager = powerManager;
}
- public void setBackupManagerBinder(IBackupManager backupManagerBinder) {
- mBackupManagerBinder = backupManagerBinder;
- }
-
public TransportManager getTransportManager() {
return mTransportManager;
}
@@ -646,35 +661,18 @@
mWakelock.setWorkSource(workSource);
}
- public void setWakelock(PowerManager.WakeLock wakelock) {
- mWakelock = wakelock;
- }
-
public Handler getBackupHandler() {
return mBackupHandler;
}
- public void setBackupHandler(BackupHandler backupHandler) {
- mBackupHandler = backupHandler;
- }
-
public PendingIntent getRunInitIntent() {
return mRunInitIntent;
}
- public void setRunInitIntent(PendingIntent runInitIntent) {
- mRunInitIntent = runInitIntent;
- }
-
public HashMap<String, BackupRequest> getPendingBackups() {
return mPendingBackups;
}
- public void setPendingBackups(
- HashMap<String, BackupRequest> pendingBackups) {
- mPendingBackups = pendingBackups;
- }
-
public Object getQueueLock() {
return mQueueLock;
}
@@ -687,10 +685,6 @@
mBackupRunning = backupRunning;
}
- public long getLastBackupPass() {
- return mLastBackupPass;
- }
-
public void setLastBackupPass(long lastBackupPass) {
mLastBackupPass = lastBackupPass;
}
@@ -699,10 +693,6 @@
return mClearDataLock;
}
- public boolean isClearingData() {
- return mClearingData;
- }
-
public void setClearingData(boolean clearingData) {
mClearingData = clearingData;
}
@@ -723,11 +713,6 @@
return mActiveRestoreSession;
}
- public void setActiveRestoreSession(
- ActiveRestoreSession activeRestoreSession) {
- mActiveRestoreSession = activeRestoreSession;
- }
-
public SparseArray<Operation> getCurrentOperations() {
return mCurrentOperations;
}
@@ -761,18 +746,10 @@
return mRng;
}
- public Set<String> getAncestralPackages() {
- return mAncestralPackages;
- }
-
public void setAncestralPackages(Set<String> ancestralPackages) {
mAncestralPackages = ancestralPackages;
}
- public long getAncestralToken() {
- return mAncestralToken;
- }
-
public void setAncestralToken(long ancestralToken) {
mAncestralToken = ancestralToken;
}
@@ -2121,7 +2098,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()
@@ -2144,8 +2122,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);
@@ -2807,8 +2783,8 @@
final long oldId = Binder.clearCallingIdentity();
try {
synchronized (this) {
- Settings.Secure.putInt(mContext.getContentResolver(),
- Settings.Secure.BACKUP_AUTO_RESTORE, doAutoRestore ? 1 : 0);
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.BACKUP_AUTO_RESTORE, doAutoRestore ? 1 : 0, mUserId);
mAutoRestore = doAutoRestore;
}
} finally {
@@ -3021,8 +2997,8 @@
private void updateStateForTransport(String newTransportName) {
// Publish the name change
- Settings.Secure.putString(mContext.getContentResolver(),
- Settings.Secure.BACKUP_TRANSPORT, newTransportName);
+ Settings.Secure.putStringForUser(mContext.getContentResolver(),
+ Settings.Secure.BACKUP_TRANSPORT, newTransportName, mUserId);
// And update our current-dataset bookkeeping
String callerLogString = "BMS.updateStateForTransport()";
@@ -3137,8 +3113,7 @@
synchronized (mAgentConnectLock) {
if (Binder.getCallingUid() == Process.SYSTEM_UID) {
Slog.d(TAG, "agentConnected pkg=" + packageName + " agent=" + agentBinder);
- IBackupAgent agent = IBackupAgent.Stub.asInterface(agentBinder);
- mConnectedAgent = agent;
+ mConnectedAgent = IBackupAgent.Stub.asInterface(agentBinder);
mConnecting = false;
} else {
Slog.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
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 45ca2af..03d4e97c 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
@@ -34,14 +34,12 @@
import android.content.pm.PackageManager;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.util.Slog;
import com.android.internal.util.Preconditions;
import com.android.server.AppWidgetBackupBridge;
import com.android.server.backup.BackupAgentTimeoutParameters;
import com.android.server.backup.BackupRestoreTask;
-import com.android.server.backup.UserBackupManagerFiles;
import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.remote.RemoteCall;
import com.android.server.backup.utils.FullBackupUtils;
@@ -78,21 +76,21 @@
private final File mFilesDir;
FullBackupRunner(
+ UserBackupManagerService userBackupManagerService,
PackageInfo packageInfo,
IBackupAgent agent,
ParcelFileDescriptor pipe,
int token,
boolean includeApks)
throws IOException {
- // TODO: http://b/22388012
- mUserId = UserHandle.USER_SYSTEM;
+ mUserId = userBackupManagerService.getUserId();
mPackageManager = backupManagerService.getPackageManager();
mPackage = packageInfo;
mAgent = agent;
mPipe = ParcelFileDescriptor.dup(pipe.getFileDescriptor());
mToken = token;
mIncludeApks = includeApks;
- mFilesDir = UserBackupManagerFiles.getFullBackupEngineFilesDir(mUserId);
+ mFilesDir = userBackupManagerService.getDataDir();
}
@Override
@@ -155,9 +153,12 @@
backupManagerService.getBackupManagerBinder(),
mTransportFlags);
} catch (IOException e) {
- Slog.e(TAG, "Error running full backup for " + mPackage.packageName);
+ Slog.e(TAG, "Error running full backup for " + mPackage.packageName, e);
} catch (RemoteException e) {
- Slog.e(TAG, "Remote agent vanished during full backup of " + mPackage.packageName);
+ Slog.e(
+ TAG,
+ "Remote agent vanished during full backup of " + mPackage.packageName,
+ e);
} finally {
try {
mPipe.close();
@@ -233,7 +234,13 @@
pipes = ParcelFileDescriptor.createPipe();
FullBackupRunner runner =
- new FullBackupRunner(mPkg, mAgent, pipes[1], mOpToken, mIncludeApks);
+ new FullBackupRunner(
+ backupManagerService,
+ mPkg,
+ mAgent,
+ pipes[1],
+ mOpToken,
+ mIncludeApks);
pipes[1].close(); // the runner has dup'd it
pipes[1] = null;
Thread t = new Thread(runner, "app-data-runner");
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/backup/java/com/android/server/backup/internal/SetupObserver.java b/services/backup/java/com/android/server/backup/internal/SetupObserver.java
index 41eb966..62ae3eb 100644
--- a/services/backup/java/com/android/server/backup/internal/SetupObserver.java
+++ b/services/backup/java/com/android/server/backup/internal/SetupObserver.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * 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.
@@ -18,6 +18,7 @@
import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
import static com.android.server.backup.BackupManagerService.TAG;
+import static com.android.server.backup.UserBackupManagerService.getSetupCompleteSettingForUser;
import android.content.Context;
import android.database.ContentObserver;
@@ -53,13 +54,7 @@
*/
public void onChange(boolean selfChange) {
boolean previousSetupComplete = mUserBackupManagerService.isSetupComplete();
- boolean newSetupComplete =
- Settings.Secure.getIntForUser(
- mContext.getContentResolver(),
- Settings.Secure.USER_SETUP_COMPLETE,
- 0,
- mUserId)
- != 0;
+ boolean newSetupComplete = getSetupCompleteSettingForUser(mContext, mUserId);
boolean resolvedSetupComplete = previousSetupComplete || newSetupComplete;
mUserBackupManagerService.setSetupComplete(resolvedSetupComplete);
diff --git a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
index 0d26ea5..45a398f 100644
--- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
@@ -678,12 +678,13 @@
* Returns whether the package is in the list of the packages for which clear app data should
* be called despite the fact that they have backup agent.
*
- * <p>The list is read from {@link Settings.Secure.PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE}.
+ * <p>The list is read from {@link Settings.Secure#PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE}.
*/
private boolean shouldForceClearAppDataOnFullRestore(String packageName) {
- String packageListString = Settings.Secure.getString(
+ String packageListString = Settings.Secure.getStringForUser(
mBackupManagerService.getContext().getContentResolver(),
- Settings.Secure.PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE);
+ Settings.Secure.PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE,
+ mBackupManagerService.getUserId());
if (TextUtils.isEmpty(packageListString)) {
return false;
}
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
index c467935..09aa421 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
@@ -95,7 +95,7 @@
final ComponentName serviceComponentName = updateServiceInfoLocked();
if (serviceComponentName == null) {
- Slog.w(TAG, "updateRemoteService(): no service componennt name");
+ if (mMaster.debug) Slog.d(TAG, "updateRemoteService(): no service component name");
return;
}
@@ -163,7 +163,10 @@
@NonNull String sessionId, int uid, int flags,
@NonNull IResultReceiver clientReceiver) {
if (!isEnabledLocked()) {
- setClientState(clientReceiver, ContentCaptureSession.STATE_DISABLED, /* binder=*/ null);
+ // TODO: it would be better to split in differet reasons, like
+ // STATE_DISABLED_NO_SERVICE and STATE_DISABLED_BY_DEVICE_POLICY
+ setClientState(clientReceiver, ContentCaptureSession.STATE_DISABLED_NO_SERVICE,
+ /* binder= */ null);
return;
}
final ComponentName serviceComponentName = getServiceComponentName();
@@ -195,8 +198,8 @@
Slog.w(TAG, "startSession(id=" + existingSession + ", token=" + activityToken
+ ": ignoring because service is not set");
// TODO(b/111276913): use a new disabled state?
- setClientState(clientReceiver, ContentCaptureSession.STATE_DISABLED,
- /* binder=*/ null);
+ setClientState(clientReceiver, ContentCaptureSession.STATE_DISABLED_NO_SERVICE,
+ /* binder= */ null);
return;
}
@@ -285,7 +288,7 @@
final int numSessions = mSessions.size();
for (int i = 0; i < numSessions; i++) {
final ContentCaptureServerSession session = mSessions.valueAt(i);
- session.destroyLocked(true);
+ session.destroyLocked(/* notifyRemoteService= */ true);
}
mSessions.clear();
}
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 2346cfc0..5cea56e 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -64,7 +64,6 @@
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;
@@ -87,6 +86,7 @@
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,6 +117,11 @@
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
@@ -171,10 +176,7 @@
private static final LocationRequest DEFAULT_LOCATION_REQUEST = new LocationRequest();
private final Context mContext;
- private final AppOpsManager mAppOps;
-
- // used internally for synchronization
- private final Object mLock = new Object();
+ private AppOpsManager mAppOps;
// --- fields below are final after systemRunning() ---
private LocationFudger mLocationFudger;
@@ -186,7 +188,7 @@
private GeocoderProxy mGeocodeProvider;
private GnssStatusListenerHelper mGnssStatusProvider;
private INetInitiatedListener mNetInitiatedListener;
- private LocationWorkerHandler mLocationHandler;
+ private final Handler mHandler;
private PassiveProvider mPassiveProvider; // track passive provider for special cases
private LocationBlacklist mBlacklist;
private GnssMeasurementsProvider mGnssMeasurementsProvider;
@@ -195,8 +197,6 @@
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 ArrayList<LocationProvider> mProviders =
- new ArrayList<>();
+ private final CopyOnWriteArrayList<LocationProvider> mProviders =
+ new CopyOnWriteArrayList<>();
// 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 ArrayList<LocationProviderProxy> mProxyProviders =
- new ArrayList<>();
+ private final CopyOnWriteArrayList<LocationProviderProxy> mProxyProviders =
+ new CopyOnWriteArrayList<>();
private final ArraySet<String> mBackgroundThrottlePackageWhitelist = new ArraySet<>();
@@ -246,9 +246,6 @@
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;
@@ -261,7 +258,7 @@
public LocationManagerService(Context context) {
super();
mContext = context;
- mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+ mHandler = BackgroundThread.getHandler();
// Let the package manager query which are the default location
// providers as they get certain permissions granted by default.
@@ -271,132 +268,92 @@
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() {
- synchronized (mLock) {
- if (D) Log.d(TAG, "systemRunning()");
+ runInternal(this::initialize);
+ }
- // fetch package manager
- mPackageManager = mContext.getPackageManager();
+ private void initialize() {
+ Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
- // fetch power manager
- mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ 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 activity manager
- mActivityManager
- = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
+ // prepare mHandler's dependents
+ mLocationFudger = new LocationFudger(mContext, mHandler);
+ mBlacklist = new LocationBlacklist(mContext, mHandler);
+ mBlacklist.init();
+ mGeofenceManager = new GeofenceManager(mContext, mBlacklist);
- // prepare worker thread
- mLocationHandler = new LocationWorkerHandler(BackgroundThread.get().getLooper());
+ 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 mLocationHandler's dependents
- mLocationFudger = new LocationFudger(mContext, mLocationHandler);
- mBlacklist = new LocationBlacklist(mContext, mLocationHandler);
- mBlacklist.init();
- mGeofenceManager = new GeofenceManager(mContext, mBlacklist);
+ mPackageManager.addOnPermissionsChangeListener(
+ uid -> runInternal(this::onPermissionsChanged));
- // 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);
+ mActivityManager.addOnUidImportanceListener(
+ (uid, importance) -> mHandler.post(
+ () -> onUidImportanceChanged(uid, importance)),
+ FOREGROUND_IMPORTANCE_CUTOFF);
- PackageManager.OnPermissionsChangedListener permissionListener = uid -> {
- synchronized (mLock) {
- applyAllProviderRequirementsLocked();
- }
- };
- mPackageManager.addOnPermissionsChangeListener(permissionListener);
+ mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ updateUserProfiles(mCurrentUserId);
- // listen for background/foreground changes
- ActivityManager.OnUidImportanceListener uidImportanceListener =
- (uid, importance) -> mLocationHandler.post(
- () -> onUidImportanceChanged(uid, importance));
- mActivityManager.addOnUidImportanceListener(uidImportanceListener,
- FOREGROUND_IMPORTANCE_CUTOFF);
+ updateBackgroundThrottlingWhitelist();
- mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
- updateUserProfiles(mCurrentUserId);
-
- updateBackgroundThrottlingWhitelistLocked();
- updateLastLocationMaxAgeLocked();
-
- // prepare providers
- loadProvidersLocked();
- updateProvidersSettingsLocked();
- for (LocationProvider provider : mProviders) {
- applyRequirementsLocked(provider.getName());
- }
+ // prepare providers
+ loadProvidersLocked();
+ updateProvidersSettings();
+ for (LocationProvider provider : mProviders) {
+ applyRequirements(provider.getName());
}
// listen for settings changes
mContext.getContentResolver().registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.LOCATION_PROVIDERS_ALLOWED), true,
- new ContentObserver(mLocationHandler) {
+ new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange) {
- synchronized (mLock) {
- updateProvidersSettingsLocked();
- }
+ onProviderAllowedChanged();
}
}, UserHandle.USER_ALL);
mContext.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS),
true,
- new ContentObserver(mLocationHandler) {
+ new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange) {
- synchronized (mLock) {
- for (LocationProvider provider : mProviders) {
- applyRequirementsLocked(provider.getName());
- }
- }
+ onBackgroundThrottleIntervalChanged();
}
}, 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(mLocationHandler) {
+ new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange) {
- synchronized (mLock) {
- updateBackgroundThrottlingWhitelistLocked();
- for (LocationProvider provider : mProviders) {
- applyRequirementsLocked(provider.getName());
- }
- }
+ onBackgroundThrottleWhitelistChanged();
}
}, UserHandle.USER_ALL);
- mPackageMonitor.register(mContext, mLocationHandler.getLooper(), true);
+ new PackageMonitor() {
+ @Override
+ public void onPackageDisappeared(String packageName, int reason) {
+ LocationManagerService.this.onPackageDisappeared(packageName);
+ }
+ }.register(mContext, mHandler.getLooper(), true);
// listen for user change
IntentFilter intentFilter = new IntentFilter();
@@ -415,73 +372,170 @@
updateUserProfiles(mCurrentUserId);
}
}
- }, UserHandle.ALL, intentFilter, null, mLocationHandler);
+ }, 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);
+ }
+ }
}
private void onUidImportanceChanged(int uid, int importance) {
+ Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
boolean foreground = isImportanceForeground(importance);
HashSet<String> affectedProviders = new HashSet<>(mRecordsByProvider.size());
- 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) {
+ 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, "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 "
+ Log.d(TAG, "request 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()));
+ record.updateForeground(foreground);
+
+ if (!isThrottlingExemptLocked(record.mReceiver.mIdentity)) {
+ affectedProviders.add(provider);
}
}
}
+ }
+ for (String provider : affectedProviders) {
+ applyRequirements(provider);
+ }
- // TODO(b/120449926): The GNSS status listeners should be handled similar to the above.
+ 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()));
+ }
+ }
}
}
@@ -489,31 +543,33 @@
return importance <= FOREGROUND_IMPORTANCE_CUTOFF;
}
- /**
- * 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) {
- int[] profileIds = mUserManager.getProfileIdsWithDisabled(currentUserId);
- synchronized (mLock) {
- mCurrentUserProfiles = profileIds;
+ private void onBackgroundThrottleIntervalChanged() {
+ Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+ for (LocationProvider provider : mProviders) {
+ applyRequirements(provider.getName());
}
}
- /**
- * Checks if the specified userId matches any of the current foreground
- * users stored in mCurrentUserProfiles.
- */
- private boolean isCurrentProfile(int userId) {
- synchronized (mLock) {
- return ArrayUtils.contains(mCurrentUserProfiles, userId);
+ private void onBackgroundThrottleWhitelistChanged() {
+ Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+ updateBackgroundThrottlingWhitelist();
+ for (LocationProvider provider : mProviders) {
+ applyRequirements(provider.getName());
}
}
+ private void updateUserProfiles(int currentUserId) {
+ Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+ mCurrentUserProfiles = mUserManager.getProfileIdsWithDisabled(currentUserId);
+ }
+
+ private boolean isCurrentProfile(int userId) {
+ Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+ 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);
@@ -584,12 +640,13 @@
}
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);
- addProviderLocked(passiveProviderManager);
+ addProvider(passiveProviderManager);
mPassiveProvider = passiveProvider;
if (GnssLocationProvider.isSupported()) {
@@ -598,14 +655,14 @@
LocationManager.GPS_PROVIDER);
GnssLocationProvider gnssProvider = new GnssLocationProvider(mContext,
gnssProviderManager,
- mLocationHandler.getLooper());
+ mHandler.getLooper());
mGnssSystemInfoProvider = gnssProvider.getGnssSystemInfoProvider();
mGnssBatchingProvider = gnssProvider.getGnssBatchingProvider();
mGnssMetricsProvider = gnssProvider.getGnssMetricsProvider();
mGnssStatusProvider = gnssProvider.getGnssStatusProvider();
mNetInitiatedListener = gnssProvider.getNetInitiatedListener();
- addProviderLocked(gnssProviderManager);
+ addProvider(gnssProviderManager);
mRealProviders.put(LocationManager.GPS_PROVIDER, gnssProviderManager);
mGnssMeasurementsProvider = gnssProvider.getGnssMeasurementsProvider();
mGnssNavigationMessageProvider = gnssProvider.getGnssNavigationMessageProvider();
@@ -647,7 +704,7 @@
if (networkProvider != null) {
mRealProviders.put(LocationManager.NETWORK_PROVIDER, networkProviderManager);
mProxyProviders.add(networkProvider);
- addProviderLocked(networkProviderManager);
+ addProvider(networkProviderManager);
} else {
Slog.w(TAG, "no network location provider found");
}
@@ -663,7 +720,7 @@
com.android.internal.R.string.config_fusedLocationProviderPackageName,
com.android.internal.R.array.config_locationProviderPackageNames);
if (fusedProvider != null) {
- addProviderLocked(fusedProviderManager);
+ addProvider(fusedProviderManager);
mProxyProviders.add(fusedProvider);
mRealProviders.put(LocationManager.FUSED_PROVIDER, fusedProviderManager);
} else {
@@ -728,28 +785,22 @@
Boolean.parseBoolean(fragments[7]) /* supportsBearing */,
Integer.parseInt(fragments[8]) /* powerRequirement */,
Integer.parseInt(fragments[9]) /* accuracy */);
- addTestProviderLocked(name, properties);
+ addTestProvider(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);
- mLocationHandler.removeMessages(MSG_LOCATION_CHANGED);
- synchronized (mLock) {
- mLastLocation.clear();
- mLastLocationCoarseInterval.clear();
- updateUserProfiles(userId);
- updateProvidersSettingsLocked();
- mCurrentUserId = userId;
- }
+ mHandler.removeMessages(MSG_LOCATION_CHANGED);
+ mLastLocation.clear();
+ mLastLocationCoarseInterval.clear();
+ updateUserProfiles(userId);
+ updateProvidersSettings();
+ mCurrentUserId = userId;
}
private static final class Identity {
@@ -808,10 +859,13 @@
}
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);
@@ -820,34 +874,38 @@
}
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) {
- runOnHandler(() -> LocationManagerService.this.reportLocation(location,
+ // no security check necessary because this is coming from an internal-only interface
+ runInternal(() -> LocationManagerService.this.handleLocationChanged(location,
mProvider == mPassiveProvider));
}
// called from any thread
@Override
public void onReportLocation(List<Location> locations) {
- runOnHandler(() -> LocationManagerService.this.reportLocationBatch(locations));
+ LocationManagerService.this.reportLocationBatch(locations);
}
// called from any thread
@Override
public void onSetEnabled(boolean enabled) {
- runOnHandler(() -> {
+ runInternal(() -> {
if (enabled == mEnabled) {
return;
}
@@ -872,18 +930,16 @@
Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
"-" + LocationManager.FUSED_PROVIDER, mCurrentUserId);
- 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();
- }
-
- updateProviderListenersLocked(mName);
+ 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);
+
mContext.sendBroadcastAsUser(new Intent(LocationManager.PROVIDERS_CHANGED_ACTION),
UserHandle.ALL);
});
@@ -891,34 +947,27 @@
@Override
public void onSetProperties(ProviderProperties properties) {
- runOnHandler(() -> mProperties = properties);
+ runInternal(() -> {
+ mProperties = properties;
+ });
}
private void setSettingsEnabled(boolean enabled) {
- 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();
- updateProviderListenersLocked(mName);
- } else if (mEnabled) {
- updateProviderListenersLocked(mName);
- }
+ Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+ if (mSettingsEnabled == enabled) {
+ return;
}
- }
- private void runOnHandler(Runnable runnable) {
- if (Looper.myLooper() == mLocationHandler.getLooper()) {
- runnable.run();
- } else {
- mLocationHandler.post(runnable);
+ 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);
}
}
}
@@ -1022,7 +1071,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 (isAllowedByUserSettingsLockedForUser(updateRecord.mProvider,
+ if (isAllowedByUserSettingsForUser(updateRecord.mProvider,
mCurrentUserId)) {
requestingLocation = true;
LocationManagerService.LocationProvider locationProvider
@@ -1122,7 +1171,7 @@
synchronized (this) {
// synchronize to ensure incrementPendingBroadcastsLocked()
// is called before decrementPendingBroadcasts()
- mPendingIntent.send(mContext, 0, statusChanged, this, mLocationHandler,
+ mPendingIntent.send(mContext, 0, statusChanged, this, mHandler,
getResolutionPermission(mAllowedResolutionLevel),
PendingIntentUtils.createDontSendToRestrictedAppsBundle(null));
// call this after broadcasting so we do not increment
@@ -1158,7 +1207,7 @@
synchronized (this) {
// synchronize to ensure incrementPendingBroadcastsLocked()
// is called before decrementPendingBroadcasts()
- mPendingIntent.send(mContext, 0, locationChanged, this, mLocationHandler,
+ mPendingIntent.send(mContext, 0, locationChanged, this, mHandler,
getResolutionPermission(mAllowedResolutionLevel),
PendingIntentUtils.createDontSendToRestrictedAppsBundle(null));
// call this after broadcasting so we do not increment
@@ -1201,7 +1250,7 @@
synchronized (this) {
// synchronize to ensure incrementPendingBroadcastsLocked()
// is called before decrementPendingBroadcasts()
- mPendingIntent.send(mContext, 0, providerIntent, this, mLocationHandler,
+ mPendingIntent.send(mContext, 0, providerIntent, this, mHandler,
getResolutionPermission(mAllowedResolutionLevel),
PendingIntentUtils.createDontSendToRestrictedAppsBundle(null));
// call this after broadcasting so we do not increment
@@ -1219,12 +1268,12 @@
public void binderDied() {
if (D) Log.d(TAG, "Location listener died");
- synchronized (mLock) {
- removeUpdatesLocked(this);
- }
- synchronized (this) {
- clearPendingBroadcastsLocked();
- }
+ runInternal(() -> {
+ removeUpdates(this);
+ synchronized (this) {
+ clearPendingBroadcastsLocked();
+ }
+ });
}
@Override
@@ -1261,28 +1310,22 @@
}
@Override
- public void locationCallbackFinished(ILocationListener listener) {
+ public void locationCallbackFinished(ILocationListener listener) throws RemoteException {
//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.
- synchronized (mLock) {
+ runFromBinderBlocking(() -> {
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) {
@@ -1292,10 +1335,6 @@
}
}
-
- /**
- * Returns the model name of the GNSS hardware.
- */
@Override
@Nullable
public String getGnssHardwareModelName() {
@@ -1306,10 +1345,6 @@
}
}
- /**
- * 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(
@@ -1329,9 +1364,6 @@
return hasLocationAccess;
}
- /**
- * Returns the GNSS batching size, if available.
- */
@Override
public int getGnssBatchSize(String packageName) {
mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
@@ -1344,10 +1376,6 @@
}
}
- /**
- * 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,
@@ -1391,9 +1419,6 @@
}
}
- /**
- * Removes callback for GNSS batching
- */
@Override
public void removeGnssBatchingCallback() {
try {
@@ -1408,10 +1433,6 @@
mGnssBatchingDeathCallback = null;
}
-
- /**
- * Starts GNSS batching, if available.
- */
@Override
public boolean startGnssBatch(long periodNanos, boolean wakeOnFifoFull, String packageName) {
mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
@@ -1432,9 +1453,6 @@
return mGnssBatchingProvider.start(periodNanos, wakeOnFifoFull);
}
- /**
- * Flushes a GNSS batch in progress
- */
@Override
public void flushGnssBatch(String packageName) {
mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
@@ -1454,9 +1472,6 @@
}
}
- /**
- * Stops GNSS batching
- */
@Override
public boolean stopGnssBatch() {
mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
@@ -1475,7 +1490,7 @@
checkCallerIsProvider();
// Currently used only for GNSS locations - update permissions check if changed
- if (isAllowedByUserSettingsLockedForUser(LocationManager.GPS_PROVIDER, mCurrentUserId)) {
+ if (isAllowedByUserSettingsForUser(LocationManager.GPS_PROVIDER, mCurrentUserId)) {
if (mGnssBatchingCallback == null) {
Slog.e(TAG, "reportLocationBatch() called without active Callback");
return;
@@ -1490,35 +1505,28 @@
}
}
- private void addProviderLocked(LocationProvider provider) {
+ private void addProvider(LocationProvider provider) {
+ Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
mProviders.add(provider);
mProvidersByName.put(provider.getName(), provider);
}
- private void removeProviderLocked(LocationProvider provider) {
+ private void removeProvider(LocationProvider provider) {
+ Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
mProviders.remove(provider);
mProvidersByName.remove(provider.getName());
}
- /**
- * 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) {
+ private boolean isAllowedByUserSettingsForUser(String provider, int userId) {
+ Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
if (LocationManager.PASSIVE_PROVIDER.equals(provider)) {
- return isLocationEnabledForUser(userId);
+ return isLocationEnabledForUserInternal(userId);
}
if (LocationManager.FUSED_PROVIDER.equals(provider)) {
- return isLocationEnabledForUser(userId);
+ return isLocationEnabledForUserInternal(userId);
}
- synchronized (mLock) {
- if (mMockProviders.containsKey(provider)) {
- return isLocationEnabledForUser(userId);
- }
+ if (mMockProviders.containsKey(provider)) {
+ return isLocationEnabledForUserInternal(userId);
}
long identity = Binder.clearCallingIdentity();
@@ -1533,29 +1541,14 @@
}
}
-
- /**
- * 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) {
+ private boolean isAllowedByUserSettings(String provider, int uid, int userId) {
+ Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
if (!isCurrentProfile(UserHandle.getUserId(uid)) && !isUidALocationProvider(uid)) {
return false;
}
- return isAllowedByUserSettingsLockedForUser(provider, userId);
+ return isAllowedByUserSettingsForUser(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:
@@ -1567,13 +1560,6 @@
}
}
- /**
- * 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) {
@@ -1586,32 +1572,16 @@
}
}
- /**
- * 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)) {
@@ -1643,13 +1613,6 @@
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);
@@ -1668,20 +1631,6 @@
}
}
- /**
- * 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) {
@@ -1739,19 +1688,15 @@
*/
@Override
public List<String> getAllProviders() {
- 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);
+ ArrayList<String> providers = new ArrayList<>(mProviders.size());
+ for (LocationProvider provider : mProviders) {
+ String name = provider.getName();
+ if (LocationManager.FUSED_PROVIDER.equals(name)) {
+ continue;
}
+ providers.add(name);
}
- if (D) Log.d(TAG, "getAllProviders()=" + out);
- return out;
+ return providers;
}
/**
@@ -1760,39 +1705,33 @@
* Can return passive provider, but never returns fused provider.
*/
@Override
- public List<String> getProviders(Criteria criteria, boolean enabledOnly) {
+ public List<String> getProviders(Criteria criteria, boolean enabledOnly)
+ throws RemoteException {
int allowedResolutionLevel = getCallerAllowedResolutionLevel();
- ArrayList<String> out;
int uid = Binder.getCallingUid();
- 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);
- }
+ return runFromBinderBlocking(() -> {
+ ArrayList<String> providers = new ArrayList<>(mProviders.size());
+ for (LocationProvider provider : mProviders) {
+ String name = provider.getName();
+ if (LocationManager.FUSED_PROVIDER.equals(name)) {
+ continue;
}
+ 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);
}
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
-
- if (D) Log.d(TAG, "getProviders()=" + out);
- return out;
+ return providers;
+ });
}
/**
@@ -1803,59 +1742,51 @@
* some simplified logic.
*/
@Override
- public String getBestProvider(Criteria criteria, boolean enabledOnly) {
- String result;
-
+ public String getBestProvider(Criteria criteria, boolean enabledOnly) throws RemoteException {
List<String> providers = getProviders(criteria, enabledOnly);
- if (!providers.isEmpty()) {
- 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 (providers.isEmpty()) {
+ providers = getProviders(null, enabledOnly);
}
- if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + null);
+ 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);
+ }
+ }
+
return null;
}
- 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);
- }
- }
-
@Override
- public boolean providerMeetsCriteria(String provider, Criteria criteria) {
- LocationProvider p = mProvidersByName.get(provider);
- if (p == null) {
- throw new IllegalArgumentException("provider=" + provider);
- }
+ public boolean providerMeetsCriteria(String provider, Criteria criteria)
+ throws RemoteException {
+ return runFromBinderBlocking(() -> {
+ 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;
+ return android.location.LocationProvider.propertiesMeetCriteria(
+ p.getName(), p.getProperties(), criteria);
+ });
}
- private void updateProvidersSettingsLocked() {
+ private void updateProvidersSettings() {
+ Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
for (LocationProvider p : mProviders) {
- p.setSettingsEnabled(isAllowedByUserSettingsLockedForUser(p.getName(), mCurrentUserId));
+ p.setSettingsEnabled(isAllowedByUserSettingsForUser(p.getName(), mCurrentUserId));
}
mContext.sendBroadcastAsUser(new Intent(LocationManager.MODE_CHANGED_ACTION),
UserHandle.ALL);
}
- private void updateProviderListenersLocked(String provider) {
+ private void updateProviderListeners(String provider) {
+ Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
LocationProvider p = mProvidersByName.get(provider);
if (p == null) return;
@@ -1880,14 +1811,15 @@
if (deadReceivers != null) {
for (int i = deadReceivers.size() - 1; i >= 0; i--) {
- removeUpdatesLocked(deadReceivers.get(i));
+ removeUpdates(deadReceivers.get(i));
}
}
- applyRequirementsLocked(provider);
+ applyRequirements(provider);
}
- private void applyRequirementsLocked(String provider) {
+ private void applyRequirements(String provider) {
+ Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
LocationProvider p = mProvidersByName.get(provider);
if (p == null) return;
@@ -1993,14 +1925,13 @@
}
@Override
- public String[] getBackgroundThrottlingWhitelist() {
- synchronized (mLock) {
- return mBackgroundThrottlePackageWhitelist.toArray(
- new String[0]);
- }
+ public String[] getBackgroundThrottlingWhitelist() throws RemoteException {
+ return runFromBinderBlocking(
+ () -> mBackgroundThrottlePackageWhitelist.toArray(new String[0]));
}
- private void updateBackgroundThrottlingWhitelistLocked() {
+ private void updateBackgroundThrottlingWhitelist() {
+ Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
String setting = Settings.Global.getString(
mContext.getContentResolver(),
Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST);
@@ -2015,15 +1946,8 @@
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;
}
@@ -2105,7 +2029,7 @@
// and also remove the Receiver if it has no more update records
if (receiverRecords.size() == 0) {
- removeUpdatesLocked(mReceiver);
+ removeUpdates(mReceiver);
}
}
@@ -2119,8 +2043,9 @@
}
}
- private Receiver getReceiverLocked(ILocationListener listener, int pid, int uid,
+ private Receiver getReceiver(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) {
@@ -2137,8 +2062,9 @@
return receiver;
}
- private Receiver getReceiverLocked(PendingIntent intent, int pid, int uid, String packageName,
+ private Receiver getReceiver(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,
@@ -2202,29 +2128,9 @@
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) {
+ PendingIntent intent, String packageName) throws RemoteException {
if (request == null) request = DEFAULT_LOCATION_REQUEST;
checkPackageName(packageName);
int allowedResolutionLevel = getCallerAllowedResolutionLevel();
@@ -2232,11 +2138,13 @@
request.getProvider());
WorkSource workSource = request.getWorkSource();
if (workSource != null && !workSource.isEmpty()) {
- checkDeviceStatsAllowed();
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.UPDATE_DEVICE_STATS, null);
}
boolean hideFromAppOps = request.getHideFromAppOps();
if (hideFromAppOps) {
- checkUpdateAppOpsAllowed();
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.UPDATE_APP_OPS_STATS, null);
}
boolean callerHasLocationHardwarePermission =
mContext.checkCallingPermission(android.Manifest.permission.LOCATION_HARDWARE)
@@ -2246,25 +2154,33 @@
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);
- synchronized (mLock) {
- Receiver recevier = checkListenerOrIntentLocked(listener, intent, pid, uid,
- packageName, workSource, hideFromAppOps);
- requestLocationUpdatesLocked(sanitizedRequest, recevier, uid, packageName);
- }
- } finally {
- Binder.restoreCallingIdentity(identity);
+ // 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);
+ }
+ requestLocationUpdates(sanitizedRequest, receiver, uid, packageName);
+ });
}
- private void requestLocationUpdatesLocked(LocationRequest request, Receiver receiver,
+ private void requestLocationUpdates(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;
@@ -2293,7 +2209,7 @@
}
if (provider.isEnabled()) {
- applyRequirementsLocked(name);
+ applyRequirements(name);
} else {
// Notify the listener that updates are currently disabled
receiver.callProviderEnabledLocked(name, false);
@@ -2305,27 +2221,31 @@
@Override
public void removeUpdates(ILocationListener listener, PendingIntent intent,
- String packageName) {
+ String packageName) throws RemoteException {
checkPackageName(packageName);
- final int pid = Binder.getCallingPid();
- final int uid = Binder.getCallingUid();
+ int pid = Binder.getCallingPid();
+ int uid = Binder.getCallingUid();
- synchronized (mLock) {
- Receiver receiver = checkListenerOrIntentLocked(listener, intent, 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);
- }
+ 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, null, false);
+ } else {
+ receiver = getReceiver(listener, pid, uid, packageName, null, false);
+ }
+ removeUpdates(receiver);
+ });
}
- private void removeUpdatesLocked(Receiver receiver) {
+ private void removeUpdates(Receiver receiver) {
+ Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
if (D) Log.i(TAG, "remove " + Integer.toHexString(System.identityHashCode(receiver)));
if (mReceivers.remove(receiver.mKey) != null && receiver.isListener()) {
@@ -2352,20 +2272,14 @@
// update provider
for (String provider : providers) {
- applyRequirementsLocked(provider);
- }
- }
-
- private void applyAllProviderRequirementsLocked() {
- for (LocationProvider p : mProviders) {
- applyRequirementsLocked(p.getName());
+ applyRequirements(provider);
}
}
@Override
- public Location getLastLocation(LocationRequest request, String packageName) {
- if (D) Log.d(TAG, "getLastLocation: " + request);
- if (request == null) request = DEFAULT_LOCATION_REQUEST;
+ public Location getLastLocation(LocationRequest r, String packageName) throws RemoteException {
+ if (D) Log.d(TAG, "getLastLocation: " + r);
+ LocationRequest request = r != null ? r : DEFAULT_LOCATION_REQUEST;
int allowedResolutionLevel = getCallerAllowedResolutionLevel();
checkPackageName(packageName);
checkResolutionLevelIsSufficientForProviderUse(allowedResolutionLevel,
@@ -2391,68 +2305,60 @@
}
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) {
+ public boolean injectLocation(Location location) throws RemoteException {
mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
"Location Hardware permission not granted to inject location");
mContext.enforceCallingPermission(android.Manifest.permission.ACCESS_FINE_LOCATION,
@@ -2464,29 +2370,31 @@
}
return false;
}
- 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");
+
+ return runFromBinderBlocking(() -> {
+ LocationProvider p = null;
+ String provider = location.getProvider();
+ if (provider != null) {
+ p = mProvidersByName.get(provider);
}
- return false;
- }
- synchronized (mLock) {
- if (!isAllowedByUserSettingsLockedForUser(provider, mCurrentUserId)) {
+ if (p == null) {
if (D) {
- Log.d(TAG, "Location disabled in Settings for current user:" + mCurrentUserId);
+ 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);
}
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) {
- updateLastLocationLocked(location, provider);
+ updateLastLocation(location, provider);
} else {
if (D) {
Log.d(TAG, "injectLocation(): Location exists. Not updating");
@@ -2494,8 +2402,8 @@
return false;
}
}
- }
- return true;
+ return true;
+ });
}
@Override
@@ -2504,7 +2412,9 @@
if (request == null) request = DEFAULT_LOCATION_REQUEST;
int allowedResolutionLevel = getCallerAllowedResolutionLevel();
checkResolutionLevelIsSufficientForGeofenceUse(allowedResolutionLevel);
- checkPendingIntent(intent);
+ if (intent == null) {
+ throw new IllegalArgumentException("invalid pending intent: " + null);
+ }
checkPackageName(packageName);
checkResolutionLevelIsSufficientForProviderUse(allowedResolutionLevel,
request.getProvider());
@@ -2515,7 +2425,10 @@
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();
@@ -2536,7 +2449,9 @@
@Override
public void removeGeofence(Geofence geofence, PendingIntent intent, String packageName) {
- checkPendingIntent(intent);
+ if (intent == null) {
+ throw new IllegalArgumentException("invalid pending intent: " + null);
+ }
checkPackageName(packageName);
if (D) Log.d(TAG, "removeGeofence: " + geofence + " " + intent);
@@ -2568,14 +2483,14 @@
@Override
public boolean addGnssMeasurementsListener(
- IGnssMeasurementsListener listener, String packageName) {
+ IGnssMeasurementsListener listener, String packageName) throws RemoteException {
if (!hasGnssPermissions(packageName) || mGnssMeasurementsProvider == null) {
return false;
}
- synchronized (mLock) {
- Identity callerIdentity
- = new Identity(Binder.getCallingUid(), Binder.getCallingPid(), packageName);
+ Identity callerIdentity =
+ new Identity(Binder.getCallingUid(), Binder.getCallingPid(), packageName);
+ return runFromBinderBlocking(() -> {
// TODO(b/120481270): Register for client death notification and update map.
mGnssMeasurementsListeners.put(listener.asBinder(), callerIdentity);
long identity = Binder.clearCallingIdentity();
@@ -2591,7 +2506,7 @@
}
return true;
- }
+ });
}
@Override
@@ -2619,28 +2534,30 @@
}
@Override
- public void removeGnssMeasurementsListener(IGnssMeasurementsListener listener) {
+ public void removeGnssMeasurementsListener(IGnssMeasurementsListener listener)
+ throws RemoteException {
if (mGnssMeasurementsProvider == null) {
return;
}
- synchronized (mLock) {
+ runFromBinderBlocking(() -> {
mGnssMeasurementsListeners.remove(listener.asBinder());
mGnssMeasurementsProvider.removeListener(listener);
- }
+ });
}
@Override
public boolean addGnssNavigationMessageListener(
IGnssNavigationMessageListener listener,
- String packageName) {
+ String packageName) throws RemoteException {
if (!hasGnssPermissions(packageName) || mGnssNavigationMessageProvider == null) {
return false;
}
- synchronized (mLock) {
- Identity callerIdentity
- = new Identity(Binder.getCallingUid(), Binder.getCallingPid(), packageName);
+ Identity callerIdentity =
+ new Identity(Binder.getCallingUid(), Binder.getCallingPid(), packageName);
+
+ return runFromBinderBlocking(() -> {
// TODO(b/120481270): Register for client death notification and update map.
mGnssNavigationMessageListeners.put(listener.asBinder(), callerIdentity);
long identity = Binder.clearCallingIdentity();
@@ -2656,21 +2573,23 @@
}
return true;
- }
+ });
}
@Override
- public void removeGnssNavigationMessageListener(IGnssNavigationMessageListener listener) {
+ public void removeGnssNavigationMessageListener(IGnssNavigationMessageListener listener)
+ throws RemoteException {
if (mGnssNavigationMessageProvider != null) {
- synchronized (mLock) {
+ runFromBinderBlocking(() -> {
mGnssNavigationMessageListeners.remove(listener.asBinder());
mGnssNavigationMessageProvider.removeListener(listener);
- }
+ });
}
}
@Override
- public boolean sendExtraCommand(String provider, String command, Bundle extras) {
+ public boolean sendExtraCommand(String provider, String command, Bundle extras)
+ throws RemoteException {
if (provider == null) {
// throw NullPointerException to remain compatible with previous implementation
throw new NullPointerException();
@@ -2684,13 +2603,13 @@
throw new SecurityException("Requires ACCESS_LOCATION_EXTRA_COMMANDS permission");
}
- synchronized (mLock) {
+ return runFromBinderBlocking(() -> {
LocationProvider p = mProvidersByName.get(provider);
if (p == null) return false;
p.sendExtraCommand(command, extras);
return true;
- }
+ });
}
@Override
@@ -2707,251 +2626,181 @@
}
}
- /**
- * @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) {
+ public ProviderProperties getProviderProperties(String provider) throws RemoteException {
checkResolutionLevelIsSufficientForProviderUse(getCallerAllowedResolutionLevel(),
provider);
- 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() {
- 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();
- }
- return null;
+ return runFromBinderBlocking(() -> {
+ LocationProvider p = mProvidersByName.get(provider);
+ if (p == null) return null;
+ return p.getProperties();
+ });
}
@Override
- public void setLocationControllerExtraPackage(String packageName) {
- mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE,
- Manifest.permission.LOCATION_HARDWARE + " permission required");
- synchronized (mLock) {
- mLocationControllerExtraPackage = packageName;
- }
- }
+ public String getNetworkProviderPackage() throws RemoteException {
+ return runFromBinderBlocking(() -> {
+ LocationProvider p = mProvidersByName.get(LocationManager.NETWORK_PROVIDER);
- @Override
- public String getLocationControllerExtraPackage() {
- synchronized (mLock) {
- return mLocationControllerExtraPackage;
- }
- }
-
- @Override
- public void setLocationControllerExtraPackageEnabled(boolean enabled) {
- mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE,
- Manifest.permission.LOCATION_HARDWARE + " permission required");
- synchronized (mLock) {
- mLocationControllerExtraPackageEnabled = enabled;
- }
- }
-
- @Override
- 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) {
- // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
- checkInteractAcrossUsersPermission(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;
+ if (p == null) {
+ return null;
}
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
+ if (p.mProvider instanceof LocationProviderProxy) {
+ return ((LocationProviderProxy) p.mProvider).getConnectedPackageName();
+ }
+ return null;
+ });
}
- /**
- * 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) {
+ public void setLocationControllerExtraPackage(String packageName) throws RemoteException {
+ mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE,
+ Manifest.permission.LOCATION_HARDWARE + " permission required");
+
+ runFromBinderBlocking(() -> mLocationControllerExtraPackage = packageName);
+ }
+
+ @Override
+ public String getLocationControllerExtraPackage() throws RemoteException {
+ return runFromBinderBlocking(() -> mLocationControllerExtraPackage);
+ }
+
+ @Override
+ public void setLocationControllerExtraPackageEnabled(boolean enabled) throws RemoteException {
+ mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE,
+ Manifest.permission.LOCATION_HARDWARE + " permission required");
+ runFromBinderBlocking(() -> mLocationControllerExtraPackageEnabled = enabled);
+ }
+
+ @Override
+ public boolean isLocationControllerExtraPackageEnabled() throws RemoteException {
+ return runFromBinderBlocking(() -> mLocationControllerExtraPackageEnabled
+ && (mLocationControllerExtraPackage != null));
+ }
+
+ @Override
+ public boolean isLocationEnabledForUser(int userId) throws RemoteException {
+ // 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");
+ }
+
+ return runFromBinderBlocking(() -> isLocationEnabledForUserInternal(userId));
+ }
+
+ 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;
+ }
+
+ @Override
+ public void setLocationEnabledForUser(boolean enabled, int userId) throws RemoteException {
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.
- checkInteractAcrossUsersPermission(userId);
-
- 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);
- }
- 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);
- }
- } finally {
- Binder.restoreCallingIdentity(identity);
+ if (UserHandle.getCallingUserId() != userId) {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.INTERACT_ACROSS_USERS,
+ "Requires INTERACT_ACROSS_USERS permission");
}
+
+ 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;
+ }
+ 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);
+ });
}
- /**
- * 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) {
+ public boolean isProviderEnabledForUser(String providerName, int userId)
+ throws RemoteException {
// Check INTERACT_ACROSS_USERS permission if userId is not current user id.
- checkInteractAcrossUsersPermission(userId);
-
- if (!isLocationEnabledForUser(userId)) {
- return false;
+ if (UserHandle.getCallingUserId() != userId) {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.INTERACT_ACROSS_USERS,
+ "Requires INTERACT_ACROSS_USERS permission");
}
// 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;
- long identity = Binder.clearCallingIdentity();
- try {
- LocationProvider provider;
- synchronized (mLock) {
- provider = mProvidersByName.get(providerName);
- }
- return provider != null && provider.isEnabled();
- } finally {
- Binder.restoreCallingIdentity(identity);
+ if (!isLocationEnabledForUser(userId)) {
+ return false;
}
+
+ return runFromBinderBlocking(() -> {
+ LocationProvider provider = mProvidersByName.get(providerName);
+ return provider != null && provider.isEnabled();
+ });
}
- /**
- * 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;
}
@@ -2970,7 +2819,6 @@
// providers installed oustide the system image. So
// also allow providers with a UID matching the
// currently bound package name
-
if (isUidALocationProvider(Binder.getCallingUid())) {
return;
}
@@ -2979,9 +2827,6 @@
"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;
@@ -2998,22 +2843,12 @@
return false;
}
+ // TODO: will be removed in future
@Override
public void reportLocation(Location location, boolean passive) {
- 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);
+ throw new IllegalStateException("operation unsupported");
}
-
private static boolean shouldBroadcastSafe(
Location loc, Location lastLoc, UpdateRecord record, long now) {
// Always broadcast the first update
@@ -3046,14 +2881,33 @@
return record.mRealRequest.getExpireAt() >= now;
}
- private void handleLocationChangedLocked(Location location, boolean passive) {
- if (D) Log.d(TAG, "incoming location: " + location);
+ 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);
long now = SystemClock.elapsedRealtime();
- String provider = (passive ? LocationManager.PASSIVE_PROVIDER : location.getProvider());
+ String provider = (passive ? LocationManager.PASSIVE_PROVIDER : myLocation.getProvider());
// Skip if the provider is unknown.
LocationProvider p = mProvidersByName.get(provider);
if (p == null) return;
- updateLastLocationLocked(location, provider);
+ updateLastLocation(myLocation, provider);
// mLastLocation should have been updated from the updateLastLocationLocked call above.
Location lastLocation = mLastLocation.get(provider);
if (lastLocation == null) {
@@ -3064,13 +2918,13 @@
// Update last known coarse interval location if enough time has passed.
Location lastLocationCoarseInterval = mLastLocationCoarseInterval.get(provider);
if (lastLocationCoarseInterval == null) {
- lastLocationCoarseInterval = new Location(location);
+ lastLocationCoarseInterval = new Location(myLocation);
mLastLocationCoarseInterval.put(provider, lastLocationCoarseInterval);
}
- long timeDiffNanos = location.getElapsedRealtimeNanos()
+ long timeDiffNanos = myLocation.getElapsedRealtimeNanos()
- lastLocationCoarseInterval.getElapsedRealtimeNanos();
if (timeDiffNanos > LocationFudger.FASTEST_INTERVAL_MS * NANOS_PER_MILLI) {
- lastLocationCoarseInterval.set(location);
+ lastLocationCoarseInterval.set(myLocation);
}
// 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
@@ -3194,24 +3048,20 @@
// remove dead records and receivers outside the loop
if (deadReceivers != null) {
for (Receiver receiver : deadReceivers) {
- removeUpdatesLocked(receiver);
+ removeUpdates(receiver);
}
}
if (deadUpdateRecords != null) {
for (UpdateRecord r : deadUpdateRecords) {
r.disposeLocked(true);
}
- applyRequirementsLocked(provider);
+ applyRequirements(provider);
}
}
- /**
- * 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) {
+ private void updateLastLocation(Location location, String provider) {
+ Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+
Location noGPSLocation = location.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
Location lastNoGPSLocation;
Location lastLocation = mLastLocation.get(provider);
@@ -3229,75 +3079,11 @@
lastLocation.set(location);
}
- 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);
- }
+ Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+ 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
@@ -3338,7 +3124,8 @@
}
@Override
- public void addTestProvider(String name, ProviderProperties properties, String opPackageName) {
+ public void addTestProvider(String name, ProviderProperties properties, String opPackageName)
+ throws RemoteException {
if (!canCallerAccessMockLocation(opPackageName)) {
return;
}
@@ -3347,23 +3134,24 @@
throw new IllegalArgumentException("Cannot mock the passive location provider");
}
- long identity = Binder.clearCallingIdentity();
- synchronized (mLock) {
+ runFromBinderBlocking(() -> {
// 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) {
- removeProviderLocked(p);
+ removeProvider(p);
}
}
- addTestProviderLocked(name, properties);
- }
- Binder.restoreCallingIdentity(identity);
+ addTestProvider(name, properties);
+ return null;
+ });
}
- private void addTestProviderLocked(String name, ProviderProperties properties) {
+ private void addTestProvider(String name, ProviderProperties properties) {
+ Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+
if (mProvidersByName.get(name) != null) {
throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
}
@@ -3371,122 +3159,103 @@
LocationProvider provider = new LocationProvider(name);
MockProvider mockProvider = new MockProvider(provider, properties);
- addProviderLocked(provider);
+ addProvider(provider);
mMockProviders.put(name, mockProvider);
mLastLocation.put(name, null);
mLastLocationCoarseInterval.put(name, null);
}
@Override
- public void removeTestProvider(String provider, String opPackageName) {
+ public void removeTestProvider(String provider, String opPackageName) throws RemoteException {
if (!canCallerAccessMockLocation(opPackageName)) {
return;
}
- synchronized (mLock) {
+ runFromBinderBlocking(() -> {
MockProvider mockProvider = mMockProviders.remove(provider);
if (mockProvider == null) {
throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
}
- long identity = Binder.clearCallingIdentity();
- try {
- removeProviderLocked(mProvidersByName.get(provider));
+ removeProvider(mProvidersByName.get(provider));
- // 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);
+ // reinstate real provider if available
+ LocationProvider realProvider = mRealProviders.get(provider);
+ if (realProvider != null) {
+ addProvider(realProvider);
}
- }
+ mLastLocation.put(provider, null);
+ mLastLocationCoarseInterval.put(provider, null);
+ });
}
@Override
- public void setTestProviderLocation(String provider, Location loc, String opPackageName) {
+ public void setTestProviderLocation(String provider, Location loc, String opPackageName)
+ throws RemoteException {
if (!canCallerAccessMockLocation(opPackageName)) {
return;
}
- synchronized (mLock) {
+ runFromBinderBlocking(() -> {
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 (bug 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
+ // (b/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());
}
- // clear calling identity so INSTALL_LOCATION_PROVIDER permission is not required
- long identity = Binder.clearCallingIdentity();
- try {
- mockProvider.setLocation(mock);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
+ mockProvider.setLocation(mock);
+ });
}
@Override
- public void setTestProviderEnabled(String provider, boolean enabled, String opPackageName) {
+ public void setTestProviderEnabled(String provider, boolean enabled, String opPackageName)
+ throws RemoteException {
if (!canCallerAccessMockLocation(opPackageName)) {
return;
}
- synchronized (mLock) {
+ runFromBinderBlocking(() -> {
MockProvider mockProvider = mMockProviders.get(provider);
if (mockProvider == null) {
throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
}
- long identity = Binder.clearCallingIdentity();
- try {
- mockProvider.setEnabled(enabled);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
+ mockProvider.setEnabled(enabled);
+ });
}
@Override
public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime,
- String opPackageName) {
+ String opPackageName) throws RemoteException {
if (!canCallerAccessMockLocation(opPackageName)) {
return;
}
- synchronized (mLock) {
+ runFromBinderBlocking(() -> {
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;
- synchronized (mLock) {
+ Runnable dump = () -> {
if (args.length > 0 && args[0].equals("--gnssmetrics")) {
if (mGnssMetricsProvider != null) {
pw.append(mGnssMetricsProvider.getGnssMetricsAsProtoString());
@@ -3579,6 +3348,14 @@
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/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index bc21610..4f21ee8 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2429,16 +2429,21 @@
return mBackgroundLaunchBroadcasts;
}
+ /**
+ * Returns true if the package {@code pkg1} running under user handle {@code uid1} is
+ * allowed association with the package {@code pkg2} running under user handle {@code uid2}.
+ * <p> If either of the packages are running as part of the core system, then the
+ * association is implicitly allowed.
+ */
boolean validateAssociationAllowedLocked(String pkg1, int uid1, String pkg2, int uid2) {
if (mAllowedAssociations == null) {
mAllowedAssociations = SystemConfig.getInstance().getAllowedAssociations();
}
// Interactions with the system uid are always allowed, since that is the core system
- // that everyone needs to be able to interact with.
- if (UserHandle.getAppId(uid1) == SYSTEM_UID) {
- return true;
- }
- if (UserHandle.getAppId(uid2) == SYSTEM_UID) {
+ // that everyone needs to be able to interact with. Also allow reflexive associations
+ // within the same uid.
+ if (uid1 == uid2 || UserHandle.getAppId(uid1) == SYSTEM_UID
+ || UserHandle.getAppId(uid2) == SYSTEM_UID) {
return true;
}
// We won't allow this association if either pkg1 or pkg2 has a limit on the
@@ -2454,6 +2459,8 @@
if (pkgs != null) {
return pkgs.contains(pkg1);
}
+ // If no explicit associations are provided in the manifest, then assume the app is
+ // allowed associations with any package.
return true;
}
@@ -5322,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) {
@@ -19687,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/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/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index 9684f4c..2a00025 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -126,21 +126,21 @@
private static final int DEFAULT_DATA_STALL_EVALUATION_TYPES =
(1 << DATA_STALL_EVALUATION_TYPE_DNS);
- static enum EvaluationResult {
+ enum EvaluationResult {
VALIDATED(true),
CAPTIVE_PORTAL(false);
- final boolean isValidated;
+ final boolean mIsValidated;
EvaluationResult(boolean isValidated) {
- this.isValidated = isValidated;
+ this.mIsValidated = isValidated;
}
}
- static enum ValidationStage {
+ enum ValidationStage {
FIRST_VALIDATION(true),
REVALIDATION(false);
- final boolean isFirstValidation;
+ final boolean mIsFirstValidation;
ValidationStage(boolean isFirstValidation) {
- this.isFirstValidation = isFirstValidation;
+ this.mIsFirstValidation = isFirstValidation;
}
}
@@ -251,7 +251,7 @@
// Start mReevaluateDelayMs at this value and double.
private static final int INITIAL_REEVALUATE_DELAY_MS = 1000;
- private static final int MAX_REEVALUATE_DELAY_MS = 10*60*1000;
+ private static final int MAX_REEVALUATE_DELAY_MS = 10 * 60 * 1000;
// Before network has been evaluated this many times, ignore repeated reevaluate requests.
private static final int IGNORE_REEVALUATE_ATTEMPTS = 5;
private int mReevaluateToken = 0;
@@ -261,7 +261,7 @@
// Stop blaming UID that requested re-evaluation after this many attempts.
private static final int BLAME_FOR_EVALUATION_ATTEMPTS = 5;
// Delay between reevaluations once a captive portal has been found.
- private static final int CAPTIVE_PORTAL_REEVALUATE_DELAY_MS = 10*60*1000;
+ private static final int CAPTIVE_PORTAL_REEVALUATE_DELAY_MS = 10 * 60 * 1000;
private static final int NUM_VALIDATION_LOG_LINES = 20;
@@ -393,10 +393,17 @@
start();
}
+ /**
+ * Request the NetworkMonitor to reevaluate the network.
+ */
public void forceReevaluation(int responsibleUid) {
sendMessage(CMD_FORCE_REEVALUATION, responsibleUid, 0);
}
+ /**
+ * Send a notification to NetworkMonitor indicating that private DNS settings have changed.
+ * @param newCfg The new private DNS configuration.
+ */
public void notifyPrivateDnsSettingsChanged(PrivateDnsConfig newCfg) {
// Cancel any outstanding resolutions.
removeMessages(CMD_PRIVATE_DNS_SETTINGS_CHANGED);
@@ -655,8 +662,9 @@
public boolean processMessage(Message message) {
switch (message.what) {
case CMD_REEVALUATE:
- if (message.arg1 != mReevaluateToken || mUserDoesNotWant)
+ if (message.arg1 != mReevaluateToken || mUserDoesNotWant) {
return HANDLED;
+ }
// Don't bother validating networks that don't satisfy the default request.
// This includes:
// - VPNs which can be considered explicitly desired by the user and the
@@ -813,9 +821,9 @@
}
private boolean isStrictModeHostnameResolved() {
- return (mPrivateDnsConfig != null) &&
- mPrivateDnsConfig.hostname.equals(mPrivateDnsProviderHostname) &&
- (mPrivateDnsConfig.ips.length > 0);
+ return (mPrivateDnsConfig != null)
+ && mPrivateDnsConfig.hostname.equals(mPrivateDnsProviderHostname)
+ && (mPrivateDnsConfig.ips.length > 0);
}
private void resolveStrictModeHostname() {
@@ -852,9 +860,9 @@
private boolean sendPrivateDnsProbe() {
// q.v. system/netd/server/dns/DnsTlsTransport.cpp
- final String ONE_TIME_HOSTNAME_SUFFIX = "-dnsotls-ds.metric.gstatic.com";
- final String host = UUID.randomUUID().toString().substring(0, 8) +
- ONE_TIME_HOSTNAME_SUFFIX;
+ final String oneTimeHostnameSuffix = "-dnsotls-ds.metric.gstatic.com";
+ final String host = UUID.randomUUID().toString().substring(0, 8)
+ + oneTimeHostnameSuffix;
final Stopwatch watch = new Stopwatch().start();
try {
final InetAddress[] ips = mNetworkAgentInfo.network().getAllByName(host);
@@ -966,7 +974,7 @@
// most one per address family. This ensures we only wait up to 20 seconds for TCP connections
// to complete, regardless of how many IP addresses a host has.
private static class OneAddressPerFamilyNetwork extends Network {
- public OneAddressPerFamilyNetwork(Network network) {
+ OneAddressPerFamilyNetwork(Network network) {
// Always bypass Private DNS.
super(network.getPrivateDnsBypassingCopy());
}
@@ -1000,7 +1008,8 @@
}
public boolean getWifiScansAlwaysAvailableDisabled() {
- return mDependencies.getSetting(mContext, Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0) == 0;
+ return mDependencies.getSetting(
+ mContext, Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0) == 0;
}
private String getCaptivePortalServerHttpsUrl() {
@@ -1246,10 +1255,10 @@
// Time how long it takes to get a response to our request
long responseTimestamp = SystemClock.elapsedRealtime();
- validationLog(probeType, url, "time=" + (responseTimestamp - requestTimestamp) + "ms" +
- " ret=" + httpResponseCode +
- " request=" + requestHeader +
- " headers=" + urlConnection.getHeaderFields());
+ validationLog(probeType, url, "time=" + (responseTimestamp - requestTimestamp) + "ms"
+ + " ret=" + httpResponseCode
+ + " request=" + requestHeader
+ + " headers=" + urlConnection.getHeaderFields());
// NOTE: We may want to consider an "HTTP/1.0 204" response to be a captive
// portal. The only example of this seen so far was a captive portal. For
// the time being go with prior behavior of assuming it's not a captive
@@ -1267,7 +1276,7 @@
// sign-in to an empty page. Probably the result of a broken transparent proxy.
// See http://b/9972012.
validationLog(probeType, url,
- "200 response with Content-length=0 interpreted as 204 response.");
+ "200 response with Content-length=0 interpreted as 204 response.");
httpResponseCode = CaptivePortalProbeResult.SUCCESS_CODE;
} else if (urlConnection.getContentLengthLong() == -1) {
// When no Content-length (default value == -1), attempt to read a byte from the
@@ -1309,7 +1318,7 @@
private final boolean mIsHttps;
private volatile CaptivePortalProbeResult mResult = CaptivePortalProbeResult.FAILED;
- public ProbeThread(boolean isHttps) {
+ ProbeThread(boolean isHttps) {
mIsHttps = isHttps;
}
@@ -1443,8 +1452,10 @@
if (cellInfo.isRegistered()) {
numRegisteredCellInfo++;
if (numRegisteredCellInfo > 1) {
- if (VDBG) logw("more than one registered CellInfo." +
- " Can't tell which is active. Bailing.");
+ if (VDBG) {
+ logw("more than one registered CellInfo."
+ + " Can't tell which is active. Bailing.");
+ }
return;
}
if (cellInfo instanceof CellInfoCdma) {
@@ -1492,14 +1503,14 @@
}
private int networkEventType(ValidationStage s, EvaluationResult r) {
- if (s.isFirstValidation) {
- if (r.isValidated) {
+ if (s.mIsFirstValidation) {
+ if (r.mIsValidated) {
return NetworkEvent.NETWORK_FIRST_VALIDATION_SUCCESS;
} else {
return NetworkEvent.NETWORK_FIRST_VALIDATION_PORTAL_FOUND;
}
} else {
- if (r.isValidated) {
+ if (r.mIsValidated) {
return NetworkEvent.NETWORK_REVALIDATION_SUCCESS;
} else {
return NetworkEvent.NETWORK_REVALIDATION_PORTAL_FOUND;
@@ -1517,7 +1528,7 @@
private void logValidationProbe(long durationMs, int probeType, int probeResult) {
int[] transports = mNetworkAgentInfo.networkCapabilities.getTransportTypes();
- boolean isFirstValidation = validationStage().isFirstValidation;
+ boolean isFirstValidation = validationStage().mIsFirstValidation;
ValidationProbeEvent ev = new ValidationProbeEvent();
ev.probeType = ValidationProbeEvent.makeProbeType(probeType, isFirstValidation);
ev.returnCode = probeResult;
@@ -1535,10 +1546,20 @@
return new Random();
}
+ /**
+ * Get the value of a global integer setting.
+ * @param symbol Name of the setting
+ * @param defaultValue Value to return if the setting is not defined.
+ */
public int getSetting(Context context, String symbol, int defaultValue) {
return Settings.Global.getInt(context.getContentResolver(), symbol, defaultValue);
}
+ /**
+ * Get the value of a global String setting.
+ * @param symbol Name of the setting
+ * @param defaultValue Value to return if the setting is not defined.
+ */
public String getSetting(Context context, String symbol, String defaultValue) {
final String value = Settings.Global.getString(context.getContentResolver(), symbol);
return value != null ? value : defaultValue;
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 d56b167..a42efe9 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
@@ -34,32 +34,53 @@
* @hide
*/
public class TetheringDependencies {
+ /**
+ * Get a reference to the offload hardware interface to be used by tethering.
+ */
public OffloadHardwareInterface getOffloadHardwareInterface(Handler h, SharedLog log) {
return new OffloadHardwareInterface(h, log);
}
+ /**
+ * Get a reference to the UpstreamNetworkMonitor to be used by tethering.
+ */
public UpstreamNetworkMonitor getUpstreamNetworkMonitor(Context ctx, StateMachine target,
SharedLog log, int what) {
return new UpstreamNetworkMonitor(ctx, target, log, what);
}
+ /**
+ * Get a reference to the IPv6TetheringCoordinator to be used by tethering.
+ */
public IPv6TetheringCoordinator getIPv6TetheringCoordinator(
ArrayList<IpServer> notifyList, SharedLog log) {
return new IPv6TetheringCoordinator(notifyList, log);
}
- public IpServer.Dependencies getIpServerDependencies() {
- return new IpServer.Dependencies();
+ /**
+ * Get dependencies to be used by IpServer.
+ */
+ public IpServer.Dependencies getIpServerDependencies(Context context) {
+ return new IpServer.Dependencies(context);
}
+ /**
+ * Indicates whether tethering is supported on the device.
+ */
public boolean isTetheringSupported() {
return true;
}
+ /**
+ * Get the NetworkRequest that should be fulfilled by the default network.
+ */
public NetworkRequest getDefaultNetworkRequest() {
return null;
}
+ /**
+ * Get a reference to the EntitlementManager to be used by tethering.
+ */
public EntitlementManager getEntitlementManager(Context ctx, SharedLog log,
MockableSystemProperties systemProperties) {
return new EntitlementManager(ctx, log, systemProperties);
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 78b3c15..5751e16 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -56,13 +56,6 @@
// 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;
@@ -126,6 +119,13 @@
private final HysteresisLevels mAmbientBrightnessThresholds;
private final HysteresisLevels mScreenBrightnessThresholds;
+ // 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.
@@ -198,7 +198,7 @@
int lightSensorRate, int initialLightSensorRate, long brighteningLightDebounceConfig,
long darkeningLightDebounceConfig, boolean resetAmbientLuxAfterWarmUpConfig,
HysteresisLevels ambientBrightnessThresholds,
- HysteresisLevels screenBrightnessThresholds) {
+ HysteresisLevels screenBrightnessThresholds, long shortTermModelTimeout) {
mCallbacks = callbacks;
mSensorManager = sensorManager;
mBrightnessMapper = mapper;
@@ -216,6 +216,7 @@
mWeightingIntercept = AMBIENT_LIGHT_LONG_HORIZON_MILLIS;
mAmbientBrightnessThresholds = ambientBrightnessThresholds;
mScreenBrightnessThresholds = screenBrightnessThresholds;
+ mShortTermModelTimeout = shortTermModelTimeout;
mShortTermModelValid = true;
mShortTermModelAnchor = -1;
@@ -295,7 +296,7 @@
}
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);
}
@@ -377,13 +378,13 @@
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();
mBrightnessMapper.dump(pw);
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 249270b..bf100eb 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,7 @@
mScreenBrightnessRangeMaximum, dozeScaleFactor, lightSensorRate,
initialLightSensorRate, brighteningLightDebounce, darkeningLightDebounce,
autoBrightnessResetAmbientLuxAfterWarmUp, ambientBrightnessThresholds,
- screenBrightnessThresholds);
+ screenBrightnessThresholds, shortTermModelTimeout);
} else {
mUseSoftwareAutoBrightnessConfig = false;
}
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 1f0f94a..ea01a0b 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): set to true to disable muting.
+ static final String PROPERTY_SYSTEM_AUDIO_MODE_MUTING_ENABLE =
+ "ro.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";
@@ -300,7 +310,7 @@
* <p>Default is true.
*/
static final String PROPERTY_ARC_SUPPORT =
- "persist.sys.hdmi.property_arc_support";
+ "persist.sys.hdmi.property_arc_support";
/**
* Property to save the audio port to switch to when system audio control is on.
@@ -310,7 +320,7 @@
* <p>Default is ARC port.
*/
static final String PROPERTY_SYSTEM_AUDIO_MODE_AUDIO_PORT =
- "persist.sys.hdmi.property_sytem_audio_mode_audio_port";
+ "persist.sys.hdmi.property_sytem_audio_mode_audio_port";
/**
* Property to save the ARC port id on system audio device.
@@ -320,6 +330,15 @@
"persist.sys.hdmi.property_sytem_audio_device_arc_port";
/**
+ * Property to indicate if a CEC audio device should forward volume keys when system audio mode
+ * is off.
+ *
+ * <p>Default is false.
+ */
+ static final String PROPERTY_CEC_AUDIO_DEVICE_FORWARD_VOLUME_KEYS_SYSTEM_AUDIO_MODE_OFF =
+ "persist.sys.hdmi.property_cec_audio_device_forward_volume_keys_system_audio_mode_off";
+
+ /**
* Property to strip local audio of amplifier and use local speaker
* when TV does not support system audio mode.
*
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
index 9690ba8..7358eaa 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
@@ -25,6 +25,7 @@
import android.media.AudioManager;
import android.media.AudioSystem;
import android.os.SystemProperties;
+import android.provider.Settings.Global;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -50,16 +51,38 @@
private boolean mTvSystemAudioModeSupport;
+ // Whether the auido system will turn TV off when it's powering off
+ private boolean mAutoTvOff;
+ // Whether the auido system will broadcast standby to the system when it's powering off
+ private boolean mAutoDeviceOff;
+
// Whether ARC is available or not. "true" means that ARC is established between TV and
// 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);
+ mAutoTvOff = mService.readBooleanSetting(
+ Global.HDMI_CONTROL_AUTO_TV_OFF_ENABLED, true);
}
@Override
@@ -74,6 +97,21 @@
mSystemAudioActivated ? "true" : "false");
}
terminateSystemAudioMode();
+
+ HdmiLogger.debug(TAG + " onStandby, initiatedByCec:" + initiatedByCec
+ + ", mAutoDeviceOff: " + mAutoDeviceOff + ", mAutoTvOff: " + mAutoTvOff);
+ if (!mService.isControlEnabled() || initiatedByCec) {
+ return;
+ }
+ if (mAutoDeviceOff) {
+ mService.sendCecCommand(
+ HdmiCecMessageBuilder.buildStandby(mAddress, Constants.ADDR_BROADCAST));
+ } else if (mAutoTvOff) {
+ mService.sendCecCommand(
+ HdmiCecMessageBuilder.buildStandby(mAddress, Constants.ADDR_TV));
+ }
+ return;
+
}
@Override
@@ -124,6 +162,20 @@
@Override
@ServiceThreadOnly
+ protected boolean handleSetStreamPath(HdmiCecMessage message) {
+ assertRunOnServiceThread();
+ int physicalAddress = HdmiUtils.twoBytesToInt(message.getParams());
+ // If current device is the target path, playback device should handle it.
+ // If the path is under the current device, should switch
+ int port = getLocalPortFromPhysicalAddress(physicalAddress);
+ if (port > 0) {
+ routeToPort(port);
+ }
+ return true;
+ }
+
+ @Override
+ @ServiceThreadOnly
protected int getPreferredAddress() {
assertRunOnServiceThread();
return SystemProperties.getInt(
@@ -371,10 +423,25 @@
mService.wakeUp();
}
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) {
@@ -386,27 +453,44 @@
}
/**
- * Method to check if the target device belongs to the subtree of the current device or not.
+ * Method to parse target physical address to the port number on the current device.
*
- * <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.
- *
+ * <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 boolean isPhysicalAddressMeOrBelow(int targetPhysicalAddress) {
+ protected int getLocalPortFromPhysicalAddress(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;
+ if (myPhysicalAddress == targetPhysicalAddress) {
+ return TARGET_SAME_PHYSICAL_ADDRESS;
}
- return false;
+ 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;
}
protected void switchToAudioInput() {
@@ -491,4 +575,27 @@
return mArcEstablished;
}
}
+
+ @ServiceThreadOnly
+ protected void setAutoTvOff(boolean autoTvOff) {
+ assertRunOnServiceThread();
+ mAutoTvOff = autoTvOff;
+ }
+
+ @Override
+ @ServiceThreadOnly
+ void setAutoDeviceOff(boolean autoDeviceOff) {
+ assertRunOnServiceThread();
+ mAutoDeviceOff = autoDeviceOff;
+ }
+
+ private void routeToPort(int portId) {
+ // TODO(AMYJOJO): route to specific input of the port
+ mLocalActivePath = portId;
+ }
+
+ @VisibleForTesting
+ protected int getLocalActivePath() {
+ return mLocalActivePath;
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 3420b26..e3a4084 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -592,6 +592,11 @@
}
// No need to propagate to HAL.
break;
+ case Global.HDMI_CONTROL_AUTO_TV_OFF_ENABLED:
+ if (isAudioSystemDevice()) {
+ audioSystem().setAutoTvOff(enabled);
+ }
+ break;
case Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED:
if (isTvDeviceEnabled()) {
tv().setSystemAudioControlFeatureEnabled(enabled);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 5a7739c..d45869e 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() {
@@ -4424,6 +4430,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 +4526,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.
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/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 139d8ac..b70c64e 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -34,7 +34,6 @@
import android.media.session.MediaController;
import android.media.session.MediaController.PlaybackInfo;
import android.media.session.MediaSession;
-import android.media.session.ParcelableVolumeInfo;
import android.media.session.PlaybackState;
import android.net.Uri;
import android.os.Binder;
@@ -628,7 +627,7 @@
if (mDestroyed) {
return;
}
- ParcelableVolumeInfo info = mController.getVolumeAttributes();
+ PlaybackInfo info = mController.getVolumeAttributes();
for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) {
ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i);
try {
@@ -1233,14 +1232,14 @@
}
@Override
- public ParcelableVolumeInfo getVolumeAttributes() {
+ public PlaybackInfo getVolumeAttributes() {
int volumeType;
AudioAttributes attributes;
synchronized (mLock) {
if (mVolumeType == PlaybackInfo.PLAYBACK_TYPE_REMOTE) {
int current = mOptimisticVolume != -1 ? mOptimisticVolume : mCurrentVolume;
- return new ParcelableVolumeInfo(
- mVolumeType, mAudioAttrs, mVolumeControlType, mMaxVolume, current);
+ return new PlaybackInfo(mVolumeType, mVolumeControlType, mMaxVolume, current,
+ mAudioAttrs);
}
volumeType = mVolumeType;
attributes = mAudioAttrs;
@@ -1248,8 +1247,8 @@
int stream = AudioAttributes.toLegacyStreamType(attributes);
int max = mAudioManager.getStreamMaxVolume(stream);
int current = mAudioManager.getStreamVolume(stream);
- return new ParcelableVolumeInfo(
- volumeType, attributes, VolumeProvider.VOLUME_CONTROL_ABSOLUTE, max, current);
+ return new PlaybackInfo(volumeType, VolumeProvider.VOLUME_CONTROL_ABSOLUTE, max,
+ current, attributes);
}
@Override
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 0d6dadf..af55605 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -270,14 +270,12 @@
* enforcement.
*
* <p>
- * This class uses 2-3 locks to synchronize state:
+ * This class uses 2 locks to synchronize state:
* <ul>
* <li>{@code mUidRulesFirstLock}: used to guard state related to individual UIDs (such as firewall
* rules).
* <li>{@code mNetworkPoliciesSecondLock}: used to guard state related to network interfaces (such
* as network policies).
- * <li>{@code allLocks}: not a "real" lock, but an indication (through @GuardedBy) that all locks
- * must be held.
* </ul>
*
* <p>
@@ -419,7 +417,8 @@
final Object mUidRulesFirstLock = new Object();
final Object mNetworkPoliciesSecondLock = new Object();
- @GuardedBy("allLocks") volatile boolean mSystemReady;
+ @GuardedBy({"mUidRulesFirstLock", "mNetworkPoliciesSecondLock"})
+ volatile boolean mSystemReady;
@GuardedBy("mUidRulesFirstLock") volatile boolean mRestrictBackground;
@GuardedBy("mUidRulesFirstLock") volatile boolean mRestrictPower;
@@ -558,7 +557,7 @@
private final ServiceThread mUidEventThread;
- @GuardedBy("allLocks")
+ @GuardedBy({"mUidRulesFirstLock", "mNetworkPoliciesSecondLock"})
private final AtomicFile mPolicyFile;
private final AppOpsManager mAppOps;
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 88d73fb1..c222e69 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -767,6 +767,23 @@
return installed;
}
+ protected Set<String> getAllowedPackages() {
+ final Set<String> allowedPackages = new ArraySet<>();
+ for (int k = 0; k < mApproved.size(); k++) {
+ ArrayMap<Boolean, ArraySet<String>> allowedByType = mApproved.valueAt(k);
+ for (int i = 0; i < allowedByType.size(); i++) {
+ final ArraySet<String> allowed = allowedByType.valueAt(i);
+ for (int j = 0; j < allowed.size(); j++) {
+ String pkgName = getPackageName(allowed.valueAt(j));
+ if (!TextUtils.isEmpty(pkgName)) {
+ allowedPackages.add(pkgName);
+ }
+ }
+ }
+ }
+ return allowedPackages;
+ }
+
private void trimApprovedListsAccordingToInstalledServices() {
int N = mApproved.size();
for (int i = 0 ; i < N; i++) {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 66dd7a3..c68e0f9 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -969,7 +969,7 @@
r.getNumSmartActionsAdded())
.addTaggedData(
MetricsEvent.NOTIFICATION_SMART_SUGGESTION_ASSISTANT_GENERATED,
- r.getSuggestionsGeneratedByAssistant());
+ r.getSuggestionsGeneratedByAssistant() ? 1 : 0);
mMetricsLogger.write(logMaker);
}
}
@@ -1701,8 +1701,16 @@
}
private void sendRegisteredOnlyBroadcast(String action) {
- getContext().sendBroadcastAsUser(new Intent(action)
- .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.ALL, null);
+ Intent intent = new Intent(action);
+ getContext().sendBroadcastAsUser(intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
+ UserHandle.ALL, null);
+ // explicitly send the broadcast to all DND packages, even if they aren't currently running
+ intent.setFlags(0);
+ final Set<String> dndApprovedPackages = mConditionProviders.getAllowedPackages();
+ for (String pkg : dndApprovedPackages) {
+ intent.setPackage(pkg);
+ getContext().sendBroadcastAsUser(intent, UserHandle.ALL);
+ }
}
@Override
@@ -2641,6 +2649,9 @@
// Zen
mConditionProviders.onPackagesChanged(true, packages, uids);
+ // Snoozing
+ mSnoozeHelper.clearData(UserHandle.getUserId(uid), packageName);
+
// Reset notification preferences
if (!fromApp) {
mPreferencesHelper.onPackagesChanged(
@@ -5697,19 +5708,17 @@
}
int indexBefore = findNotificationRecordIndexLocked(record);
boolean interceptBefore = record.isIntercepted();
- float contactAffinityBefore = record.getContactAffinity();
int visibilityBefore = record.getPackageVisibilityOverride();
recon.applyChangesLocked(record);
applyZenModeLocked(record);
mRankingHelper.sort(mNotificationList);
int indexAfter = findNotificationRecordIndexLocked(record);
boolean interceptAfter = record.isIntercepted();
- float contactAffinityAfter = record.getContactAffinity();
int visibilityAfter = record.getPackageVisibilityOverride();
changed = indexBefore != indexAfter || interceptBefore != interceptAfter
|| visibilityBefore != visibilityAfter;
if (interceptBefore && !interceptAfter
- && Float.compare(contactAffinityBefore, contactAffinityAfter) != 0) {
+ && record.isNewEnoughForAlerting(System.currentTimeMillis())) {
buzzBeepBlinkLocked(record);
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index d19fbfaa..9942f59 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -92,6 +92,8 @@
static final String TAG = "NotificationRecord";
static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
private static final int MAX_LOGTAG_LENGTH = 35;
+ // the period after which a notification is updated where it can make sound
+ private static final int MAX_SOUND_DELAY_MS = 2000;
final StatusBarNotification sbn;
IActivityManager mAm;
UriGrantsManagerInternal mUgmInternal;
@@ -125,7 +127,8 @@
private long mVisibleSinceMs;
// The most recent update time, or the creation time if no updates.
- private long mUpdateTimeMs;
+ @VisibleForTesting
+ final long mUpdateTimeMs;
// The most recent interruption time, or the creation time if no updates. Differs from the
// above value because updates are filtered based on whether they actually interrupted the
@@ -826,6 +829,10 @@
return mIntercept;
}
+ public boolean isNewEnoughForAlerting(long now) {
+ return getFreshnessMs(now) <= MAX_SOUND_DELAY_MS;
+ }
+
public void setHidden(boolean hidden) {
mHidden = hidden;
}
diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java
index 2b581d6..abc9841 100644
--- a/services/core/java/com/android/server/notification/SnoozeHelper.java
+++ b/services/core/java/com/android/server/notification/SnoozeHelper.java
@@ -17,6 +17,7 @@
import android.annotation.NonNull;
import android.app.AlarmManager;
+import android.app.Notification;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -163,7 +164,6 @@
mSnoozedNotifications.get(userId).get(pkg);
if (recordsForPkg != null) {
final Set<Map.Entry<String, NotificationRecord>> records = recordsForPkg.entrySet();
- String key = null;
for (Map.Entry<String, NotificationRecord> record : records) {
final StatusBarNotification sbn = record.getValue().sbn;
if (Objects.equals(sbn.getTag(), tag) && sbn.getId() == id) {
@@ -305,6 +305,30 @@
}
}
+ protected void clearData(int userId, String pkg) {
+ ArrayMap<String, ArrayMap<String, NotificationRecord>> records =
+ mSnoozedNotifications.get(userId);
+ if (records == null) {
+ return;
+ }
+ ArrayMap<String, NotificationRecord> pkgRecords = records.get(pkg);
+ if (pkgRecords == null) {
+ return;
+ }
+ for (int i = pkgRecords.size() - 1; i >= 0; i--) {
+ final NotificationRecord r = pkgRecords.removeAt(i);
+ if (r != null) {
+ mPackages.remove(r.getKey());
+ mUsers.remove(r.getKey());
+ final PendingIntent pi = createPendingIntent(pkg, r.getKey(), userId);
+ mAm.cancel(pi);
+ MetricsLogger.action(r.getLogMaker()
+ .setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED)
+ .setType(MetricsProto.MetricsEvent.TYPE_DISMISS));
+ }
+ }
+ }
+
private PendingIntent createPendingIntent(String pkg, String key, int userId) {
return PendingIntent.getBroadcast(mContext,
REQUEST_CODE_REPOST,
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index e5b1878..afc0b72 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -375,8 +375,7 @@
newConfig = mConfig.copy();
for (int i = newConfig.automaticRules.size() - 1; i >= 0; i--) {
ZenRule rule = newConfig.automaticRules.get(newConfig.automaticRules.keyAt(i));
- if (rule.component.getPackageName().equals(packageName)
- && canManageAutomaticZenRule(rule)) {
+ if (rule.pkg.equals(packageName) && canManageAutomaticZenRule(rule)) {
newConfig.automaticRules.removeAt(i);
}
}
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 982daa5..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;
}
}
@@ -1374,7 +1372,8 @@
"Missing existing base package");
}
// Default to require only if existing base has fs-verity.
- mVerityFound = params.mode == SessionParams.MODE_INHERIT_EXISTING
+ mVerityFound = PackageManagerServiceUtils.isApkVerityEnabled()
+ && params.mode == SessionParams.MODE_INHERIT_EXISTING
&& VerityUtils.hasFsverity(pkgInfo.applicationInfo.getBaseCodePath());
try {
@@ -2011,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 fe89be6..522ab0b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -8572,7 +8572,7 @@
* match one in a trusted source, and should be done separately.
*/
private boolean canSkipForcedApkVerification(String apkPath) {
- if (!PackageManagerServiceUtils.isLegacyApkVerityMode()) {
+ if (!PackageManagerServiceUtils.isLegacyApkVerityEnabled()) {
return VerityUtils.hasFsverity(apkPath);
}
@@ -16866,10 +16866,11 @@
*/
private void setUpFsVerityIfPossible(PackageParser.Package pkg) throws InstallerException,
PrepareFailure, IOException, DigestException, NoSuchAlgorithmException {
- if (!PackageManagerServiceUtils.isApkVerityEnabled()) {
+ final boolean standardMode = PackageManagerServiceUtils.isApkVerityEnabled();
+ final boolean legacyMode = PackageManagerServiceUtils.isLegacyApkVerityEnabled();
+ if (!standardMode && !legacyMode) {
return;
}
- final boolean legacyMode = PackageManagerServiceUtils.isLegacyApkVerityMode();
// Collect files we care for fs-verity setup.
ArrayMap<String, String> fsverityCandidates = new ArrayMap<>();
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 25169a2..6134d30 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -555,19 +555,19 @@
/** Standard fs-verity. */
private static final int FSVERITY_ENABLED = 2;
- /** Returns true if APK Verity is enabled. */
+ /** Returns true if standard APK Verity is enabled. */
static boolean isApkVerityEnabled() {
- int mode = SystemProperties.getInt("ro.apk_verity.mode", FSVERITY_DISABLED);
- return mode == FSVERITY_LEGACY || mode == FSVERITY_ENABLED;
+ return SystemProperties.getInt("ro.apk_verity.mode", FSVERITY_DISABLED) == FSVERITY_ENABLED;
}
- static boolean isLegacyApkVerityMode() {
+ static boolean isLegacyApkVerityEnabled() {
return SystemProperties.getInt("ro.apk_verity.mode", FSVERITY_DISABLED) == FSVERITY_LEGACY;
}
/** Returns true to force apk verification if the updated package (in /data) is a priv app. */
static boolean isApkVerificationForced(@Nullable PackageSetting disabledPs) {
- return disabledPs != null && disabledPs.isPrivileged() && isApkVerityEnabled();
+ return disabledPs != null && disabledPs.isPrivileged() && (
+ isApkVerityEnabled() || isLegacyApkVerityEnabled());
}
/**
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..2329356 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -17,10 +17,23 @@
package com.android.server.pm;
import android.annotation.NonNull;
+import android.apex.ApexInfo;
+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,14 +84,70 @@
return new ParceledListSlice<>(result);
}
+ 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;
+ }
+
+ 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;
+ }
+
void commitSession(@NonNull PackageInstallerSession sessionInfo) {
updateStoredSession(sessionInfo);
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);
+
+ SessionInfo session = sessionInfo.generateInfo(false);
+ // For APEXes, we validate the signature here before we write the package to the
+ // staging directory. 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.
+ if ((sessionInfo.params.installFlags & PackageManager.INSTALL_APEX) != 0) {
+ if (!validateApexSignatureLocked(session.resolvedBaseCodePath,
+ session.appPackageName)) {
+ sessionInfo.setStagedSessionFailed(SessionInfo.VERIFICATION_FAILED);
+ }
+ }
+
+ mPm.sendSessionUpdatedBroadcast(sessionInfo.generateInfo(false), sessionInfo.userId);
});
}
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/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 5adc248..c3f20aa 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -755,9 +755,10 @@
};
/**
- * Plays the wireless charging sound for both wireless and non-wireless charging
+ * If enabled, plays a sound and/or vibration when wireless or non-wireless charging has started
*/
- private void playChargingStartedSound(@UserIdInt int userId) {
+ private void playChargingStartedFeedback(@UserIdInt int userId) {
+ playChargingStartedVibration(userId);
final String soundPath = Settings.Global.getString(mContext.getContentResolver(),
Settings.Global.CHARGING_STARTED_SOUND);
if (isChargingFeedbackEnabled(userId) && soundPath != null) {
@@ -773,8 +774,7 @@
}
private void showWirelessChargingStarted(int batteryLevel, @UserIdInt int userId) {
- playWirelessChargingVibration(userId);
- playChargingStartedSound(userId);
+ playChargingStartedFeedback(userId);
if (mStatusBarManagerInternal != null) {
mStatusBarManagerInternal.showChargingAnimation(batteryLevel);
}
@@ -782,7 +782,7 @@
}
private void showWiredChargingStarted(@UserIdInt int userId) {
- playChargingStartedSound(userId);
+ playChargingStartedFeedback(userId);
mSuspendBlocker.release();
}
@@ -790,7 +790,7 @@
mTrustManager.setDeviceLockedForUser(userId, true /*locked*/);
}
- private void playWirelessChargingVibration(@UserIdInt int userId) {
+ private void playChargingStartedVibration(@UserIdInt int userId) {
final boolean vibrateEnabled = Settings.Secure.getIntForUser(mContext.getContentResolver(),
Settings.Secure.CHARGING_VIBRATION_ENABLED, 1, userId) != 0;
if (vibrateEnabled && isChargingFeedbackEnabled(userId)) {
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
index f262f6d..a60f16d 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
@@ -15,8 +15,14 @@
*/
package com.android.server.power.batterysaver;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
import android.database.ContentObserver;
import android.os.Handler;
import android.os.PowerManager;
@@ -26,6 +32,7 @@
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BackgroundThread;
@@ -46,6 +53,8 @@
*/
public class BatterySaverStateMachine {
private static final String TAG = "BatterySaverStateMachine";
+ private static final String DYNAMIC_MODE_NOTIF_CHANNEL_ID = "dynamic_mode_notification";
+ private static final int DYNAMIC_MODE_NOTIFICATION_ID = 1992;
private final Object mLock;
private static final boolean DEBUG = BatterySaverPolicy.DEBUG;
@@ -484,6 +493,13 @@
}
mBatterySaverController.enableBatterySaver(enable, intReason);
+ // Handle triggering the notification to show/hide when appropriate
+ if (intReason == BatterySaverController.REASON_DYNAMIC_POWER_SAVINGS_AUTOMATIC_ON) {
+ runOnBgThread(this::triggerDynamicModeNotification);
+ } else if (!enable) {
+ runOnBgThread(this::hideDynamicModeNotification);
+ }
+
if (DEBUG) {
Slog.d(TAG, "Battery saver: Enabled=" + enable
+ " manual=" + manual
@@ -491,6 +507,44 @@
}
}
+ private void triggerDynamicModeNotification() {
+ NotificationManager manager = mContext.getSystemService(NotificationManager.class);
+ ensureNotificationChannelExists(manager);
+
+ manager.notify(DYNAMIC_MODE_NOTIFICATION_ID, buildNotification());
+ }
+
+ private void ensureNotificationChannelExists(NotificationManager manager) {
+ NotificationChannel channel = new NotificationChannel(
+ DYNAMIC_MODE_NOTIF_CHANNEL_ID,
+ mContext.getText(
+ R.string.dynamic_mode_notification_channel_name),
+ NotificationManager.IMPORTANCE_DEFAULT);
+ channel.setSound(null, null);
+ manager.createNotificationChannel(channel);
+ }
+
+ private Notification buildNotification() {
+ Resources res = mContext.getResources();
+ Intent intent = new Intent(Intent.ACTION_POWER_USAGE_SUMMARY);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ PendingIntent batterySaverIntent = PendingIntent.getActivity(
+ mContext, 0 /* requestCode */, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+
+ return new Notification.Builder(mContext, DYNAMIC_MODE_NOTIF_CHANNEL_ID)
+ .setSmallIcon(R.drawable.ic_battery)
+ .setContentTitle(res.getString(R.string.dynamic_mode_notification_title))
+ .setContentText(res.getString(R.string.dynamic_mode_notification_summary))
+ .setContentIntent(batterySaverIntent)
+ .setOnlyAlertOnce(true)
+ .build();
+ }
+
+ private void hideDynamicModeNotification() {
+ NotificationManager manager = mContext.getSystemService(NotificationManager.class);
+ manager.cancel(DYNAMIC_MODE_NOTIFICATION_ID);
+ }
+
@GuardedBy("mLock")
private void updateSnoozingLocked(boolean snoozing, String reason) {
if (mBatterySaverSnoozing == snoozing) {
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index f008770..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;
@@ -350,12 +351,34 @@
}
private void notifyWallpaperColorsChanged(@NonNull WallpaperData wallpaper, int which) {
+ if (wallpaper.connection != null) {
+ wallpaper.connection.forEachDisplayConnector(connector -> {
+ notifyWallpaperColorsChangedOnDisplay(wallpaper, which, connector.mDisplayId);
+ });
+ } else { // Lock wallpaper does not have WallpaperConnection.
+ notifyWallpaperColorsChangedOnDisplay(wallpaper, which, DEFAULT_DISPLAY);
+ }
+ }
+
+ private RemoteCallbackList<IWallpaperManagerCallback> getWallpaperCallbacks(int userId,
+ int displayId) {
+ RemoteCallbackList<IWallpaperManagerCallback> listeners = null;
+ final SparseArray<RemoteCallbackList<IWallpaperManagerCallback>> displayListeners =
+ mColorsChangedListeners.get(userId);
+ if (displayListeners != null) {
+ listeners = displayListeners.get(displayId);
+ }
+ return listeners;
+ }
+
+ private void notifyWallpaperColorsChangedOnDisplay(@NonNull WallpaperData wallpaper, int which,
+ int displayId) {
boolean needsExtraction;
synchronized (mLock) {
final RemoteCallbackList<IWallpaperManagerCallback> currentUserColorListeners =
- mColorsChangedListeners.get(wallpaper.userId);
+ getWallpaperCallbacks(wallpaper.userId, displayId);
final RemoteCallbackList<IWallpaperManagerCallback> userAllColorListeners =
- mColorsChangedListeners.get(UserHandle.USER_ALL);
+ getWallpaperCallbacks(UserHandle.USER_ALL, displayId);
// No-op until someone is listening to it.
if (emptyCallbackList(currentUserColorListeners) &&
emptyCallbackList(userAllColorListeners)) {
@@ -363,7 +386,7 @@
}
if (DEBUG) {
- Slog.v(TAG, "notifyWallpaperColorsChanged " + which);
+ Slog.v(TAG, "notifyWallpaperColorsChangedOnDisplay " + which);
}
needsExtraction = wallpaper.primaryColors == null;
@@ -371,7 +394,7 @@
// Let's notify the current values, it's fine if it's null, it just means
// that we don't know yet.
- notifyColorListeners(wallpaper.primaryColors, which, wallpaper.userId);
+ notifyColorListeners(wallpaper.primaryColors, which, wallpaper.userId, displayId);
if (needsExtraction) {
extractColors(wallpaper);
@@ -381,7 +404,7 @@
return;
}
}
- notifyColorListeners(wallpaper.primaryColors, which, wallpaper.userId);
+ notifyColorListeners(wallpaper.primaryColors, which, wallpaper.userId, displayId);
}
}
@@ -390,14 +413,14 @@
}
private void notifyColorListeners(@NonNull WallpaperColors wallpaperColors, int which,
- int userId) {
+ int userId, int displayId) {
final IWallpaperManagerCallback keyguardListener;
final ArrayList<IWallpaperManagerCallback> colorListeners = new ArrayList<>();
synchronized (mLock) {
final RemoteCallbackList<IWallpaperManagerCallback> currentUserColorListeners =
- mColorsChangedListeners.get(userId);
+ getWallpaperCallbacks(userId, displayId);
final RemoteCallbackList<IWallpaperManagerCallback> userAllColorListeners =
- mColorsChangedListeners.get(UserHandle.USER_ALL);
+ getWallpaperCallbacks(UserHandle.USER_ALL, displayId);
keyguardListener = mKeyguardListener;
if (currentUserColorListeners != null) {
@@ -427,7 +450,8 @@
}
}
- if (keyguardListener != null) {
+ // Only shows Keyguard on default display
+ if (keyguardListener != null && displayId == DEFAULT_DISPLAY) {
try {
keyguardListener.onWallpaperColorsChanged(wallpaperColors, which, userId);
} catch (RemoteException e) {
@@ -446,6 +470,11 @@
String cropFile = null;
int wallpaperId;
+ if (wallpaper.equals(mFallbackWallpaper)) {
+ extractDefaultImageWallpaperColors();
+ return;
+ }
+
synchronized (mLock) {
// Not having a wallpaperComponent means it's a lock screen wallpaper.
final boolean imageWallpaper = mImageWallpaper.equals(wallpaper.wallpaperComponent)
@@ -482,6 +511,39 @@
}
}
+ private void extractDefaultImageWallpaperColors() {
+ synchronized (mLock) {
+ if (mFallbackWallpaper.primaryColors != null) return;
+ }
+
+ if (DEBUG) Slog.d(TAG, "Extract default image wallpaper colors");
+ WallpaperColors colors = null;
+ final InputStream is = WallpaperManager.openDefaultWallpaper(mContext, FLAG_SYSTEM);
+ if (is != null) {
+ try {
+ final BitmapFactory.Options options = new BitmapFactory.Options();
+ final Bitmap bitmap = BitmapFactory.decodeStream(is, null, options);
+ if (bitmap != null) {
+ colors = WallpaperColors.fromBitmap(bitmap);
+ bitmap.recycle();
+ }
+ } catch (OutOfMemoryError e) {
+ Slog.w(TAG, "Can't decode default wallpaper stream", e);
+ } finally {
+ IoUtils.closeQuietly(is);
+ }
+ }
+
+ if (colors == null) {
+ Slog.e(TAG, "Extract default image wallpaper colors failed");
+ return;
+ }
+
+ synchronized (mLock) {
+ mFallbackWallpaper.primaryColors = colors;
+ }
+ }
+
/**
* Once a new wallpaper has been written via setWallpaper(...), it needs to be cropped
* for display.
@@ -539,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);
@@ -696,6 +772,11 @@
targetWallpaper.connection.removeDisplayConnector(displayId);
removeDisplayData(displayId);
}
+ for (int i = mColorsChangedListeners.size() - 1; i >= 0; i--) {
+ final SparseArray<RemoteCallbackList<IWallpaperManagerCallback>> callbacks =
+ mColorsChangedListeners.valueAt(i);
+ callbacks.delete(displayId);
+ }
}
}
@@ -706,9 +787,11 @@
/**
* Map of color listeners per user id.
- * The key will be the id of a user or UserHandle.USER_ALL - for wildcard listeners.
+ * The first key will be the id of a user or UserHandle.USER_ALL - for wildcard listeners.
+ * The secondary key will be the display id, which means which display the listener is
+ * interested in.
*/
- private final SparseArray<RemoteCallbackList<IWallpaperManagerCallback>>
+ private final SparseArray<SparseArray<RemoteCallbackList<IWallpaperManagerCallback>>>
mColorsChangedListeners;
private WallpaperData mLastWallpaper;
private IWallpaperManagerCallback mKeyguardListener;
@@ -1228,9 +1311,10 @@
/**
* Called by a live wallpaper if its colors have changed.
* @param primaryColors representation of wallpaper primary colors
+ * @param displayId for which display
*/
@Override
- public void onWallpaperColorsChanged(WallpaperColors primaryColors) {
+ public void onWallpaperColorsChanged(WallpaperColors primaryColors, int displayId) {
int which;
synchronized (mLock) {
// Do not broadcast changes on ImageWallpaper since it's handled
@@ -1243,14 +1327,16 @@
// Live wallpapers always are system wallpapers.
which = FLAG_SYSTEM;
- // It's also the lock screen wallpaper when we don't have a bitmap in there
- WallpaperData lockedWallpaper = mLockWallpaperMap.get(mWallpaper.userId);
- if (lockedWallpaper == null) {
- which |= FLAG_LOCK;
+ // It's also the lock screen wallpaper when we don't have a bitmap in there.
+ if (displayId == DEFAULT_DISPLAY) {
+ final WallpaperData lockedWallpaper = mLockWallpaperMap.get(mWallpaper.userId);
+ if (lockedWallpaper == null) {
+ which |= FLAG_LOCK;
+ }
}
}
if (which != 0) {
- notifyWallpaperColorsChanged(mWallpaper, which);
+ notifyWallpaperColorsChangedOnDisplay(mWallpaper, which, displayId);
}
}
@@ -1277,16 +1363,12 @@
Slog.w(TAG, "Failed to set ambient mode state", e);
}
}
- // TODO(multi-display) So far, we have shared the same wallpaper on each display.
- // Once we have multiple wallpapers on multiple displays, please complete here.
- if (displayId == DEFAULT_DISPLAY) {
- try {
- // This will trigger onComputeColors in the wallpaper engine.
- // It's fine to be locked in here since the binder is oneway.
- connector.mEngine.requestWallpaperColors();
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to request wallpaper colors", e);
- }
+ try {
+ // This will trigger onComputeColors in the wallpaper engine.
+ // It's fine to be locked in here since the binder is oneway.
+ connector.mEngine.requestWallpaperColors();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to request wallpaper colors", e);
}
}
}
@@ -1681,6 +1763,7 @@
FgThread.getHandler().post(() -> {
notifyWallpaperColorsChanged(systemWallpaper, FLAG_SYSTEM);
notifyWallpaperColorsChanged(lockWallpaper, FLAG_LOCK);
+ notifyWallpaperColorsChanged(mFallbackWallpaper, FLAG_SYSTEM);
});
}
@@ -1743,6 +1826,7 @@
// When clearing a wallpaper, broadcast new valid colors
if (data != null) {
notifyWallpaperColorsChanged(data, which);
+ notifyWallpaperColorsChanged(mFallbackWallpaper, FLAG_SYSTEM);
}
}
@@ -2084,35 +2168,47 @@
}
@Override
- public void registerWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId) {
+ public void registerWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId,
+ int displayId) {
userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
userId, true, true, "registerWallpaperColorsCallback", null);
synchronized (mLock) {
- RemoteCallbackList<IWallpaperManagerCallback> userColorsChangedListeners =
- mColorsChangedListeners.get(userId);
- if (userColorsChangedListeners == null) {
- userColorsChangedListeners = new RemoteCallbackList<>();
- mColorsChangedListeners.put(userId, userColorsChangedListeners);
+ SparseArray<RemoteCallbackList<IWallpaperManagerCallback>>
+ userDisplayColorsChangedListeners = mColorsChangedListeners.get(userId);
+ if (userDisplayColorsChangedListeners == null) {
+ userDisplayColorsChangedListeners = new SparseArray<>();
+ mColorsChangedListeners.put(userId, userDisplayColorsChangedListeners);
}
- userColorsChangedListeners.register(cb);
+ RemoteCallbackList<IWallpaperManagerCallback> displayChangedListeners =
+ userDisplayColorsChangedListeners.get(displayId);
+ if (displayChangedListeners == null) {
+ displayChangedListeners = new RemoteCallbackList<>();
+ userDisplayColorsChangedListeners.put(displayId, displayChangedListeners);
+ }
+ displayChangedListeners.register(cb);
}
}
@Override
- public void unregisterWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId) {
+ public void unregisterWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId,
+ int displayId) {
userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
userId, true, true, "unregisterWallpaperColorsCallback", null);
synchronized (mLock) {
- final RemoteCallbackList<IWallpaperManagerCallback> userColorsChangedListeners =
- mColorsChangedListeners.get(userId);
- if (userColorsChangedListeners != null) {
- userColorsChangedListeners.unregister(cb);
+ SparseArray<RemoteCallbackList<IWallpaperManagerCallback>>
+ userDisplayColorsChangedListeners = mColorsChangedListeners.get(userId);
+ if (userDisplayColorsChangedListeners != null) {
+ RemoteCallbackList<IWallpaperManagerCallback> displayChangedListeners =
+ userDisplayColorsChangedListeners.get(displayId);
+ if (displayChangedListeners != null) {
+ displayChangedListeners.unregister(cb);
+ }
}
}
}
/**
- * TODO(b/115486823) Extends this method with specific display.
+ * TODO(multi-display) Extends this method with specific display.
* Propagate ambient state to wallpaper engine.
*
* @param inAmbientMode {@code true} when in ambient mode, {@code false} otherwise.
@@ -2125,7 +2221,7 @@
final WallpaperData data = mWallpaperMap.get(mCurrentUserId);
if (data != null && data.connection != null && data.connection.mInfo != null
&& data.connection.mInfo.supportsAmbientMode()) {
- // TODO(b/115486823) Extends this method with specific display.
+ // TODO(multi-display) Extends this method with specific display.
engine = data.connection.getDisplayConnectorOrCreate(DEFAULT_DISPLAY).mEngine;
} else {
engine = null;
@@ -2151,7 +2247,8 @@
}
@Override
- public WallpaperColors getWallpaperColors(int which, int userId) throws RemoteException {
+ public WallpaperColors getWallpaperColors(int which, int userId, int displayId)
+ throws RemoteException {
if (which != FLAG_LOCK && which != FLAG_SYSTEM) {
throw new IllegalArgumentException("which should be either FLAG_LOCK or FLAG_SYSTEM");
}
@@ -2169,7 +2266,7 @@
// Try to get the system wallpaper anyway since it might
// also be the lock screen wallpaper
if (wallpaperData == null) {
- wallpaperData = mWallpaperMap.get(userId);
+ wallpaperData = findWallpaperAtDisplay(userId, displayId);
}
if (wallpaperData == null) {
@@ -2187,6 +2284,15 @@
}
}
+ private WallpaperData findWallpaperAtDisplay(int userId, int displayId) {
+ if (mFallbackWallpaper != null && mFallbackWallpaper.connection != null
+ && mFallbackWallpaper.connection.containsDisplay(displayId)) {
+ return mFallbackWallpaper;
+ } else {
+ return mWallpaperMap.get(userId);
+ }
+ }
+
@Override
public ParcelFileDescriptor setWallpaper(String name, String callingPackage,
Rect cropHint, boolean allowBackup, Bundle extras, int which,
@@ -2382,6 +2488,7 @@
if (shouldNotifyColors) {
notifyWallpaperColorsChanged(wallpaper, which);
+ notifyWallpaperColorsChanged(mFallbackWallpaper, FLAG_SYSTEM);
}
}
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index fe0b5c25..caebf15 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -1066,93 +1066,19 @@
final int visibleWindowCount = visibleWindows.size();
HashSet<Integer> skipRemainingWindowsForTasks = new HashSet<>();
- for (int i = visibleWindowCount - 1; i >= 0; i--) {
+
+ // Iterate until we figure out what is touchable for the entire screen.
+ for (int i = visibleWindowCount - 1; i >= 0 && !unaccountedSpace.isEmpty(); i--) {
final WindowState windowState = visibleWindows.valueAt(i);
- final int flags = windowState.mAttrs.flags;
- final Task task = windowState.getTask();
- // If the window is part of a task that we're finished with - ignore.
- if (task != null && skipRemainingWindowsForTasks.contains(task.mTaskId)) {
- continue;
- }
-
- // Ignore non-touchable windows, except the split-screen divider, which is
- // occasionally non-touchable but still useful for identifying split-screen
- // mode.
- if (((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0)
- && (windowState.mAttrs.type != TYPE_DOCK_DIVIDER)) {
- continue;
- }
-
- // Compute the bounds in the screen.
final Rect boundsInScreen = mTempRect;
computeWindowBoundsInScreen(windowState, boundsInScreen);
- // If the window is completely covered by other windows - ignore.
- if (unaccountedSpace.quickReject(boundsInScreen)) {
- continue;
- }
-
- // Add windows of certain types not covered by modal windows.
- if (isReportedWindowType(windowState.mAttrs.type)) {
- // Add the window to the ones to be reported.
+ if (windowMattersToAccessibility(windowState, boundsInScreen, unaccountedSpace,
+ skipRemainingWindowsForTasks)) {
addPopulatedWindowInfo(windowState, boundsInScreen, windows, addedWindows);
- if (windowState.isFocused()) {
- focusedWindowAdded = true;
- }
- }
-
- if (windowState.mAttrs.type !=
- WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY) {
-
- // Account for the space this window takes if the window
- // is not an accessibility overlay which does not change
- // the reported windows.
- unaccountedSpace.op(boundsInScreen, unaccountedSpace,
- Region.Op.REVERSE_DIFFERENCE);
-
- // If a window is modal it prevents other windows from being touched
- if ((flags & (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL)) == 0) {
- // Account for all space in the task, whether the windows in it are
- // touchable or not. The modal window blocks all touches from the task's
- // area.
- unaccountedSpace.op(windowState.getDisplayFrameLw(), unaccountedSpace,
- Region.Op.REVERSE_DIFFERENCE);
-
- if (task != null) {
- // If the window is associated with a particular task, we can skip the
- // rest of the windows for that task.
- skipRemainingWindowsForTasks.add(task.mTaskId);
- continue;
- } else {
- // If the window is not associated with a particular task, then it is
- // globally modal. In this case we can skip all remaining windows.
- break;
- }
- }
- }
-
- // We figured out what is touchable for the entire screen - done.
- if (unaccountedSpace.isEmpty()) {
- break;
- }
- }
-
- // Always report the focused window.
- if (!focusedWindowAdded) {
- for (int i = visibleWindowCount - 1; i >= 0; i--) {
- WindowState windowState = visibleWindows.valueAt(i);
- if (windowState.isFocused()) {
- // Compute the bounds in the screen.
- Rect boundsInScreen = mTempRect;
- computeWindowBoundsInScreen(windowState, boundsInScreen);
-
- // Add the window to the ones to be reported.
- addPopulatedWindowInfo(
- windowState, boundsInScreen, windows, addedWindows);
- break;
- }
+ updateUnaccountedSpace(windowState, boundsInScreen, unaccountedSpace,
+ skipRemainingWindowsForTasks);
}
}
@@ -1221,6 +1147,73 @@
clearAndRecycleWindows(windows);
}
+ private boolean windowMattersToAccessibility(WindowState windowState, Rect boundsInScreen,
+ Region unaccountedSpace, HashSet<Integer> skipRemainingWindowsForTasks) {
+ if (windowState.isFocused()) {
+ return true;
+ }
+
+ // If the window is part of a task that we're finished with - ignore.
+ final Task task = windowState.getTask();
+ if (task != null && skipRemainingWindowsForTasks.contains(task.mTaskId)) {
+ return false;
+ }
+
+ // Ignore non-touchable windows, except the split-screen divider, which is
+ // occasionally non-touchable but still useful for identifying split-screen
+ // mode.
+ if (((windowState.mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0)
+ && (windowState.mAttrs.type != TYPE_DOCK_DIVIDER)) {
+ return false;
+ }
+
+ // If the window is completely covered by other windows - ignore.
+ if (unaccountedSpace.quickReject(boundsInScreen)) {
+ return false;
+ }
+
+ // Add windows of certain types not covered by modal windows.
+ if (isReportedWindowType(windowState.mAttrs.type)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ private void updateUnaccountedSpace(WindowState windowState, Rect boundsInScreen,
+ Region unaccountedSpace, HashSet<Integer> skipRemainingWindowsForTasks) {
+ if (windowState.mAttrs.type
+ != WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY) {
+
+ // Account for the space this window takes if the window
+ // is not an accessibility overlay which does not change
+ // the reported windows.
+ unaccountedSpace.op(boundsInScreen, unaccountedSpace,
+ Region.Op.REVERSE_DIFFERENCE);
+
+ // If a window is modal it prevents other windows from being touched
+ if ((windowState.mAttrs.flags & (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL)) == 0) {
+ // Account for all space in the task, whether the windows in it are
+ // touchable or not. The modal window blocks all touches from the task's
+ // area.
+ unaccountedSpace.op(windowState.getDisplayFrameLw(), unaccountedSpace,
+ Region.Op.REVERSE_DIFFERENCE);
+
+ final Task task = windowState.getTask();
+ if (task != null) {
+ // If the window is associated with a particular task, we can skip the
+ // rest of the windows for that task.
+ skipRemainingWindowsForTasks.add(task.mTaskId);
+ } else {
+ // If the window is not associated with a particular task, then it is
+ // globally modal. In this case we can skip all remaining windows.
+ unaccountedSpace.setEmpty();
+ }
+ }
+ }
+ }
+
private void computeWindowBoundsInScreen(WindowState windowState, Rect outBounds) {
// Get the touchable frame.
Region touchableRegion = mTempRegion1;
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 8884615..6f8f85f 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -728,9 +728,10 @@
mLastReportedMultiWindowMode = inPictureInPictureMode;
final Configuration newConfig = new Configuration();
if (targetStackBounds != null && !targetStackBounds.isEmpty()) {
- task.computeResolvedOverrideConfiguration(newConfig,
- task.getParent().getConfiguration(),
- task.getRequestedOverrideConfiguration());
+ newConfig.setTo(task.getRequestedOverrideConfiguration());
+ Rect outBounds = newConfig.windowConfiguration.getBounds();
+ task.adjustForMinimalTaskDimensions(outBounds, outBounds);
+ task.computeConfigResourceOverrides(newConfig, task.getParent().getConfiguration());
}
schedulePictureInPictureModeChanged(newConfig);
scheduleMultiWindowModeChanged(newConfig);
@@ -2503,7 +2504,8 @@
return;
}
- final IBinder binder = freezeScreenIfNeeded ? appToken.asBinder() : null;
+ final IBinder binder =
+ (freezeScreenIfNeeded && appToken != null) ? appToken.asBinder() : null;
mAppWindowToken.setOrientation(requestedOrientation, binder, this);
}
@@ -2547,7 +2549,6 @@
// TODO(b/36505427): Consider moving this method and similar ones to ConfigurationContainer.
private void updateOverrideConfiguration() {
- mTmpConfig.unset();
computeBounds(mTmpBounds);
if (mTmpBounds.equals(getRequestedOverrideBounds())) {
@@ -2558,8 +2559,10 @@
// Bounds changed...update configuration to match.
if (!matchParentBounds()) {
- task.computeResolvedOverrideConfiguration(mTmpConfig,
- task.getParent().getConfiguration(), getRequestedOverrideConfiguration());
+ mTmpConfig.setTo(getRequestedOverrideConfiguration());
+ task.computeConfigResourceOverrides(mTmpConfig, task.getParent().getConfiguration());
+ } else {
+ mTmpConfig.unset();
}
onRequestedOverrideConfigurationChanged(mTmpConfig);
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index a5341ca..2157ef6 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -1714,7 +1714,8 @@
if (mLetterbox == null) {
mLetterbox = new Letterbox(() -> makeChildSurface(null));
}
- mLetterbox.layout(getParent().getBounds(), w.getFrameLw());
+ getPosition(mTmpPoint);
+ mLetterbox.layout(getParent().getBounds(), w.getFrameLw(), mTmpPoint);
} else if (mLetterbox != null) {
mLetterbox.hide();
}
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index ded45c9..650d0be 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -37,6 +37,7 @@
import android.annotation.CallSuper;
import android.app.WindowConfiguration;
import android.content.res.Configuration;
+import android.graphics.Point;
import android.graphics.Rect;
import android.util.proto.ProtoOutputStream;
@@ -243,6 +244,14 @@
}
/**
+ * Sets {@code out} to the top-left corner of the bounds as returned by {@link #getBounds()}.
+ */
+ public void getPosition(Point out) {
+ Rect bounds = getBounds();
+ out.set(bounds.left, bounds.top);
+ }
+
+ /**
* Returns the bounds requested on this container. These may not be the actual bounds the
* container ends up with due to policy constraints. The {@link Rect} handed back is
* shared for all calls to this method and should not be modified.
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index fecc8da..740d472 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -3656,6 +3656,12 @@
}
}
+ /** @returns the orientation of the display when it's rotation is ROTATION_0. */
+ int getNaturalOrientation() {
+ return mBaseDisplayWidth < mBaseDisplayHeight
+ ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
+ }
+
void performLayout(boolean initial, boolean updateInputWindows) {
if (!isLayoutNeeded()) {
return;
diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
index 1a2aa2f..33ff194 100644
--- a/services/core/java/com/android/server/wm/Letterbox.java
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -18,6 +18,7 @@
import static android.view.SurfaceControl.HIDDEN;
+import android.graphics.Point;
import android.graphics.Rect;
import android.view.SurfaceControl;
@@ -30,6 +31,7 @@
public class Letterbox {
private static final Rect EMPTY_RECT = new Rect();
+ private static final Point ZERO_POINT = new Point(0, 0);
private final Supplier<SurfaceControl.Builder> mFactory;
private final Rect mOuter = new Rect();
@@ -53,14 +55,19 @@
* frames will be covered by black color surfaces.
*
* The caller must use {@link #applySurfaceChanges} to apply the new layout to the surface.
- *
* @param outer the outer frame of the letterbox (this frame will be black, except the area
- * that intersects with the {code inner} frame).
- * @param inner the inner frame of the letterbox (this frame will be clear)
+ * that intersects with the {code inner} frame), in global coordinates
+ * @param inner the inner frame of the letterbox (this frame will be clear), in global
+ * coordinates
+ * @param surfaceOrigin the origin of the surface factory in global coordinates
*/
- public void layout(Rect outer, Rect inner) {
+ public void layout(Rect outer, Rect inner, Point surfaceOrigin) {
mOuter.set(outer);
mInner.set(inner);
+ mOuter.offset(-surfaceOrigin.x, -surfaceOrigin.y);
+ mInner.offset(-surfaceOrigin.x, -surfaceOrigin.y);
+ outer = mOuter;
+ inner = mInner;
mTop.layout(outer.left, outer.top, inner.right, inner.top);
mLeft.layout(outer.left, inner.top, inner.left, outer.bottom);
@@ -94,7 +101,7 @@
* The caller must use {@link #applySurfaceChanges} to apply the new layout to the surface.
*/
public void hide() {
- layout(EMPTY_RECT, EMPTY_RECT);
+ layout(EMPTY_RECT, EMPTY_RECT, ZERO_POINT);
}
/**
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index d85fdb0..937c9d9 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -187,7 +187,7 @@
Rect outFrame, Rect outOverscanInsets, Rect outContentInsets, Rect outVisibleInsets,
Rect outStableInsets, Rect outsets, Rect outBackdropFrame,
DisplayCutout.ParcelableWrapper cutout, MergedConfiguration mergedConfiguration,
- Surface outSurface, InsetsState outInsetsState) {
+ SurfaceControl outSurfaceControl, InsetsState outInsetsState) {
if (false) Slog.d(TAG_WM, ">>>>>> ENTERED relayout from "
+ Binder.getCallingPid());
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, mRelayoutTag);
@@ -195,7 +195,7 @@
requestedWidth, requestedHeight, viewFlags, flags, frameNumber,
outFrame, outOverscanInsets, outContentInsets, outVisibleInsets,
outStableInsets, outsets, outBackdropFrame, cutout,
- mergedConfiguration, outSurface, outInsetsState);
+ mergedConfiguration, outSurfaceControl, outInsetsState);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
if (false) Slog.d(TAG_WM, "<<<<<< EXITING relayout to "
+ Binder.getCallingPid());
diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java
index e343322..1d56f04 100644
--- a/services/core/java/com/android/server/wm/TaskRecord.java
+++ b/services/core/java/com/android/server/wm/TaskRecord.java
@@ -44,6 +44,9 @@
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_AND_PIPABLE_DEPRECATED;
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
import static android.view.Display.DEFAULT_DISPLAY;
@@ -1259,10 +1262,6 @@
setFrontOfTask();
}
- void addActivityAtBottom(ActivityRecord r) {
- addActivityAtIndex(0, r);
- }
-
void addActivityToTop(ActivityRecord r) {
addActivityAtIndex(mActivities.size(), r);
}
@@ -1278,6 +1277,34 @@
}
/**
+ * Checks if the root activity requires a particular orientation (either by override or
+ * activityInfo) and returns that. Otherwise, this returns ORIENTATION_UNDEFINED.
+ */
+ private int getRootActivityRequestedOrientation() {
+ ActivityRecord root = getRootActivity();
+ if (getRequestedOverrideConfiguration().orientation != ORIENTATION_UNDEFINED
+ || root == null) {
+ return getRequestedOverrideConfiguration().orientation;
+ }
+ int rootScreenOrientation = root.getOrientation();
+ if (rootScreenOrientation == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) {
+ // NOSENSOR means the display's "natural" orientation, so return that.
+ ActivityDisplay display = mStack != null ? mStack.getDisplay() : null;
+ if (display != null && display.mDisplayContent != null) {
+ return mStack.getDisplay().mDisplayContent.getNaturalOrientation();
+ }
+ } else if (rootScreenOrientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) {
+ // LOCKED means the activity's orientation remains unchanged, so return existing value.
+ return root.getConfiguration().orientation;
+ } else if (ActivityInfo.isFixedOrientationLandscape(rootScreenOrientation)) {
+ return ORIENTATION_LANDSCAPE;
+ } else if (ActivityInfo.isFixedOrientationPortrait(rootScreenOrientation)) {
+ return ORIENTATION_PORTRAIT;
+ }
+ return ORIENTATION_UNDEFINED;
+ }
+
+ /**
* Adds an activity {@param r} at the given {@param index}. The activity {@param r} must either
* be in the current task or unparented to any task.
*/
@@ -1741,7 +1768,7 @@
updateTaskDescription();
}
- private void adjustForMinimalTaskDimensions(Rect bounds, Rect previousBounds) {
+ void adjustForMinimalTaskDimensions(Rect bounds, Rect previousBounds) {
if (bounds == null) {
return;
}
@@ -1853,11 +1880,27 @@
@Override
public void onConfigurationChanged(Configuration newParentConfig) {
+ // Check if the new configuration supports persistent bounds (eg. is Freeform) and if so
+ // restore the last recorded non-fullscreen bounds.
+ final boolean prevPersistTaskBounds = getWindowConfiguration().persistTaskBounds();
+ final boolean nextPersistTaskBounds =
+ getRequestedOverrideConfiguration().windowConfiguration.persistTaskBounds()
+ || newParentConfig.windowConfiguration.persistTaskBounds();
+ if (!prevPersistTaskBounds && nextPersistTaskBounds
+ && mLastNonFullscreenBounds != null && !mLastNonFullscreenBounds.isEmpty()) {
+ // Bypass onRequestedOverrideConfigurationChanged here to avoid infinite loop.
+ getRequestedOverrideConfiguration().windowConfiguration
+ .setBounds(mLastNonFullscreenBounds);
+ }
+
final boolean wasInMultiWindowMode = inMultiWindowMode();
super.onConfigurationChanged(newParentConfig);
if (wasInMultiWindowMode != inMultiWindowMode()) {
mService.mStackSupervisor.scheduleUpdateMultiWindowMode(this);
}
+
+ // If the configuration supports persistent bounds (eg. Freeform), keep track of the
+ // current (non-fullscreen) bounds for persistence.
if (getWindowConfiguration().persistTaskBounds()) {
final Rect currentBounds = getRequestedOverrideBounds();
if (!currentBounds.isEmpty()) {
@@ -2047,7 +2090,7 @@
* configuring an "inherit-bounds" window which means that all configuration settings would
* just be inherited from the parent configuration.
**/
- void computeConfigResourceOverrides(@NonNull Configuration inOutConfig, @NonNull Rect bounds,
+ void computeConfigResourceOverrides(@NonNull Configuration inOutConfig,
@NonNull Configuration parentConfig) {
int windowingMode = inOutConfig.windowConfiguration.getWindowingMode();
if (windowingMode == WINDOWING_MODE_UNDEFINED) {
@@ -2060,6 +2103,7 @@
}
density *= DisplayMetrics.DENSITY_DEFAULT_SCALE;
+ final Rect bounds = inOutConfig.windowConfiguration.getBounds();
Rect outAppBounds = inOutConfig.windowConfiguration.getAppBounds();
if (outAppBounds == null || outAppBounds.isEmpty()) {
inOutConfig.windowConfiguration.setAppBounds(bounds);
@@ -2107,13 +2151,14 @@
// Iterating across all screen orientations, and return the minimum of the task
// width taking into account that the bounds might change because the snap
// algorithm snaps to a different value
- getSmallestScreenWidthDpForDockedBounds(bounds);
+ inOutConfig.smallestScreenWidthDp =
+ getSmallestScreenWidthDpForDockedBounds(bounds);
}
// otherwise, it will just inherit
}
}
- if (inOutConfig.orientation == Configuration.ORIENTATION_UNDEFINED) {
+ if (inOutConfig.orientation == ORIENTATION_UNDEFINED) {
inOutConfig.orientation = (inOutConfig.screenWidthDp <= inOutConfig.screenHeightDp)
? Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE;
}
@@ -2134,36 +2179,56 @@
}
}
- // TODO(b/113900640): remove this once ActivityRecord is changed to not need it anymore.
- void computeResolvedOverrideConfiguration(Configuration inOutConfig, Configuration parentConfig,
- Configuration overrideConfig) {
- // Save previous bounds because adjustForMinimalTaskDimensions uses that to determine if it
- // changes left bound vs. right bound, or top bound vs. bottom bound.
- mTmpBounds.set(inOutConfig.windowConfiguration.getBounds());
-
- inOutConfig.setTo(overrideConfig);
-
- Rect outOverrideBounds = inOutConfig.windowConfiguration.getBounds();
- if (outOverrideBounds != null && !outOverrideBounds.isEmpty()) {
- adjustForMinimalTaskDimensions(outOverrideBounds, mTmpBounds);
-
- int windowingMode = overrideConfig.windowConfiguration.getWindowingMode();
- if (windowingMode == WINDOWING_MODE_UNDEFINED) {
- windowingMode = parentConfig.windowConfiguration.getWindowingMode();
- }
- if (windowingMode == WINDOWING_MODE_FREEFORM) {
- // by policy, make sure the window remains within parent
- fitWithinBounds(outOverrideBounds, parentConfig.windowConfiguration.getBounds());
- }
-
- computeConfigResourceOverrides(inOutConfig, outOverrideBounds, parentConfig);
- }
- }
-
@Override
void resolveOverrideConfiguration(Configuration newParentConfig) {
- computeResolvedOverrideConfiguration(getResolvedOverrideConfiguration(), newParentConfig,
- getRequestedOverrideConfiguration());
+ mTmpBounds.set(getResolvedOverrideConfiguration().windowConfiguration.getBounds());
+ super.resolveOverrideConfiguration(newParentConfig);
+ int windowingMode =
+ getRequestedOverrideConfiguration().windowConfiguration.getWindowingMode();
+ if (windowingMode == WINDOWING_MODE_UNDEFINED) {
+ windowingMode = newParentConfig.windowConfiguration.getWindowingMode();
+ }
+ Rect outOverrideBounds =
+ getResolvedOverrideConfiguration().windowConfiguration.getBounds();
+
+ if (windowingMode == WINDOWING_MODE_FULLSCREEN) {
+ // In FULLSCREEN mode, always start with empty bounds to indicate "fill parent"
+ outOverrideBounds.setEmpty();
+
+ // If the task or its root activity require a different orientation, make it fit the
+ // available bounds by scaling down its bounds.
+ int forcedOrientation = getRootActivityRequestedOrientation();
+ if (forcedOrientation != ORIENTATION_UNDEFINED
+ && forcedOrientation != newParentConfig.orientation) {
+ final Rect parentBounds = newParentConfig.windowConfiguration.getBounds();
+ final int parentWidth = parentBounds.width();
+ final int parentHeight = parentBounds.height();
+ final float aspect = ((float) parentHeight) / parentWidth;
+ if (forcedOrientation == ORIENTATION_LANDSCAPE) {
+ final int height = (int) (parentWidth / aspect);
+ final int top = parentBounds.centerY() - height / 2;
+ outOverrideBounds.set(
+ parentBounds.left, top, parentBounds.right, top + height);
+ } else {
+ final int width = (int) (parentHeight * aspect);
+ final int left = parentBounds.centerX() - width / 2;
+ outOverrideBounds.set(
+ left, parentBounds.top, left + width, parentBounds.bottom);
+ }
+ }
+ }
+
+ if (outOverrideBounds.isEmpty()) {
+ // If the task fills the parent, just inherit all the other configs from parent.
+ return;
+ }
+
+ adjustForMinimalTaskDimensions(outOverrideBounds, mTmpBounds);
+ if (windowingMode == WINDOWING_MODE_FREEFORM) {
+ // by policy, make sure the window remains within parent somewhere
+ fitWithinBounds(outOverrideBounds, newParentConfig.windowConfiguration.getBounds());
+ }
+ computeConfigResourceOverrides(getResolvedOverrideConfiguration(), newParentConfig);
}
Rect updateOverrideConfigurationFromLaunchBounds() {
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index 9a56606..2d3e3ae 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -111,6 +111,7 @@
private static final String TITLE_FORMAT = "SnapshotStartingWindow for taskId=%s";
private final Window mWindow;
private final Surface mSurface;
+ private SurfaceControl mSurfaceControl;
private SurfaceControl mChildSurfaceControl;
private final IWindowSession mSession;
private final WindowManagerService mService;
@@ -136,7 +137,7 @@
final Window window = new Window();
final IWindowSession session = WindowManagerGlobal.getWindowSession();
window.setSession(session);
- final Surface surface = new Surface();
+ final SurfaceControl surfaceControl = new SurfaceControl();
final Rect tmpRect = new Rect();
final DisplayCutout.ParcelableWrapper tmpCutout = new DisplayCutout.ParcelableWrapper();
final Rect tmpFrame = new Rect();
@@ -213,14 +214,14 @@
// Local call.
}
final TaskSnapshotSurface snapshotSurface = new TaskSnapshotSurface(service, window,
- surface, snapshot, layoutParams.getTitle(), backgroundColor, statusBarColor,
+ surfaceControl, snapshot, layoutParams.getTitle(), backgroundColor, statusBarColor,
navigationBarColor, sysUiVis, windowFlags, windowPrivateFlags, taskBounds,
currentOrientation);
window.setOuter(snapshotSurface);
try {
session.relayout(window, window.mSeq, layoutParams, -1, -1, View.VISIBLE, 0, -1,
tmpFrame, tmpRect, tmpContentInsets, tmpRect, tmpStableInsets, tmpRect, tmpRect,
- tmpCutout, tmpMergedConfiguration, surface, mTmpInsetsState);
+ tmpCutout, tmpMergedConfiguration, surfaceControl, mTmpInsetsState);
} catch (RemoteException e) {
// Local call.
}
@@ -230,15 +231,16 @@
}
@VisibleForTesting
- TaskSnapshotSurface(WindowManagerService service, Window window, Surface surface,
+ TaskSnapshotSurface(WindowManagerService service, Window window, SurfaceControl surfaceControl,
TaskSnapshot snapshot, CharSequence title, int backgroundColor, int statusBarColor,
int navigationBarColor, int sysUiVis, int windowFlags, int windowPrivateFlags,
Rect taskBounds, int currentOrientation) {
mService = service;
+ mSurface = new Surface();
mHandler = new Handler(mService.mH.getLooper());
mSession = WindowManagerGlobal.getWindowSession();
mWindow = window;
- mSurface = surface;
+ mSurfaceControl = surfaceControl;
mSnapshot = snapshot;
mTitle = title;
mBackgroundPaint.setColor(backgroundColor != 0 ? backgroundColor : WHITE);
@@ -281,6 +283,8 @@
private void drawSnapshot() {
final GraphicBuffer buffer = mSnapshot.getSnapshot();
+ mSurface.copyFrom(mSurfaceControl);
+
if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Drawing snapshot surface sizeMismatch="
+ mSizeMismatch);
if (mSizeMismatch) {
@@ -310,13 +314,14 @@
if (!mSurface.isValid()) {
throw new IllegalStateException("mSurface does not hold a valid surface.");
}
- final SurfaceSession session = new SurfaceSession(mSurface);
+ final SurfaceSession session = new SurfaceSession();
// Keep a reference to it such that it doesn't get destroyed when finalized.
mChildSurfaceControl = new SurfaceControl.Builder(session)
.setName(mTitle + " - task-snapshot-surface")
.setBufferSize(buffer.getWidth(), buffer.getHeight())
.setFormat(buffer.getFormat())
+ .setParent(mSurfaceControl)
.build();
Surface surface = new Surface();
surface.copyFrom(mChildSurfaceControl);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index f5f55e2..91aac7e 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1874,7 +1874,7 @@
long frameNumber, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
Rect outVisibleInsets, Rect outStableInsets, Rect outOutsets, Rect outBackdropFrame,
DisplayCutout.ParcelableWrapper outCutout, MergedConfiguration mergedConfiguration,
- Surface outSurface, InsetsState outInsetsState) {
+ SurfaceControl outSurfaceControl, InsetsState outInsetsState) {
int result = 0;
boolean configChanged;
final boolean hasStatusBarPermission =
@@ -2047,7 +2047,7 @@
result = win.relayoutVisibleWindow(result, attrChanges, oldVisibility);
try {
- result = createSurfaceControl(outSurface, result, win, winAnimator);
+ result = createSurfaceControl(outSurfaceControl, result, win, winAnimator);
} catch (Exception e) {
displayContent.getInputMonitor().updateInputWindowsLw(true /*force*/);
@@ -2078,7 +2078,7 @@
// handled yet, or it might want to draw a last frame. If we already have a
// surface, let the client use that, but don't create new surface at this point.
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: getSurface");
- winAnimator.mSurfaceController.getSurface(outSurface);
+ winAnimator.mSurfaceController.getSurfaceControl(outSurfaceControl);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
} else {
if (DEBUG_VISIBILITY) Slog.i(TAG_WM, "Releasing surface in: " + win);
@@ -2086,7 +2086,7 @@
try {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wmReleaseOutSurface_"
+ win.mAttrs.getTitle());
- outSurface.release();
+ outSurfaceControl.release();
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
@@ -2182,7 +2182,7 @@
+ ", requestedHeight=" + requestedHeight
+ ", viewVisibility=" + viewVisibility
+ "\nRelayout returning frame=" + outFrame
- + ", surface=" + outSurface);
+ + ", surface=" + outSurfaceControl);
if (localLOGV || DEBUG_FOCUS) Slog.v(
TAG_WM, "Relayout of " + win + ": focusMayChange=" + focusMayChange);
@@ -2252,7 +2252,7 @@
return focusMayChange;
}
- private int createSurfaceControl(Surface outSurface, int result, WindowState win,
+ private int createSurfaceControl(SurfaceControl outSurfaceControl, int result, WindowState win,
WindowStateAnimator winAnimator) {
if (!win.mHasSurface) {
result |= RELAYOUT_RES_SURFACE_CHANGED;
@@ -2266,13 +2266,13 @@
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
if (surfaceController != null) {
- surfaceController.getSurface(outSurface);
- if (SHOW_TRANSACTIONS) Slog.i(TAG_WM, " OUT SURFACE " + outSurface + ": copied");
+ surfaceController.getSurfaceControl(outSurfaceControl);
+ if (SHOW_TRANSACTIONS) Slog.i(TAG_WM, " OUT SURFACE " + outSurfaceControl + ": copied");
} else {
// For some reason there isn't a surface. Clear the
// caller's object so they see the same state.
Slog.w(TAG_WM, "Failed to create surface control for " + win);
- outSurface.release();
+ outSurfaceControl.release();
}
return result;
@@ -6166,7 +6166,7 @@
dumpWindowsLocked(pw, true, null);
}
return;
- } else if ("all".equals(cmd) || "a".equals(cmd)) {
+ } else if ("all".equals(cmd)) {
synchronized (mGlobalLock) {
dumpWindowsLocked(pw, true, null);
}
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index ce627e2..c2a8e7e 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -478,8 +478,8 @@
return mSurfaceControl.getHandle();
}
- void getSurface(Surface outSurface) {
- outSurface.copyFrom(mSurfaceControl);
+ void getSurfaceControl(SurfaceControl outSurfaceControl) {
+ outSurfaceControl.copyFrom(mSurfaceControl);
}
int getLayer() {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/AbUpdateInstaller.java b/services/devicepolicy/java/com/android/server/devicepolicy/AbUpdateInstaller.java
index 05912a5..de5dd17 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/AbUpdateInstaller.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/AbUpdateInstaller.java
@@ -66,6 +66,9 @@
map.put(
DOWNLOAD_STATE_INITIALIZATION_ERROR,
InstallUpdateCallback.UPDATE_ERROR_INCORRECT_OS_VERSION);
+ map.put(
+ UpdateEngine.ErrorCodeConstants.PAYLOAD_TIMESTAMP_ERROR,
+ InstallUpdateCallback.UPDATE_ERROR_INCORRECT_OS_VERSION);
// Error constants corresponding to errors related to bad update file.
map.put(
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 781e1c4..7f6895a 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -4990,26 +4990,22 @@
private boolean resetPasswordInternal(String password, long tokenHandle, byte[] token,
int flags, int callingUid, int userHandle) {
int quality;
- final int realQuality;
synchronized (getLockObject()) {
quality = getPasswordQuality(null, userHandle, /* parent */ false);
if (quality == DevicePolicyManager.PASSWORD_QUALITY_MANAGED) {
quality = PASSWORD_QUALITY_UNSPECIFIED;
}
final PasswordMetrics metrics = PasswordMetrics.computeForPassword(password);
- realQuality = metrics.quality;
- if (quality != PASSWORD_QUALITY_UNSPECIFIED) {
-
- if (realQuality < quality
- && quality != DevicePolicyManager.PASSWORD_QUALITY_COMPLEX) {
- Slog.w(LOG_TAG, "resetPassword: password quality 0x"
- + Integer.toHexString(realQuality)
- + " does not meet required quality 0x"
- + Integer.toHexString(quality));
- return false;
- }
- quality = Math.max(realQuality, quality);
+ final int realQuality = metrics.quality;
+ if (realQuality < quality
+ && quality != DevicePolicyManager.PASSWORD_QUALITY_COMPLEX) {
+ Slog.w(LOG_TAG, "resetPassword: password quality 0x"
+ + Integer.toHexString(realQuality)
+ + " does not meet required quality 0x"
+ + Integer.toHexString(quality));
+ return false;
}
+ quality = Math.max(realQuality, quality);
int length = getPasswordMinimumLength(null, userHandle, /* parent */ false);
if (password.length() < length) {
Slog.w(LOG_TAG, "resetPassword: password length " + password.length()
@@ -5086,7 +5082,7 @@
try {
if (token == null) {
if (!TextUtils.isEmpty(password)) {
- mLockPatternUtils.saveLockPassword(password, null, realQuality, userHandle);
+ mLockPatternUtils.saveLockPassword(password, null, quality, userHandle);
} else {
mLockPatternUtils.clearLock(null, userHandle);
}
@@ -5095,7 +5091,7 @@
result = mLockPatternUtils.setLockCredentialWithToken(password,
TextUtils.isEmpty(password) ? LockPatternUtils.CREDENTIAL_TYPE_NONE
: LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
- realQuality, tokenHandle, token, userHandle);
+ quality, tokenHandle, token, userHandle);
}
boolean requireEntry = (flags & DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY) != 0;
if (requireEntry) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index be09aea..4326c39 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -145,6 +145,7 @@
import com.android.server.wm.ActivityTaskManagerService;
import com.android.server.wm.WindowManagerGlobalLock;
import com.android.server.wm.WindowManagerService;
+import com.google.android.startop.iorap.IorapForwardingService;
import dalvik.system.VMRuntime;
@@ -1007,10 +1008,13 @@
mSystemServiceManager.startService(PinnerService.class);
traceEnd();
+ traceBeginAndSlog("IorapForwardingService");
+ mSystemServiceManager.startService(IorapForwardingService.class);
+ traceEnd();
+
traceBeginAndSlog("SignedConfigService");
SignedConfigService.registerUpdateReceiver(mSystemContext);
traceEnd();
-
} catch (RuntimeException e) {
Slog.e("System", "******************************************");
Slog.e("System", "************ Failure starting core service", e);
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/dhcp/DhcpServingParamsParcelExt.java b/services/net/java/android/net/dhcp/DhcpServingParamsParcelExt.java
new file mode 100644
index 0000000..f068c3a
--- /dev/null
+++ b/services/net/java/android/net/dhcp/DhcpServingParamsParcelExt.java
@@ -0,0 +1,172 @@
+/*
+ * 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;
+
+import static android.net.NetworkUtils.inet4AddressToIntHTH;
+
+import android.annotation.NonNull;
+import android.net.LinkAddress;
+
+import com.google.android.collect.Sets;
+
+import java.net.Inet4Address;
+import java.util.Collection;
+import java.util.Set;
+
+/**
+ * Subclass of {@link DhcpServingParamsParcel} with additional utility methods for building.
+ *
+ * <p>This utility class does not check for validity of the parameters: invalid parameters are
+ * reported by the receiving module when unparceling the parcel.
+ *
+ * @see DhcpServingParams
+ * @hide
+ */
+public class DhcpServingParamsParcelExt extends DhcpServingParamsParcel {
+ public static final int MTU_UNSET = 0;
+
+ /**
+ * Set the server address and served prefix for the DHCP server.
+ *
+ * <p>This parameter is required.
+ */
+ public DhcpServingParamsParcelExt setServerAddr(@NonNull LinkAddress serverAddr) {
+ this.serverAddr = inet4AddressToIntHTH((Inet4Address) serverAddr.getAddress());
+ this.serverAddrPrefixLength = serverAddr.getPrefixLength();
+ return this;
+ }
+
+ /**
+ * Set the default routers to be advertised to DHCP clients.
+ *
+ * <p>Each router must be inside the served prefix. This may be an empty set, but it must
+ * always be set explicitly.
+ */
+ public DhcpServingParamsParcelExt setDefaultRouters(@NonNull Set<Inet4Address> defaultRouters) {
+ this.defaultRouters = toIntArray(defaultRouters);
+ return this;
+ }
+
+ /**
+ * Set the default routers to be advertised to DHCP clients.
+ *
+ * <p>Each router must be inside the served prefix. This may be an empty list of routers,
+ * but it must always be set explicitly.
+ */
+ public DhcpServingParamsParcelExt setDefaultRouters(@NonNull Inet4Address... defaultRouters) {
+ return setDefaultRouters(Sets.newArraySet(defaultRouters));
+ }
+
+ /**
+ * Convenience method to build the parameters with no default router.
+ *
+ * <p>Equivalent to calling {@link #setDefaultRouters(Inet4Address...)} with no address.
+ */
+ public DhcpServingParamsParcelExt setNoDefaultRouter() {
+ return setDefaultRouters();
+ }
+
+ /**
+ * Set the DNS servers to be advertised to DHCP clients.
+ *
+ * <p>This may be an empty set, but it must always be set explicitly.
+ */
+ public DhcpServingParamsParcelExt setDnsServers(@NonNull Set<Inet4Address> dnsServers) {
+ this.dnsServers = toIntArray(dnsServers);
+ return this;
+ }
+
+ /**
+ * Set the DNS servers to be advertised to DHCP clients.
+ *
+ * <p>This may be an empty list of servers, but it must always be set explicitly.
+ */
+ public DhcpServingParamsParcelExt setDnsServers(@NonNull Inet4Address... dnsServers) {
+ return setDnsServers(Sets.newArraySet(dnsServers));
+ }
+
+ /**
+ * Convenience method to build the parameters with no DNS server.
+ *
+ * <p>Equivalent to calling {@link #setDnsServers(Inet4Address...)} with no address.
+ */
+ public DhcpServingParamsParcelExt setNoDnsServer() {
+ return setDnsServers();
+ }
+
+ /**
+ * Set excluded addresses that the DHCP server is not allowed to assign to clients.
+ *
+ * <p>This parameter is optional. DNS servers and default routers are always excluded
+ * and do not need to be set here.
+ */
+ public DhcpServingParamsParcelExt setExcludedAddrs(@NonNull Set<Inet4Address> excludedAddrs) {
+ this.excludedAddrs = toIntArray(excludedAddrs);
+ return this;
+ }
+
+ /**
+ * Set excluded addresses that the DHCP server is not allowed to assign to clients.
+ *
+ * <p>This parameter is optional. DNS servers and default routers are always excluded
+ * and do not need to be set here.
+ */
+ public DhcpServingParamsParcelExt setExcludedAddrs(@NonNull Inet4Address... excludedAddrs) {
+ return setExcludedAddrs(Sets.newArraySet(excludedAddrs));
+ }
+
+ /**
+ * Set the lease time for leases assigned by the DHCP server.
+ *
+ * <p>This parameter is required.
+ */
+ public DhcpServingParamsParcelExt setDhcpLeaseTimeSecs(long dhcpLeaseTimeSecs) {
+ this.dhcpLeaseTimeSecs = dhcpLeaseTimeSecs;
+ return this;
+ }
+
+ /**
+ * Set the link MTU to be advertised to DHCP clients.
+ *
+ * <p>If set to {@link #MTU_UNSET}, no MTU will be advertised to clients. This parameter
+ * is optional and defaults to {@link #MTU_UNSET}.
+ */
+ public DhcpServingParamsParcelExt setLinkMtu(int linkMtu) {
+ this.linkMtu = linkMtu;
+ return this;
+ }
+
+ /**
+ * Set whether the DHCP server should send the ANDROID_METERED vendor-specific option.
+ *
+ * <p>If not set, the default value is false.
+ */
+ public DhcpServingParamsParcelExt setMetered(boolean metered) {
+ this.metered = metered;
+ return this;
+ }
+
+ private static int[] toIntArray(@NonNull Collection<Inet4Address> addrs) {
+ int[] res = new int[addrs.size()];
+ int i = 0;
+ for (Inet4Address addr : addrs) {
+ res[i] = inet4AddressToIntHTH(addr);
+ i++;
+ }
+ return res;
+ }
+}
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 5a73a4e..8b7b59d 100644
--- a/services/net/java/android/net/util/SharedLog.java
+++ b/services/net/java/android/net/util/SharedLog.java
@@ -32,11 +32,12 @@
*
* 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 {
- private final static int DEFAULT_MAX_RECORDS = 500;
- private final static String COMPONENT_DELIMITER = ".";
+ private static final int DEFAULT_MAX_RECORDS = 500;
+ private static final String COMPONENT_DELIMITER = ".";
private enum Category {
NONE,
@@ -69,6 +70,9 @@
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;
@@ -76,6 +80,11 @@
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);
}
@@ -84,10 +93,21 @@
// 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));
}
@@ -96,7 +116,7 @@
* 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).
+ * 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) {
@@ -106,10 +126,20 @@
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));
}
@@ -118,14 +148,30 @@
// 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);
}
diff --git a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java
index b682def..769a9d4 100644
--- a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java
@@ -16,6 +16,7 @@
package com.android.server.backup;
+import static android.Manifest.permission.BACKUP;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static com.android.server.backup.testing.BackupManagerServiceTestUtils.startBackupThread;
@@ -27,7 +28,6 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.robolectric.Shadows.shadowOf;
import static org.testng.Assert.expectThrows;
@@ -201,7 +201,7 @@
backupManagerService.stopServiceForUser(mUserOneId);
verify(mUserOneService).tearDownService();
- verifyNoMoreInteractions(mUserTwoService);
+ verify(mUserTwoService, never()).tearDownService();
}
/** Test that the service unregisters users when stopped. */
@@ -1542,6 +1542,7 @@
}
private BackupManagerService createService() {
+ mShadowContext.grantPermissions(BACKUP);
return new BackupManagerService(
mContext, new Trampoline(mContext), startBackupThread(null));
}
diff --git a/services/robotests/backup/src/com/android/server/backup/internal/SetupObserverTest.java b/services/robotests/backup/src/com/android/server/backup/internal/SetupObserverTest.java
index b56b5e6..b754356 100644
--- a/services/robotests/backup/src/com/android/server/backup/internal/SetupObserverTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/internal/SetupObserverTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * 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.
diff --git a/services/startop/Android.bp b/services/startop/Android.bp
new file mode 100644
index 0000000..093b4ec
--- /dev/null
+++ b/services/startop/Android.bp
@@ -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 permissions and
+ * limitations under the License.
+ */
+
+java_library_static {
+ name: "services.startop",
+
+ static_libs: [
+ // frameworks/base/startop/iorap
+ "services.startop.iorap",
+ ],
+}
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
index abf9040..4742a73 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -389,7 +389,7 @@
if (attr == null) return; //sampling not supported on device, skip remainder of test.
boolean enabled = displayManager.setDisplayedContentSamplingEnabledInternal(0, true, 0, 0);
- assertTrue(!enabled);
+ assertTrue(enabled);
displayManager.setDisplayedContentSamplingEnabledInternal(0, false, 0, 0);
DisplayedContentSample sample = displayManager.getDisplayedContentSampleInternal(0, 0, 0);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
index 7484edd..4255e37 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
@@ -18,8 +18,12 @@
import android.hardware.hdmi.HdmiPortInfo;
import android.hardware.tv.cec.V1_0.SendMessageResult;
import android.os.MessageQueue;
+
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.hdmi.HdmiCecController.NativeWrapper;
+
+import com.google.common.collect.Iterables;
+
import java.util.ArrayList;
import java.util.List;
@@ -45,7 +49,6 @@
};
private final List<HdmiCecMessage> mResultMessages = new ArrayList<>();
- private HdmiCecMessage mResultMessage;
private int mMyPhysicalAddress = 0;
@Override
@@ -112,11 +115,12 @@
return new ArrayList<>(mResultMessages);
}
- public HdmiCecMessage getOnlyResultMessage() throws Exception {
- if (mResultMessages.size() != 1) {
- throw new Exception("There is not exactly one message");
- }
- return mResultMessages.get(0);
+ public HdmiCecMessage getOnlyResultMessage() throws IllegalArgumentException {
+ return Iterables.getOnlyElement(mResultMessages);
+ }
+
+ public void clearResultMessages() {
+ mResultMessages.clear();
}
public void setPollAddressResponse(int logicalAddress, int response) {
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 9e3a0ea..bdede33 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
@@ -20,22 +20,20 @@
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;
-import static com.google.common.truth.Truth.assertThat;
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
+import static com.google.common.truth.Truth.assertThat;
import android.media.AudioManager;
import android.os.Looper;
import android.os.SystemProperties;
import android.os.test.TestLooper;
+
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
+
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;
@@ -61,6 +59,7 @@
private int mMusicVolume;
private int mMusicMaxVolume;
private boolean mMusicMute;
+ private int mAvrPhysicalAddress;
@Before
public void setUp() {
@@ -115,7 +114,7 @@
@Override
public void setWiredDeviceConnectionState(
- int type, int state, String address, String name) {
+ int type, int state, String address, String name) {
// Do nothing.
}
};
@@ -123,6 +122,11 @@
@Override
void wakeUp() {}
+
+ @Override
+ boolean isControlEnabled() {
+ return true;
+ }
};
mMyLooper = mTestLooper.getLooper();
@@ -140,11 +144,15 @@
// No TV device interacts with AVR so system audio control won't be turned on here
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
- public void handleGiveAudioStatus_volume_10_mute_true() {
+ public void handleGiveAudioStatus_volume_10_mute_true() throws Exception {
mMusicVolume = 10;
mMusicMute = true;
mMusicMaxVolume = 20;
@@ -154,14 +162,13 @@
ADDR_AUDIO_SYSTEM, ADDR_TV, scaledVolume, true);
HdmiCecMessage messageGive =
HdmiCecMessageBuilder.buildGiveAudioStatus(ADDR_TV, ADDR_AUDIO_SYSTEM);
- assertThat(mHdmiCecLocalDeviceAudioSystem.handleGiveAudioStatus(messageGive))
- .isTrue();
+ assertThat(mHdmiCecLocalDeviceAudioSystem.handleGiveAudioStatus(messageGive)).isTrue();
mTestLooper.dispatchAll();
- assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
+ assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
}
@Test
- public void handleGiveSystemAudioModeStatus_originalOff() {
+ public void handleGiveSystemAudioModeStatus_originalOff() throws Exception {
HdmiCecMessage expectedMessage =
HdmiCecMessageBuilder.buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, false);
HdmiCecMessage messageGive =
@@ -169,10 +176,9 @@
assertThat(mHdmiCecLocalDeviceAudioSystem.handleGiveSystemAudioModeStatus(messageGive))
.isTrue();
mTestLooper.dispatchAll();
- assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
+ assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
}
- @Ignore("b/80297700")
@Test
public void handleRequestShortAudioDescriptor_featureDisabled() throws Exception {
HdmiCecMessage expectedMessage =
@@ -228,7 +234,7 @@
}
@Test
- public void handleSetSystemAudioMode_setOn_orignalOff() {
+ public void handleSetSystemAudioMode_setOn_orignalOff() throws Exception {
mMusicMute = true;
HdmiCecMessage messageSet =
HdmiCecMessageBuilder.buildSetSystemAudioMode(ADDR_TV, ADDR_AUDIO_SYSTEM, true);
@@ -240,23 +246,22 @@
assertThat(mHdmiCecLocalDeviceAudioSystem.handleGiveSystemAudioModeStatus(messageGive))
.isTrue();
mTestLooper.dispatchAll();
- assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
+ assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
// Check if correctly turned on
+ mNativeWrapper.clearResultMessages();
expectedMessage =
HdmiCecMessageBuilder.buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, true);
- assertThat(mHdmiCecLocalDeviceAudioSystem.handleSetSystemAudioMode(messageSet))
- .isTrue();
+ assertThat(mHdmiCecLocalDeviceAudioSystem.handleSetSystemAudioMode(messageSet)).isTrue();
mTestLooper.dispatchAll();
assertThat(mHdmiCecLocalDeviceAudioSystem.handleGiveSystemAudioModeStatus(messageGive))
.isTrue();
mTestLooper.dispatchAll();
- assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
+ assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
assertThat(mMusicMute).isFalse();
}
- @Ignore("b/80297700")
@Test
- public void handleSystemAudioModeRequest_turnOffByTv() {
+ public void handleSystemAudioModeRequest_turnOffByTv() throws Exception {
assertThat(mMusicMute).isFalse();
// Check if feature correctly turned off
HdmiCecMessage messageGive =
@@ -270,19 +275,22 @@
assertThat(mHdmiCecLocalDeviceAudioSystem.handleSystemAudioModeRequest(messageRequestOff))
.isTrue();
mTestLooper.dispatchAll();
- assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
+ assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
+
+ mNativeWrapper.clearResultMessages();
expectedMessage =
HdmiCecMessageBuilder.buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, false);
assertThat(mHdmiCecLocalDeviceAudioSystem.handleGiveSystemAudioModeStatus(messageGive))
.isTrue();
mTestLooper.dispatchAll();
- assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
+ assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
assertThat(mMusicMute).isTrue();
}
- @Ignore("b/80297700")
@Test
- public void onStandbyAudioSystem_currentSystemAudioControlOn() {
+ public void onStandbyAudioSystem_currentSystemAudioControlOn() throws Exception {
+ mHdmiCecLocalDeviceAudioSystem.setAutoDeviceOff(false);
+ mHdmiCecLocalDeviceAudioSystem.setAutoTvOff(false);
// Set system audio control on first
mHdmiCecLocalDeviceAudioSystem.setSystemAudioMode(true);
// Check if standby correctly turns off the feature
@@ -291,12 +299,12 @@
HdmiCecMessage expectedMessage =
HdmiCecMessageBuilder.buildSetSystemAudioMode(
ADDR_AUDIO_SYSTEM, ADDR_BROADCAST, false);
- assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
+ assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
assertThat(mMusicMute).isTrue();
}
@Test
- public void systemAudioControlOnPowerOn_alwaysOn() {
+ public void systemAudioControlOnPowerOn_alwaysOn() throws Exception {
mHdmiCecLocalDeviceAudioSystem.removeAction(SystemAudioInitiationActionFromAvr.class);
mHdmiCecLocalDeviceAudioSystem.systemAudioControlOnPowerOn(
Constants.ALWAYS_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, true);
@@ -307,7 +315,7 @@
}
@Test
- public void systemAudioControlOnPowerOn_neverOn() {
+ public void systemAudioControlOnPowerOn_neverOn() throws Exception {
mHdmiCecLocalDeviceAudioSystem.removeAction(SystemAudioInitiationActionFromAvr.class);
mHdmiCecLocalDeviceAudioSystem.systemAudioControlOnPowerOn(
Constants.NEVER_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, false);
@@ -318,7 +326,7 @@
}
@Test
- public void systemAudioControlOnPowerOn_useLastState_off() {
+ public void systemAudioControlOnPowerOn_useLastState_off() throws Exception {
mHdmiCecLocalDeviceAudioSystem.removeAction(SystemAudioInitiationActionFromAvr.class);
mHdmiCecLocalDeviceAudioSystem.systemAudioControlOnPowerOn(
Constants.USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, false);
@@ -329,7 +337,7 @@
}
@Test
- public void systemAudioControlOnPowerOn_useLastState_on() {
+ public void systemAudioControlOnPowerOn_useLastState_on() throws Exception {
mHdmiCecLocalDeviceAudioSystem.removeAction(SystemAudioInitiationActionFromAvr.class);
mHdmiCecLocalDeviceAudioSystem.systemAudioControlOnPowerOn(
Constants.USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, true);
@@ -340,18 +348,17 @@
}
@Test
- public void handleActiveSource_updateActiveSource() {
+ public void handleActiveSource_updateActiveSource() throws Exception {
HdmiCecMessage message = HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
ActiveSource expectedActiveSource = new ActiveSource(ADDR_TV, 0x0000);
- assertThat(mHdmiCecLocalDeviceAudioSystem.handleActiveSource(message))
- .isTrue();
+ assertThat(mHdmiCecLocalDeviceAudioSystem.handleActiveSource(message)).isTrue();
mTestLooper.dispatchAll();
assertThat(mHdmiCecLocalDeviceAudioSystem.getActiveSource().equals(expectedActiveSource))
.isTrue();
}
@Test
- public void terminateSystemAudioMode_systemAudioModeOff() {
+ public void terminateSystemAudioMode_systemAudioModeOff() throws Exception {
mHdmiCecLocalDeviceAudioSystem.setSystemAudioMode(false);
assertThat(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated()).isFalse();
mMusicMute = false;
@@ -361,12 +368,11 @@
mHdmiCecLocalDeviceAudioSystem.terminateSystemAudioMode();
assertThat(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated()).isFalse();
assertThat(mMusicMute).isFalse();
- assertThat(mNativeWrapper.getResultMessages()).doesNotContain(message);
+ assertThat(mNativeWrapper.getResultMessages()).isEmpty();
}
- @Ignore("b/80297700")
@Test
- public void terminateSystemAudioMode_systemAudioModeOn() {
+ public void terminateSystemAudioMode_systemAudioModeOn() throws Exception {
mHdmiCecLocalDeviceAudioSystem.setSystemAudioMode(true);
assertThat(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated()).isTrue();
mMusicMute = false;
@@ -381,124 +387,146 @@
}
@Test
- public void isPhysicalAddressMeOrBelow_isMe() {
+ 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() {
+ 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() {
+ 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
- public void handleRequestArcInitiate_isNotDirectConnectedToTv() {
- HdmiCecMessage message = HdmiCecMessageBuilder
- .buildRequestArcInitiation(ADDR_TV, ADDR_AUDIO_SYSTEM);
- HdmiCecMessage expectedMessage = HdmiCecMessageBuilder
- .buildFeatureAbortCommand(
- ADDR_AUDIO_SYSTEM, ADDR_TV,
- Constants.MESSAGE_REQUEST_ARC_INITIATION,
- Constants.ABORT_NOT_IN_CORRECT_MODE);
+ public void handleRequestArcInitiate_isNotDirectConnectedToTv() throws Exception {
+ HdmiCecMessage message =
+ HdmiCecMessageBuilder.buildRequestArcInitiation(ADDR_TV, ADDR_AUDIO_SYSTEM);
+ HdmiCecMessage expectedMessage =
+ HdmiCecMessageBuilder.buildFeatureAbortCommand(
+ ADDR_AUDIO_SYSTEM,
+ ADDR_TV,
+ Constants.MESSAGE_REQUEST_ARC_INITIATION,
+ Constants.ABORT_NOT_IN_CORRECT_MODE);
mNativeWrapper.setPhysicalAddress(0x1100);
- assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcInitiate(message))
- .isTrue();
+ assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcInitiate(message)).isTrue();
mTestLooper.dispatchAll();
assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
}
@Test
- public void handleRequestArcInitiate_startArcInitiationActionFromAvr() {
- HdmiCecMessage message = HdmiCecMessageBuilder
- .buildRequestArcInitiation(ADDR_TV, ADDR_AUDIO_SYSTEM);
+ public void handleRequestArcInitiate_startArcInitiationActionFromAvr() throws Exception {
+ HdmiCecMessage message =
+ HdmiCecMessageBuilder.buildRequestArcInitiation(ADDR_TV, ADDR_AUDIO_SYSTEM);
mNativeWrapper.setPhysicalAddress(0x1000);
- mHdmiCecLocalDeviceAudioSystem.removeAction(
- ArcInitiationActionFromAvr.class);
+ mHdmiCecLocalDeviceAudioSystem.removeAction(ArcInitiationActionFromAvr.class);
- assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcInitiate(message))
- .isTrue();
+ assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcInitiate(message)).isTrue();
mTestLooper.dispatchAll();
- assertThat(mHdmiCecLocalDeviceAudioSystem
- .getActions(ArcInitiationActionFromAvr.class)).isNotEmpty();
+ assertThat(mHdmiCecLocalDeviceAudioSystem.getActions(ArcInitiationActionFromAvr.class))
+ .isNotEmpty();
}
@Test
- public void handleRequestArcTerminate_arcIsOn_startTerminationActionFromAvr() {
+ public void handleRequestArcTerminate_arcIsOn_startTerminationActionFromAvr() throws Exception {
mHdmiCecLocalDeviceAudioSystem.setArcStatus(true);
assertThat(mHdmiCecLocalDeviceAudioSystem.isArcEnabled()).isTrue();
- HdmiCecMessage message = HdmiCecMessageBuilder
- .buildRequestArcTermination(ADDR_TV, ADDR_AUDIO_SYSTEM);
- mHdmiCecLocalDeviceAudioSystem.removeAction(
- ArcTerminationActionFromAvr.class);
+ HdmiCecMessage message =
+ HdmiCecMessageBuilder.buildRequestArcTermination(ADDR_TV, ADDR_AUDIO_SYSTEM);
+ mHdmiCecLocalDeviceAudioSystem.removeAction(ArcTerminationActionFromAvr.class);
- assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcTermination(message))
- .isTrue();
+ assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcTermination(message)).isTrue();
mTestLooper.dispatchAll();
- assertThat(mHdmiCecLocalDeviceAudioSystem
- .getActions(ArcTerminationActionFromAvr.class)).isNotEmpty();
+ assertThat(mHdmiCecLocalDeviceAudioSystem.getActions(ArcTerminationActionFromAvr.class))
+ .isNotEmpty();
}
@Test
- public void handleRequestArcTerminate_arcIsNotOn() {
+ public void handleRequestArcTerminate_arcIsNotOn() throws Exception {
assertThat(mHdmiCecLocalDeviceAudioSystem.isArcEnabled()).isFalse();
- HdmiCecMessage message = HdmiCecMessageBuilder
- .buildRequestArcTermination(ADDR_TV, ADDR_AUDIO_SYSTEM);
- HdmiCecMessage expectedMessage = HdmiCecMessageBuilder
- .buildFeatureAbortCommand(
- ADDR_AUDIO_SYSTEM, ADDR_TV,
- Constants.MESSAGE_REQUEST_ARC_TERMINATION,
- Constants.ABORT_NOT_IN_CORRECT_MODE);
+ HdmiCecMessage message =
+ HdmiCecMessageBuilder.buildRequestArcTermination(ADDR_TV, ADDR_AUDIO_SYSTEM);
+ HdmiCecMessage expectedMessage =
+ HdmiCecMessageBuilder.buildFeatureAbortCommand(
+ ADDR_AUDIO_SYSTEM,
+ ADDR_TV,
+ Constants.MESSAGE_REQUEST_ARC_TERMINATION,
+ Constants.ABORT_NOT_IN_CORRECT_MODE);
- assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcTermination(message))
- .isTrue();
+ assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcTermination(message)).isTrue();
mTestLooper.dispatchAll();
assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
}
@Test
- public void handleRequestArcInit_arcIsNotSupported() {
- HdmiCecMessage message = HdmiCecMessageBuilder
- .buildRequestArcInitiation(ADDR_TV, ADDR_AUDIO_SYSTEM);
- HdmiCecMessage expectedMessage = HdmiCecMessageBuilder
- .buildFeatureAbortCommand(
- ADDR_AUDIO_SYSTEM, ADDR_TV,
- Constants.MESSAGE_REQUEST_ARC_INITIATION,
- Constants.ABORT_UNRECOGNIZED_OPCODE);
+ public void handleRequestArcInit_arcIsNotSupported() throws Exception {
+ HdmiCecMessage message =
+ HdmiCecMessageBuilder.buildRequestArcInitiation(ADDR_TV, ADDR_AUDIO_SYSTEM);
+ HdmiCecMessage expectedMessage =
+ HdmiCecMessageBuilder.buildFeatureAbortCommand(
+ ADDR_AUDIO_SYSTEM,
+ ADDR_TV,
+ Constants.MESSAGE_REQUEST_ARC_INITIATION,
+ Constants.ABORT_UNRECOGNIZED_OPCODE);
SystemProperties.set(Constants.PROPERTY_ARC_SUPPORT, "false");
- assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcInitiate(message))
- .isTrue();
+ assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcInitiate(message)).isTrue();
mTestLooper.dispatchAll();
- assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
+ assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
+ }
+
+ @Test
+ public void onStandby_setAutoDeviceOff_true() throws Exception {
+ HdmiCecMessage expectedMessage =
+ HdmiCecMessageBuilder.buildStandby(ADDR_AUDIO_SYSTEM, ADDR_BROADCAST);
+ mHdmiCecLocalDeviceAudioSystem.setAutoDeviceOff(true);
+ mHdmiCecLocalDeviceAudioSystem.onStandby(false, STANDBY_SCREEN_OFF);
+
+ mTestLooper.dispatchAll();
+ assertThat(mNativeWrapper.getOnlyResultMessage()).isEqualTo(expectedMessage);
+ }
+
+ @Test
+ public void handleSetStreamPath_underCurrentDevice() {
+ assertThat(mHdmiCecLocalDeviceAudioSystem.getLocalActivePath()).isEqualTo(0);
+ HdmiCecMessage message =
+ HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV, 0x2100);
+ assertThat(mHdmiCecLocalDeviceAudioSystem.handleSetStreamPath(message)).isTrue();
+ assertThat(mHdmiCecLocalDeviceAudioSystem.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/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java
index 8496a96..b348aee 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppTimeLimitControllerTests.java
@@ -699,10 +699,52 @@
assertTrue(hasUsageSessionObserver(UID, OBS_ID1));
}
+ /** Verify the timeout message is delivered at the right time after past usage was reported */
+ @Test
+ public void testAppUsageObserver_PastUsage() throws Exception {
+ setTime(10_000L);
+ addAppUsageObserver(OBS_ID1, GROUP1, 6_000L);
+ setTime(20_000L);
+ startPastUsage(PKG_SOC1, 5_000);
+ setTime(21_000L);
+ assertTrue(mLimitReachedLatch.await(2_000L, TimeUnit.MILLISECONDS));
+ stopUsage(PKG_SOC1);
+ // Verify that the observer was removed
+ assertFalse(hasAppUsageObserver(UID, OBS_ID1));
+ }
+
+ /**
+ * Verify the timeout message is delivered at the right time after past usage was reported
+ * that overlaps with already known usage
+ */
+ @Test
+ public void testAppUsageObserver_PastUsageOverlap() throws Exception {
+ setTime(0L);
+ addAppUsageObserver(OBS_ID1, GROUP1, 20_000L);
+ setTime(10_000L);
+ startUsage(PKG_SOC1);
+ setTime(20_000L);
+ stopUsage(PKG_SOC1);
+ setTime(25_000L);
+ startPastUsage(PKG_SOC1, 9_000);
+ setTime(26_000L);
+ // the 4 seconds of overlapped usage should not be counted
+ assertFalse(mLimitReachedLatch.await(2_000L, TimeUnit.MILLISECONDS));
+ setTime(30_000L);
+ assertTrue(mLimitReachedLatch.await(4_000L, TimeUnit.MILLISECONDS));
+ stopUsage(PKG_SOC1);
+ // Verify that the observer was removed
+ assertFalse(hasAppUsageObserver(UID, OBS_ID1));
+ }
+
private void startUsage(String packageName) {
mController.noteUsageStart(packageName, USER_ID);
}
+ private void startPastUsage(String packageName, int timeAgo) {
+ mController.noteUsageStart(packageName, USER_ID, timeAgo);
+ }
+
private void stopUsage(String packageName) {
mController.noteUsageStop(packageName, USER_ID);
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
index 8b65e76..20f72bf 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -613,7 +613,7 @@
}
@Test
- public void testGetAllowedPackages() throws Exception {
+ public void testGetAllowedPackages_byUser() throws Exception {
for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) {
ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
mIpm, approvalLevel);
@@ -681,6 +681,30 @@
}
@Test
+ public void testGetAllowedPackages() throws Exception {
+ ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
+ mIpm, APPROVAL_BY_COMPONENT);
+ loadXml(service);
+ service.mApprovalLevel = APPROVAL_BY_PACKAGE;
+ loadXml(service);
+
+ List<String> allowedPackages = new ArrayList<>();
+ allowedPackages.add("this.is.a.package.name");
+ allowedPackages.add("another.package");
+ allowedPackages.add("secondary");
+ allowedPackages.add("this.is.another.package");
+ allowedPackages.add("package");
+ allowedPackages.add("component");
+ allowedPackages.add("bananas!");
+
+ Set<String> actual = service.getAllowedPackages();
+ assertEquals(allowedPackages.size(), actual.size());
+ for (String pkg : allowedPackages) {
+ assertTrue(actual.contains(pkg));
+ }
+ }
+
+ @Test
public void testOnUserRemoved() throws Exception {
for (int approvalLevel : new int[] {APPROVAL_BY_COMPONENT, APPROVAL_BY_PACKAGE}) {
ManagedServices service = new TestManagedServices(getContext(), mLock, mUserProfiles,
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
index 65e640f..1458266 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
@@ -825,4 +825,24 @@
assertNotEquals(-1, record.getLastAudiblyAlertedMs());
}
+
+ @Test
+ public void testIsNewEnoughForAlerting_new() {
+ StatusBarNotification sbn = getNotification(PKG_O, true /* noisy */,
+ true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
+ false /* lights */, false /* defaultLights */, groupId /* group */);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+
+ assertTrue(record.isNewEnoughForAlerting(record.mUpdateTimeMs));
+ }
+
+ @Test
+ public void testIsNewEnoughForAlerting_old() {
+ StatusBarNotification sbn = getNotification(PKG_O, true /* noisy */,
+ true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
+ false /* lights */, false /* defaultLights */, groupId /* group */);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+
+ assertFalse(record.isNewEnoughForAlerting(record.mUpdateTimeMs + (1000 * 60 * 60)));
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
index 49f134f..174c5fa 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
@@ -299,6 +299,59 @@
assertEquals(1, mSnoozeHelper.getSnoozed(UserHandle.USER_SYSTEM, "pkg").size());
}
+ @Test
+ public void testClearData() {
+ // snooze 2 from same package
+ NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
+ NotificationRecord r2 = getNotificationRecord("pkg", 2, "two", UserHandle.SYSTEM);
+ mSnoozeHelper.snooze(r, 1000);
+ mSnoozeHelper.snooze(r2, 1000);
+ assertTrue(mSnoozeHelper.isSnoozed(
+ UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey()));
+ assertTrue(mSnoozeHelper.isSnoozed(
+ UserHandle.USER_SYSTEM, r2.sbn.getPackageName(), r2.getKey()));
+
+ // clear data
+ mSnoozeHelper.clearData(UserHandle.USER_SYSTEM, "pkg");
+
+ // nothing snoozed; alarms canceled
+ assertFalse(mSnoozeHelper.isSnoozed(
+ UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey()));
+ assertFalse(mSnoozeHelper.isSnoozed(
+ UserHandle.USER_SYSTEM, r2.sbn.getPackageName(), r2.getKey()));
+ // twice for initial snooze, twice for canceling the snooze
+ verify(mAm, times(4)).cancel(any(PendingIntent.class));
+ }
+
+ @Test
+ public void testClearData_otherRecordsUntouched() {
+ // 2 packages, 2 users
+ NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
+ NotificationRecord r2 = getNotificationRecord("pkg", 2, "two", UserHandle.ALL);
+ NotificationRecord r3 = getNotificationRecord("pkg2", 3, "three", UserHandle.SYSTEM);
+ mSnoozeHelper.snooze(r, 1000);
+ mSnoozeHelper.snooze(r2, 1000);
+ mSnoozeHelper.snooze(r3, 1000);
+ assertTrue(mSnoozeHelper.isSnoozed(
+ UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey()));
+ assertTrue(mSnoozeHelper.isSnoozed(
+ UserHandle.USER_ALL, r2.sbn.getPackageName(), r2.getKey()));
+ assertTrue(mSnoozeHelper.isSnoozed(
+ UserHandle.USER_SYSTEM, r3.sbn.getPackageName(), r3.getKey()));
+
+ // clear data
+ mSnoozeHelper.clearData(UserHandle.USER_SYSTEM, "pkg");
+
+ assertFalse(mSnoozeHelper.isSnoozed(
+ UserHandle.USER_SYSTEM, r.sbn.getPackageName(), r.getKey()));
+ assertTrue(mSnoozeHelper.isSnoozed(
+ UserHandle.USER_ALL, r2.sbn.getPackageName(), r2.getKey()));
+ assertTrue(mSnoozeHelper.isSnoozed(
+ UserHandle.USER_SYSTEM, r3.sbn.getPackageName(), r3.getKey()));
+ // once for each initial snooze, once for canceling one snooze
+ verify(mAm, times(4)).cancel(any(PendingIntent.class));
+ }
+
private NotificationRecord getNotificationRecord(String pkg, int id, String tag,
UserHandle user, String groupKey, boolean groupSummary) {
Notification n = new Notification.Builder(getContext(), TEST_CHANNEL_ID)
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 569c6d4..ee336a8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -31,6 +31,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
@@ -38,6 +39,9 @@
import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doCallRealMethod;
+
import android.app.ActivityManagerInternal;
import android.app.ActivityOptions;
import android.app.IApplicationThread;
@@ -115,6 +119,10 @@
@After
public void tearDownBase() {
mTestInjector.tearDown();
+ if (mService != null) {
+ mService.setWindowManager(null);
+ mService = null;
+ }
}
ActivityTaskManagerService createActivityTaskManagerService() {
@@ -144,6 +152,13 @@
return display;
}
+ /** Creates and adds a {@link TestActivityDisplay} to supervisor at the given position. */
+ TestActivityDisplay addNewActivityDisplayAt(DisplayInfo info, int position) {
+ final TestActivityDisplay display = createNewActivityDisplay(info);
+ mRootActivityContainer.addChild(display, position);
+ return display;
+ }
+
/**
* Builder for creating new activities.
*/
@@ -234,6 +249,10 @@
mService.mStackSupervisor, null /* options */, null /* sourceRecord */);
spyOn(activity);
activity.mAppWindowToken = mock(AppWindowToken.class);
+ doCallRealMethod().when(activity.mAppWindowToken).getOrientationIgnoreVisibility();
+ doCallRealMethod().when(activity.mAppWindowToken)
+ .setOrientation(anyInt(), any(), any());
+ doCallRealMethod().when(activity.mAppWindowToken).setOrientation(anyInt());
doNothing().when(activity).removeWindowContainer();
if (mTaskRecord != null) {
@@ -346,6 +365,7 @@
mStack.addTask(task, true, "creating test task");
task.setStack(mStack);
task.setTask();
+ mStack.getWindowContainerController().mContainer.addChild(task.mTask, 0);
}
task.touchActiveTime();
@@ -365,7 +385,10 @@
setTask();
}
- private void setTask() {
+ void setTask() {
+ Task mockTask = mock(Task.class);
+ mockTask.mTaskRecord = this;
+ doCallRealMethod().when(mockTask).onDescendantOrientationChanged(any(), any());
setTask(mock(Task.class));
}
}
@@ -619,7 +642,13 @@
}
}
+ private static WindowManagerService sMockWindowManagerService;
+
private static WindowManagerService prepareMockWindowManager() {
+ if (sMockWindowManagerService != null) {
+ return sMockWindowManagerService;
+ }
+
final WindowManagerService service = mock(WindowManagerService.class);
service.mRoot = mock(RootWindowContainer.class);
@@ -631,6 +660,7 @@
return null;
}).when(service).inSurfaceTransaction(any());
+ sMockWindowManagerService = service;
return service;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
index 6b31e6f..198e7ce 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
@@ -63,6 +63,7 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
@@ -86,7 +87,7 @@
private StatusBarManagerInternal mPreviousStatusBarManagerInternal;
- private WindowManagerService mMockWm;
+ private static WindowManagerService sMockWm;
private DisplayContent mMockDisplayContent;
private DisplayPolicy mMockDisplayPolicy;
private Context mMockContext;
@@ -108,13 +109,16 @@
private DisplayRotation mTarget;
+ @BeforeClass
+ public static void setUpOnce() {
+ sMockWm = mock(WindowManagerService.class);
+ sMockWm.mPowerManagerInternal = mock(PowerManagerInternal.class);
+ }
+
@Before
public void setUp() {
FakeSettingsProvider.clearSettingsProvider();
- mMockWm = mock(WindowManagerService.class);
- mMockWm.mPowerManagerInternal = mock(PowerManagerInternal.class);
-
mPreviousStatusBarManagerInternal = LocalServices.getService(
StatusBarManagerInternal.class);
LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
@@ -452,7 +456,7 @@
mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90));
assertTrue(waitForUiHandler());
- verify(mMockWm).updateRotation(false, false);
+ verify(sMockWm).updateRotation(false, false);
}
@Test
@@ -833,8 +837,9 @@
.thenReturn(mFakeSettingsProvider.getIContentProvider());
mMockDisplayWindowSettings = mock(DisplayWindowSettings.class);
- mTarget = new DisplayRotation(mMockWm, mMockDisplayContent, mMockDisplayPolicy,
+ mTarget = new DisplayRotation(sMockWm, mMockDisplayContent, mMockDisplayPolicy,
mMockDisplayWindowSettings, mMockContext, new Object());
+ reset(sMockWm);
captureObservers();
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
index a9f150b..e24eb75 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
@@ -34,7 +34,12 @@
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.isNull;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
@@ -47,10 +52,6 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
import android.app.StatusBarManager;
import android.app.admin.DevicePolicyManager;
@@ -115,6 +116,7 @@
private LockTaskController mLockTaskController;
private Context mContext;
+ private String mPackageName;
private String mLockToAppSetting;
@Before
@@ -122,6 +124,7 @@
MockitoAnnotations.initMocks(this);
mContext = getInstrumentation().getTargetContext();
+ mPackageName = mContext.getPackageName();
mLockToAppSetting = Settings.Secure.getString(mContext.getContentResolver(),
Settings.Secure.LOCK_TO_APP_EXIT_LOCKED);
@@ -146,6 +149,7 @@
@After
public void tearDown() throws Exception {
+ mLockTaskController.setWindowManager(null);
Settings.Secure.putString(mContext.getContentResolver(),
Settings.Secure.LOCK_TO_APP_EXIT_LOCKED, mLockToAppSetting);
}
@@ -223,7 +227,7 @@
// THEN lock task mode should be started
verifyLockTaskStarted(STATUS_BAR_MASK_PINNED, DISABLE2_NONE);
// THEN screen pinning toast should be shown
- verify(mStatusBarService).showPinningEnterExitToast(true /* entering */);
+ verify(mStatusBarService).showPinningEnterExitToast(eq(true /* entering */));
}
@Test
@@ -390,9 +394,9 @@
// THEN lock task mode should have been finished
verifyLockTaskStopped(times(1));
// THEN the keyguard should be shown
- verify(mLockPatternUtils).requireCredentialEntry(UserHandle.USER_ALL);
+ verify(mLockPatternUtils).requireCredentialEntry(eq(UserHandle.USER_ALL));
// THEN screen pinning toast should be shown
- verify(mStatusBarService).showPinningEnterExitToast(false /* entering */);
+ verify(mStatusBarService).showPinningEnterExitToast(eq(false /* entering */));
}
@Test
@@ -509,9 +513,9 @@
& ~DISABLE_HOME;
int expectedFlags2 = DISABLE2_MASK;
verify(mStatusBarService).disable(eq(expectedFlags), any(IBinder.class),
- eq(mContext.getPackageName()));
+ eq(mPackageName));
verify(mStatusBarService).disable2(eq(expectedFlags2), any(IBinder.class),
- eq(mContext.getPackageName()));
+ eq(mPackageName));
// reset invocation counter
reset(mStatusBarService);
@@ -526,9 +530,9 @@
expectedFlags2 = DISABLE2_MASK
& ~DISABLE2_NOTIFICATION_SHADE;
verify(mStatusBarService).disable(eq(expectedFlags), any(IBinder.class),
- eq(mContext.getPackageName()));
+ eq(mPackageName));
verify(mStatusBarService).disable2(eq(expectedFlags2), any(IBinder.class),
- eq(mContext.getPackageName()));
+ eq(mPackageName));
}
@Test
@@ -548,9 +552,9 @@
// THEN status bar shouldn't change
verify(mStatusBarService, never()).disable(anyInt(), any(IBinder.class),
- eq(mContext.getPackageName()));
+ eq(mPackageName));
verify(mStatusBarService, never()).disable2(anyInt(), any(IBinder.class),
- eq(mContext.getPackageName()));
+ eq(mPackageName));
}
@Test
@@ -657,14 +661,14 @@
verify(mWindowManager).disableKeyguard(any(IBinder.class), anyString(), eq(TEST_USER_ID));
// THEN the status bar should have been disabled
verify(mStatusBarService).disable(eq(statusBarMask), any(IBinder.class),
- eq(mContext.getPackageName()));
+ eq(mPackageName));
verify(mStatusBarService).disable2(eq(statusBarMask2), any(IBinder.class),
- eq(mContext.getPackageName()));
+ eq(mPackageName));
// THEN recents should have been notified
verify(mRecentTasks).onLockTaskModeStateChanged(anyInt(), eq(TEST_USER_ID));
// THEN the DO/PO should be informed about the operation
- verify(mDevicePolicyManager).notifyLockTaskModeChanged(true, TEST_PACKAGE_NAME,
- TEST_USER_ID);
+ verify(mDevicePolicyManager).notifyLockTaskModeChanged(eq(true), eq(TEST_PACKAGE_NAME),
+ eq(TEST_USER_ID));
}
private void verifyLockTaskStopped(VerificationMode mode) throws Exception {
@@ -672,11 +676,12 @@
verify(mWindowManager, mode).reenableKeyguard(any(IBinder.class), eq(TEST_USER_ID));
// THEN the status bar should have been disabled
verify(mStatusBarService, mode).disable(eq(StatusBarManager.DISABLE_NONE),
- any(IBinder.class), eq(mContext.getPackageName()));
+ any(IBinder.class), eq(mPackageName));
verify(mStatusBarService, mode).disable2(eq(StatusBarManager.DISABLE2_NONE),
- any(IBinder.class), eq(mContext.getPackageName()));
+ any(IBinder.class), eq(mPackageName));
// THEN the DO/PO should be informed about the operation
- verify(mDevicePolicyManager, mode).notifyLockTaskModeChanged(false, null, TEST_USER_ID);
+ verify(mDevicePolicyManager, mode).notifyLockTaskModeChanged(eq(false), isNull(),
+ eq(TEST_USER_ID));
}
/**
diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index ad2a708..413b6f4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -143,8 +143,8 @@
@Test
public void testTimeout_scaled() throws Exception {
- mWm.setAnimationScale(2, 5.0f);
try {
+ mWm.setAnimationScale(2, 5.0f);
final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION,
"testWin");
final AnimationAdapter adapter = mController.createAnimationAdapter(win.mAppToken,
diff --git a/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java b/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java
index 36eccd1..03aba39 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java
@@ -33,6 +33,8 @@
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
@@ -58,7 +60,6 @@
import android.view.WindowManager;
import android.widget.TextView;
-import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import org.junit.After;
@@ -80,8 +81,8 @@
@Presubmit
public class ScreenDecorWindowTests {
- private final Context mContext = InstrumentationRegistry.getTargetContext();
- private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ private final Context mContext = getInstrumentation().getTargetContext();
+ private final Instrumentation mInstrumentation = getInstrumentation();
private WindowManager mWm;
private ArrayList<View> mWindows = new ArrayList<>();
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 7da85af..00bec3f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -21,6 +21,11 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.sameInstance;
@@ -133,17 +138,6 @@
assertTrue(task.returnsToHomeStack());
}
- /** Ensures that bounds are clipped to their parent. */
- @Test
- public void testAppBounds_BoundsClipping() {
- final Rect shiftedBounds = new Rect(mParentBounds);
- shiftedBounds.offset(10, 10);
- final Rect expectedBounds = new Rect(mParentBounds);
- expectedBounds.intersect(shiftedBounds);
- testStackBoundsConfiguration(WINDOWING_MODE_FULLSCREEN, mParentBounds, shiftedBounds,
- expectedBounds);
- }
-
/** Ensures that empty bounds are not propagated to the configuration. */
@Test
public void testAppBounds_EmptyBounds() {
@@ -167,18 +161,108 @@
final Rect insetBounds = new Rect(mParentBounds);
insetBounds.inset(5, 5, 5, 5);
testStackBoundsConfiguration(
- WINDOWING_MODE_FULLSCREEN, mParentBounds, insetBounds, insetBounds);
+ WINDOWING_MODE_FREEFORM, mParentBounds, insetBounds, insetBounds);
}
- /** Ensures that full screen free form bounds are clipped */
+ /** Tests that the task bounds adjust properly to changes between FULLSCREEN and FREEFORM */
@Test
- public void testAppBounds_FullScreenFreeFormBounds() {
+ public void testBoundsOnModeChangeFreeformToFullscreen() {
ActivityDisplay display = mService.mRootActivityContainer.getDefaultDisplay();
+ ActivityStack stack = new StackBuilder(mRootActivityContainer).setDisplay(display)
+ .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
+ TaskRecord task = stack.getChildAt(0);
+ task.getRootActivity().mAppWindowToken.setOrientation(SCREEN_ORIENTATION_UNSPECIFIED);
DisplayInfo info = new DisplayInfo();
display.mDisplay.getDisplayInfo(info);
final Rect fullScreenBounds = new Rect(0, 0, info.logicalWidth, info.logicalHeight);
- testStackBoundsConfiguration(WINDOWING_MODE_FULLSCREEN, mParentBounds, fullScreenBounds,
- mParentBounds);
+ final Rect freeformBounds = new Rect(fullScreenBounds);
+ freeformBounds.inset((int) (freeformBounds.width() * 0.2),
+ (int) (freeformBounds.height() * 0.2));
+ task.setBounds(freeformBounds);
+
+ assertEquals(freeformBounds, task.getBounds());
+
+ // FULLSCREEN inherits bounds
+ stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ assertEquals(fullScreenBounds, task.getBounds());
+ assertEquals(freeformBounds, task.mLastNonFullscreenBounds);
+
+ // FREEFORM restores bounds
+ stack.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ assertEquals(freeformBounds, task.getBounds());
+ }
+
+ /**
+ * This is a temporary hack to trigger an onConfigurationChange at the task level after an
+ * orientation is requested. Normally this is done by the onDescendentOrientationChanged call
+ * up the WM hierarchy, but since the WM hierarchy is mocked out, it doesn't happen here.
+ * TODO: remove this when we either get a WM hierarchy or when hierarchies are merged.
+ */
+ private void setActivityRequestedOrientation(ActivityRecord activity, int orientation) {
+ activity.setRequestedOrientation(orientation);
+ ConfigurationContainer taskRecord = activity.getParent();
+ taskRecord.onConfigurationChanged(taskRecord.getParent().getConfiguration());
+ }
+
+ /**
+ * Tests that a task with forced orientation has orientation-consistent bounds within the
+ * parent.
+ */
+ @Test
+ public void testFullscreenBoundsForcedOrientation() {
+ final Rect fullScreenBounds = new Rect(0, 0, 1920, 1080);
+ final Rect fullScreenBoundsPort = new Rect(0, 0, 1080, 1920);
+ DisplayInfo info = new DisplayInfo();
+ info.logicalWidth = fullScreenBounds.width();
+ info.logicalHeight = fullScreenBounds.height();
+ ActivityDisplay display = addNewActivityDisplayAt(info, POSITION_TOP);
+ assertTrue(mRootActivityContainer.getActivityDisplay(display.mDisplayId) != null);
+ ActivityStack stack = new StackBuilder(mRootActivityContainer)
+ .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build();
+ TaskRecord task = stack.getChildAt(0);
+ ActivityRecord root = task.getRootActivity();
+ ActivityRecord top = new ActivityBuilder(mService).setTask(task).setStack(stack).build();
+ assertEquals(root, task.getRootActivity());
+
+ assertEquals(fullScreenBounds, task.getBounds());
+
+ // Setting app to fixed portrait fits within parent
+ setActivityRequestedOrientation(root, SCREEN_ORIENTATION_PORTRAIT);
+ assertEquals(root, task.getRootActivity());
+ assertEquals(SCREEN_ORIENTATION_PORTRAIT, task.getRootActivity().getOrientation());
+ assertTrue(task.getBounds().width() < task.getBounds().height());
+ assertEquals(fullScreenBounds.height(), task.getBounds().height());
+
+ // Setting non-root app has no effect
+ setActivityRequestedOrientation(root, SCREEN_ORIENTATION_LANDSCAPE);
+ assertTrue(task.getBounds().width() < task.getBounds().height());
+
+ // Setting app to unspecified restores
+ setActivityRequestedOrientation(root, SCREEN_ORIENTATION_UNSPECIFIED);
+ assertEquals(fullScreenBounds, task.getBounds());
+
+ // Setting app to fixed landscape and changing display
+ setActivityRequestedOrientation(root, SCREEN_ORIENTATION_LANDSCAPE);
+ display.setBounds(fullScreenBoundsPort);
+ assertTrue(task.getBounds().width() > task.getBounds().height());
+ assertEquals(fullScreenBoundsPort.width(), task.getBounds().width());
+
+ // in FREEFORM, no constraint
+ final Rect freeformBounds = new Rect(display.getBounds());
+ freeformBounds.inset((int) (freeformBounds.width() * 0.2),
+ (int) (freeformBounds.height() * 0.2));
+ stack.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ task.setBounds(freeformBounds);
+ assertEquals(freeformBounds, task.getBounds());
+
+ // FULLSCREEN letterboxes bounds
+ stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ assertTrue(task.getBounds().width() > task.getBounds().height());
+ assertEquals(fullScreenBoundsPort.width(), task.getBounds().width());
+
+ // FREEFORM restores bounds as before
+ stack.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ assertEquals(freeformBounds, task.getBounds());
}
/** Ensures that the alias intent won't have target component resolved. */
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
index 624ef9b..ca815ec 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
@@ -39,6 +39,7 @@
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.view.Surface;
+import android.view.SurfaceControl;
import androidx.test.filters.SmallTest;
@@ -65,7 +66,7 @@
final TaskSnapshot snapshot = new TaskSnapshot(new ComponentName("", ""), buffer,
ORIENTATION_PORTRAIT, contentInsets, false, 1.0f, true /* isRealSnapshot */,
WINDOWING_MODE_FULLSCREEN, 0 /* systemUiVisibility */, false /* isTranslucent */);
- mSurface = new TaskSnapshotSurface(mWm, new Window(), new Surface(), snapshot, "Test",
+ mSurface = new TaskSnapshotSurface(mWm, new Window(), new SurfaceControl(), snapshot, "Test",
Color.WHITE, Color.RED, Color.BLUE, sysuiVis, windowFlags, 0, taskBounds,
ORIENTATION_PORTRAIT);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java
deleted file mode 100644
index 04e433e..0000000
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRule.java
+++ /dev/null
@@ -1,223 +0,0 @@
-/*
- * 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.wm;
-
-import static android.testing.DexmakerShareClassLoaderRule.runWithDexmakerShareClassLoader;
-import static android.view.Display.DEFAULT_DISPLAY;
-
-import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.when;
-
-import android.app.ActivityManagerInternal;
-import android.content.Context;
-import android.hardware.display.DisplayManagerInternal;
-import android.os.PowerManagerInternal;
-import android.os.PowerSaveState;
-import android.view.Display;
-import android.view.InputChannel;
-import android.view.SurfaceControl;
-import android.view.SurfaceControl.Transaction;
-
-import com.android.server.LocalServices;
-import com.android.server.input.InputManagerService;
-import com.android.server.policy.WindowManagerPolicy;
-
-import org.junit.rules.TestRule;
-import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
-import org.mockito.invocation.InvocationOnMock;
-
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * A test rule that sets up a fresh WindowManagerService instance before each test and makes sure
- * to properly tear it down after.
- *
- * <p>
- * Usage:
- * <pre>
- * {@literal @}Rule
- * public final WindowManagerServiceRule mWmRule = new WindowManagerServiceRule();
- * </pre>
- */
-public class WindowManagerServiceRule implements TestRule {
-
- private WindowManagerService mService;
- private TestWindowManagerPolicy mPolicy;
- // Record all {@link SurfaceControl.Transaction} created while testing and releases native
- // resources when test finishes.
- private final List<WeakReference<Transaction>> mSurfaceTransactions = new ArrayList<>();
- // Record all {@link SurfaceControl} created while testing and releases native resources when
- // test finishes.
- private final List<WeakReference<SurfaceControl>> mSurfaceControls = new ArrayList<>();
-
- @Override
- public Statement apply(Statement base, Description description) {
- return new Statement() {
- @Override
- public void evaluate() throws Throwable {
- runWithDexmakerShareClassLoader(this::setUp);
- try {
- base.evaluate();
- } finally {
- tearDown();
- }
- }
-
- private void setUp() {
- final Context context = getInstrumentation().getTargetContext();
-
- removeServices();
-
- LocalServices.addService(DisplayManagerInternal.class,
- mock(DisplayManagerInternal.class));
-
- LocalServices.addService(PowerManagerInternal.class,
- mock(PowerManagerInternal.class));
- final PowerManagerInternal pm =
- LocalServices.getService(PowerManagerInternal.class);
- doNothing().when(pm).registerLowPowerModeObserver(any());
- PowerSaveState state = new PowerSaveState.Builder().build();
- doReturn(state).when(pm).getLowPowerState(anyInt());
-
- LocalServices.addService(ActivityManagerInternal.class,
- mock(ActivityManagerInternal.class));
- LocalServices.addService(ActivityTaskManagerInternal.class,
- mock(ActivityTaskManagerInternal.class));
- final ActivityTaskManagerInternal atm =
- LocalServices.getService(ActivityTaskManagerInternal.class);
- doAnswer((InvocationOnMock invocationOnMock) -> {
- final Runnable runnable = invocationOnMock.<Runnable>getArgument(0);
- if (runnable != null) {
- runnable.run();
- }
- return null;
- }).when(atm).notifyKeyguardFlagsChanged(any(), anyInt());
-
- InputManagerService ims = mock(InputManagerService.class);
- // InputChannel is final and can't be mocked.
- InputChannel[] input = InputChannel.openInputChannelPair(TAG_WM);
- if (input != null && input.length > 1) {
- doReturn(input[1]).when(ims).monitorInput(anyString(), anyInt());
- }
- ActivityTaskManagerService atms = mock(ActivityTaskManagerService.class);
- when(atms.getGlobalLock()).thenReturn(new WindowManagerGlobalLock());
-
- mService = WindowManagerService.main(context, ims, false, false,
- mPolicy = new TestWindowManagerPolicy(
- WindowManagerServiceRule.this::getWindowManagerService), atms);
- mService.mTransactionFactory = () -> {
- final SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
- mSurfaceTransactions.add(new WeakReference<>(transaction));
- return transaction;
- };
- mService.mSurfaceBuilderFactory = session -> new SurfaceControl.Builder(session) {
- @Override
- public SurfaceControl build() {
- final SurfaceControl control = super.build();
- mSurfaceControls.add(new WeakReference<>(control));
- return control;
- }
- };
-
- mService.onInitReady();
-
- final Display display = mService.mDisplayManager.getDisplay(DEFAULT_DISPLAY);
- // Display creation is driven by the ActivityManagerService via
- // ActivityStackSupervisor. We emulate those steps here.
- mService.mRoot.createDisplayContent(display, mock(ActivityDisplay.class));
- }
-
- private void removeServices() {
- LocalServices.removeServiceForTest(DisplayManagerInternal.class);
- LocalServices.removeServiceForTest(PowerManagerInternal.class);
- LocalServices.removeServiceForTest(ActivityManagerInternal.class);
- LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class);
- LocalServices.removeServiceForTest(WindowManagerInternal.class);
- LocalServices.removeServiceForTest(WindowManagerPolicy.class);
- }
-
- private void tearDown() {
- cancelAllPendingAnimations();
- waitUntilWindowManagerHandlersIdle();
- destroyAllSurfaceTransactions();
- destroyAllSurfaceControls();
- removeServices();
- mService = null;
- mPolicy = null;
- }
- };
- }
-
- WindowManagerService getWindowManagerService() {
- return mService;
- }
-
- private void cancelAllPendingAnimations() {
- for (final WeakReference<SurfaceControl> reference : mSurfaceControls) {
- final SurfaceControl sc = reference.get();
- if (sc != null) {
- mService.mSurfaceAnimationRunner.onAnimationCancelled(sc);
- }
- }
- }
-
- void waitUntilWindowManagerHandlersIdle() {
- final WindowManagerService wm = getWindowManagerService();
- if (wm == null) {
- return;
- }
- wm.mH.removeCallbacksAndMessages(null);
- wm.mAnimationHandler.removeCallbacksAndMessages(null);
- SurfaceAnimationThread.getHandler().removeCallbacksAndMessages(null);
- wm.mH.runWithScissors(() -> { }, 0);
- wm.mAnimationHandler.runWithScissors(() -> { }, 0);
- SurfaceAnimationThread.getHandler().runWithScissors(() -> { }, 0);
- }
-
- private void destroyAllSurfaceTransactions() {
- for (final WeakReference<Transaction> reference : mSurfaceTransactions) {
- final Transaction transaction = reference.get();
- if (transaction != null) {
- reference.clear();
- transaction.close();
- }
- }
- }
-
- private void destroyAllSurfaceControls() {
- for (final WeakReference<SurfaceControl> reference : mSurfaceControls) {
- final SurfaceControl control = reference.get();
- if (control != null) {
- reference.clear();
- control.destroy();
- }
- }
- }
-}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRuleTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRuleTest.java
deleted file mode 100644
index 343d359..0000000
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceRuleTest.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * 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.wm;
-
-import static org.hamcrest.CoreMatchers.notNullValue;
-import static org.junit.Assert.assertThat;
-
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.filters.SmallTest;
-
-import org.junit.Rule;
-import org.junit.Test;
-
-/**
- * Build/InstallRun:
- * atest FrameworksServicesTests:WindowManagerServiceRuleTest
- */
-@Presubmit
-@SmallTest
-public class WindowManagerServiceRuleTest {
-
- @Rule
- public final WindowManagerServiceRule mRule = new WindowManagerServiceRule();
-
- @Test
- public void testWindowManagerSetUp() {
- assertThat(mRule.getWindowManagerService(), notNullValue());
- }
-}
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 5c3368b..638cb03 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -53,6 +53,7 @@
import com.android.server.AttributeCache;
import org.junit.After;
+import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
@@ -95,17 +96,22 @@
public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
new DexmakerShareClassLoaderRule();
- @Rule
- public final WindowManagerServiceRule mWmRule = new WindowManagerServiceRule();
-
- static WindowState.PowerManagerWrapper sPowerManagerWrapper; // TODO(roosa): make non-static.
+ static WindowState.PowerManagerWrapper sPowerManagerWrapper;
@BeforeClass
public static void setUpOnceBase() {
AttributeCache.init(getInstrumentation().getTargetContext());
+
+ WmServiceUtils.setUpWindowManagerService();
+
sPowerManagerWrapper = mock(WindowState.PowerManagerWrapper.class);
}
+ @AfterClass
+ public static void tearDonwOnceBase() {
+ WmServiceUtils.tearDownWindowManagerService();
+ }
+
@Before
public void setUpBase() {
// If @Before throws an exception, the error isn't logged. This will make sure any failures
@@ -115,7 +121,7 @@
final Context context = getInstrumentation().getTargetContext();
- mWm = mWmRule.getWindowManagerService();
+ mWm = WmServiceUtils.getWindowManagerService();
beforeCreateDisplay();
context.getDisplay().getDisplayInfo(mDisplayInfo);
@@ -192,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;
@@ -214,7 +220,7 @@
* Waits until the main handler for WM has processed all messages.
*/
void waitUntilHandlersIdle() {
- mWmRule.waitUntilWindowManagerHandlersIdle();
+ WmServiceUtils.waitUntilWindowManagerHandlersIdle();
}
private WindowToken createWindowToken(
diff --git a/services/tests/wmtests/src/com/android/server/wm/WmServiceUtils.java b/services/tests/wmtests/src/com/android/server/wm/WmServiceUtils.java
new file mode 100644
index 0000000..05ac8c1
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/WmServiceUtils.java
@@ -0,0 +1,214 @@
+/*
+ * 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.wm;
+
+import static android.testing.DexmakerShareClassLoaderRule.runWithDexmakerShareClassLoader;
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.nullable;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
+import android.app.ActivityManagerInternal;
+import android.app.AppOpsManager;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.IntentFilter;
+import android.database.ContentObserver;
+import android.hardware.display.DisplayManagerInternal;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.PowerManagerInternal;
+import android.os.PowerSaveState;
+import android.os.UserHandle;
+import android.view.Display;
+import android.view.InputChannel;
+
+import com.android.dx.mockito.inline.extended.StaticMockitoSession;
+import com.android.server.LocalServices;
+import com.android.server.LockGuard;
+import com.android.server.Watchdog;
+import com.android.server.input.InputManagerService;
+import com.android.server.policy.WindowManagerPolicy;
+
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.quality.Strictness;
+
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * A Test utility class to create a mock {@link WindowManagerService} instance for tests.
+ */
+class WmServiceUtils {
+ private static StaticMockitoSession sMockitoSession;
+ private static WindowManagerService sService;
+ private static TestWindowManagerPolicy sPolicy;
+
+ static void setUpWindowManagerService() {
+ sMockitoSession = mockitoSession()
+ .spyStatic(LockGuard.class)
+ .spyStatic(Watchdog.class)
+ .strictness(Strictness.LENIENT)
+ .startMocking();
+
+ runWithDexmakerShareClassLoader(WmServiceUtils::setUpTestWindowService);
+ }
+
+ static void tearDownWindowManagerService() {
+ waitUntilWindowManagerHandlersIdle();
+ removeLocalServices();
+ sService = null;
+ sPolicy = null;
+
+ sMockitoSession.finishMocking();
+ }
+
+ private static void setUpTestWindowService() {
+ doReturn(null).when(() -> LockGuard.installLock(any(), anyInt()));
+ doReturn(mock(Watchdog.class)).when(Watchdog::getInstance);
+
+ final Context context = getInstrumentation().getTargetContext();
+ spyOn(context);
+
+ doReturn(null).when(context)
+ .registerReceiver(nullable(BroadcastReceiver.class), any(IntentFilter.class));
+ doReturn(null).when(context)
+ .registerReceiverAsUser(any(BroadcastReceiver.class), any(UserHandle.class),
+ any(IntentFilter.class), nullable(String.class), nullable(Handler.class));
+
+ final ContentResolver contentResolver = context.getContentResolver();
+ spyOn(contentResolver);
+ doNothing().when(contentResolver)
+ .registerContentObserver(any(Uri.class), anyBoolean(), any(ContentObserver.class),
+ anyInt());
+
+ final AppOpsManager appOpsManager = mock(AppOpsManager.class);
+ doReturn(appOpsManager).when(context)
+ .getSystemService(eq(Context.APP_OPS_SERVICE));
+
+ removeLocalServices();
+
+ final DisplayManagerInternal dmi = mock(DisplayManagerInternal.class);
+ LocalServices.addService(DisplayManagerInternal.class, dmi);
+
+ final PowerManagerInternal pmi = mock(PowerManagerInternal.class);
+ LocalServices.addService(PowerManagerInternal.class, pmi);
+ final PowerSaveState state = new PowerSaveState.Builder().build();
+ doReturn(state).when(pmi).getLowPowerState(anyInt());
+
+ final ActivityManagerInternal ami = mock(ActivityManagerInternal.class);
+ LocalServices.addService(ActivityManagerInternal.class, ami);
+
+ final ActivityTaskManagerInternal atmi = mock(ActivityTaskManagerInternal.class);
+ LocalServices.addService(ActivityTaskManagerInternal.class, atmi);
+ doAnswer((InvocationOnMock invocationOnMock) -> {
+ final Runnable runnable = invocationOnMock.getArgument(0);
+ if (runnable != null) {
+ runnable.run();
+ }
+ return null;
+ }).when(atmi).notifyKeyguardFlagsChanged(nullable(Runnable.class), anyInt());
+
+ final InputManagerService ims = mock(InputManagerService.class);
+ // InputChannel is final and can't be mocked.
+ final InputChannel[] input = InputChannel.openInputChannelPair(TAG_WM);
+ if (input != null && input.length > 1) {
+ doReturn(input[1]).when(ims).monitorInput(anyString(), anyInt());
+ }
+
+ final ActivityTaskManagerService atms = mock(ActivityTaskManagerService.class);
+ final WindowManagerGlobalLock wmLock = new WindowManagerGlobalLock();
+ doReturn(wmLock).when(atms).getGlobalLock();
+
+ sPolicy = new TestWindowManagerPolicy(WmServiceUtils::getWindowManagerService);
+ sService = WindowManagerService.main(context, ims, false, false, sPolicy, atms);
+
+ sService.onInitReady();
+
+ final Display display = sService.mDisplayManager.getDisplay(DEFAULT_DISPLAY);
+ // Display creation is driven by the ActivityManagerService via
+ // ActivityStackSupervisor. We emulate those steps here.
+ sService.mRoot.createDisplayContent(display, mock(ActivityDisplay.class));
+ }
+
+ private static void removeLocalServices() {
+ LocalServices.removeServiceForTest(DisplayManagerInternal.class);
+ LocalServices.removeServiceForTest(PowerManagerInternal.class);
+ LocalServices.removeServiceForTest(ActivityManagerInternal.class);
+ LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class);
+ LocalServices.removeServiceForTest(WindowManagerInternal.class);
+ LocalServices.removeServiceForTest(WindowManagerPolicy.class);
+ }
+
+ static WindowManagerService getWindowManagerService() {
+ 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) {
+ return;
+ }
+ // Removing delayed FORCE_GC message decreases time for waiting idle.
+ wm.mH.removeMessages(WindowManagerService.H.FORCE_GC);
+ waitHandlerIdle(wm.mH);
+ waitHandlerIdle(wm.mAnimationHandler);
+ waitHandlerIdle(SurfaceAnimationThread.getHandler());
+ }
+
+ private static void waitHandlerIdle(Handler handler) {
+ if (!handler.hasMessagesOrCallbacks()) {
+ return;
+ }
+ final CountDownLatch latch = new CountDownLatch(1);
+ // Wait for delayed messages are processed.
+ handler.getLooper().getQueue().addIdleHandler(() -> {
+ if (handler.hasMessagesOrCallbacks()) {
+ return true; // keep idle handler.
+ }
+ latch.countDown();
+ return false; // remove idle handler.
+ });
+ try {
+ latch.await();
+ } catch (InterruptedException e) {
+ }
+ }
+}
diff --git a/services/usage/java/com/android/server/usage/AppTimeLimitController.java b/services/usage/java/com/android/server/usage/AppTimeLimitController.java
index 8e1ede1..2ed11fe 100644
--- a/services/usage/java/com/android/server/usage/AppTimeLimitController.java
+++ b/services/usage/java/com/android/server/usage/AppTimeLimitController.java
@@ -23,7 +23,6 @@
import android.os.Message;
import android.os.SystemClock;
import android.util.ArrayMap;
-import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
@@ -62,6 +61,8 @@
private static final long ONE_MINUTE = 60_000L;
+ private static final Integer ONE = new Integer(1);
+
/** Collection of data for each user that has reported usage */
@GuardedBy("mLock")
private final SparseArray<UserData> mUsers = new SparseArray<>();
@@ -79,11 +80,11 @@
private @UserIdInt
int userId;
- /** Set of the currently active entities */
- private final ArraySet<String> currentlyActive = new ArraySet<>();
+ /** Count of the currently active entities */
+ public final ArrayMap<String, Integer> currentlyActive = new ArrayMap<>();
/** Map from entity name for quick lookup */
- private final ArrayMap<String, ArrayList<UsageGroup>> observedMap = new ArrayMap<>();
+ public final ArrayMap<String, ArrayList<UsageGroup>> observedMap = new ArrayMap<>();
private UserData(@UserIdInt int userId) {
this.userId = userId;
@@ -94,7 +95,7 @@
// TODO: Consider using a bloom filter here if number of actives becomes large
final int size = entities.length;
for (int i = 0; i < size; i++) {
- if (currentlyActive.contains(entities[i])) {
+ if (currentlyActive.containsKey(entities[i])) {
return true;
}
}
@@ -137,7 +138,7 @@
pw.print(" Currently Active:");
final int nActive = currentlyActive.size();
for (int i = 0; i < nActive; i++) {
- pw.print(currentlyActive.valueAt(i));
+ pw.print(currentlyActive.keyAt(i));
pw.print(", ");
}
pw.println();
@@ -233,6 +234,7 @@
protected long mUsageTimeMs;
protected int mActives;
protected long mLastKnownUsageTimeMs;
+ protected long mLastUsageEndTimeMs;
protected WeakReference<UserData> mUserRef;
protected WeakReference<ObserverAppData> mObserverAppRef;
protected PendingIntent mLimitReachedCallback;
@@ -271,9 +273,15 @@
@GuardedBy("mLock")
void noteUsageStart(long startTimeMs, long currentTimeMs) {
if (mActives++ == 0) {
+ // If last known usage ended after the start of this usage, there is overlap
+ // between the last usage session and this one. Avoid double counting by only
+ // counting from the end of the last session. This has a rare side effect that some
+ // usage will not be accounted for if the previous session started and stopped
+ // within this current usage.
+ startTimeMs = mLastUsageEndTimeMs > startTimeMs ? mLastUsageEndTimeMs : startTimeMs;
mLastKnownUsageTimeMs = startTimeMs;
final long timeRemaining =
- mTimeLimitMs - mUsageTimeMs + currentTimeMs - startTimeMs;
+ mTimeLimitMs - mUsageTimeMs - currentTimeMs + startTimeMs;
if (timeRemaining > 0) {
if (DEBUG) {
Slog.d(TAG, "Posting timeout for " + mObserverId + " for "
@@ -287,7 +295,7 @@
mActives = mObserved.length;
final UserData user = mUserRef.get();
if (user == null) return;
- final Object[] array = user.currentlyActive.toArray();
+ final Object[] array = user.currentlyActive.keySet().toArray();
Slog.e(TAG,
"Too many noted usage starts! Observed entities: " + Arrays.toString(
mObserved) + " Active Entities: " + Arrays.toString(array));
@@ -300,6 +308,8 @@
if (--mActives == 0) {
final boolean limitNotCrossed = mUsageTimeMs < mTimeLimitMs;
mUsageTimeMs += stopTimeMs - mLastKnownUsageTimeMs;
+
+ mLastUsageEndTimeMs = stopTimeMs;
if (limitNotCrossed && mUsageTimeMs >= mTimeLimitMs) {
// Crossed the limit
if (DEBUG) Slog.d(TAG, "MTB informing group obs=" + mObserverId);
@@ -312,7 +322,7 @@
mActives = 0;
final UserData user = mUserRef.get();
if (user == null) return;
- final Object[] array = user.currentlyActive.toArray();
+ final Object[] array = user.currentlyActive.keySet().toArray();
Slog.e(TAG,
"Too many noted usage stops! Observed entities: " + Arrays.toString(
mObserved) + " Active Entities: " + Arrays.toString(array));
@@ -409,7 +419,6 @@
}
class SessionUsageGroup extends UsageGroup {
- private long mLastUsageEndTimeMs;
private long mNewSessionThresholdMs;
private PendingIntent mSessionEndCallback;
@@ -451,7 +460,6 @@
public void noteUsageStop(long stopTimeMs) {
super.noteUsageStop(stopTimeMs);
if (mActives == 0) {
- mLastUsageEndTimeMs = stopTimeMs;
if (mUsageTimeMs >= mTimeLimitMs) {
// Usage has ended. Schedule the session end callback to be triggered once
// the new session threshold has been reached
@@ -467,7 +475,10 @@
UserData user = mUserRef.get();
if (user == null) return;
if (mListener != null) {
- mListener.onSessionEnd(mObserverId, user.userId, mUsageTimeMs, mSessionEndCallback);
+ mListener.onSessionEnd(mObserverId,
+ user.userId,
+ mUsageTimeMs,
+ mSessionEndCallback);
}
}
@@ -599,7 +610,7 @@
// TODO: Consider using a bloom filter here if number of actives becomes large
final int size = group.mObserved.length;
for (int i = 0; i < size; i++) {
- if (user.currentlyActive.contains(group.mObserved[i])) {
+ if (user.currentlyActive.containsKey(group.mObserved[i])) {
// Entity is currently active. Start group's usage.
group.noteUsageStart(currentTimeMs);
}
@@ -717,21 +728,28 @@
/**
* Called when an entity becomes active.
*
- * @param name The entity that became active
- * @param userId The user
+ * @param name The entity that became active
+ * @param userId The user
+ * @param timeAgoMs Time since usage was started
*/
- public void noteUsageStart(String name, int userId) throws IllegalArgumentException {
+ public void noteUsageStart(String name, int userId, long timeAgoMs)
+ throws IllegalArgumentException {
synchronized (mLock) {
UserData user = getOrCreateUserDataLocked(userId);
if (DEBUG) Slog.d(TAG, "Usage entity " + name + " became active");
- if (user.currentlyActive.contains(name)) {
- throw new IllegalArgumentException(
- "Unable to start usage for " + name + ", already in use");
+
+ final int index = user.currentlyActive.indexOfKey(name);
+ if (index >= 0) {
+ final Integer count = user.currentlyActive.valueAt(index);
+ if (count != null) {
+ // There are multiple instances of this entity. Just increment the count.
+ user.currentlyActive.setValueAt(index, count + 1);
+ return;
+ }
}
final long currentTime = getUptimeMillis();
- // Add to the list of active entities
- user.currentlyActive.add(name);
+ user.currentlyActive.put(name, ONE);
ArrayList<UsageGroup> groups = user.observedMap.get(name);
if (groups == null) return;
@@ -739,12 +757,22 @@
final int size = groups.size();
for (int i = 0; i < size; i++) {
UsageGroup group = groups.get(i);
- group.noteUsageStart(currentTime);
+ group.noteUsageStart(currentTime - timeAgoMs, currentTime);
}
}
}
/**
+ * Called when an entity becomes active.
+ *
+ * @param name The entity that became active
+ * @param userId The user
+ */
+ public void noteUsageStart(String name, int userId) throws IllegalArgumentException {
+ noteUsageStart(name, userId, 0);
+ }
+
+ /**
* Called when an entity becomes inactive.
*
* @param name The entity that became inactive
@@ -754,10 +782,21 @@
synchronized (mLock) {
UserData user = getOrCreateUserDataLocked(userId);
if (DEBUG) Slog.d(TAG, "Usage entity " + name + " became inactive");
- if (!user.currentlyActive.remove(name)) {
+
+ final int index = user.currentlyActive.indexOfKey(name);
+ if (index < 0) {
throw new IllegalArgumentException(
"Unable to stop usage for " + name + ", not in use");
}
+
+ final Integer count = user.currentlyActive.valueAt(index);
+ if (!count.equals(ONE)) {
+ // There are multiple instances of this entity. Just decrement the count.
+ user.currentlyActive.setValueAt(index, count - 1);
+ return;
+ }
+
+ user.currentlyActive.removeAt(index);
final long currentTime = getUptimeMillis();
// Check if any of the groups need to watch for this entity
@@ -769,6 +808,7 @@
UsageGroup group = groups.get(i);
group.noteUsageStop(currentTime);
}
+
}
}
@@ -780,7 +820,8 @@
@GuardedBy("mLock")
private void postInformSessionEndListenerLocked(SessionUsageGroup group, long timeout) {
- mHandler.sendMessageDelayed(mHandler.obtainMessage(MyHandler.MSG_INFORM_SESSION_END, group),
+ mHandler.sendMessageDelayed(
+ mHandler.obtainMessage(MyHandler.MSG_INFORM_SESSION_END, group),
timeout);
}
@@ -800,7 +841,27 @@
mHandler.removeMessages(MyHandler.MSG_CHECK_TIMEOUT, group);
}
- void dump(PrintWriter pw) {
+ void dump(String[] args, PrintWriter pw) {
+ if (args != null) {
+ for (int i = 0; i < args.length; i++) {
+ String arg = args[i];
+ if ("actives".equals(arg)) {
+ synchronized (mLock) {
+ final int nUsers = mUsers.size();
+ for (int user = 0; user < nUsers; user++) {
+ final ArrayMap<String, Integer> actives =
+ mUsers.valueAt(user).currentlyActive;
+ final int nActive = actives.size();
+ for (int active = 0; active < nActive; active++) {
+ pw.println(actives.keyAt(active));
+ }
+ }
+ }
+ return;
+ }
+ }
+ }
+
synchronized (mLock) {
pw.println("\n App Time Limits");
final int nUsers = mUsers.size();
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 57dc08f..f146370 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -53,6 +53,7 @@
import android.os.Environment;
import android.os.FileUtils;
import android.os.Handler;
+import android.os.IBinder;
import android.os.IDeviceIdleController;
import android.os.Looper;
import android.os.Message;
@@ -105,6 +106,8 @@
private static final boolean ENABLE_KERNEL_UPDATES = true;
private static final File KERNEL_COUNTER_FILE = new File("/proc/uid_procstat/set");
+ private static final char TOKEN_DELIMITER = '/';
+
// Handler message types.
static final int MSG_REPORT_EVENT = 0;
static final int MSG_FLUSH_TO_DISK = 1;
@@ -135,6 +138,10 @@
/** Manages app time limit observers */
AppTimeLimitController mAppTimeLimit;
+ final SparseArray<ArraySet<String>> mUsageReporters = new SparseArray();
+ final SparseArray<String> mVisibleActivities = new SparseArray();
+
+
private UsageStatsManagerInternal.AppIdleStateChangeListener mStandbyChangeListener =
new UsageStatsManagerInternal.AppIdleStateChangeListener() {
@Override
@@ -270,7 +277,7 @@
mHandler.obtainMessage(MSG_REMOVE_USER, userId, 0).sendToTarget();
}
} else if (Intent.ACTION_USER_STARTED.equals(action)) {
- if (userId >=0) {
+ if (userId >= 0) {
mAppStandby.postCheckIdleStates(userId);
}
}
@@ -434,17 +441,46 @@
mAppStandby.reportEvent(event, elapsedRealtime, userId);
switch (event.mEventType) {
case Event.ACTIVITY_RESUMED:
- try {
- mAppTimeLimit.noteUsageStart(event.getPackageName(), userId);
- } catch (IllegalArgumentException iae) {
- Slog.e(TAG, "Failed to note usage start", iae);
+ synchronized (mVisibleActivities) {
+ mVisibleActivities.put(event.mInstanceId, event.getClassName());
+ try {
+ mAppTimeLimit.noteUsageStart(event.getPackageName(), userId);
+ } catch (IllegalArgumentException iae) {
+ Slog.e(TAG, "Failed to note usage start", iae);
+ }
}
break;
- case Event.ACTIVITY_PAUSED:
- try {
- mAppTimeLimit.noteUsageStop(event.getPackageName(), userId);
- } catch (IllegalArgumentException iae) {
- Slog.e(TAG, "Failed to note usage stop", iae);
+ case Event.ACTIVITY_STOPPED:
+ case Event.ACTIVITY_DESTROYED:
+ ArraySet<String> tokens;
+ synchronized (mUsageReporters) {
+ tokens = mUsageReporters.removeReturnOld(event.mInstanceId);
+ }
+ if (tokens != null) {
+ synchronized (tokens) {
+ final int size = tokens.size();
+ // Stop usage on behalf of a UsageReporter that stopped
+ for (int i = 0; i < size; i++) {
+ final String token = tokens.valueAt(i);
+ try {
+ mAppTimeLimit.noteUsageStop(
+ buildFullToken(event.getPackageName(), token), userId);
+ } catch (IllegalArgumentException iae) {
+ Slog.w(TAG, "Failed to stop usage for during reporter death: "
+ + iae);
+ }
+ }
+ }
+ }
+
+ synchronized (mVisibleActivities) {
+ if (mVisibleActivities.removeReturnOld(event.mInstanceId) != null) {
+ try {
+ mAppTimeLimit.noteUsageStop(event.getPackageName(), userId);
+ } catch (IllegalArgumentException iae) {
+ Slog.w(TAG, "Failed to note usage stop", iae);
+ }
+ }
}
break;
}
@@ -599,6 +635,14 @@
return beginTime <= currentTime && beginTime < endTime;
}
+ private String buildFullToken(String packageName, String token) {
+ final StringBuilder sb = new StringBuilder(packageName.length() + token.length() + 1);
+ sb.append(packageName);
+ sb.append(TOKEN_DELIMITER);
+ sb.append(token);
+ return sb.toString();
+ }
+
private void flushToDiskLocked() {
final int userCount = mUserState.size();
for (int i = 0; i < userCount; i++) {
@@ -627,8 +671,7 @@
String arg = args[i];
if ("--checkin".equals(arg)) {
checkin = true;
- } else
- if ("-c".equals(arg)) {
+ } else if ("-c".equals(arg)) {
compact = true;
} else if ("flush".equals(arg)) {
flushToDiskLocked();
@@ -637,6 +680,15 @@
} else if ("is-app-standby-enabled".equals(arg)) {
pw.println(mAppStandby.mAppIdleEnabled);
return;
+ } else if ("apptimelimit".equals(arg)) {
+ if (i + 1 >= args.length) {
+ mAppTimeLimit.dump(null, pw);
+ } else {
+ final String[] remainingArgs =
+ Arrays.copyOfRange(args, i + 1, args.length);
+ mAppTimeLimit.dump(remainingArgs, pw);
+ }
+ return;
} else if (arg != null && !arg.startsWith("-")) {
// Anything else that doesn't start with '-' is a pkg to filter
pkg = arg;
@@ -666,7 +718,7 @@
mAppStandby.dumpState(args, pw);
}
- mAppTimeLimit.dump(pw);
+ mAppTimeLimit.dump(null, pw);
}
}
@@ -1231,16 +1283,82 @@
final int userId = UserHandle.getUserId(callingUid);
final long token = Binder.clearCallingIdentity();
try {
- UsageStatsService.this.unregisterUsageSessionObserver(callingUid, sessionObserverId, userId);
+ UsageStatsService.this.unregisterUsageSessionObserver(callingUid, sessionObserverId,
+ userId);
} finally {
Binder.restoreCallingIdentity(token);
}
}
+
+ @Override
+ public void reportUsageStart(IBinder activity, String token, String callingPackage) {
+ reportPastUsageStart(activity, token, 0, callingPackage);
+ }
+
+ @Override
+ public void reportPastUsageStart(IBinder activity, String token, long timeAgoMs,
+ String callingPackage) {
+
+ final int callingUid = Binder.getCallingUid();
+ final int userId = UserHandle.getUserId(callingUid);
+ final long binderToken = Binder.clearCallingIdentity();
+ try {
+ ArraySet<String> tokens;
+ synchronized (mUsageReporters) {
+ tokens = mUsageReporters.get(activity.hashCode());
+ if (tokens == null) {
+ tokens = new ArraySet();
+ mUsageReporters.put(activity.hashCode(), tokens);
+ }
+ }
+
+ synchronized (tokens) {
+ if (!tokens.add(token)) {
+ throw new IllegalArgumentException(token + " for " + callingPackage
+ + " is already reported as started for this activity");
+ }
+ }
+
+ mAppTimeLimit.noteUsageStart(buildFullToken(callingPackage, token),
+ userId, timeAgoMs);
+ } finally {
+ Binder.restoreCallingIdentity(binderToken);
+ }
+ }
+
+ @Override
+ public void reportUsageStop(IBinder activity, String token, String callingPackage) {
+ final int callingUid = Binder.getCallingUid();
+ final int userId = UserHandle.getUserId(callingUid);
+ final long binderToken = Binder.clearCallingIdentity();
+ try {
+ ArraySet<String> tokens;
+ synchronized (mUsageReporters) {
+ tokens = mUsageReporters.get(activity.hashCode());
+ if (tokens == null) {
+ throw new IllegalArgumentException(
+ "Unknown reporter trying to stop token " + token + " for "
+ + callingPackage);
+ }
+ }
+
+ synchronized (tokens) {
+ if (!tokens.remove(token)) {
+ throw new IllegalArgumentException(token + " for " + callingPackage
+ + " is already reported as stopped for this activity");
+ }
+ }
+ mAppTimeLimit.noteUsageStop(buildFullToken(callingPackage, token), userId);
+ } finally {
+ Binder.restoreCallingIdentity(binderToken);
+ }
+ }
}
void registerAppUsageObserver(int callingUid, int observerId, String[] packages,
long timeLimitMs, PendingIntent callbackIntent, int userId) {
- mAppTimeLimit.addAppUsageObserver(callingUid, observerId, packages, timeLimitMs, callbackIntent,
+ mAppTimeLimit.addAppUsageObserver(callingUid, observerId, packages, timeLimitMs,
+ callbackIntent,
userId);
}
diff --git a/startop/iorap/Android.bp b/startop/iorap/Android.bp
index b3b0900..59a80fb 100644
--- a/startop/iorap/Android.bp
+++ b/startop/iorap/Android.bp
@@ -13,7 +13,7 @@
// limitations under the License.
java_library_static {
- name: "libiorap-java",
+ name: "services.startop.iorap",
aidl: {
include_dirs: [
@@ -21,6 +21,8 @@
],
},
+ libs: ["services.core"],
+
srcs: [
":iorap-aidl",
"**/*.java",
diff --git a/startop/iorap/src/com/google/android/startop/iorap/AppLaunchEvent.java b/startop/iorap/src/com/google/android/startop/iorap/AppLaunchEvent.java
new file mode 100644
index 0000000..c2e4581
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/AppLaunchEvent.java
@@ -0,0 +1,368 @@
+/*
+ * 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.google.android.startop.iorap;
+
+import android.annotation.LongDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+// TODO: fix this. either move this class into system server or add a dependency on
+// these wm classes to libiorap-java and libiorap-java-tests (somehow).
+import com.android.server.wm.ActivityMetricsLaunchObserver;
+import com.android.server.wm.ActivityMetricsLaunchObserver.ActivityRecordProto;
+import com.android.server.wm.ActivityMetricsLaunchObserver.Temperature;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Objects;
+
+/**
+ * Provide a hint to iorapd that an app launch sequence has transitioned state.<br /><br />
+ *
+ * Knowledge of when an activity starts/stops can be used by iorapd to increase system
+ * performance (e.g. by launching perfetto tracing to record an io profile, or by
+ * playing back an ioprofile via readahead) over the long run.<br /><br />
+ *
+ * /@see com.google.android.startop.iorap.IIorap#onAppLaunchEvent <br /><br />
+ * @see com.android.server.wm.ActivityMetricsLaunchObserver
+ * ActivityMetricsLaunchObserver for the possible event states.
+ * @hide
+ */
+public abstract class AppLaunchEvent implements Parcelable {
+ @LongDef
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SequenceId {}
+
+ public final @SequenceId
+ long sequenceId;
+
+ protected AppLaunchEvent(@SequenceId long sequenceId) {
+ this.sequenceId = sequenceId;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof AppLaunchEvent) {
+ return equals((AppLaunchEvent) other);
+ }
+ return false;
+ }
+
+ protected boolean equals(AppLaunchEvent other) {
+ return sequenceId == other.sequenceId;
+ }
+
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() +
+ "{" + "sequenceId=" + Long.toString(sequenceId) +
+ toStringBody() + "}";
+ }
+
+ protected String toStringBody() { return ""; };
+
+ // List of possible variants:
+
+ public static final class IntentStarted extends AppLaunchEvent {
+ @NonNull
+ public final Intent intent;
+
+ public IntentStarted(@SequenceId long sequenceId, Intent intent) {
+ super(sequenceId);
+ this.intent = intent;
+
+ Objects.requireNonNull(intent, "intent");
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof IntentStarted) {
+ return intent.equals(((IntentStarted)other).intent) &&
+ super.equals(other);
+ }
+ return false;
+ }
+
+ @Override
+ protected String toStringBody() {
+ return ", intent=" + intent.toString();
+ }
+
+
+ @Override
+ protected void writeToParcelImpl(Parcel p, int flags) {
+ super.writeToParcelImpl(p, flags);
+ intent.writeToParcel(p, flags);
+ }
+
+ IntentStarted(Parcel p) {
+ super(p);
+ intent = Intent.CREATOR.createFromParcel(p);
+ }
+ }
+
+ public static final class IntentFailed extends AppLaunchEvent {
+ public IntentFailed(@SequenceId long sequenceId) {
+ super(sequenceId);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof IntentFailed) {
+ return super.equals(other);
+ }
+ return false;
+ }
+
+ IntentFailed(Parcel p) {
+ super(p);
+ }
+ }
+
+ public static abstract class BaseWithActivityRecordData extends AppLaunchEvent {
+ public final @NonNull
+ @ActivityRecordProto byte[] activityRecordSnapshot;
+
+ protected BaseWithActivityRecordData(@SequenceId long sequenceId,
+ @NonNull @ActivityRecordProto byte[] snapshot) {
+ super(sequenceId);
+ activityRecordSnapshot = snapshot;
+
+ Objects.requireNonNull(snapshot, "snapshot");
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof BaseWithActivityRecordData) {
+ return activityRecordSnapshot.equals(
+ ((BaseWithActivityRecordData)other).activityRecordSnapshot) &&
+ super.equals(other);
+ }
+ return false;
+ }
+
+ @Override
+ protected String toStringBody() {
+ return ", " + activityRecordSnapshot.toString();
+ }
+
+ @Override
+ protected void writeToParcelImpl(Parcel p, int flags) {
+ super.writeToParcelImpl(p, flags);
+ ActivityRecordProtoParcelable.write(p, activityRecordSnapshot, flags);
+ }
+
+ BaseWithActivityRecordData(Parcel p) {
+ super(p);
+ activityRecordSnapshot = ActivityRecordProtoParcelable.create(p);
+ }
+ }
+
+ public static final class ActivityLaunched extends BaseWithActivityRecordData {
+ public final @ActivityMetricsLaunchObserver.Temperature
+ int temperature;
+
+ public ActivityLaunched(@SequenceId long sequenceId,
+ @NonNull @ActivityRecordProto byte[] snapshot,
+ @ActivityMetricsLaunchObserver.Temperature int temperature) {
+ super(sequenceId, snapshot);
+ this.temperature = temperature;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof ActivityLaunched) {
+ return temperature == ((ActivityLaunched)other).temperature &&
+ super.equals(other);
+ }
+ return false;
+ }
+
+ @Override
+ protected String toStringBody() {
+ return ", temperature=" + Integer.toString(temperature);
+ }
+
+ @Override
+ protected void writeToParcelImpl(Parcel p, int flags) {
+ super.writeToParcelImpl(p, flags);
+ p.writeInt(temperature);
+ }
+
+ ActivityLaunched(Parcel p) {
+ super(p);
+ temperature = p.readInt();
+ }
+ }
+
+ public static final class ActivityLaunchFinished extends BaseWithActivityRecordData {
+ public ActivityLaunchFinished(@SequenceId long sequenceId,
+ @NonNull @ActivityRecordProto byte[] snapshot) {
+ super(sequenceId, snapshot);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof ActivityLaunched) {
+ return super.equals(other);
+ }
+ return false;
+ }
+ }
+
+ public static class ActivityLaunchCancelled extends AppLaunchEvent {
+ public final @Nullable
+ @ActivityRecordProto byte[] activityRecordSnapshot;
+
+ public ActivityLaunchCancelled(@SequenceId long sequenceId,
+ @Nullable @ActivityRecordProto byte[] snapshot) {
+ super(sequenceId);
+ activityRecordSnapshot = snapshot;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof ActivityLaunchCancelled) {
+ return Objects.equals(activityRecordSnapshot,
+ ((ActivityLaunchCancelled)other).activityRecordSnapshot) &&
+ super.equals(other);
+ }
+ return false;
+ }
+
+ @Override
+ protected String toStringBody() {
+ return ", " + activityRecordSnapshot.toString();
+ }
+
+ @Override
+ protected void writeToParcelImpl(Parcel p, int flags) {
+ super.writeToParcelImpl(p, flags);
+ if (activityRecordSnapshot != null) {
+ p.writeBoolean(true);
+ ActivityRecordProtoParcelable.write(p, activityRecordSnapshot, flags);
+ } else {
+ p.writeBoolean(false);
+ }
+ }
+
+ ActivityLaunchCancelled(Parcel p) {
+ super(p);
+ if (p.readBoolean()) {
+ activityRecordSnapshot = ActivityRecordProtoParcelable.create(p);
+ } else {
+ activityRecordSnapshot = null;
+ }
+ }
+ }
+
+ @Override
+ public @ContentsFlags int describeContents() { return 0; }
+
+ @Override
+ public void writeToParcel(Parcel p, @WriteFlags int flags) {
+ p.writeInt(getTypeIndex());
+
+ writeToParcelImpl(p, flags);
+ }
+
+
+ public static Creator<AppLaunchEvent> CREATOR =
+ new Creator<AppLaunchEvent>() {
+ @Override
+ public AppLaunchEvent createFromParcel(Parcel source) {
+ int typeIndex = source.readInt();
+
+ Class<?> kls = getClassFromTypeIndex(typeIndex);
+ if (kls == null) {
+ throw new IllegalArgumentException("Invalid type index: " + typeIndex);
+ }
+
+ try {
+ return (AppLaunchEvent) kls.getConstructor(Parcel.class).newInstance(source);
+ } catch (InstantiationException e) {
+ throw new AssertionError(e);
+ } catch (IllegalAccessException e) {
+ throw new AssertionError(e);
+ } catch (InvocationTargetException e) {
+ throw new AssertionError(e);
+ } catch (NoSuchMethodException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ @Override
+ public AppLaunchEvent[] newArray(int size) {
+ return new AppLaunchEvent[0];
+ }
+ };
+
+ protected void writeToParcelImpl(Parcel p, int flags) {
+ p.writeLong(sequenceId);
+ }
+
+ protected AppLaunchEvent(Parcel p) {
+ sequenceId = p.readLong();
+ }
+
+ private int getTypeIndex() {
+ for (int i = 0; i < sTypes.length; ++i) {
+ if (sTypes[i].equals(this.getClass())) {
+ return i;
+ }
+ }
+ throw new AssertionError("sTypes did not include this type: " + this.getClass());
+ }
+
+ private static @Nullable Class<?> getClassFromTypeIndex(int typeIndex) {
+ if (typeIndex >= 0 && typeIndex < sTypes.length) {
+ return sTypes[typeIndex];
+ }
+ return null;
+ }
+
+ // Index position matters: It is used to encode the specific type in parceling.
+ // Keep up-to-date with C++ side.
+ private static Class<?>[] sTypes = new Class[] {
+ IntentStarted.class,
+ IntentFailed.class,
+ ActivityLaunched.class,
+ ActivityLaunchFinished.class,
+ ActivityLaunchCancelled.class,
+ };
+
+ // TODO: move to @ActivityRecordProto byte[] once we have unit tests.
+ public static class ActivityRecordProtoParcelable {
+ public static void write(Parcel p, @ActivityRecordProto byte[] activityRecordSnapshot,
+ int flags) {
+ p.writeByteArray(activityRecordSnapshot);
+ }
+
+ public static @ActivityRecordProto byte[] create(Parcel p) {
+ byte[] data = p.createByteArray();
+
+ return data;
+ }
+ }
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java b/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java
new file mode 100644
index 0000000..7fcad36
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java
@@ -0,0 +1,216 @@
+/*
+ * 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.google.android.startop.iorap;
+// TODO: rename to com.android.server.startop.iorap
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+import com.android.server.wm.ActivityMetricsLaunchObserver;
+import com.android.server.wm.ActivityMetricsLaunchObserver.ActivityRecordProto;
+import com.android.server.wm.ActivityMetricsLaunchObserver.Temperature;
+import com.android.server.wm.ActivityMetricsLaunchObserverRegistry;
+import com.android.server.wm.ActivityTaskManagerInternal;
+
+/**
+ * System-server-local proxy into the {@code IIorap} native service.
+ */
+public class IorapForwardingService extends SystemService {
+
+ public static final boolean DEBUG = true; // TODO: read from a getprop?
+ public static final String TAG = "IorapForwardingService";
+
+ private IIorap mIorapRemote;
+
+ /**
+ * Initializes the system service.
+ * <p>
+ * Subclasses must define a single argument constructor that accepts the context
+ * and passes it to super.
+ * </p>
+ *
+ * @param context The system server context.
+ */
+ public IorapForwardingService(Context context) {
+ super(context);
+ }
+
+ //<editor-fold desc="Providers">
+ /*
+ * Providers for external dependencies:
+ * - These are marked as protected to allow tests to inject different values via mocks.
+ */
+
+ @VisibleForTesting
+ protected ActivityMetricsLaunchObserverRegistry provideLaunchObserverRegistry() {
+ ActivityTaskManagerInternal amtInternal =
+ LocalServices.getService(ActivityTaskManagerInternal.class);
+ ActivityMetricsLaunchObserverRegistry launchObserverRegistry =
+ amtInternal.getLaunchObserverRegistry();
+ return launchObserverRegistry;
+ }
+
+ @VisibleForTesting
+ protected IIorap provideIorapRemote() {
+ try {
+ return IIorap.Stub.asInterface(ServiceManager.getServiceOrThrow("iorapd"));
+ } catch (ServiceManager.ServiceNotFoundException e) {
+ // TODO: how do we handle service being missing?
+ throw new AssertionError(e);
+ }
+ }
+
+ //</editor-fold>
+
+ @Override
+ public void onStart() {
+ if (DEBUG) {
+ Log.v(TAG, "onStart");
+ }
+
+ // Connect to the native binder service.
+ mIorapRemote = provideIorapRemote();
+ invokeRemote( () -> mIorapRemote.setTaskListener(new RemoteTaskListener()) );
+
+ // Listen to App Launch Sequence events from ActivityTaskManager,
+ // and forward them to the native binder service.
+ ActivityMetricsLaunchObserverRegistry launchObserverRegistry =
+ provideLaunchObserverRegistry();
+ launchObserverRegistry.registerLaunchObserver(new AppLaunchObserver());
+ }
+
+ private class AppLaunchObserver implements ActivityMetricsLaunchObserver {
+ // We add a synthetic sequence ID here to make it easier to differentiate new
+ // launch sequences on the native side.
+ private @AppLaunchEvent.SequenceId long mSequenceId = -1;
+
+ @Override
+ public void onIntentStarted(@NonNull Intent intent) {
+ // #onIntentStarted [is the only transition that] initiates a new launch sequence.
+ ++mSequenceId;
+
+ if (DEBUG) {
+ Log.v(TAG, String.format("AppLaunchObserver#onIntentStarted(%d, %s)",
+ mSequenceId, intent));
+ }
+
+ invokeRemote(() ->
+ mIorapRemote.onAppLaunchEvent(RequestId.nextValueForSequence(),
+ new AppLaunchEvent.IntentStarted(mSequenceId, intent))
+ );
+ }
+
+ @Override
+ public void onIntentFailed() {
+ if (DEBUG) {
+ Log.v(TAG, String.format("AppLaunchObserver#onIntentFailed(%d)", mSequenceId));
+ }
+
+ invokeRemote(() ->
+ mIorapRemote.onAppLaunchEvent(RequestId.nextValueForSequence(),
+ new AppLaunchEvent.IntentFailed(mSequenceId))
+ );
+ }
+
+ @Override
+ public void onActivityLaunched(@NonNull @ActivityRecordProto byte[] activity,
+ @Temperature int temperature) {
+ if (DEBUG) {
+ Log.v(TAG, String.format("AppLaunchObserver#onActivityLaunched(%d, %s, %d)",
+ mSequenceId, activity, temperature));
+ }
+
+ invokeRemote(() ->
+ mIorapRemote.onAppLaunchEvent(RequestId.nextValueForSequence(),
+ new AppLaunchEvent.ActivityLaunched(mSequenceId, activity, temperature))
+ );
+ }
+
+ @Override
+ public void onActivityLaunchCancelled(@Nullable @ActivityRecordProto byte[] activity) {
+ if (DEBUG) {
+ Log.v(TAG, String.format("AppLaunchObserver#onActivityLaunchCancelled(%d, %s)",
+ mSequenceId, activity));
+ }
+
+ invokeRemote(() ->
+ mIorapRemote.onAppLaunchEvent(RequestId.nextValueForSequence(),
+ new AppLaunchEvent.ActivityLaunchCancelled(mSequenceId,
+ activity)));
+ }
+
+ @Override
+ public void onActivityLaunchFinished(@NonNull @ActivityRecordProto byte[] activity) {
+ if (DEBUG) {
+ Log.v(TAG, String.format("AppLaunchObserver#onActivityLaunchFinished(%d, %s)",
+ mSequenceId, activity));
+ }
+
+ invokeRemote(() ->
+ mIorapRemote.onAppLaunchEvent(RequestId.nextValueForSequence(),
+ new AppLaunchEvent.ActivityLaunchCancelled(mSequenceId, activity))
+ );
+ }
+ }
+
+ private class RemoteTaskListener extends ITaskListener.Stub {
+ @Override
+ public void onProgress(RequestId requestId, TaskResult result) throws RemoteException {
+ if (DEBUG) {
+ Log.v(TAG,
+ String.format("RemoteTaskListener#onProgress(%s, %s)", requestId, result));
+ }
+
+ // TODO: implement rest.
+ }
+
+ @Override
+ public void onComplete(RequestId requestId, TaskResult result) throws RemoteException {
+ if (DEBUG) {
+ Log.v(TAG,
+ String.format("RemoteTaskListener#onComplete(%s, %s)", requestId, result));
+ }
+
+ // TODO: implement rest.
+ }
+ }
+
+ private interface RemoteRunnable {
+ void run() throws RemoteException;
+ }
+
+ private static void invokeRemote(RemoteRunnable r) {
+ try {
+ r.run();
+ } catch (RemoteException e) {
+ // TODO: what do we do with exceptions?
+ throw new AssertionError("not implemented", e);
+ }
+ }
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/RequestId.java b/startop/iorap/src/com/google/android/startop/iorap/RequestId.java
index 2c79319..adb3a91 100644
--- a/startop/iorap/src/com/google/android/startop/iorap/RequestId.java
+++ b/startop/iorap/src/com/google/android/startop/iorap/RequestId.java
@@ -71,7 +71,7 @@
@Override
public String toString() {
- return String.format("{requestId: %ld}", requestId);
+ return String.format("{requestId: %d}", requestId);
}
@Override
diff --git a/startop/iorap/tests/Android.bp b/startop/iorap/tests/Android.bp
index 7605784..5ac4a46 100644
--- a/startop/iorap/tests/Android.bp
+++ b/startop/iorap/tests/Android.bp
@@ -18,8 +18,15 @@
srcs: ["src/**/*.kt"],
static_libs: [
- // non-test dependencies
- "libiorap-java",
+ // Non-test dependencies
+
+ // library under test
+ "services.startop.iorap",
+ // need the system_server code to be on the classpath,
+ "services.core",
+
+ // Test Dependencies
+
// test android dependencies
"platform-test-annotations",
"android-support-test",
diff --git a/startop/view_compiler/Android.bp b/startop/view_compiler/Android.bp
index 91cec554..2fc3a0d 100644
--- a/startop/view_compiler/Android.bp
+++ b/startop/view_compiler/Android.bp
@@ -22,18 +22,37 @@
shared_libs: [
"libbase",
"libdexfile",
+ "libz",
"slicer",
],
static_libs: [
"libtinyxml2",
+ "liblog",
+ "libutils",
+ "libziparchive",
],
+ cppflags: ["-std=c++17"],
+ target: {
+ android: {
+ shared_libs: [
+ "libandroidfw",
+ ],
+ },
+ host: {
+ static_libs: [
+ "libandroidfw",
+ ],
+ },
+ },
}
cc_library_host_static {
name: "libviewcompiler",
defaults: ["viewcompiler_defaults"],
srcs: [
+ "apk_layout_compiler.cc",
"dex_builder.cc",
+ "dex_layout_compiler.cc",
"java_lang_builder.cc",
"tinyxml_layout_parser.cc",
"util.cc",
diff --git a/startop/view_compiler/apk_layout_compiler.cc b/startop/view_compiler/apk_layout_compiler.cc
new file mode 100644
index 0000000..e95041b
--- /dev/null
+++ b/startop/view_compiler/apk_layout_compiler.cc
@@ -0,0 +1,159 @@
+/*
+ * 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.
+ */
+
+#include "apk_layout_compiler.h"
+#include "dex_layout_compiler.h"
+#include "java_lang_builder.h"
+#include "layout_validation.h"
+#include "util.h"
+
+#include "androidfw/ApkAssets.h"
+#include "androidfw/AssetManager2.h"
+#include "androidfw/ResourceTypes.h"
+
+#include <iostream>
+#include <locale>
+
+#include "android-base/stringprintf.h"
+
+namespace startop {
+
+using android::ResXMLParser;
+using android::base::StringPrintf;
+
+class ResXmlVisitorAdapter {
+ public:
+ ResXmlVisitorAdapter(ResXMLParser* parser) : parser_{parser} {}
+
+ template <typename Visitor>
+ void Accept(Visitor* visitor) {
+ size_t depth{0};
+ do {
+ switch (parser_->next()) {
+ case ResXMLParser::START_DOCUMENT:
+ depth++;
+ visitor->VisitStartDocument();
+ break;
+ case ResXMLParser::END_DOCUMENT:
+ depth--;
+ visitor->VisitEndDocument();
+ break;
+ case ResXMLParser::START_TAG: {
+ depth++;
+ size_t name_length = 0;
+ const char16_t* name = parser_->getElementName(&name_length);
+ visitor->VisitStartTag(std::u16string{name, name_length});
+ break;
+ }
+ case ResXMLParser::END_TAG:
+ depth--;
+ visitor->VisitEndTag();
+ break;
+ default:;
+ }
+ } while (depth > 0 || parser_->getEventType() == ResXMLParser::FIRST_CHUNK_CODE);
+ }
+
+ private:
+ ResXMLParser* parser_;
+};
+
+bool CanCompileLayout(ResXMLParser* parser) {
+ ResXmlVisitorAdapter adapter{parser};
+ LayoutValidationVisitor visitor;
+ adapter.Accept(&visitor);
+
+ return visitor.can_compile();
+}
+
+void CompileApkLayouts(const std::string& filename, CompilationTarget target,
+ std::ostream& target_out) {
+ auto assets = android::ApkAssets::Load(filename);
+ android::AssetManager2 resources;
+ resources.SetApkAssets({assets.get()});
+
+ std::string package_name;
+
+ // TODO: handle multiple packages better
+ bool first = true;
+ for (const auto& package : assets->GetLoadedArsc()->GetPackages()) {
+ CHECK(first);
+ package_name = package->GetPackageName();
+ first = false;
+ }
+
+ dex::DexBuilder dex_file;
+ dex::ClassBuilder compiled_view{
+ dex_file.MakeClass(StringPrintf("%s.CompiledView", package_name.c_str()))};
+ std::vector<dex::MethodBuilder> methods;
+
+ assets->ForEachFile("res/", [&](const android::StringPiece& s, android::FileType) {
+ if (s == "layout") {
+ auto path = StringPrintf("res/%s/", s.to_string().c_str());
+ assets->ForEachFile(path, [&](const android::StringPiece& layout_file, android::FileType) {
+ auto layout_path = StringPrintf("%s%s", path.c_str(), layout_file.to_string().c_str());
+ android::ApkAssetsCookie cookie = android::kInvalidCookie;
+ auto asset = resources.OpenNonAsset(layout_path, android::Asset::ACCESS_RANDOM, &cookie);
+ CHECK(asset);
+ CHECK(android::kInvalidCookie != cookie);
+ const auto dynamic_ref_table = resources.GetDynamicRefTableForCookie(cookie);
+ CHECK(nullptr != dynamic_ref_table);
+ android::ResXMLTree xml_tree{dynamic_ref_table};
+ xml_tree.setTo(asset->getBuffer(/*wordAligned=*/true),
+ asset->getLength(),
+ /*copy_data=*/true);
+ android::ResXMLParser parser{xml_tree};
+ parser.restart();
+ if (CanCompileLayout(&parser)) {
+ parser.restart();
+ const std::string layout_name = startop::util::FindLayoutNameFromFilename(layout_path);
+ ResXmlVisitorAdapter adapter{&parser};
+ switch (target) {
+ case CompilationTarget::kDex: {
+ methods.push_back(compiled_view.CreateMethod(
+ layout_name,
+ dex::Prototype{dex::TypeDescriptor::FromClassname("android.view.View"),
+ dex::TypeDescriptor::FromClassname("android.content.Context"),
+ dex::TypeDescriptor::Int()}));
+ DexViewBuilder builder(&methods.back());
+ builder.Start();
+ LayoutCompilerVisitor visitor{&builder};
+ adapter.Accept(&visitor);
+ builder.Finish();
+ methods.back().Encode();
+ break;
+ }
+ case CompilationTarget::kJavaLanguage: {
+ JavaLangViewBuilder builder{package_name, layout_name, target_out};
+ builder.Start();
+ LayoutCompilerVisitor visitor{&builder};
+ adapter.Accept(&visitor);
+ builder.Finish();
+ break;
+ }
+ }
+ }
+ });
+ }
+ });
+
+ if (target == CompilationTarget::kDex) {
+ slicer::MemView image{dex_file.CreateImage()};
+ target_out.write(image.ptr<const char>(), image.size());
+ }
+}
+
+} // namespace startop
diff --git a/startop/view_compiler/apk_layout_compiler.h b/startop/view_compiler/apk_layout_compiler.h
new file mode 100644
index 0000000..c85ddd6
--- /dev/null
+++ b/startop/view_compiler/apk_layout_compiler.h
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+#ifndef APK_LAYOUT_COMPILER_H_
+#define APK_LAYOUT_COMPILER_H_
+
+#include <string>
+
+namespace startop {
+
+enum class CompilationTarget { kJavaLanguage, kDex };
+
+void CompileApkLayouts(const std::string& filename, CompilationTarget target,
+ std::ostream& target_out);
+
+} // namespace startop
+
+#endif // APK_LAYOUT_COMPILER_H_
\ No newline at end of file
diff --git a/startop/view_compiler/dex_builder_test/Android.bp b/startop/view_compiler/dex_builder_test/Android.bp
index 4449ea0..d4f38ed 100644
--- a/startop/view_compiler/dex_builder_test/Android.bp
+++ b/startop/view_compiler/dex_builder_test/Android.bp
@@ -14,16 +14,30 @@
// limitations under the License.
//
+genrule {
+ name: "generate_compiled_layout",
+ tools: [":viewcompiler"],
+ cmd: "$(location :viewcompiler) $(in) --dex --out $(out) --package android.startop.test",
+ srcs: ["res/layout/layout1.xml"],
+ out: [
+ "layout1.dex",
+ ],
+}
+
android_test {
name: "dex-builder-test",
- srcs: ["src/android/startop/test/DexBuilderTest.java"],
+ srcs: [
+ "src/android/startop/test/DexBuilderTest.java",
+ "src/android/startop/test/LayoutCompilerTest.java",
+ ],
sdk_version: "current",
- data: [":generate_dex_testcases"],
+ data: [":generate_dex_testcases", ":generate_compiled_layout"],
static_libs: [
"android-support-test",
"guava",
],
manifest: "AndroidManifest.xml",
+ resource_dirs: ["res"],
test_config: "AndroidTest.xml",
test_suites: ["general-tests"],
}
diff --git a/startop/view_compiler/dex_builder_test/AndroidTest.xml b/startop/view_compiler/dex_builder_test/AndroidTest.xml
index 6f90cf3..68d8fdc 100644
--- a/startop/view_compiler/dex_builder_test/AndroidTest.xml
+++ b/startop/view_compiler/dex_builder_test/AndroidTest.xml
@@ -25,6 +25,7 @@
<option name="cleanup" value="true" />
<option name="push" value="trivial.dex->/data/local/tmp/dex-builder-test/trivial.dex" />
<option name="push" value="simple.dex->/data/local/tmp/dex-builder-test/simple.dex" />
+ <option name="push" value="layout1.dex->/data/local/tmp/dex-builder-test/layout1.dex" />
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
diff --git a/startop/view_compiler/dex_builder_test/res/layout/layout1.xml b/startop/view_compiler/dex_builder_test/res/layout/layout1.xml
new file mode 100644
index 0000000..0f9375c
--- /dev/null
+++ b/startop/view_compiler/dex_builder_test/res/layout/layout1.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:orientation="vertical"
+ android:gravity="center">
+
+ <Button
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+ <Button
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+
+ </LinearLayout>
diff --git a/startop/view_compiler/dex_builder_test/src/android/startop/test/LayoutCompilerTest.java b/startop/view_compiler/dex_builder_test/src/android/startop/test/LayoutCompilerTest.java
new file mode 100644
index 0000000..ce3ce83
--- /dev/null
+++ b/startop/view_compiler/dex_builder_test/src/android/startop/test/LayoutCompilerTest.java
@@ -0,0 +1,46 @@
+/*
+ * 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.startop.test;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.view.View;
+import com.google.common.io.ByteStreams;
+import dalvik.system.InMemoryDexClassLoader;
+import dalvik.system.PathClassLoader;
+import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
+import org.junit.Assert;
+import org.junit.Test;
+
+// Adding tests here requires changes in several other places. See README.md in
+// the view_compiler directory for more information.
+public class LayoutCompilerTest {
+ static ClassLoader loadDexFile(String filename) throws Exception {
+ return new PathClassLoader("/data/local/tmp/dex-builder-test/" + filename,
+ ClassLoader.getSystemClassLoader());
+ }
+
+ @Test
+ public void loadAndInflaterLayout1() throws Exception {
+ ClassLoader dex_file = loadDexFile("layout1.dex");
+ Class compiled_view = dex_file.loadClass("android.startop.test.CompiledView");
+ Method layout1 = compiled_view.getMethod("layout1", Context.class, int.class);
+ Context context = InstrumentationRegistry.getTargetContext();
+ layout1.invoke(null, context, R.layout.layout1);
+ }
+}
diff --git a/startop/view_compiler/dex_layout_compiler.cc b/startop/view_compiler/dex_layout_compiler.cc
new file mode 100644
index 0000000..c68793d
--- /dev/null
+++ b/startop/view_compiler/dex_layout_compiler.cc
@@ -0,0 +1,226 @@
+/*
+ * 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.
+ */
+
+#include "dex_layout_compiler.h"
+#include "layout_validation.h"
+
+#include "android-base/stringprintf.h"
+
+namespace startop {
+
+using android::base::StringPrintf;
+
+void LayoutValidationVisitor::VisitStartTag(const std::u16string& name) {
+ if (0 == name.compare(u"merge")) {
+ message_ = "Merge tags are not supported";
+ can_compile_ = false;
+ }
+ if (0 == name.compare(u"include")) {
+ message_ = "Include tags are not supported";
+ can_compile_ = false;
+ }
+ if (0 == name.compare(u"view")) {
+ message_ = "View tags are not supported";
+ can_compile_ = false;
+ }
+ if (0 == name.compare(u"fragment")) {
+ message_ = "Fragment tags are not supported";
+ can_compile_ = false;
+ }
+}
+
+DexViewBuilder::DexViewBuilder(dex::MethodBuilder* method)
+ : method_{method},
+ context_{dex::Value::Parameter(0)},
+ resid_{dex::Value::Parameter(1)},
+ inflater_{method->MakeRegister()},
+ xml_{method->MakeRegister()},
+ attrs_{method->MakeRegister()},
+ classname_tmp_{method->MakeRegister()},
+ xml_next_{method->dex_file()->GetOrDeclareMethod(
+ dex::TypeDescriptor::FromClassname("android.content.res.XmlResourceParser"), "next",
+ dex::Prototype{dex::TypeDescriptor::Int()})},
+ try_create_view_{method->dex_file()->GetOrDeclareMethod(
+ dex::TypeDescriptor::FromClassname("android.view.LayoutInflater"), "tryCreateView",
+ dex::Prototype{dex::TypeDescriptor::FromClassname("android.view.View"),
+ dex::TypeDescriptor::FromClassname("android.view.View"),
+ dex::TypeDescriptor::FromClassname("java.lang.String"),
+ dex::TypeDescriptor::FromClassname("android.content.Context"),
+ dex::TypeDescriptor::FromClassname("android.util.AttributeSet")})},
+ generate_layout_params_{method->dex_file()->GetOrDeclareMethod(
+ dex::TypeDescriptor::FromClassname("android.view.ViewGroup"), "generateLayoutParams",
+ dex::Prototype{dex::TypeDescriptor::FromClassname("android.view.ViewGroup$LayoutParams"),
+ dex::TypeDescriptor::FromClassname("android.util.AttributeSet")})},
+ add_view_{method->dex_file()->GetOrDeclareMethod(
+ dex::TypeDescriptor::FromClassname("android.view.ViewGroup"), "addView",
+ dex::Prototype{
+ dex::TypeDescriptor::Void(),
+ dex::TypeDescriptor::FromClassname("android.view.View"),
+ dex::TypeDescriptor::FromClassname("android.view.ViewGroup$LayoutParams")})},
+ // The register stack starts with one register, which will be null for the root view.
+ register_stack_{{method->MakeRegister()}} {}
+
+void DexViewBuilder::Start() {
+ dex::DexBuilder* const dex = method_->dex_file();
+
+ // LayoutInflater inflater = LayoutInflater.from(context);
+ auto layout_inflater_from = dex->GetOrDeclareMethod(
+ dex::TypeDescriptor::FromClassname("android.view.LayoutInflater"),
+ "from",
+ dex::Prototype{dex::TypeDescriptor::FromClassname("android.view.LayoutInflater"),
+ dex::TypeDescriptor::FromClassname("android.content.Context")});
+ method_->AddInstruction(
+ dex::Instruction::InvokeStaticObject(layout_inflater_from.id, /*dest=*/inflater_, context_));
+
+ // Resources res = context.getResources();
+ auto context_type = dex::TypeDescriptor::FromClassname("android.content.Context");
+ auto resources_type = dex::TypeDescriptor::FromClassname("android.content.res.Resources");
+ auto get_resources =
+ dex->GetOrDeclareMethod(context_type, "getResources", dex::Prototype{resources_type});
+ method_->AddInstruction(dex::Instruction::InvokeVirtualObject(get_resources.id, xml_, context_));
+
+ // XmlResourceParser xml = res.getLayout(resid);
+ auto xml_resource_parser_type =
+ dex::TypeDescriptor::FromClassname("android.content.res.XmlResourceParser");
+ auto get_layout =
+ dex->GetOrDeclareMethod(resources_type,
+ "getLayout",
+ dex::Prototype{xml_resource_parser_type, dex::TypeDescriptor::Int()});
+ method_->AddInstruction(dex::Instruction::InvokeVirtualObject(get_layout.id, xml_, xml_, resid_));
+
+ // AttributeSet attrs = Xml.asAttributeSet(xml);
+ auto as_attribute_set = dex->GetOrDeclareMethod(
+ dex::TypeDescriptor::FromClassname("android.util.Xml"),
+ "asAttributeSet",
+ dex::Prototype{dex::TypeDescriptor::FromClassname("android.util.AttributeSet"),
+ dex::TypeDescriptor::FromClassname("org.xmlpull.v1.XmlPullParser")});
+ method_->AddInstruction(dex::Instruction::InvokeStaticObject(as_attribute_set.id, attrs_, xml_));
+
+ // xml.next(); // start document
+ method_->AddInstruction(dex::Instruction::InvokeInterface(xml_next_.id, {}, xml_));
+}
+
+void DexViewBuilder::Finish() {}
+
+namespace {
+std::string ResolveName(const std::string& name) {
+ if (name == "View") return "android.view.View";
+ if (name == "ViewGroup") return "android.view.ViewGroup";
+ if (name.find(".") == std::string::npos) {
+ return StringPrintf("android.widget.%s", name.c_str());
+ }
+ return name;
+}
+} // namespace
+
+void DexViewBuilder::StartView(const std::string& name, bool is_viewgroup) {
+ bool const is_root_view = view_stack_.empty();
+
+ // xml.next(); // start tag
+ method_->AddInstruction(dex::Instruction::InvokeInterface(xml_next_.id, {}, xml_));
+
+ dex::Value view = AcquireRegister();
+ // try to create the view using the factories
+ method_->BuildConstString(classname_tmp_,
+ name); // TODO: the need to fully qualify the classname
+ if (is_root_view) {
+ dex::Value null = AcquireRegister();
+ method_->BuildConst4(null, 0);
+ method_->AddInstruction(dex::Instruction::InvokeVirtualObject(
+ try_create_view_.id, view, inflater_, null, classname_tmp_, context_, attrs_));
+ ReleaseRegister();
+ } else {
+ method_->AddInstruction(dex::Instruction::InvokeVirtualObject(
+ try_create_view_.id, view, inflater_, GetCurrentView(), classname_tmp_, context_, attrs_));
+ }
+ auto label = method_->MakeLabel();
+ // branch if not null
+ method_->AddInstruction(
+ dex::Instruction::OpWithArgs(dex::Instruction::Op::kBranchNEqz, /*dest=*/{}, view, label));
+
+ // If null, create the class directly.
+ method_->BuildNew(view,
+ dex::TypeDescriptor::FromClassname(ResolveName(name)),
+ dex::Prototype{dex::TypeDescriptor::Void(),
+ dex::TypeDescriptor::FromClassname("android.content.Context"),
+ dex::TypeDescriptor::FromClassname("android.util.AttributeSet")},
+ context_,
+ attrs_);
+
+ method_->AddInstruction(
+ dex::Instruction::OpWithArgs(dex::Instruction::Op::kBindLabel, /*dest=*/{}, label));
+
+ if (is_viewgroup) {
+ // Cast to a ViewGroup so we can add children later.
+ const ir::Type* view_group_def = method_->dex_file()->GetOrAddType(
+ dex::TypeDescriptor::FromClassname("android.view.ViewGroup").descriptor());
+ method_->AddInstruction(dex::Instruction::Cast(view, dex::Value::Type(view_group_def->orig_index)));
+ }
+
+ if (!is_root_view) {
+ // layout_params = parent.generateLayoutParams(attrs);
+ dex::Value layout_params{AcquireRegister()};
+ method_->AddInstruction(dex::Instruction::InvokeVirtualObject(
+ generate_layout_params_.id, layout_params, GetCurrentView(), attrs_));
+ view_stack_.push_back({view, layout_params});
+ } else {
+ view_stack_.push_back({view, {}});
+ }
+}
+
+void DexViewBuilder::FinishView() {
+ if (view_stack_.size() == 1) {
+ method_->BuildReturn(GetCurrentView(), /*is_object=*/true);
+ } else {
+ // parent.add(view, layout_params)
+ method_->AddInstruction(dex::Instruction::InvokeVirtual(
+ add_view_.id, /*dest=*/{}, GetParentView(), GetCurrentView(), GetCurrentLayoutParams()));
+ // xml.next(); // end tag
+ method_->AddInstruction(dex::Instruction::InvokeInterface(xml_next_.id, {}, xml_));
+ }
+ PopViewStack();
+}
+
+dex::Value DexViewBuilder::AcquireRegister() {
+ top_register_++;
+ if (register_stack_.size() == top_register_) {
+ register_stack_.push_back(method_->MakeRegister());
+ }
+ return register_stack_[top_register_];
+}
+
+void DexViewBuilder::ReleaseRegister() { top_register_--; }
+
+dex::Value DexViewBuilder::GetCurrentView() const { return view_stack_.back().view; }
+dex::Value DexViewBuilder::GetCurrentLayoutParams() const {
+ return view_stack_.back().layout_params.value();
+}
+dex::Value DexViewBuilder::GetParentView() const {
+ return view_stack_[view_stack_.size() - 2].view;
+}
+
+void DexViewBuilder::PopViewStack() {
+ const auto& top = view_stack_.back();
+ // release the layout params if we have them
+ if (top.layout_params.has_value()) {
+ ReleaseRegister();
+ }
+ // Unconditionally release the view register.
+ ReleaseRegister();
+ view_stack_.pop_back();
+}
+
+} // namespace startop
\ No newline at end of file
diff --git a/startop/view_compiler/dex_layout_compiler.h b/startop/view_compiler/dex_layout_compiler.h
new file mode 100644
index 0000000..170a1a6
--- /dev/null
+++ b/startop/view_compiler/dex_layout_compiler.h
@@ -0,0 +1,118 @@
+/*
+ * 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.
+ */
+
+#ifndef DEX_LAYOUT_COMPILER_H_
+#define DEX_LAYOUT_COMPILER_H_
+
+#include "dex_builder.h"
+
+#include <codecvt>
+#include <locale>
+#include <string>
+#include <vector>
+
+namespace startop {
+
+// This visitor does the actual view compilation, using a supplied builder.
+template <typename Builder>
+class LayoutCompilerVisitor {
+ public:
+ explicit LayoutCompilerVisitor(Builder* builder) : builder_{builder} {}
+
+ void VisitStartDocument() { builder_->Start(); }
+ void VisitEndDocument() { builder_->Finish(); }
+ void VisitStartTag(const std::u16string& name) {
+ parent_stack_.push_back(ViewEntry{
+ std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}.to_bytes(name), {}});
+ }
+ void VisitEndTag() {
+ auto entry = parent_stack_.back();
+ parent_stack_.pop_back();
+
+ if (parent_stack_.empty()) {
+ GenerateCode(entry);
+ } else {
+ parent_stack_.back().children.push_back(entry);
+ }
+ }
+
+ private:
+ struct ViewEntry {
+ std::string name;
+ std::vector<ViewEntry> children;
+ };
+
+ void GenerateCode(const ViewEntry& view) {
+ builder_->StartView(view.name, !view.children.empty());
+ for (const auto& child : view.children) {
+ GenerateCode(child);
+ }
+ builder_->FinishView();
+ }
+
+ Builder* builder_;
+
+ std::vector<ViewEntry> parent_stack_;
+};
+
+class DexViewBuilder {
+ public:
+ DexViewBuilder(dex::MethodBuilder* method);
+
+ void Start();
+ void Finish();
+ void StartView(const std::string& name, bool is_viewgroup);
+ void FinishView();
+
+ private:
+ // Accessors for the stack of views that are under construction.
+ dex::Value AcquireRegister();
+ void ReleaseRegister();
+ dex::Value GetCurrentView() const;
+ dex::Value GetCurrentLayoutParams() const;
+ dex::Value GetParentView() const;
+ void PopViewStack();
+
+ dex::MethodBuilder* method_;
+
+ // Registers used for code generation
+ dex::Value const context_;
+ dex::Value const resid_;
+ const dex::Value inflater_;
+ const dex::Value xml_;
+ const dex::Value attrs_;
+ const dex::Value classname_tmp_;
+
+ const dex::MethodDeclData xml_next_;
+ const dex::MethodDeclData try_create_view_;
+ const dex::MethodDeclData generate_layout_params_;
+ const dex::MethodDeclData add_view_;
+
+ // used for keeping track of which registers are in use
+ size_t top_register_{0};
+ std::vector<dex::Value> register_stack_;
+
+ // Keep track of the views currently in progress.
+ struct ViewEntry {
+ dex::Value view;
+ std::optional<dex::Value> layout_params;
+ };
+ std::vector<ViewEntry> view_stack_;
+};
+
+} // namespace startop
+
+#endif // DEX_LAYOUT_COMPILER_H_
diff --git a/startop/view_compiler/java_lang_builder.cc b/startop/view_compiler/java_lang_builder.cc
index 0b8754f..920caee 100644
--- a/startop/view_compiler/java_lang_builder.cc
+++ b/startop/view_compiler/java_lang_builder.cc
@@ -67,7 +67,7 @@
"}\n"; // end CompiledView
}
-void JavaLangViewBuilder::StartView(const string& class_name) {
+void JavaLangViewBuilder::StartView(const string& class_name, bool /*is_viewgroup*/) {
const string view_var = MakeVar("view");
const string layout_var = MakeVar("layout");
std::string parent = "null";
diff --git a/startop/view_compiler/java_lang_builder.h b/startop/view_compiler/java_lang_builder.h
index c8d20b2..69356d3 100644
--- a/startop/view_compiler/java_lang_builder.h
+++ b/startop/view_compiler/java_lang_builder.h
@@ -35,7 +35,7 @@
void Finish() const;
// Begin creating a view (i.e. process the opening tag)
- void StartView(const std::string& class_name);
+ void StartView(const std::string& class_name, bool is_viewgroup);
// Finish a view, after all of its child nodes have been processed.
void FinishView();
diff --git a/startop/view_compiler/main.cc b/startop/view_compiler/main.cc
index 609bcf3..871a421 100644
--- a/startop/view_compiler/main.cc
+++ b/startop/view_compiler/main.cc
@@ -16,8 +16,12 @@
#include "gflags/gflags.h"
+#include "android-base/stringprintf.h"
+#include "apk_layout_compiler.h"
#include "dex_builder.h"
+#include "dex_layout_compiler.h"
#include "java_lang_builder.h"
+#include "layout_validation.h"
#include "tinyxml_layout_parser.h"
#include "util.h"
@@ -32,43 +36,60 @@
namespace {
using namespace tinyxml2;
+using android::base::StringPrintf;
+using startop::dex::ClassBuilder;
+using startop::dex::DexBuilder;
+using startop::dex::MethodBuilder;
+using startop::dex::Prototype;
+using startop::dex::TypeDescriptor;
using namespace startop::util;
using std::string;
constexpr char kStdoutFilename[]{"stdout"};
+DEFINE_bool(apk, false, "Compile layouts in an APK");
DEFINE_bool(dex, false, "Generate a DEX file instead of Java");
DEFINE_string(out, kStdoutFilename, "Where to write the generated class");
DEFINE_string(package, "", "The package name for the generated class (required)");
-class ViewCompilerXmlVisitor : public XMLVisitor {
+template <typename Visitor>
+class XmlVisitorAdapter : public XMLVisitor {
public:
- explicit ViewCompilerXmlVisitor(JavaLangViewBuilder* builder) : builder_(builder) {}
+ explicit XmlVisitorAdapter(Visitor* visitor) : visitor_{visitor} {}
bool VisitEnter(const XMLDocument& /*doc*/) override {
- builder_->Start();
+ visitor_->VisitStartDocument();
return true;
}
bool VisitExit(const XMLDocument& /*doc*/) override {
- builder_->Finish();
+ visitor_->VisitEndDocument();
return true;
}
bool VisitEnter(const XMLElement& element, const XMLAttribute* /*firstAttribute*/) override {
- builder_->StartView(element.Name());
+ visitor_->VisitStartTag(
+ std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}.from_bytes(
+ element.Name()));
return true;
}
bool VisitExit(const XMLElement& /*element*/) override {
- builder_->FinishView();
+ visitor_->VisitEndTag();
return true;
}
private:
- JavaLangViewBuilder* builder_;
+ Visitor* visitor_;
};
+template <typename Builder>
+void CompileLayout(XMLDocument* xml, Builder* builder) {
+ startop::LayoutCompilerVisitor visitor{builder};
+ XmlVisitorAdapter<decltype(visitor)> adapter{&visitor};
+ xml->Accept(&adapter);
+}
+
} // end namespace
int main(int argc, char** argv) {
@@ -88,16 +109,23 @@
return 1;
}
- if (FLAGS_dex) {
- startop::dex::WriteTestDexFile("test.dex");
+ const char* const filename = argv[kFileNameParam];
+ const bool is_stdout = FLAGS_out == kStdoutFilename;
+
+ std::ofstream outfile;
+ if (!is_stdout) {
+ outfile.open(FLAGS_out);
+ }
+
+ if (FLAGS_apk) {
+ startop::CompileApkLayouts(
+ filename,
+ FLAGS_dex ? startop::CompilationTarget::kDex : startop::CompilationTarget::kJavaLanguage,
+ is_stdout ? std::cout : outfile);
return 0;
}
- const char* const filename = argv[kFileNameParam];
- const string layout_name = FindLayoutNameFromFilename(filename);
-
- // We want to generate Java language code to inflate exactly this layout. This means
- // generating code to walk the resource XML too.
+ const string layout_name = startop::util::FindLayoutNameFromFilename(filename);
XMLDocument xml;
xml.LoadFile(filename);
@@ -108,15 +136,27 @@
return 1;
}
- std::ofstream outfile;
- if (FLAGS_out != kStdoutFilename) {
- outfile.open(FLAGS_out);
+ if (FLAGS_dex) {
+ DexBuilder dex_file;
+ string class_name = StringPrintf("%s.CompiledView", FLAGS_package.c_str());
+ ClassBuilder compiled_view{dex_file.MakeClass(class_name)};
+ MethodBuilder method{compiled_view.CreateMethod(
+ layout_name,
+ Prototype{TypeDescriptor::FromClassname("android.view.View"),
+ TypeDescriptor::FromClassname("android.content.Context"),
+ TypeDescriptor::Int()})};
+ startop::DexViewBuilder builder{&method};
+ CompileLayout(&xml, &builder);
+ method.Encode();
+
+ slicer::MemView image{dex_file.CreateImage()};
+
+ (is_stdout ? std::cout : outfile).write(image.ptr<const char>(), image.size());
+ } else {
+ // Generate Java language output.
+ JavaLangViewBuilder builder{FLAGS_package, layout_name, is_stdout ? std::cout : outfile};
+
+ CompileLayout(&xml, &builder);
}
- JavaLangViewBuilder builder{
- FLAGS_package, layout_name, FLAGS_out == kStdoutFilename ? std::cout : outfile};
-
- ViewCompilerXmlVisitor visitor{&builder};
- xml.Accept(&visitor);
-
return 0;
}
diff --git a/telecomm/java/android/telecom/CallRedirectionService.java b/telecomm/java/android/telecom/CallRedirectionService.java
index b906d0b..3299117 100644
--- a/telecomm/java/android/telecom/CallRedirectionService.java
+++ b/telecomm/java/android/telecom/CallRedirectionService.java
@@ -16,6 +16,7 @@
package android.telecom;
+import android.annotation.NonNull;
import android.annotation.SdkConstant;
import android.app.Service;
import android.content.Intent;
@@ -27,8 +28,8 @@
import android.os.RemoteException;
import com.android.internal.os.SomeArgs;
-import com.android.internal.telecom.ICallRedirectionService;
import com.android.internal.telecom.ICallRedirectionAdapter;
+import com.android.internal.telecom.ICallRedirectionService;
/**
* This service can be implemented to interact between Telecom and its implementor
@@ -62,22 +63,35 @@
/**
* Telecom calls this method to inform the implemented {@link CallRedirectionService} of
- * a new outgoing call which is being placed.
+ * a new outgoing call which is being placed. Telecom does not request to redirect emergency
+ * calls and does not request to redirect calls with gateway information.
*
- * The implemented {@link CallRedirectionService} can call {@link #placeCallUnmodified()},
- * {@link #redirectCall(Uri, PhoneAccountHandle)}, and {@link #cancelCall()} only from here.
+ * <p>Telecom will cancel the call if Telecom does not receive a response in 5 seconds from
+ * the implemented {@link CallRedirectionService} set by users.
*
- * @param handle the phone number dialed by the user
- * @param targetPhoneAccount the {@link PhoneAccountHandle} on which the call will be placed.
+ * <p>The implemented {@link CallRedirectionService} can call {@link #placeCallUnmodified()},
+ * {@link #redirectCall(Uri, PhoneAccountHandle, boolean)}, and {@link #cancelCall()} only
+ * from here.
+ *
+ * @param handle the phone number dialed by the user, represented in E.164 format if possible
+ * @param initialPhoneAccount the {@link PhoneAccountHandle} on which the call will be placed.
+ * @param allowInteractiveResponse a boolean to tell if the implemented
+ * {@link CallRedirectionService} should allow interactive
+ * responses with users. Will be {@code false} if, for example
+ * the device is in car mode and the user would not be able to
+ * interact with their device.
*/
- public abstract void onPlaceCall(Uri handle, PhoneAccountHandle targetPhoneAccount);
+ public abstract void onPlaceCall(@NonNull Uri handle,
+ @NonNull PhoneAccountHandle initialPhoneAccount,
+ boolean allowInteractiveResponse);
/**
* The implemented {@link CallRedirectionService} calls this method to response a request
- * received via {@link #onPlaceCall(Uri, PhoneAccountHandle)} to inform Telecom that no changes
- * are required to the outgoing call, and that the call should be placed as-is.
+ * received via {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)} to inform Telecom that
+ * no changes are required to the outgoing call, and that the call should be placed as-is.
*
- * This can only be called from implemented {@link #onPlaceCall(Uri, PhoneAccountHandle)}.
+ * <p>This can only be called from implemented
+ * {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)}.
*
*/
public final void placeCallUnmodified() {
@@ -89,29 +103,39 @@
/**
* The implemented {@link CallRedirectionService} calls this method to response a request
- * received via {@link #onPlaceCall(Uri, PhoneAccountHandle)} to inform Telecom that changes
- * are required to the phone number or/and {@link PhoneAccountHandle} for the outgoing call.
+ * received via {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)} to inform Telecom that
+ * changes are required to the phone number or/and {@link PhoneAccountHandle} for the outgoing
+ * call. Telecom will cancel the call if the implemented {@link CallRedirectionService}
+ * replies Telecom a handle for an emergency number.
*
- * This can only be called from implemented {@link #onPlaceCall(Uri, PhoneAccountHandle)}.
+ * <p>This can only be called from implemented
+ * {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)}.
*
* @param handle the new phone number to dial
* @param targetPhoneAccount the {@link PhoneAccountHandle} to use when placing the call.
* If {@code null}, no change will be made to the
* {@link PhoneAccountHandle} used to place the call.
+ * @param confirmFirst Telecom will ask users to confirm the redirection via a yes/no dialog
+ * if the confirmFirst is true, and if the redirection request of this
+ * response was sent with a true flag of allowInteractiveResponse via
+ * {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)}
*/
- public final void redirectCall(Uri handle, PhoneAccountHandle targetPhoneAccount) {
+ public final void redirectCall(@NonNull Uri handle,
+ @NonNull PhoneAccountHandle targetPhoneAccount,
+ boolean confirmFirst) {
try {
- mCallRedirectionAdapter.redirectCall(handle, targetPhoneAccount);
+ mCallRedirectionAdapter.redirectCall(handle, targetPhoneAccount, confirmFirst);
} catch (RemoteException e) {
}
}
/**
* The implemented {@link CallRedirectionService} calls this method to response a request
- * received via {@link #onPlaceCall(Uri, PhoneAccountHandle)} to inform Telecom that an outgoing
- * call should be canceled entirely.
+ * received via {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)} to inform Telecom that
+ * an outgoing call should be canceled entirely.
*
- * This can only be called from implemented {@link #onPlaceCall(Uri, PhoneAccountHandle)}.
+ * <p>This can only be called from implemented
+ * {@link #onPlaceCall(Uri, PhoneAccountHandle, boolean)}.
*
*/
public final void cancelCall() {
@@ -137,7 +161,8 @@
SomeArgs args = (SomeArgs) msg.obj;
try {
mCallRedirectionAdapter = (ICallRedirectionAdapter) args.arg1;
- onPlaceCall((Uri) args.arg2, (PhoneAccountHandle) args.arg3);
+ onPlaceCall((Uri) args.arg2, (PhoneAccountHandle) args.arg3,
+ (boolean) args.arg4);
} finally {
args.recycle();
}
@@ -152,15 +177,20 @@
* Telecom calls this method to inform the CallRedirectionService of a new outgoing call
* which is about to be placed.
* @param handle the phone number dialed by the user
- * @param targetPhoneAccount the URI of the number the user dialed
+ * @param initialPhoneAccount the URI of the number the user dialed
+ * @param allowInteractiveResponse a boolean to tell if the implemented
+ * {@link CallRedirectionService} should allow interactive
+ * responses with users.
*/
@Override
- public void placeCall(ICallRedirectionAdapter adapter, Uri handle,
- PhoneAccountHandle targetPhoneAccount) {
+ public void placeCall(@NonNull ICallRedirectionAdapter adapter, @NonNull Uri handle,
+ @NonNull PhoneAccountHandle initialPhoneAccount,
+ boolean allowInteractiveResponse) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = adapter;
args.arg2 = handle;
- args.arg3 = targetPhoneAccount;
+ args.arg3 = initialPhoneAccount;
+ args.arg4 = allowInteractiveResponse;
mHandler.obtainMessage(MSG_PLACE_CALL, args).sendToTarget();
}
}
diff --git a/telecomm/java/com/android/internal/telecom/ICallRedirectionAdapter.aidl b/telecomm/java/com/android/internal/telecom/ICallRedirectionAdapter.aidl
index 46bf983..0a42a3f 100644
--- a/telecomm/java/com/android/internal/telecom/ICallRedirectionAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/ICallRedirectionAdapter.aidl
@@ -31,5 +31,6 @@
void placeCallUnmodified();
- void redirectCall(in Uri handle, in PhoneAccountHandle targetPhoneAccount);
+ void redirectCall(in Uri handle, in PhoneAccountHandle targetPhoneAccount,
+ boolean confirmFirst);
}
diff --git a/telecomm/java/com/android/internal/telecom/ICallRedirectionService.aidl b/telecomm/java/com/android/internal/telecom/ICallRedirectionService.aidl
index d8d360b..c1bc440 100644
--- a/telecomm/java/com/android/internal/telecom/ICallRedirectionService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ICallRedirectionService.aidl
@@ -30,5 +30,5 @@
*/
oneway interface ICallRedirectionService {
void placeCall(in ICallRedirectionAdapter adapter, in Uri handle,
- in PhoneAccountHandle targetPhoneAccount);
+ in PhoneAccountHandle initialPhoneAccount, boolean allowInteractiveResponse);
}
diff --git a/telephony/java/android/telephony/ims/RcsParticipant.java b/telephony/java/android/telephony/ims/RcsParticipant.java
index 70500aa..f678ec7e 100644
--- a/telephony/java/android/telephony/ims/RcsParticipant.java
+++ b/telephony/java/android/telephony/ims/RcsParticipant.java
@@ -15,22 +15,111 @@
*/
package android.telephony.ims;
+import static android.telephony.ims.RcsMessageStore.TAG;
+
+import android.annotation.NonNull;
+import android.annotation.WorkerThread;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.telephony.Rlog;
+import android.telephony.ims.aidl.IRcs;
+import android.text.TextUtils;
+
+import com.android.internal.util.Preconditions;
/**
* RcsParticipant is an RCS capable contact that can participate in {@link RcsThread}s.
* @hide - TODO(sahinc) make this public
*/
public class RcsParticipant implements Parcelable {
+ // The row ID of this participant in the database
+ private int mId;
+ // The phone number of this participant
+ private String mCanonicalAddress;
+ // The RCS alias of this participant. This is different than the name of the contact in the
+ // Contacts app - i.e. RCS protocol allows users to define aliases for themselves that doesn't
+ // require other users to add them as contacts and give them a name.
+ private String mAlias;
+
/**
- * Returns the row id of this participant.
+ * Constructor for {@link com.android.internal.telephony.ims.RcsMessageStoreController}
+ * to create instances of participants. This is not meant to be part of the SDK.
*
- * TODO(sahinc) implement
+ * @hide
+ */
+ public RcsParticipant(int id, @NonNull String canonicalAddress) {
+ mId = id;
+ mCanonicalAddress = canonicalAddress;
+ }
+
+ /**
+ * @return Returns the canonical address (i.e. normalized phone number) for this participant
+ */
+ public String getCanonicalAddress() {
+ return mCanonicalAddress;
+ }
+
+ /**
+ * Sets the canonical address for this participant and updates it in storage.
+ * @param canonicalAddress the canonical address to update to.
+ */
+ @WorkerThread
+ public void setCanonicalAddress(@NonNull String canonicalAddress) {
+ Preconditions.checkNotNull(canonicalAddress);
+ if (canonicalAddress.equals(mCanonicalAddress)) {
+ return;
+ }
+
+ mCanonicalAddress = canonicalAddress;
+
+ try {
+ IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs"));
+ if (iRcs != null) {
+ iRcs.updateRcsParticipantCanonicalAddress(mId, mCanonicalAddress);
+ }
+ } catch (RemoteException re) {
+ Rlog.e(TAG, "RcsParticipant: Exception happened during setCanonicalAddress", re);
+ }
+ }
+
+ /**
+ * @return Returns the alias for this participant. Alias is usually the real name of the person
+ * themselves.
+ */
+ public String getAlias() {
+ return mAlias;
+ }
+
+ /**
+ * Sets the alias for this participant and persists it in storage. Alias is usually the real
+ * name of the person themselves.
+ */
+ @WorkerThread
+ public void setAlias(String alias) {
+ if (TextUtils.equals(mAlias, alias)) {
+ return;
+ }
+ mAlias = alias;
+
+ try {
+ IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs"));
+ if (iRcs != null) {
+ iRcs.updateRcsParticipantAlias(mId, mAlias);
+ }
+ } catch (RemoteException re) {
+ Rlog.e(TAG, "RcsParticipant: Exception happened during setCanonicalAddress", re);
+ }
+ }
+
+ /**
+ * Returns the row id of this participant. This is not meant to be part of the SDK
+ *
* @hide
*/
public int getId() {
- return 12345;
+ return mId;
}
public static final Creator<RcsParticipant> CREATOR = new Creator<RcsParticipant>() {
@@ -46,6 +135,9 @@
};
protected RcsParticipant(Parcel in) {
+ mId = in.readInt();
+ mCanonicalAddress = in.readString();
+ mAlias = in.readString();
}
@Override
@@ -55,6 +147,8 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
-
+ dest.writeInt(mId);
+ dest.writeString(mCanonicalAddress);
+ dest.writeString(mAlias);
}
}
diff --git a/telephony/java/android/telephony/ims/aidl/IRcs.aidl b/telephony/java/android/telephony/ims/aidl/IRcs.aidl
index 9badac5..0c958ba 100644
--- a/telephony/java/android/telephony/ims/aidl/IRcs.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IRcs.aidl
@@ -37,8 +37,13 @@
Rcs1To1Thread createRcs1To1Thread(in RcsParticipant participant);
- RcsParticipant createRcsParticipant(String canonicalAddress);
-
// RcsThread APIs
int getMessageCount(int rcsThreadId);
+
+ // RcsParticipant APIs
+ RcsParticipant createRcsParticipant(String canonicalAddress);
+
+ void updateRcsParticipantCanonicalAddress(int id, String canonicalAddress);
+
+ void updateRcsParticipantAlias(int id, String alias);
}
\ No newline at end of file
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/RcsTests/src/com/android/tests/ims/RcsParticipantTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsParticipantTest.java
new file mode 100644
index 0000000..c402dbf
--- /dev/null
+++ b/tests/RcsTests/src/com/android/tests/ims/RcsParticipantTest.java
@@ -0,0 +1,46 @@
+/*
+ * 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.tests.ims;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Bundle;
+import android.support.test.runner.AndroidJUnit4;
+import android.telephony.ims.RcsParticipant;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class RcsParticipantTest {
+ private static final int ID = 123;
+ private static final String ALIAS = "alias";
+ private static final String CANONICAL_ADDRESS = "+1234567890";
+
+ @Test
+ public void testCanUnparcel() {
+ RcsParticipant rcsParticipant = new RcsParticipant(ID, CANONICAL_ADDRESS);
+ rcsParticipant.setAlias(ALIAS);
+
+ Bundle bundle = new Bundle();
+ bundle.putParcelable("Some key", rcsParticipant);
+ rcsParticipant = bundle.getParcelable("Some key");
+
+ assertThat(rcsParticipant.getId()).isEqualTo(ID);
+ assertThat(rcsParticipant.getAlias()).isEqualTo(ALIAS);
+ assertThat(rcsParticipant.getCanonicalAddress()).isEqualTo(CANONICAL_ADDRESS);
+ }
+}
diff --git a/tests/UsageReportingTest/Android.mk b/tests/UsageReportingTest/Android.mk
new file mode 100644
index 0000000..afb6e16b1
--- /dev/null
+++ b/tests/UsageReportingTest/Android.mk
@@ -0,0 +1,17 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+# Only compile source java files in this apk.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_USE_AAPT2 := true
+LOCAL_STATIC_ANDROID_LIBRARIES := androidx.legacy_legacy-support-v4
+
+LOCAL_CERTIFICATE := platform
+
+LOCAL_PACKAGE_NAME := UsageReportingTest
+LOCAL_PRIVATE_PLATFORM_APIS := true
+
+include $(BUILD_PACKAGE)
diff --git a/tests/UsageReportingTest/AndroidManifest.xml b/tests/UsageReportingTest/AndroidManifest.xml
new file mode 100644
index 0000000..be0b09e
--- /dev/null
+++ b/tests/UsageReportingTest/AndroidManifest.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ Note: Add android:sharedUserId="android.uid.system" to the root element to simulate the system UID
+ caller case.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.tests.usagereporter"
+ >
+
+ <application android:label="@string/reporter_app">
+ <activity android:name="UsageReporterActivity"
+ android:label="UsageReporter">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ </application>
+</manifest>
diff --git a/tests/UsageReportingTest/res/layout/row_item.xml b/tests/UsageReportingTest/res/layout/row_item.xml
new file mode 100644
index 0000000..1eb2dab
--- /dev/null
+++ b/tests/UsageReportingTest/res/layout/row_item.xml
@@ -0,0 +1,36 @@
+<?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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:background="@color/inactive_color">
+
+ <TextView android:id="@+id/token"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textStyle="bold"/>
+
+ <Button android:id="@+id/start" style="@style/ActionButton"
+ android:text="@string/start" />
+
+ <Button android:id="@+id/stop" style="@style/ActionButton"
+ android:text="@string/stop" />
+</LinearLayout>
diff --git a/tests/UsageReportingTest/res/menu/main.xml b/tests/UsageReportingTest/res/menu/main.xml
new file mode 100644
index 0000000..9847c2d
--- /dev/null
+++ b/tests/UsageReportingTest/res/menu/main.xml
@@ -0,0 +1,28 @@
+<?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.
+-->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@+id/add_token"
+ android:title="@string/add_token"/>
+ <item android:id="@+id/add_many_tokens"
+ android:title="@string/add_many_tokens"/>
+ <item android:id="@+id/stop_all"
+ android:title="@string/stop_all_tokens"/>
+ <group android:checkableBehavior="all">
+ <item android:id="@+id/restore_on_start"
+ android:title="@string/restore_tokens_on_start"/>
+ </group>
+</menu>
\ No newline at end of file
diff --git a/tests/UsageReportingTest/res/values/colors.xml b/tests/UsageReportingTest/res/values/colors.xml
new file mode 100644
index 0000000..03bcf8a
--- /dev/null
+++ b/tests/UsageReportingTest/res/values/colors.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+<resources>
+ <color name="active_color">#FFF</color>
+ <color name="inactive_color">#AAA</color>
+</resources>
\ No newline at end of file
diff --git a/tests/UsageReportingTest/res/values/strings.xml b/tests/UsageReportingTest/res/values/strings.xml
new file mode 100644
index 0000000..015290e
--- /dev/null
+++ b/tests/UsageReportingTest/res/values/strings.xml
@@ -0,0 +1,47 @@
+<?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.
+-->
+
+<resources>
+ <!-- Do not translate -->
+ <string name="reporter_app">Usage Reporter App</string>
+ <!-- Do not translate -->
+ <string name="start">Start</string>
+ <!-- Do not translate -->
+ <string name="stop">Stop</string>
+ <!-- Do not translate -->
+ <string name="default_token">SuperSecretToken</string>
+
+ <!-- Do not translate -->
+ <string name="add_token">Add Token</string>
+ <!-- Do not translate -->
+ <string name="add_many_tokens">Add Many Tokens</string>
+ <!-- Do not translate -->
+ <string name="stop_all_tokens">Stop All</string>
+ <!-- Do not translate -->
+ <string name="restore_tokens_on_start">Readd Tokens on Start</string>
+
+
+ <!-- Do not translate -->
+ <string name="token_query">Enter token(s) (delimit tokens with commas)</string>
+ <!-- Do not translate -->
+ <string name="many_tokens_query">Generate how many tokens?</string>
+ <!-- Do not translate -->
+ <string name="stop_all_tokens_query">Stop all tokens?</string>
+ <!-- Do not translate -->
+ <string name="ok">OK</string>
+ <!-- Do not translate -->
+ <string name="cancel">Cancel</string>
+</resources>
\ No newline at end of file
diff --git a/tests/UsageReportingTest/res/values/styles.xml b/tests/UsageReportingTest/res/values/styles.xml
new file mode 100644
index 0000000..e5b86c5
--- /dev/null
+++ b/tests/UsageReportingTest/res/values/styles.xml
@@ -0,0 +1,31 @@
+<?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.
+-->
+
+<resources>
+ <style name="ActionButton">
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:textAppearance">@style/TextAppearance.ActionButton</item>
+ </style>
+
+ <style name="TextAppearance" parent="android:TextAppearance">
+ </style>
+
+ <style name="TextAppearance.ActionButton">
+ <item name="android:textStyle">italic</item>
+ </style>
+
+</resources>
diff --git a/tests/UsageReportingTest/src/com/android/tests/usagereporter/UsageReporterActivity.java b/tests/UsageReportingTest/src/com/android/tests/usagereporter/UsageReporterActivity.java
new file mode 100644
index 0000000..946be8f
--- /dev/null
+++ b/tests/UsageReportingTest/src/com/android/tests/usagereporter/UsageReporterActivity.java
@@ -0,0 +1,320 @@
+/*
+ * 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.tests.usagereporter;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.ListActivity;
+import android.app.usage.UsageStatsManager;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.text.InputType;
+import android.text.TextUtils;
+import android.util.ArraySet;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import java.util.ArrayList;
+
+public class UsageReporterActivity extends ListActivity {
+
+ private Activity mActivity;
+ private final ArrayList<String> mTokens = new ArrayList();
+ private final ArraySet<String> mActives = new ArraySet();
+ private UsageStatsManager mUsageStatsManager;
+ private Adapter mAdapter;
+ private boolean mRestoreOnStart = false;
+ private static Context sContext;
+
+ /** Called with the activity is first created. */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ sContext = getApplicationContext();
+
+ mUsageStatsManager = (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE);
+
+ mAdapter = new Adapter();
+ setListAdapter(mAdapter);
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ mActivity = this;
+
+
+ if (mRestoreOnStart) {
+ ArrayList<String> removed = null;
+ for (String token : mActives) {
+ try {
+ mUsageStatsManager.reportUsageStart(mActivity, token);
+ } catch (Exception e) {
+ // Somthing went wrong, recover and move on
+ if (removed == null) {
+ removed = new ArrayList();
+ }
+ removed.add(token);
+ }
+ }
+ if (removed != null) {
+ for (String token : removed) {
+ mActives.remove(token);
+ }
+ }
+ } else {
+ mActives.clear();
+ }
+ }
+
+ /**
+ * Called when the activity is about to start interacting with the user.
+ */
+ @Override
+ protected void onResume() {
+ super.onResume();
+ mAdapter.notifyDataSetChanged();
+ }
+
+
+ /**
+ * Called when your activity's options menu needs to be created.
+ */
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.main, menu);
+ return super.onCreateOptionsMenu(menu);
+ }
+
+
+ /**
+ * Called when a menu item is selected.
+ */
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.add_token:
+ callAddToken();
+ return true;
+ case R.id.add_many_tokens:
+ callAddManyTokens();
+ return true;
+ case R.id.stop_all:
+ callStopAll();
+ return true;
+ case R.id.restore_on_start:
+ mRestoreOnStart = !mRestoreOnStart;
+ item.setChecked(mRestoreOnStart);
+ return true;
+ }
+
+ return super.onOptionsItemSelected(item);
+ }
+
+ private void callAddToken() {
+ final AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(getString(R.string.token_query));
+ final EditText input = new EditText(this);
+ input.setInputType(InputType.TYPE_CLASS_TEXT);
+ input.setHint(getString(R.string.default_token));
+ builder.setView(input);
+
+ builder.setPositiveButton(getString(R.string.ok), new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ String tokenNames = input.getText().toString().trim();
+ if (TextUtils.isEmpty(tokenNames)) {
+ tokenNames = getString(R.string.default_token);
+ }
+ String[] tokens = tokenNames.split(",");
+ for (String token : tokens) {
+ if (mTokens.contains(token)) continue;
+ mTokens.add(token);
+ }
+ mAdapter.notifyDataSetChanged();
+
+ }
+ });
+ builder.setNegativeButton(getString(R.string.cancel),
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.cancel();
+ }
+ });
+
+ builder.show();
+ }
+
+ private void callAddManyTokens() {
+ final AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(getString(R.string.many_tokens_query));
+ final EditText input = new EditText(this);
+ input.setInputType(InputType.TYPE_CLASS_NUMBER);
+ builder.setView(input);
+
+ builder.setPositiveButton(getString(R.string.ok),
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ String val = input.getText().toString().trim();
+ if (TextUtils.isEmpty(val)) return;
+ int n = Integer.parseInt(val);
+ for (int i = 0; i < n; i++) {
+ final String token = getString(R.string.default_token) + i;
+ if (mTokens.contains(token)) continue;
+ mTokens.add(token);
+ }
+ mAdapter.notifyDataSetChanged();
+
+ }
+ });
+ builder.setNegativeButton(getString(R.string.cancel),
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.cancel();
+ }
+ });
+
+ builder.show();
+ }
+
+ private void callStopAll() {
+ final AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(getString(R.string.stop_all_tokens_query));
+
+ builder.setPositiveButton(getString(R.string.ok), new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ for (String token : mActives) {
+ mUsageStatsManager.reportUsageStop(mActivity, token);
+ }
+ mActives.clear();
+ mAdapter.notifyDataSetChanged();
+ }
+ });
+ builder.setNegativeButton(getString(R.string.cancel),
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.cancel();
+ }
+ });
+ builder.show();
+ }
+
+ /**
+ * A call-back for when the user presses the back button.
+ */
+ OnClickListener mStartListener = new OnClickListener() {
+ public void onClick(View v) {
+ final View parent = (View) v.getParent();
+ final String token = ((TextView) parent.findViewById(R.id.token)).getText().toString();
+ try {
+ mUsageStatsManager.reportUsageStart(mActivity, token);
+ } catch (Exception e) {
+ Toast.makeText(sContext, e.toString(), Toast.LENGTH_LONG).show();
+ }
+ parent.setBackgroundColor(getColor(R.color.active_color));
+ mActives.add(token);
+ }
+ };
+
+ /**
+ * A call-back for when the user presses the clear button.
+ */
+ OnClickListener mStopListener = new OnClickListener() {
+ public void onClick(View v) {
+ final View parent = (View) v.getParent();
+
+ final String token = ((TextView) parent.findViewById(R.id.token)).getText().toString();
+ try {
+ mUsageStatsManager.reportUsageStop(mActivity, token);
+ } catch (Exception e) {
+ Toast.makeText(sContext, e.toString(), Toast.LENGTH_LONG).show();
+ }
+ parent.setBackgroundColor(getColor(R.color.inactive_color));
+ mActives.remove(token);
+ }
+ };
+
+
+ private class Adapter extends BaseAdapter {
+ @Override
+ public int getCount() {
+ return mTokens.size();
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return mTokens.get(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ final ViewHolder holder;
+ if (convertView == null) {
+ convertView = LayoutInflater.from(UsageReporterActivity.this)
+ .inflate(R.layout.row_item, parent, false);
+ holder = new ViewHolder();
+ holder.tokenName = (TextView) convertView.findViewById(R.id.token);
+
+ holder.startButton = ((Button) convertView.findViewById(R.id.start));
+ holder.startButton.setOnClickListener(mStartListener);
+ holder.stopButton = ((Button) convertView.findViewById(R.id.stop));
+ holder.stopButton.setOnClickListener(mStopListener);
+ convertView.setTag(holder);
+ } else {
+ holder = (ViewHolder) convertView.getTag();
+ }
+
+ final String token = mTokens.get(position);
+ holder.tokenName.setText(mTokens.get(position));
+ if (mActives.contains(token)) {
+ convertView.setBackgroundColor(getColor(R.color.active_color));
+ } else {
+ convertView.setBackgroundColor(getColor(R.color.inactive_color));
+ }
+ return convertView;
+ }
+ }
+
+ private static class ViewHolder {
+ public TextView tokenName;
+ public Button startButton;
+ public Button stopButton;
+ }
+}
diff --git a/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java b/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java
index 3d8ce21..3c628f6 100644
--- a/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java
+++ b/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java
@@ -155,7 +155,7 @@
intent.setPackage(getPackageName());
intent.putExtra(EXTRA_KEY_TIMEOUT, true);
mUsageStatsManager.registerAppUsageObserver(1, packages,
- 30, TimeUnit.SECONDS, PendingIntent.getActivity(UsageStatsActivity.this,
+ 60, TimeUnit.SECONDS, PendingIntent.getActivity(UsageStatsActivity.this,
1, intent, 0));
}
}
diff --git a/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java b/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java
index d5987a5..8564698 100644
--- a/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java
+++ b/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java
@@ -28,6 +28,7 @@
import android.view.IWindowSession;
import android.view.InsetsState;
import android.view.Surface;
+import android.view.SurfaceControl;
import android.view.View;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
@@ -106,7 +107,7 @@
window.mSeq, mLayoutParams, -1, -1, View.VISIBLE, 0, -1, mTmpRect,
mTmpRect, mTmpRect, mTmpRect, mTmpRect, mTmpRect, mTmpRect,
new DisplayCutout.ParcelableWrapper(), new MergedConfiguration(),
- new Surface(), new InsetsState());
+ new SurfaceControl(), new InsetsState());
} catch (RemoteException e) {
e.printStackTrace();
}
diff --git a/tests/net/java/android/net/dhcp/DhcpServingParamsParcelExtTest.java b/tests/net/java/android/net/dhcp/DhcpServingParamsParcelExtTest.java
new file mode 100644
index 0000000..4a6f20a
--- /dev/null
+++ b/tests/net/java/android/net/dhcp/DhcpServingParamsParcelExtTest.java
@@ -0,0 +1,113 @@
+/*
+ * 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;
+
+import static android.net.InetAddresses.parseNumericAddress;
+
+import static com.google.android.collect.Sets.newHashSet;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.net.LinkAddress;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.net.Inet4Address;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class DhcpServingParamsParcelExtTest {
+ private static final Inet4Address TEST_ADDRESS = inet4Addr("192.168.0.123");
+ private static final int TEST_ADDRESS_PARCELED = 0xc0a8007b;
+ private static final int TEST_PREFIX_LENGTH = 17;
+ private static final int TEST_LEASE_TIME_SECS = 120;
+ private static final int TEST_MTU = 1000;
+ private static final Set<Inet4Address> TEST_ADDRESS_SET =
+ newHashSet(inet4Addr("192.168.1.123"), inet4Addr("192.168.1.124"));
+ private static final Set<Integer> TEST_ADDRESS_SET_PARCELED =
+ newHashSet(0xc0a8017b, 0xc0a8017c);
+
+ private DhcpServingParamsParcelExt mParcel;
+
+ @Before
+ public void setUp() {
+ mParcel = new DhcpServingParamsParcelExt();
+ }
+
+ @Test
+ public void testSetServerAddr() {
+ mParcel.setServerAddr(new LinkAddress(TEST_ADDRESS, TEST_PREFIX_LENGTH));
+
+ assertEquals(TEST_ADDRESS_PARCELED, mParcel.serverAddr);
+ assertEquals(TEST_PREFIX_LENGTH, mParcel.serverAddrPrefixLength);
+ }
+
+ @Test
+ public void testSetDefaultRouters() {
+ mParcel.setDefaultRouters(TEST_ADDRESS_SET);
+ assertEquals(TEST_ADDRESS_SET_PARCELED, asSet(mParcel.defaultRouters));
+ }
+
+ @Test
+ public void testSetDnsServers() {
+ mParcel.setDnsServers(TEST_ADDRESS_SET);
+ assertEquals(TEST_ADDRESS_SET_PARCELED, asSet(mParcel.dnsServers));
+ }
+
+ @Test
+ public void testSetExcludedAddrs() {
+ mParcel.setExcludedAddrs(TEST_ADDRESS_SET);
+ assertEquals(TEST_ADDRESS_SET_PARCELED, asSet(mParcel.excludedAddrs));
+ }
+
+ @Test
+ public void testSetDhcpLeaseTimeSecs() {
+ mParcel.setDhcpLeaseTimeSecs(TEST_LEASE_TIME_SECS);
+ assertEquals(TEST_LEASE_TIME_SECS, mParcel.dhcpLeaseTimeSecs);
+ }
+
+ @Test
+ public void testSetLinkMtu() {
+ mParcel.setLinkMtu(TEST_MTU);
+ assertEquals(TEST_MTU, mParcel.linkMtu);
+ }
+
+ @Test
+ public void testSetMetered() {
+ mParcel.setMetered(true);
+ assertTrue(mParcel.metered);
+ mParcel.setMetered(false);
+ assertFalse(mParcel.metered);
+ }
+
+ private static Inet4Address inet4Addr(String addr) {
+ return (Inet4Address) parseNumericAddress(addr);
+ }
+
+ private static Set<Integer> asSet(int[] ints) {
+ return IntStream.of(ints).boxed().collect(Collectors.toSet());
+ }
+}
diff --git a/tests/net/java/android/net/ip/IpServerTest.java b/tests/net/java/android/net/ip/IpServerTest.java
index 2c675c6..c3162af 100644
--- a/tests/net/java/android/net/ip/IpServerTest.java
+++ b/tests/net/java/android/net/ip/IpServerTest.java
@@ -16,31 +16,37 @@
package android.net.ip;
+import static android.net.ConnectivityManager.TETHERING_BLUETOOTH;
+import static android.net.ConnectivityManager.TETHERING_USB;
+import static android.net.ConnectivityManager.TETHERING_WIFI;
+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;
import static org.mockito.Mockito.when;
-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.ConnectivityManager.TETHERING_BLUETOOTH;
-import static android.net.ConnectivityManager.TETHERING_USB;
-import static android.net.ConnectivityManager.TETHERING_WIFI;
-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 android.net.INetworkStatsService;
import android.net.InterfaceConfiguration;
import android.net.IpPrefix;
@@ -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;
@@ -60,8 +67,6 @@
import android.support.test.runner.AndroidJUnit4;
import android.text.TextUtils;
-import java.net.Inet4Address;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -71,6 +76,8 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.net.Inet4Address;
+
@RunWith(AndroidJUnit4.class)
@SmallTest
public class IpServerTest {
@@ -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/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 1700006..07f7cb3 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -16,7 +16,6 @@
package android.net.wifi;
-
import android.content.pm.ParceledListSlice;
import android.net.wifi.hotspot2.OsuProvider;
@@ -61,11 +60,11 @@
ParceledListSlice getConfiguredNetworks(String packageName);
- ParceledListSlice getPrivilegedConfiguredNetworks();
+ ParceledListSlice getPrivilegedConfiguredNetworks(String packageName);
- List<WifiConfiguration> getAllMatchingWifiConfigs(in List<ScanResult> scanResult);
+ Map getAllMatchingFqdnsForScanResults(in List<ScanResult> scanResult);
- List<OsuProvider> getMatchingOsuProviders(in List<ScanResult> scanResult);
+ Map getMatchingOsuProviders(in List<ScanResult> scanResult);
Map getMatchingPasspointConfigsForOsuProviders(in List<OsuProvider> osuProviders);
@@ -77,6 +76,8 @@
List<PasspointConfiguration> getPasspointConfigurations();
+ List<WifiConfiguration> getWifiConfigsForPasspointProfiles(in List<String> fqdnList);
+
void queryPasspointIcon(long bssid, String fileName);
int matchProviderWithCurrentNetwork(String fqdn);
diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java
index 7cdd16a..af5ad51 100644
--- a/wifi/java/android/net/wifi/WifiInfo.java
+++ b/wifi/java/android/net/wifi/WifiInfo.java
@@ -114,6 +114,11 @@
private boolean mTrusted;
/**
+ * OSU (Online Sign Up) AP for Passpoint R2.
+ */
+ private boolean mOsuAp;
+
+ /**
* Running total count of lost (not ACKed) transmitted unicast data packets.
* @hide
*/
@@ -190,6 +195,7 @@
setFrequency(-1);
setMeteredHint(false);
setEphemeral(false);
+ setOsuAp(false);
txBad = 0;
txSuccess = 0;
rxSuccess = 0;
@@ -219,6 +225,7 @@
mMeteredHint = source.mMeteredHint;
mEphemeral = source.mEphemeral;
mTrusted = source.mTrusted;
+ mOsuAp = source.mOsuAp;
txBad = source.txBad;
txRetries = source.txRetries;
txSuccess = source.txSuccess;
@@ -411,6 +418,15 @@
return mTrusted;
}
+ /** {@hide} */
+ public void setOsuAp(boolean osuAp) {
+ mOsuAp = osuAp;
+ }
+
+ /** {@hide} */
+ public boolean isOsuAp() {
+ return mOsuAp;
+ }
/** @hide */
@UnsupportedAppUsage
@@ -565,6 +581,7 @@
dest.writeLong(rxSuccess);
dest.writeDouble(rxSuccessRate);
mSupplicantState.writeToParcel(dest, flags);
+ dest.writeInt(mOsuAp ? 1 : 0);
}
/** Implement the Parcelable interface {@hide} */
@@ -600,6 +617,7 @@
info.rxSuccess = in.readLong();
info.rxSuccessRate = in.readDouble();
info.mSupplicantState = SupplicantState.CREATOR.createFromParcel(in);
+ info.mOsuAp = in.readInt() != 0;
return info;
}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index e67e8ea..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;
@@ -44,6 +48,7 @@
import android.os.RemoteException;
import android.os.WorkSource;
import android.util.Log;
+import android.util.Pair;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
@@ -1135,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>
@@ -1161,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 =
@@ -1176,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();
}
@@ -1191,25 +1201,39 @@
}
/**
- * Returns all matching WifiConfigurations for a given list of ScanResult.
+ * Returns a list of all matching WifiConfigurations for a given list of ScanResult.
*
* An empty list will be returned when no configurations are installed or if no configurations
* match the ScanResult.
-
+ *
* @param scanResults a list of scanResult that represents the BSSID
- * @return A list of {@link WifiConfiguration} that can have duplicate entries.
+ * @return List that consists of {@link WifiConfiguration} and corresponding scanResults.
* @throws UnsupportedOperationException if Passpoint is not enabled on the device.
* @hide
*/
- @SystemApi
@RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
- public List<WifiConfiguration> getAllMatchingWifiConfigs(
+ public List<Pair<WifiConfiguration, List<ScanResult>>> getAllMatchingWifiConfigs(
@NonNull List<ScanResult> scanResults) {
+ List<Pair<WifiConfiguration, List<ScanResult>>> configs = new ArrayList<>();
try {
- return mService.getAllMatchingWifiConfigs(scanResults);
+ Map<String, List<ScanResult>> results = mService.getAllMatchingFqdnsForScanResults(
+ scanResults);
+ if (results.isEmpty()) {
+ return configs;
+ }
+ List<WifiConfiguration> wifiConfigurations =
+ mService.getWifiConfigsForPasspointProfiles(new ArrayList<>(results.keySet()));
+ for (WifiConfiguration configuration : wifiConfigurations) {
+ List<ScanResult> scanResultList = results.get(configuration.FQDN);
+ if (scanResultList != null) {
+ configs.add(Pair.create(configuration, scanResultList));
+ }
+ }
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
+
+ return configs;
}
/**
@@ -1219,12 +1243,13 @@
* An empty list will be returned if no match is found.
*
* @param scanResults a list of ScanResult
- * @return A list of {@link OsuProvider} that does not contain duplicate entries.
+ * @return Map that consists {@link OsuProvider} and a list of matching {@link ScanResult}
* @throws UnsupportedOperationException if Passpoint is not enabled on the device.
* @hide
*/
@RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
- public List<OsuProvider> getMatchingOsuProviders(List<ScanResult> scanResults) {
+ public Map<OsuProvider, List<ScanResult>> getMatchingOsuProviders(
+ List<ScanResult> scanResults) {
try {
return mService.getMatchingOsuProviders(scanResults);
} catch (RemoteException e) {
diff --git a/wifi/java/com/android/server/wifi/AbstractWifiService.java b/wifi/java/com/android/server/wifi/AbstractWifiService.java
index e94b9e6..f06346c 100644
--- a/wifi/java/com/android/server/wifi/AbstractWifiService.java
+++ b/wifi/java/com/android/server/wifi/AbstractWifiService.java
@@ -21,6 +21,7 @@
import android.content.pm.ParceledListSlice;
import android.net.DhcpInfo;
import android.net.Network;
+import android.net.wifi.IDppCallback;
import android.net.wifi.INetworkRequestMatchCallback;
import android.net.wifi.ISoftApCallback;
import android.net.wifi.ITrafficStateCallback;
@@ -35,7 +36,9 @@
import android.net.wifi.hotspot2.PasspointConfiguration;
import android.os.IBinder;
import android.os.Messenger;
+import android.os.RemoteException;
import android.os.ResultReceiver;
+import android.os.RemoteException;
import android.os.WorkSource;
import java.util.List;
@@ -79,51 +82,19 @@
}
@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();
}
@Override
- public List<WifiConfiguration> getAllMatchingWifiConfigs(List<ScanResult> scanResults) {
- 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) {
+ public Map<String, List<ScanResult>> getAllMatchingFqdnsForScanResults(
+ List<ScanResult> scanResults) {
throw new UnsupportedOperationException();
}
@Override
- public List<OsuProvider> getMatchingOsuProviders(List<ScanResult> scanResults) {
+ public Map<OsuProvider, List<ScanResult>> getMatchingOsuProviders(
+ List<ScanResult> scanResults) {
throw new UnsupportedOperationException();
}
@@ -155,6 +126,11 @@
}
@Override
+ public List<WifiConfiguration> getWifiConfigsForPasspointProfiles(List<String> fqdnList) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public void queryPasspointIcon(long bssid, String fileName) {
throw new UnsupportedOperationException();
}
@@ -464,4 +440,26 @@
public String[] getFactoryMacAddresses() {
throw new UnsupportedOperationException();
}
+
+ @Override
+ public void setDeviceMobilityState(int state) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void startDppAsConfiguratorInitiator(IBinder binder, String enrolleeUri,
+ int selectedNetworkId, int netRole, IDppCallback callback) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void startDppAsEnrolleeInitiator(IBinder binder, String configuratorUri,
+ IDppCallback callback) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void stopDppSession() throws RemoteException {
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/wifi/tests/src/android/net/wifi/WifiInfoTest.java b/wifi/tests/src/android/net/wifi/WifiInfoTest.java
index fb0af5f..677bf37 100644
--- a/wifi/tests/src/android/net/wifi/WifiInfoTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiInfoTest.java
@@ -47,6 +47,7 @@
writeWifiInfo.txBad = TEST_TX_BAD;
writeWifiInfo.rxSuccess = TEST_RX_SUCCESS;
writeWifiInfo.setTrusted(true);
+ writeWifiInfo.setOsuAp(true);
Parcel parcel = Parcel.obtain();
writeWifiInfo.writeToParcel(parcel, 0);
@@ -60,5 +61,6 @@
assertEquals(TEST_TX_BAD, readWifiInfo.txBad);
assertEquals(TEST_RX_SUCCESS, readWifiInfo.rxSuccess);
assertTrue(readWifiInfo.isTrusted());
+ assertTrue(readWifiInfo.isOsuAp());
}
}
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index c43948b..4fbef5a 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -77,7 +77,9 @@
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
/**
* Unit tests for {@link android.net.wifi.WifiManager}.
@@ -1274,14 +1276,20 @@
}
/**
- * Check the call to getAllMatchingWifiConfigs calls getAllMatchingWifiConfigs of WifiService
- * with the provided a list of ScanResult.
+ * Check the call to getAllMatchingWifiConfigs calls getAllMatchingFqdnsForScanResults and
+ * getWifiConfigsForPasspointProfiles of WifiService in order.
*/
@Test
public void testGetAllMatchingWifiConfigs() throws Exception {
+ Map<String, List<ScanResult>> fqdns = new HashMap<>();
+ fqdns.put("www.test.com", new ArrayList<>());
+ when(mWifiService.getAllMatchingFqdnsForScanResults(any(List.class))).thenReturn(fqdns);
+ InOrder inOrder = inOrder(mWifiService);
+
mWifiManager.getAllMatchingWifiConfigs(new ArrayList<>());
- verify(mWifiService).getAllMatchingWifiConfigs(any(List.class));
+ inOrder.verify(mWifiService).getAllMatchingFqdnsForScanResults(any(List.class));
+ inOrder.verify(mWifiService).getWifiConfigsForPasspointProfiles(any(List.class));
}
/**