Merge "Move dimens from overlay to framework"
diff --git a/Android.bp b/Android.bp
index b64907a..0547481 100644
--- a/Android.bp
+++ b/Android.bp
@@ -414,6 +414,7 @@
"libcore-platform-compat-config",
"services-platform-compat-config",
"media-provider-platform-compat-config",
+ "services-devicepolicy-platform-compat-config",
],
sdk_version: "core_platform",
}
@@ -1595,4 +1596,4 @@
"core/java/com/android/internal/util/State.java",
"core/java/com/android/internal/util/StateMachine.java",
],
-}
\ No newline at end of file
+}
diff --git a/apct-tests/perftests/autofill/src/android/view/autofill/AbstractAutofillPerfTestCase.java b/apct-tests/perftests/autofill/src/android/view/autofill/AbstractAutofillPerfTestCase.java
index 6979f0f..48ce8ab 100644
--- a/apct-tests/perftests/autofill/src/android/view/autofill/AbstractAutofillPerfTestCase.java
+++ b/apct-tests/perftests/autofill/src/android/view/autofill/AbstractAutofillPerfTestCase.java
@@ -20,9 +20,9 @@
import android.os.Looper;
import android.perftests.utils.PerfStatusReporter;
+import android.perftests.utils.PerfTestActivity;
import android.perftests.utils.SettingsHelper;
import android.perftests.utils.SettingsStateKeeperRule;
-import android.perftests.utils.StubActivity;
import android.provider.Settings;
import androidx.test.InstrumentationRegistry;
@@ -46,8 +46,8 @@
Settings.Secure.AUTOFILL_SERVICE);
@Rule
- public ActivityTestRule<StubActivity> mActivityRule =
- new ActivityTestRule<StubActivity>(StubActivity.class);
+ public ActivityTestRule<PerfTestActivity> mActivityRule =
+ new ActivityTestRule<>(PerfTestActivity.class);
@Rule
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
@@ -68,7 +68,7 @@
Looper.getMainLooper().getThread() == Thread.currentThread());
assertTrue("We should be running on the main thread",
Looper.myLooper() == Looper.getMainLooper());
- StubActivity activity = mActivityRule.getActivity();
+ PerfTestActivity activity = mActivityRule.getActivity();
activity.setContentView(mLayoutId);
onCreate(activity);
});
@@ -89,9 +89,9 @@
}
/**
- * Initializes the {@link StubActivity} after it was launched.
+ * Initializes the {@link PerfTestActivity} after it was launched.
*/
- protected abstract void onCreate(StubActivity activity);
+ protected abstract void onCreate(PerfTestActivity activity);
/**
* Uses the {@code settings} binary to set the autofill service.
diff --git a/apct-tests/perftests/autofill/src/android/view/autofill/LoginTest.java b/apct-tests/perftests/autofill/src/android/view/autofill/LoginTest.java
index 8090826..fb5ea80 100644
--- a/apct-tests/perftests/autofill/src/android/view/autofill/LoginTest.java
+++ b/apct-tests/perftests/autofill/src/android/view/autofill/LoginTest.java
@@ -20,7 +20,7 @@
import static android.view.autofill.AutofillManager.AutofillCallback.EVENT_INPUT_SHOWN;
import android.perftests.utils.BenchmarkState;
-import android.perftests.utils.StubActivity;
+import android.perftests.utils.PerfTestActivity;
import android.view.View;
import android.widget.EditText;
@@ -39,7 +39,7 @@
}
@Override
- protected void onCreate(StubActivity activity) {
+ protected void onCreate(PerfTestActivity activity) {
View root = activity.getWindow().getDecorView();
mUsername = root.findViewById(R.id.username);
mPassword = root.findViewById(R.id.password);
diff --git a/apct-tests/perftests/core/AndroidManifest.xml b/apct-tests/perftests/core/AndroidManifest.xml
index 525975d..290f178 100644
--- a/apct-tests/perftests/core/AndroidManifest.xml
+++ b/apct-tests/perftests/core/AndroidManifest.xml
@@ -13,7 +13,7 @@
<application>
<uses-library android:name="android.test.runner" />
- <activity android:name="android.perftests.utils.StubActivity">
+ <activity android:name="android.perftests.utils.PerfTestActivity">
<intent-filter>
<action android:name="com.android.perftests.core.PERFTEST" />
</intent-filter>
diff --git a/apct-tests/perftests/core/src/android/app/PendingIntentPerfTest.java b/apct-tests/perftests/core/src/android/app/PendingIntentPerfTest.java
index b3f8359..a320514 100644
--- a/apct-tests/perftests/core/src/android/app/PendingIntentPerfTest.java
+++ b/apct-tests/perftests/core/src/android/app/PendingIntentPerfTest.java
@@ -20,7 +20,7 @@
import android.content.Intent;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.perftests.utils.StubActivity;
+import android.perftests.utils.PerfTestActivity;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.LargeTest;
@@ -48,7 +48,7 @@
@Before
public void setUp() {
mContext = InstrumentationRegistry.getTargetContext();
- mIntent = StubActivity.createLaunchIntent(mContext);
+ mIntent = PerfTestActivity.createLaunchIntent(mContext);
}
/**
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 3a80020..b9c7af4 100644
--- a/apct-tests/perftests/core/src/android/graphics/perftests/PaintHasGlyphPerfTest.java
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/PaintHasGlyphPerfTest.java
@@ -19,7 +19,7 @@
import android.graphics.Paint;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.perftests.utils.StubActivity;
+import android.perftests.utils.PerfTestActivity;
import androidx.test.filters.LargeTest;
import androidx.test.rule.ActivityTestRule;
@@ -58,7 +58,8 @@
}
@Rule
- public ActivityTestRule<StubActivity> mActivityRule = new ActivityTestRule(StubActivity.class);
+ public ActivityTestRule<PerfTestActivity> mActivityRule =
+ new ActivityTestRule<>(PerfTestActivity.class);
@Rule
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
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 3b2b8a9..d14e93e 100644
--- a/apct-tests/perftests/core/src/android/graphics/perftests/VectorDrawablePerfTest.java
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/VectorDrawablePerfTest.java
@@ -26,7 +26,7 @@
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.BitmapUtils;
import android.perftests.utils.PerfStatusReporter;
-import android.perftests.utils.StubActivity;
+import android.perftests.utils.PerfTestActivity;
import android.test.suitebuilder.annotation.LargeTest;
import androidx.test.rule.ActivityTestRule;
@@ -48,8 +48,8 @@
private int[] mTestHeights = {512, 1024};
@Rule
- public ActivityTestRule<StubActivity> mActivityRule =
- new ActivityTestRule(StubActivity.class);
+ public ActivityTestRule<PerfTestActivity> mActivityRule =
+ new ActivityTestRule<>(PerfTestActivity.class);
@Rule
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
diff --git a/apct-tests/perftests/core/src/android/text/DynamicLayoutPerfTest.java b/apct-tests/perftests/core/src/android/text/DynamicLayoutPerfTest.java
index 5be99d9..6b295e5 100644
--- a/apct-tests/perftests/core/src/android/text/DynamicLayoutPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/DynamicLayoutPerfTest.java
@@ -23,7 +23,7 @@
import android.graphics.Paint.FontMetricsInt;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.perftests.utils.StubActivity;
+import android.perftests.utils.PerfTestActivity;
import android.text.style.ReplacementSpan;
import android.util.ArraySet;
@@ -75,7 +75,8 @@
}
@Rule
- public ActivityTestRule<StubActivity> mActivityRule = new ActivityTestRule(StubActivity.class);
+ public ActivityTestRule<PerfTestActivity> mActivityRule =
+ new ActivityTestRule<>(PerfTestActivity.class);
@Rule
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
diff --git a/apct-tests/perftests/core/src/android/view/ViewShowHidePerfTest.java b/apct-tests/perftests/core/src/android/view/ViewShowHidePerfTest.java
index b34001d..b0edb11 100644
--- a/apct-tests/perftests/core/src/android/view/ViewShowHidePerfTest.java
+++ b/apct-tests/perftests/core/src/android/view/ViewShowHidePerfTest.java
@@ -23,7 +23,7 @@
import android.graphics.drawable.ColorDrawable;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.perftests.utils.StubActivity;
+import android.perftests.utils.PerfTestActivity;
import android.view.View.MeasureSpec;
import android.widget.FrameLayout;
import android.widget.ImageView;
@@ -46,7 +46,8 @@
public class ViewShowHidePerfTest {
@Rule
- public ActivityTestRule mActivityRule = new ActivityTestRule(StubActivity.class);
+ public ActivityTestRule<PerfTestActivity> mActivityRule =
+ new ActivityTestRule<>(PerfTestActivity.class);
@Rule
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
diff --git a/apct-tests/perftests/core/src/android/widget/EditTextBackspacePerfTest.java b/apct-tests/perftests/core/src/android/widget/EditTextBackspacePerfTest.java
index b3ea62a..270b4e5 100644
--- a/apct-tests/perftests/core/src/android/widget/EditTextBackspacePerfTest.java
+++ b/apct-tests/perftests/core/src/android/widget/EditTextBackspacePerfTest.java
@@ -18,7 +18,7 @@
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.perftests.utils.StubActivity;
+import android.perftests.utils.PerfTestActivity;
import android.text.Selection;
import android.view.KeyEvent;
import android.view.View.MeasureSpec;
@@ -80,7 +80,8 @@
}
@Rule
- public ActivityTestRule<StubActivity> mActivityRule = new ActivityTestRule(StubActivity.class);
+ public ActivityTestRule<PerfTestActivity> mActivityRule =
+ new ActivityTestRule<>(PerfTestActivity.class);
@Rule
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
diff --git a/apct-tests/perftests/core/src/android/widget/EditTextCursorMovementPerfTest.java b/apct-tests/perftests/core/src/android/widget/EditTextCursorMovementPerfTest.java
index aa47d5b..8028f11 100644
--- a/apct-tests/perftests/core/src/android/widget/EditTextCursorMovementPerfTest.java
+++ b/apct-tests/perftests/core/src/android/widget/EditTextCursorMovementPerfTest.java
@@ -18,7 +18,7 @@
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.perftests.utils.StubActivity;
+import android.perftests.utils.PerfTestActivity;
import android.text.Selection;
import android.view.KeyEvent;
import android.view.View.MeasureSpec;
@@ -74,7 +74,8 @@
}
@Rule
- public ActivityTestRule<StubActivity> mActivityRule = new ActivityTestRule(StubActivity.class);
+ public ActivityTestRule<PerfTestActivity> mActivityRule =
+ new ActivityTestRule<>(PerfTestActivity.class);
@Rule
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
diff --git a/apct-tests/perftests/core/src/android/widget/EditTextLongTextPerfTest.java b/apct-tests/perftests/core/src/android/widget/EditTextLongTextPerfTest.java
index e50016c..f4ad5dd 100644
--- a/apct-tests/perftests/core/src/android/widget/EditTextLongTextPerfTest.java
+++ b/apct-tests/perftests/core/src/android/widget/EditTextLongTextPerfTest.java
@@ -19,7 +19,7 @@
import android.app.Activity;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.perftests.utils.StubActivity;
+import android.perftests.utils.PerfTestActivity;
import android.view.KeyEvent;
import android.view.View.MeasureSpec;
import android.view.ViewGroup;
@@ -59,7 +59,8 @@
}
@Rule
- public ActivityTestRule<StubActivity> mActivityRule = new ActivityTestRule(StubActivity.class);
+ public ActivityTestRule<PerfTestActivity> mActivityRule =
+ new ActivityTestRule<>(PerfTestActivity.class);
@Rule
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
diff --git a/apct-tests/perftests/core/src/android/widget/LayoutPerfTest.java b/apct-tests/perftests/core/src/android/widget/LayoutPerfTest.java
index 644095b..223a316 100644
--- a/apct-tests/perftests/core/src/android/widget/LayoutPerfTest.java
+++ b/apct-tests/perftests/core/src/android/widget/LayoutPerfTest.java
@@ -28,7 +28,7 @@
import android.os.Looper;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.perftests.utils.StubActivity;
+import android.perftests.utils.PerfTestActivity;
import android.view.View;
import android.view.ViewGroup;
@@ -72,8 +72,8 @@
}
@Rule
- public ActivityTestRule<StubActivity> mActivityRule =
- new ActivityTestRule(StubActivity.class);
+ public ActivityTestRule<PerfTestActivity> mActivityRule =
+ new ActivityTestRule(PerfTestActivity.class);
@Rule
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
diff --git a/apct-tests/perftests/core/src/android/widget/TextViewAutoSizeLayoutPerfTest.java b/apct-tests/perftests/core/src/android/widget/TextViewAutoSizeLayoutPerfTest.java
index bed173b..694e1f4 100644
--- a/apct-tests/perftests/core/src/android/widget/TextViewAutoSizeLayoutPerfTest.java
+++ b/apct-tests/perftests/core/src/android/widget/TextViewAutoSizeLayoutPerfTest.java
@@ -22,7 +22,7 @@
import android.os.Looper;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.perftests.utils.StubActivity;
+import android.perftests.utils.PerfTestActivity;
import androidx.test.filters.LargeTest;
import androidx.test.rule.ActivityTestRule;
@@ -64,8 +64,8 @@
}
@Rule
- public ActivityTestRule<StubActivity> mActivityRule =
- new ActivityTestRule(StubActivity.class);
+ public ActivityTestRule<PerfTestActivity> mActivityRule =
+ new ActivityTestRule(PerfTestActivity.class);
@Rule
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
diff --git a/apct-tests/perftests/core/src/android/widget/TextViewSetTextLocalePerfTest.java b/apct-tests/perftests/core/src/android/widget/TextViewSetTextLocalePerfTest.java
index 00bd8db..a546667 100644
--- a/apct-tests/perftests/core/src/android/widget/TextViewSetTextLocalePerfTest.java
+++ b/apct-tests/perftests/core/src/android/widget/TextViewSetTextLocalePerfTest.java
@@ -18,7 +18,7 @@
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.perftests.utils.StubActivity;
+import android.perftests.utils.PerfTestActivity;
import androidx.test.filters.LargeTest;
import androidx.test.rule.ActivityTestRule;
@@ -56,7 +56,8 @@
}
@Rule
- public ActivityTestRule<StubActivity> mActivityRule = new ActivityTestRule(StubActivity.class);
+ public ActivityTestRule<PerfTestActivity> mActivityRule =
+ new ActivityTestRule<>(PerfTestActivity.class);
@Rule
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
diff --git a/apct-tests/perftests/core/src/android/wm/InternalWindowOperationPerfTest.java b/apct-tests/perftests/core/src/android/wm/InternalWindowOperationPerfTest.java
new file mode 100644
index 0000000..c096cd2
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/wm/InternalWindowOperationPerfTest.java
@@ -0,0 +1,121 @@
+/*
+ * 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 android.wm;
+
+import static android.perftests.utils.ManualBenchmarkState.StatsReport;
+
+import android.os.ParcelFileDescriptor;
+import android.os.SystemClock;
+import android.perftests.utils.ManualBenchmarkState;
+import android.perftests.utils.ManualBenchmarkState.ManualBenchmarkTest;
+import android.perftests.utils.PerfManualStatusReporter;
+import android.perftests.utils.TraceMarkParser;
+import android.perftests.utils.TraceMarkParser.TraceMarkSlice;
+import android.util.Log;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.lifecycle.Stage;
+
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.concurrent.TimeUnit;
+
+/** Measure the performance of internal methods in window manager service by trace tag. */
+@LargeTest
+public class InternalWindowOperationPerfTest extends WindowManagerPerfTestBase {
+ private static final String TAG = InternalWindowOperationPerfTest.class.getSimpleName();
+
+ @Rule
+ public final PerfManualStatusReporter mPerfStatusReporter = new PerfManualStatusReporter();
+
+ @Rule
+ public final PerfTestActivityRule mActivityRule = new PerfTestActivityRule();
+
+ private final TraceMarkParser mTraceMarkParser = new TraceMarkParser(
+ "applyPostLayoutPolicy",
+ "applySurfaceChanges",
+ "AppTransitionReady",
+ "closeSurfaceTransactiom",
+ "openSurfaceTransaction",
+ "performLayout",
+ "performSurfacePlacement",
+ "prepareSurfaces",
+ "updateInputWindows",
+ "WSA#startAnimation",
+ "activityIdle",
+ "activityPaused",
+ "activityStopped",
+ "activityDestroyed",
+ "finishActivity",
+ "startActivityInner");
+
+ @Test
+ @ManualBenchmarkTest(
+ targetTestDurationNs = 20 * TIME_1_S_IN_NS,
+ statsReport = @StatsReport(
+ flags = StatsReport.FLAG_ITERATION | StatsReport.FLAG_MEAN
+ | StatsReport.FLAG_MAX | StatsReport.FLAG_COEFFICIENT_VAR))
+ public void testLaunchAndFinishActivity() throws Throwable {
+ final ManualBenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ long measuredTimeNs = 0;
+ boolean isTraceStarted = false;
+
+ while (state.keepRunning(measuredTimeNs)) {
+ if (!isTraceStarted && !state.isWarmingUp()) {
+ startAsyncAtrace();
+ isTraceStarted = true;
+ }
+ final long startTime = SystemClock.elapsedRealtimeNanos();
+ mActivityRule.launchActivity();
+ mActivityRule.finishActivity();
+ mActivityRule.waitForIdleSync(Stage.DESTROYED);
+ measuredTimeNs = SystemClock.elapsedRealtimeNanos() - startTime;
+ }
+
+ stopAsyncAtrace();
+
+ mTraceMarkParser.forAllSlices((key, slices) -> {
+ for (TraceMarkSlice slice : slices) {
+ state.addExtraResult(key, (long) (slice.getDurarionInSeconds() * NANOS_PER_S));
+ }
+ });
+
+ Log.i(TAG, String.valueOf(mTraceMarkParser));
+ }
+
+ private void startAsyncAtrace() throws IOException {
+ sUiAutomation.executeShellCommand("atrace -b 32768 --async_start wm");
+ // Avoid atrace isn't ready immediately.
+ SystemClock.sleep(TimeUnit.NANOSECONDS.toMillis(TIME_1_S_IN_NS));
+ }
+
+ private void stopAsyncAtrace() throws IOException {
+ final ParcelFileDescriptor pfd = sUiAutomation.executeShellCommand("atrace --async_stop");
+ final InputStream inputStream = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
+ try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
+ String line;
+ while ((line = reader.readLine()) != null) {
+ mTraceMarkParser.visit(line);
+ }
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/wm/RecentsAnimationPerfTest.java b/apct-tests/perftests/core/src/android/wm/RecentsAnimationPerfTest.java
index 9cfc3d2..73b4a19 100644
--- a/apct-tests/perftests/core/src/android/wm/RecentsAnimationPerfTest.java
+++ b/apct-tests/perftests/core/src/android/wm/RecentsAnimationPerfTest.java
@@ -16,16 +16,13 @@
package android.wm;
-import static android.perftests.utils.ManualBenchmarkState.STATS_REPORT_COEFFICIENT_VAR;
-import static android.perftests.utils.ManualBenchmarkState.STATS_REPORT_ITERATION;
-import static android.perftests.utils.ManualBenchmarkState.STATS_REPORT_MEAN;
+import static android.perftests.utils.ManualBenchmarkState.StatsReport;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static org.hamcrest.core.AnyOf.anyOf;
import static org.hamcrest.core.Is.is;
-import android.app.Activity;
import android.app.ActivityManager.TaskSnapshot;
import android.app.ActivityTaskManager;
import android.app.IActivityTaskManager;
@@ -39,23 +36,16 @@
import android.perftests.utils.ManualBenchmarkState;
import android.perftests.utils.ManualBenchmarkState.ManualBenchmarkTest;
import android.perftests.utils.PerfManualStatusReporter;
-import android.perftests.utils.StubActivity;
import android.util.Pair;
import android.view.IRecentsAnimationController;
import android.view.IRecentsAnimationRunner;
import android.view.RemoteAnimationTarget;
-import android.view.WindowManager;
import androidx.test.filters.LargeTest;
-import androidx.test.rule.ActivityTestRule;
-import androidx.test.runner.lifecycle.ActivityLifecycleCallback;
-import androidx.test.runner.lifecycle.ActivityLifecycleMonitorRegistry;
import androidx.test.runner.lifecycle.Stage;
-import org.junit.After;
import org.junit.AfterClass;
import org.junit.Assume;
-import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
@@ -77,11 +67,10 @@
public final PerfManualStatusReporter mPerfStatusReporter = new PerfManualStatusReporter();
@Rule
- public final ActivityTestRule<StubActivity> mActivityRule = new ActivityTestRule<>(
- StubActivity.class, false /* initialTouchMode */, false /* launchActivity */);
+ public final PerfTestActivityRule mActivityRule =
+ new PerfTestActivityRule(true /* launchActivity */);
private long mMeasuredTimeNs;
- private LifecycleListener mLifecycleListener;
@Parameterized.Parameter(0)
public int intervalBetweenOperations;
@@ -127,24 +116,6 @@
sUiAutomation.dropShellPermissionIdentity();
}
- @Before
- @Override
- public void setUp() {
- super.setUp();
- final Activity testActivity = mActivityRule.launchActivity(null /* intent */);
- try {
- mActivityRule.runOnUiThread(() -> testActivity.getWindow()
- .addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON));
- } catch (Throwable ignored) { }
- mLifecycleListener = new LifecycleListener(testActivity);
- ActivityLifecycleMonitorRegistry.getInstance().addLifecycleCallback(mLifecycleListener);
- }
-
- @After
- public void tearDown() {
- ActivityLifecycleMonitorRegistry.getInstance().removeLifecycleCallback(mLifecycleListener);
- }
-
/** Simulate the timing of touch. */
private void makeInterval() {
SystemClock.sleep(intervalBetweenOperations);
@@ -167,8 +138,8 @@
@ManualBenchmarkTest(
warmupDurationNs = TIME_1_S_IN_NS,
targetTestDurationNs = TIME_5_S_IN_NS,
- statsReportFlags =
- STATS_REPORT_ITERATION | STATS_REPORT_MEAN | STATS_REPORT_COEFFICIENT_VAR)
+ statsReport = @StatsReport(flags = StatsReport.FLAG_ITERATION | StatsReport.FLAG_MEAN
+ | StatsReport.FLAG_COEFFICIENT_VAR))
public void testRecentsAnimation() throws Throwable {
final ManualBenchmarkState state = mPerfStatusReporter.getBenchmarkState();
final IActivityTaskManager atm = ActivityTaskManager.getService();
@@ -201,7 +172,7 @@
state.addExtraResult(finishCase.first, elapsedTimeNsOfFinish);
if (moveRecentsToTop) {
- mLifecycleListener.waitForIdleSync(Stage.STOPPED);
+ mActivityRule.waitForIdleSync(Stage.STOPPED);
startTime = SystemClock.elapsedRealtimeNanos();
atm.startActivityFromRecents(testActivityTaskId, null /* options */);
@@ -209,7 +180,7 @@
mMeasuredTimeNs += elapsedTimeNs;
state.addExtraResult("startFromRecents", elapsedTimeNs);
- mLifecycleListener.waitForIdleSync(Stage.RESUMED);
+ mActivityRule.waitForIdleSync(Stage.RESUMED);
}
makeInterval();
@@ -223,55 +194,18 @@
}
};
+ recentsSemaphore.tryAcquire();
while (state.keepRunning(mMeasuredTimeNs)) {
- Assume.assumeTrue(recentsSemaphore.tryAcquire(TIME_5_S_IN_NS, TimeUnit.NANOSECONDS));
+ mMeasuredTimeNs = 0;
final long startTime = SystemClock.elapsedRealtimeNanos();
atm.startRecentsActivity(sRecentsIntent, null /* unused */, anim);
final long elapsedTimeNsOfStart = SystemClock.elapsedRealtimeNanos() - startTime;
mMeasuredTimeNs += elapsedTimeNsOfStart;
state.addExtraResult("start", elapsedTimeNsOfStart);
- }
- // Ensure the last round of animation callback is done.
- recentsSemaphore.tryAcquire(TIME_5_S_IN_NS, TimeUnit.NANOSECONDS);
- recentsSemaphore.release();
- }
-
- private static class LifecycleListener implements ActivityLifecycleCallback {
- private final Activity mTargetActivity;
- private Stage mWaitingStage;
- private Stage mReceivedStage;
-
- LifecycleListener(Activity activity) {
- mTargetActivity = activity;
- }
-
- void waitForIdleSync(Stage state) {
- synchronized (this) {
- if (state != mReceivedStage) {
- mWaitingStage = state;
- try {
- wait(TimeUnit.NANOSECONDS.toMillis(TIME_5_S_IN_NS));
- } catch (InterruptedException impossible) { }
- }
- mWaitingStage = mReceivedStage = null;
- }
- getInstrumentation().waitForIdleSync();
- }
-
- @Override
- public void onActivityLifecycleChanged(Activity activity, Stage stage) {
- if (mTargetActivity != activity) {
- return;
- }
-
- synchronized (this) {
- mReceivedStage = stage;
- if (mWaitingStage == mReceivedStage) {
- notifyAll();
- }
- }
+ // Ensure the animation callback is done.
+ Assume.assumeTrue(recentsSemaphore.tryAcquire(TIME_5_S_IN_NS, TimeUnit.NANOSECONDS));
}
}
}
diff --git a/apct-tests/perftests/core/src/android/wm/RelayoutPerfTest.java b/apct-tests/perftests/core/src/android/wm/RelayoutPerfTest.java
index f0c474b..f43bdf8 100644
--- a/apct-tests/perftests/core/src/android/wm/RelayoutPerfTest.java
+++ b/apct-tests/perftests/core/src/android/wm/RelayoutPerfTest.java
@@ -24,7 +24,7 @@
import android.os.RemoteException;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
-import android.perftests.utils.StubActivity;
+import android.perftests.utils.PerfTestActivity;
import android.util.MergedConfiguration;
import android.view.DisplayCutout;
import android.view.IWindow;
@@ -57,8 +57,8 @@
public final PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
@Rule
- public final ActivityTestRule<StubActivity> mActivityRule =
- new ActivityTestRule<>(StubActivity.class);
+ public final ActivityTestRule<PerfTestActivity> mActivityRule =
+ new ActivityTestRule<>(PerfTestActivity.class);
/** This is only a placement to match the input parameters from {@link #getParameters}. */
@Parameterized.Parameter(0)
diff --git a/apct-tests/perftests/core/src/android/wm/WindowManagerPerfTestBase.java b/apct-tests/perftests/core/src/android/wm/WindowManagerPerfTestBase.java
index 4864da4..4d278c3 100644
--- a/apct-tests/perftests/core/src/android/wm/WindowManagerPerfTestBase.java
+++ b/apct-tests/perftests/core/src/android/wm/WindowManagerPerfTestBase.java
@@ -18,9 +18,21 @@
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+import android.app.Activity;
import android.app.UiAutomation;
+import android.content.Intent;
+import android.perftests.utils.PerfTestActivity;
-import org.junit.Before;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.lifecycle.ActivityLifecycleCallback;
+import androidx.test.runner.lifecycle.ActivityLifecycleMonitorRegistry;
+import androidx.test.runner.lifecycle.Stage;
+
+import org.junit.BeforeClass;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import java.util.concurrent.TimeUnit;
public class WindowManagerPerfTestBase {
static final UiAutomation sUiAutomation = getInstrumentation().getUiAutomation();
@@ -28,10 +40,102 @@
static final long TIME_1_S_IN_NS = 1 * NANOS_PER_S;
static final long TIME_5_S_IN_NS = 5 * NANOS_PER_S;
- @Before
- public void setUp() {
+ @BeforeClass
+ public static void setUpOnce() {
// In order to be closer to the real use case.
sUiAutomation.executeShellCommand("input keyevent KEYCODE_WAKEUP");
sUiAutomation.executeShellCommand("wm dismiss-keyguard");
+ getInstrumentation().getContext().startActivity(new Intent(Intent.ACTION_MAIN)
+ .addCategory(Intent.CATEGORY_HOME).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+ }
+
+ /**
+ * Provides an activity that keeps screen on and is able to wait for a stable lifecycle stage.
+ */
+ static class PerfTestActivityRule extends ActivityTestRule<PerfTestActivity> {
+ private final Intent mStartIntent =
+ new Intent().putExtra(PerfTestActivity.INTENT_EXTRA_KEEP_SCREEN_ON, true);
+ private final LifecycleListener mLifecycleListener = new LifecycleListener();
+
+ PerfTestActivityRule() {
+ this(false /* launchActivity */);
+ }
+
+ PerfTestActivityRule(boolean launchActivity) {
+ super(PerfTestActivity.class, false /* initialTouchMode */, launchActivity);
+ }
+
+ @Override
+ public Statement apply(Statement base, Description description) {
+ final Statement wrappedStatement = new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ ActivityLifecycleMonitorRegistry.getInstance()
+ .addLifecycleCallback(mLifecycleListener);
+ base.evaluate();
+ ActivityLifecycleMonitorRegistry.getInstance()
+ .removeLifecycleCallback(mLifecycleListener);
+ }
+ };
+ return super.apply(wrappedStatement, description);
+ }
+
+ @Override
+ protected Intent getActivityIntent() {
+ return mStartIntent;
+ }
+
+ @Override
+ public PerfTestActivity launchActivity(Intent intent) {
+ final PerfTestActivity activity = super.launchActivity(intent);
+ mLifecycleListener.setTargetActivity(activity);
+ return activity;
+ }
+
+ PerfTestActivity launchActivity() {
+ return launchActivity(mStartIntent);
+ }
+
+ void waitForIdleSync(Stage state) {
+ mLifecycleListener.waitForIdleSync(state);
+ }
+ }
+
+ static class LifecycleListener implements ActivityLifecycleCallback {
+ private Activity mTargetActivity;
+ private Stage mWaitingStage;
+ private Stage mReceivedStage;
+
+ void setTargetActivity(Activity activity) {
+ mTargetActivity = activity;
+ mReceivedStage = mWaitingStage = null;
+ }
+
+ void waitForIdleSync(Stage stage) {
+ synchronized (this) {
+ if (stage != mReceivedStage) {
+ mWaitingStage = stage;
+ try {
+ wait(TimeUnit.NANOSECONDS.toMillis(TIME_5_S_IN_NS));
+ } catch (InterruptedException impossible) { }
+ }
+ mWaitingStage = mReceivedStage = null;
+ }
+ getInstrumentation().waitForIdleSync();
+ }
+
+ @Override
+ public void onActivityLifecycleChanged(Activity activity, Stage stage) {
+ if (mTargetActivity != activity) {
+ return;
+ }
+
+ synchronized (this) {
+ mReceivedStage = stage;
+ if (mWaitingStage == mReceivedStage) {
+ notifyAll();
+ }
+ }
+ }
}
}
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/ManualBenchmarkState.java b/apct-tests/perftests/utils/src/android/perftests/utils/ManualBenchmarkState.java
index ffe39e8..a83254b 100644
--- a/apct-tests/perftests/utils/src/android/perftests/utils/ManualBenchmarkState.java
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/ManualBenchmarkState.java
@@ -59,27 +59,37 @@
public final class ManualBenchmarkState {
private static final String TAG = ManualBenchmarkState.class.getSimpleName();
- @IntDef(prefix = {"STATS_REPORT"}, value = {
- STATS_REPORT_MEDIAN,
- STATS_REPORT_MEAN,
- STATS_REPORT_MIN,
- STATS_REPORT_MAX,
- STATS_REPORT_PERCENTILE90,
- STATS_REPORT_PERCENTILE95,
- STATS_REPORT_STDDEV,
- STATS_REPORT_ITERATION,
- })
- public @interface StatsReport {}
+ @Target(ElementType.ANNOTATION_TYPE)
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface StatsReport {
+ int FLAG_MEDIAN = 0x00000001;
+ int FLAG_MEAN = 0x00000002;
+ int FLAG_MIN = 0x00000004;
+ int FLAG_MAX = 0x00000008;
+ int FLAG_STDDEV = 0x00000010;
+ int FLAG_COEFFICIENT_VAR = 0x00000020;
+ int FLAG_ITERATION = 0x00000040;
- public static final int STATS_REPORT_MEDIAN = 0x00000001;
- public static final int STATS_REPORT_MEAN = 0x00000002;
- public static final int STATS_REPORT_MIN = 0x00000004;
- public static final int STATS_REPORT_MAX = 0x00000008;
- public static final int STATS_REPORT_PERCENTILE90 = 0x00000010;
- public static final int STATS_REPORT_PERCENTILE95 = 0x00000020;
- public static final int STATS_REPORT_STDDEV = 0x00000040;
- public static final int STATS_REPORT_COEFFICIENT_VAR = 0x00000080;
- public static final int STATS_REPORT_ITERATION = 0x00000100;
+ @Retention(RetentionPolicy.RUNTIME)
+ @IntDef(value = {
+ FLAG_MEDIAN,
+ FLAG_MEAN,
+ FLAG_MIN,
+ FLAG_MAX,
+ FLAG_STDDEV,
+ FLAG_COEFFICIENT_VAR,
+ FLAG_ITERATION,
+ })
+ @interface Flag {}
+
+ /** Defines which type of statistics should output. */
+ @Flag int flags() default -1;
+ /** An array with value 0~100 to provide the percentiles. */
+ int[] percentiles() default {};
+ }
+
+ /** It means the entire {@link StatsReport} is not given. */
+ private static final int DEFAULT_STATS_REPORT = -2;
// TODO: Tune these values.
// warm-up for duration
@@ -116,8 +126,9 @@
// The computation needs double precision, but long int is fine for final reporting.
private Stats mStats;
- private int mStatsReportFlags = STATS_REPORT_MEDIAN | STATS_REPORT_MEAN
- | STATS_REPORT_PERCENTILE90 | STATS_REPORT_PERCENTILE95 | STATS_REPORT_STDDEV;
+ private int mStatsReportFlags =
+ StatsReport.FLAG_MEDIAN | StatsReport.FLAG_MEAN | StatsReport.FLAG_STDDEV;
+ private int[] mStatsReportPercentiles = {90 , 95};
private boolean shouldReport(int statsReportFlag) {
return (mStatsReportFlags & statsReportFlag) != 0;
@@ -136,9 +147,10 @@
if (targetTestDurationNs >= 0) {
mTargetTestDurationNs = targetTestDurationNs;
}
- final int statsReportFlags = testAnnotation.statsReportFlags();
- if (statsReportFlags >= 0) {
- mStatsReportFlags = statsReportFlags;
+ final StatsReport statsReport = testAnnotation.statsReport();
+ if (statsReport != null && statsReport.flags() != DEFAULT_STATS_REPORT) {
+ mStatsReportFlags = statsReport.flags();
+ mStatsReportPercentiles = statsReport.percentiles();
}
}
@@ -189,11 +201,20 @@
}
/**
- * Adds additional result while this benchmark is running. It is used when a sequence of
+ * @return {@code true} if the benchmark is in warmup state. It can be used to skip the
+ * operations or measurements that are unnecessary while the test isn't running the
+ * actual benchmark.
+ */
+ public boolean isWarmingUp() {
+ return mState == WARMUP;
+ }
+
+ /**
+ * Adds additional result while this benchmark isn't warming up. It is used when a sequence of
* operations is executed consecutively, the duration of each operation can also be recorded.
*/
public void addExtraResult(String key, long duration) {
- if (mState != RUNNING) {
+ if (isWarmingUp()) {
return;
}
if (mExtraResults == null) {
@@ -221,31 +242,30 @@
}
private void fillStatus(Bundle status, String key, Stats stats) {
- if (shouldReport(STATS_REPORT_ITERATION)) {
+ if (shouldReport(StatsReport.FLAG_ITERATION)) {
status.putLong(key + "_iteration", stats.getSize());
}
- if (shouldReport(STATS_REPORT_MEDIAN)) {
+ if (shouldReport(StatsReport.FLAG_MEDIAN)) {
status.putLong(key + "_median", stats.getMedian());
}
- if (shouldReport(STATS_REPORT_MEAN)) {
+ if (shouldReport(StatsReport.FLAG_MEAN)) {
status.putLong(key + "_mean", Math.round(stats.getMean()));
}
- if (shouldReport(STATS_REPORT_MIN)) {
+ if (shouldReport(StatsReport.FLAG_MIN)) {
status.putLong(key + "_min", stats.getMin());
}
- if (shouldReport(STATS_REPORT_MAX)) {
+ if (shouldReport(StatsReport.FLAG_MAX)) {
status.putLong(key + "_max", stats.getMax());
}
- if (shouldReport(STATS_REPORT_PERCENTILE90)) {
- status.putLong(key + "_percentile90", stats.getPercentile90());
+ if (mStatsReportPercentiles != null) {
+ for (int percentile : mStatsReportPercentiles) {
+ status.putLong(key + "_percentile" + percentile, stats.getPercentile(percentile));
+ }
}
- if (shouldReport(STATS_REPORT_PERCENTILE95)) {
- status.putLong(key + "_percentile95", stats.getPercentile95());
- }
- if (shouldReport(STATS_REPORT_STDDEV)) {
+ if (shouldReport(StatsReport.FLAG_STDDEV)) {
status.putLong(key + "_stddev", Math.round(stats.getStandardDeviation()));
}
- if (shouldReport(STATS_REPORT_COEFFICIENT_VAR)) {
+ if (shouldReport(StatsReport.FLAG_COEFFICIENT_VAR)) {
status.putLong(key + "_cv",
Math.round((100 * stats.getStandardDeviation() / stats.getMean())));
}
@@ -276,6 +296,6 @@
public @interface ManualBenchmarkTest {
long warmupDurationNs() default -1;
long targetTestDurationNs() default -1;
- @StatsReport int statsReportFlags() default -1;
+ StatsReport statsReport() default @StatsReport(flags = DEFAULT_STATS_REPORT);
}
}
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/PerfTestActivity.java b/apct-tests/perftests/utils/src/android/perftests/utils/PerfTestActivity.java
new file mode 100644
index 0000000..e934feb
--- /dev/null
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/PerfTestActivity.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2016 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.perftests.utils;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.WindowManager;
+
+/**
+ * A simple activity used for testing, e.g. performance of activity switching, or as a base
+ * container of testing view.
+ */
+public class PerfTestActivity extends Activity {
+ public static final String INTENT_EXTRA_KEEP_SCREEN_ON = "keep_screen_on";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (getIntent().getBooleanExtra(INTENT_EXTRA_KEEP_SCREEN_ON, false)) {
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ }
+ }
+
+ public static Intent createLaunchIntent(Context context) {
+ final Intent intent = new Intent();
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.setClass(context, PerfTestActivity.class);
+ return intent;
+ }
+}
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/Stats.java b/apct-tests/perftests/utils/src/android/perftests/utils/Stats.java
index f650e81..fb516a8 100644
--- a/apct-tests/perftests/utils/src/android/perftests/utils/Stats.java
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/Stats.java
@@ -16,34 +16,34 @@
package android.perftests.utils;
+import android.annotation.IntRange;
+
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Stats {
- private long mMedian, mMin, mMax, mPercentile90, mPercentile95;
+ private long mMedian, mMin, mMax;
private double mMean, mStandardDeviation;
- private final int mSize;
+ private final List<Long> mValues;
/* Calculate stats in constructor. */
public Stats(List<Long> values) {
- // make a copy since we're modifying it
- values = new ArrayList<>(values);
final int size = values.size();
if (size < 2) {
throw new IllegalArgumentException("At least two results are necessary.");
}
+ // Make a copy since we're modifying it.
+ mValues = values = new ArrayList<>(values);
+
Collections.sort(values);
- mSize = size;
mMin = values.get(0);
mMax = values.get(values.size() - 1);
mMedian = size % 2 == 0 ? (values.get(size / 2) + values.get(size / 2 - 1)) / 2 :
values.get(size / 2);
- mPercentile90 = getPercentile(values, 90);
- mPercentile95 = getPercentile(values, 95);
for (int i = 0; i < size; ++i) {
long result = values.get(i);
@@ -59,7 +59,7 @@
}
public int getSize() {
- return mSize;
+ return mValues.size();
}
public double getMean() {
@@ -82,12 +82,8 @@
return mStandardDeviation;
}
- public long getPercentile90() {
- return mPercentile90;
- }
-
- public long getPercentile95() {
- return mPercentile95;
+ public long getPercentile(@IntRange(from = 0, to = 100) int percentile) {
+ return getPercentile(mValues, percentile);
}
private static long getPercentile(List<Long> values, int percentile) {
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/StubActivity.java b/apct-tests/perftests/utils/src/android/perftests/utils/StubActivity.java
deleted file mode 100644
index 8f03f7e..0000000
--- a/apct-tests/perftests/utils/src/android/perftests/utils/StubActivity.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2016 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.perftests.utils;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-
-public class StubActivity extends Activity {
- public static Intent createLaunchIntent(Context context) {
- final Intent intent = new Intent();
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.setClass(context, StubActivity.class);
- return intent;
- }
-}
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/TraceMarkParser.java b/apct-tests/perftests/utils/src/android/perftests/utils/TraceMarkParser.java
new file mode 100644
index 0000000..1afed3a
--- /dev/null
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/TraceMarkParser.java
@@ -0,0 +1,232 @@
+/*
+ * 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 android.perftests.utils;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.BiConsumer;
+import java.util.function.Predicate;
+
+/**
+ * Utility to get the slice of tracing_mark_write S,F,B,E (Async: start, finish, Sync: begin, end).
+ * Use {@link #visit(String)} to process the trace in text form. The filtered results can be
+ * obtained by {@link #forAllSlices(BiConsumer)}.
+ *
+ * @see android.os.Trace
+ */
+public class TraceMarkParser {
+ /** All slices by the name of {@link TraceMarkLine}. */
+ private final Map<String, List<TraceMarkSlice>> mSlicesMap = new HashMap<>();
+ /** The nested depth of each task-pid. */
+ private final Map<String, Integer> mDepthMap = new HashMap<>();
+ /** The start trace lines that haven't matched the corresponding end. */
+ private final Map<String, TraceMarkLine> mPendingStarts = new HashMap<>();
+
+ private final Predicate<TraceMarkLine> mTraceLineFilter;
+
+ public TraceMarkParser(Predicate<TraceMarkLine> traceLineFilter) {
+ mTraceLineFilter = traceLineFilter;
+ }
+
+ /** Only accept the trace event with the given names. */
+ public TraceMarkParser(String... traceNames) {
+ this(line -> {
+ for (String name : traceNames) {
+ if (name.equals(line.name)) {
+ return true;
+ }
+ }
+ return false;
+ });
+ }
+
+ /** Computes {@link TraceMarkSlice} by the given trace line. */
+ public void visit(String textTraceLine) {
+ final TraceMarkLine line = TraceMarkLine.parse(textTraceLine);
+ if (line == null) {
+ return;
+ }
+
+ if (line.isAsync) {
+ // Async-trace contains name in the start and finish event.
+ if (mTraceLineFilter.test(line)) {
+ if (line.isBegin) {
+ mPendingStarts.put(line.name, line);
+ } else {
+ final TraceMarkLine start = mPendingStarts.remove(line.name);
+ if (start != null) {
+ addSlice(start, line);
+ }
+ }
+ }
+ return;
+ }
+
+ int depth = 1;
+ if (line.isBegin) {
+ final Integer existingDepth = mDepthMap.putIfAbsent(line.taskPid, 1);
+ if (existingDepth != null) {
+ mDepthMap.put(line.taskPid, depth = existingDepth + 1);
+ }
+ // Sync-trace only contains name in the begin event.
+ if (mTraceLineFilter.test(line)) {
+ mPendingStarts.put(getSyncPendingStartKey(line, depth), line);
+ }
+ } else {
+ final Integer existingDepth = mDepthMap.get(line.taskPid);
+ if (existingDepth != null) {
+ depth = existingDepth;
+ mDepthMap.put(line.taskPid, existingDepth - 1);
+ }
+ final TraceMarkLine begin = mPendingStarts.remove(getSyncPendingStartKey(line, depth));
+ if (begin != null) {
+ addSlice(begin, line);
+ }
+ }
+ }
+
+ private static String getSyncPendingStartKey(TraceMarkLine line, int depth) {
+ return line.taskPid + "@" + depth;
+ }
+
+ private void addSlice(TraceMarkLine begin, TraceMarkLine end) {
+ mSlicesMap.computeIfAbsent(
+ begin.name, k -> new ArrayList<>()).add(new TraceMarkSlice(begin, end));
+ }
+
+ public void forAllSlices(BiConsumer<String, List<TraceMarkSlice>> consumer) {
+ for (Map.Entry<String, List<TraceMarkSlice>> entry : mSlicesMap.entrySet()) {
+ consumer.accept(entry.getKey(), entry.getValue());
+ }
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ forAllSlices((key, slices) -> {
+ double totalMs = 0;
+ for (TraceMarkSlice s : slices) {
+ totalMs += s.getDurarionInSeconds() * 1000;
+ }
+ sb.append(key).append(" count=").append(slices.size()).append(" avg=")
+ .append(totalMs / slices.size()).append("ms\n");
+ });
+
+ if (!mPendingStarts.isEmpty()) {
+ sb.append("[Warning] Unresolved events:").append(mPendingStarts).append("\n");
+ }
+ return sb.toString();
+ }
+
+ public static class TraceMarkSlice {
+ public final TraceMarkLine begin;
+ public final TraceMarkLine end;
+
+ TraceMarkSlice(TraceMarkLine begin, TraceMarkLine end) {
+ this.begin = begin;
+ this.end = end;
+ }
+
+ public double getDurarionInSeconds() {
+ return end.timestamp - begin.timestamp;
+ }
+ }
+
+ // taskPid timestamp name
+ // # Async:
+ // Binder:129_F-349 ( 1296) [003] ...1 12.2776: tracing_mark_write: S|1296|launching: a.test|0
+ // android.anim-135 ( 1296) [005] ...1 12.3361: tracing_mark_write: F|1296|launching: a.test|0
+ // # Normal:
+ // Binder:129_6-315 ( 1296) [007] ...1 97.4576: tracing_mark_write: B|1296|relayoutWindow: xxx
+ // ... there may have other nested begin/end
+ // Binder:129_6-315 ( 1296) [007] ...1 97.4580: tracing_mark_write: E|1296
+ public static class TraceMarkLine {
+ static final String EVENT_KEYWORD = ": tracing_mark_write: ";
+ static final char ASYNC_START = 'S';
+ static final char ASYNC_FINISH = 'F';
+ static final char SYNC_BEGIN = 'B';
+ static final char SYNC_END = 'E';
+
+ public final String taskPid;
+ public final double timestamp;
+ public final String name;
+ public final boolean isAsync;
+ public final boolean isBegin;
+
+ TraceMarkLine(String rawLine, int typePos, int type) throws IllegalArgumentException {
+ taskPid = rawLine.substring(0, rawLine.indexOf('(')).trim();
+ final int timeEnd = rawLine.indexOf(':', taskPid.length());
+ if (timeEnd < 0) {
+ throw new IllegalArgumentException("Timestamp end not found");
+ }
+ final int timeBegin = rawLine.lastIndexOf(' ', timeEnd);
+ if (timeBegin < 0) {
+ throw new IllegalArgumentException("Timestamp start not found");
+ }
+ timestamp = Double.parseDouble(rawLine.substring(timeBegin, timeEnd));
+ isAsync = type == ASYNC_START || type == ASYNC_FINISH;
+ isBegin = type == ASYNC_START || type == SYNC_BEGIN;
+
+ if (!isAsync && !isBegin) {
+ name = "";
+ } else {
+ // Get the position of the second '|' from "S|1234|name".
+ final int nameBegin = rawLine.indexOf('|', typePos + 2) + 1;
+ if (nameBegin == 0) {
+ throw new IllegalArgumentException("Name begin not found");
+ }
+ if (isAsync) {
+ // Get the name from "S|1234|name|0".
+ name = rawLine.substring(nameBegin, rawLine.lastIndexOf('|'));
+ } else {
+ name = rawLine.substring(nameBegin);
+ }
+ }
+ }
+
+ static TraceMarkLine parse(String rawLine) {
+ final int eventPos = rawLine.indexOf(EVENT_KEYWORD);
+ if (eventPos < 0) {
+ return null;
+ }
+ final int typePos = eventPos + EVENT_KEYWORD.length();
+ if (typePos >= rawLine.length()) {
+ return null;
+ }
+ final int type = rawLine.charAt(typePos);
+ if (type != ASYNC_START && type != ASYNC_FINISH
+ && type != SYNC_BEGIN && type != SYNC_END) {
+ return null;
+ }
+
+ try {
+ return new TraceMarkLine(rawLine, typePos, type);
+ } catch (IllegalArgumentException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ return "TraceMarkLine{pid=" + taskPid + " time=" + timestamp + " name=" + name
+ + " async=" + isAsync + " begin=" + isBegin + "}";
+ }
+ }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java b/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java
index b97da59..aa7696d 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java
@@ -17,13 +17,8 @@
package com.android.server.job.restrictions;
import android.app.job.JobParameters;
-import android.content.Context;
-import android.os.IThermalService;
-import android.os.IThermalStatusListener;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.Temperature;
-import android.util.Slog;
+import android.os.PowerManager;
+import android.os.PowerManager.OnThermalStatusChangedListener;
import android.util.proto.ProtoOutputStream;
import com.android.internal.util.IndentingPrintWriter;
@@ -36,31 +31,29 @@
private volatile boolean mIsThermalRestricted = false;
+ private PowerManager mPowerManager;
+
public ThermalStatusRestriction(JobSchedulerService service) {
super(service, JobParameters.REASON_DEVICE_THERMAL);
}
@Override
public void onSystemServicesReady() {
- final IThermalService thermalService = IThermalService.Stub.asInterface(
- ServiceManager.getService(Context.THERMAL_SERVICE));
- if (thermalService != null) {
- try {
- thermalService.registerThermalStatusListener(new IThermalStatusListener.Stub() {
- @Override
- public void onStatusChange(int status) {
- final boolean shouldBeActive = status >= Temperature.THROTTLING_SEVERE;
- if (mIsThermalRestricted == shouldBeActive) {
- return;
- }
- mIsThermalRestricted = shouldBeActive;
- mService.onControllerStateChanged();
- }
- });
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to register thermal callback.", e);
+ mPowerManager = mService.getContext().getSystemService(PowerManager.class);
+ // Use MainExecutor
+ mPowerManager.addThermalStatusListener(new OnThermalStatusChangedListener() {
+ @Override
+ public void onThermalStatusChanged(int status) {
+ // This is called on the main thread. Do not do any slow operations in it.
+ // mService.onControllerStateChanged() will just post a message, which is okay.
+ final boolean shouldBeActive = status >= PowerManager.THERMAL_STATUS_SEVERE;
+ if (mIsThermalRestricted == shouldBeActive) {
+ return;
+ }
+ mIsThermalRestricted = shouldBeActive;
+ mService.onControllerStateChanged();
}
- }
+ });
}
@Override
diff --git a/api/current.txt b/api/current.txt
index 603dec2..40106b3 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5872,6 +5872,7 @@
method public android.service.notification.StatusBarNotification[] getActiveNotifications();
method public android.app.AutomaticZenRule getAutomaticZenRule(String);
method public java.util.Map<java.lang.String,android.app.AutomaticZenRule> getAutomaticZenRules();
+ method @Nullable public android.app.NotificationManager.Policy getConsolidatedNotificationPolicy();
method public final int getCurrentInterruptionFilter();
method public int getImportance();
method public android.app.NotificationChannel getNotificationChannel(String);
@@ -24651,6 +24652,7 @@
field public static final int DolbyVisionLevelUhd30 = 64; // 0x40
field public static final int DolbyVisionLevelUhd48 = 128; // 0x80
field public static final int DolbyVisionLevelUhd60 = 256; // 0x100
+ field public static final int DolbyVisionProfileDvav110 = 1024; // 0x400
field public static final int DolbyVisionProfileDvavPen = 2; // 0x2
field public static final int DolbyVisionProfileDvavPer = 1; // 0x1
field public static final int DolbyVisionProfileDvavSe = 512; // 0x200
@@ -38280,6 +38282,7 @@
field public static final String COLUMN_MIME_TYPE = "mime_type";
field public static final String COLUMN_SIZE = "_size";
field public static final String COLUMN_SUMMARY = "summary";
+ field public static final int FLAG_DIR_BLOCKS_TREE = 32768; // 0x8000
field public static final int FLAG_DIR_PREFERS_GRID = 16; // 0x10
field public static final int FLAG_DIR_PREFERS_LAST_MODIFIED = 32; // 0x20
field public static final int FLAG_DIR_SUPPORTS_CREATE = 8; // 0x8
@@ -38498,16 +38501,17 @@
public static final class MediaStore.Audio {
ctor public MediaStore.Audio();
- method public static String keyFor(String);
+ method @Deprecated @Nullable public static String keyFor(@Nullable String);
}
public static interface MediaStore.Audio.AlbumColumns {
field public static final String ALBUM = "album";
field @Deprecated public static final String ALBUM_ART = "album_art";
field public static final String ALBUM_ID = "album_id";
- field public static final String ALBUM_KEY = "album_key";
+ field @Deprecated public static final String ALBUM_KEY = "album_key";
field public static final String ARTIST = "artist";
field public static final String ARTIST_ID = "artist_id";
+ field @Deprecated public static final String ARTIST_KEY = "artist_key";
field public static final String FIRST_YEAR = "minyear";
field public static final String LAST_YEAR = "maxyear";
field public static final String NUMBER_OF_SONGS = "numsongs";
@@ -38526,7 +38530,7 @@
public static interface MediaStore.Audio.ArtistColumns {
field public static final String ARTIST = "artist";
- field public static final String ARTIST_KEY = "artist_key";
+ field @Deprecated public static final String ARTIST_KEY = "artist_key";
field public static final String NUMBER_OF_ALBUMS = "number_of_albums";
field public static final String NUMBER_OF_TRACKS = "number_of_tracks";
}
@@ -38549,19 +38553,23 @@
public static interface MediaStore.Audio.AudioColumns extends android.provider.MediaStore.MediaColumns {
field public static final String ALBUM = "album";
field public static final String ALBUM_ID = "album_id";
- field public static final String ALBUM_KEY = "album_key";
+ field @Deprecated public static final String ALBUM_KEY = "album_key";
field public static final String ARTIST = "artist";
field public static final String ARTIST_ID = "artist_id";
- field public static final String ARTIST_KEY = "artist_key";
+ field @Deprecated public static final String ARTIST_KEY = "artist_key";
field public static final String BOOKMARK = "bookmark";
field public static final String COMPOSER = "composer";
+ field public static final String GENRE = "genre";
+ field public static final String GENRE_ID = "genre_id";
+ field @Deprecated public static final String GENRE_KEY = "genre_key";
field public static final String IS_ALARM = "is_alarm";
field public static final String IS_AUDIOBOOK = "is_audiobook";
field public static final String IS_MUSIC = "is_music";
field public static final String IS_NOTIFICATION = "is_notification";
field public static final String IS_PODCAST = "is_podcast";
field public static final String IS_RINGTONE = "is_ringtone";
- field public static final String TITLE_KEY = "title_key";
+ field @Deprecated public static final String TITLE_KEY = "title_key";
+ field public static final String TITLE_RESOURCE_URI = "title_resource_uri";
field public static final String TRACK = "track";
field public static final String YEAR = "year";
}
@@ -44250,6 +44258,7 @@
field public static final String KEY_DEFAULT_VM_NUMBER_STRING = "default_vm_number_string";
field public static final String KEY_DIAL_STRING_REPLACE_STRING_ARRAY = "dial_string_replace_string_array";
field public static final String KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL = "disable_cdma_activation_code_bool";
+ field public static final String KEY_DISCONNECT_CAUSE_PLAY_BUSYTONE_INT_ARRAY = "disconnect_cause_play_busytone_int_array";
field public static final String KEY_DISPLAY_HD_AUDIO_PROPERTY_BOOL = "display_hd_audio_property_bool";
field public static final String KEY_DROP_VIDEO_CALL_WHEN_ANSWERING_AUDIO_CALL_BOOL = "drop_video_call_when_answering_audio_call_bool";
field public static final String KEY_DTMF_TYPE_ENABLED_BOOL = "dtmf_type_enabled_bool";
@@ -44595,6 +44604,7 @@
method public int describeContents();
method public int getAsuLevel();
method public int getDbm();
+ method public int getEcNo();
method @IntRange(from=android.telephony.CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN, to=android.telephony.CellSignalStrength.SIGNAL_STRENGTH_GREAT) public int getLevel();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellSignalStrengthWcdma> CREATOR;
@@ -45100,6 +45110,7 @@
method public long getDataLimitBytes();
method public long getDataUsageBytes();
method public long getDataUsageTime();
+ method @Nullable public int[] getNetworkTypes();
method @Nullable public CharSequence getSummary();
method @Nullable public CharSequence getTitle();
method public void writeToParcel(android.os.Parcel, int);
@@ -45119,6 +45130,7 @@
method public static android.telephony.SubscriptionPlan.Builder createRecurring(java.time.ZonedDateTime, java.time.Period);
method public android.telephony.SubscriptionPlan.Builder setDataLimit(long, int);
method public android.telephony.SubscriptionPlan.Builder setDataUsage(long, long);
+ method @NonNull public android.telephony.SubscriptionPlan.Builder setNetworkTypes(@Nullable int[]);
method public android.telephony.SubscriptionPlan.Builder setSummary(@Nullable CharSequence);
method public android.telephony.SubscriptionPlan.Builder setTitle(@Nullable CharSequence);
}
diff --git a/api/lint-baseline.txt b/api/lint-baseline.txt
index e19c1fd..2ca8cf4 100644
--- a/api/lint-baseline.txt
+++ b/api/lint-baseline.txt
@@ -8,7 +8,7 @@
AllUpper: android.media.MediaCodecInfo.CodecCapabilities#FEATURE_LowLatency:
- Constant field names must be named with only upper case characters: `android.media.MediaCodecInfo.CodecCapabilities#FEATURE_LowLatency`, should be `FEATURE__LOW_LATENCY`?
+
ArrayReturn: android.app.Notification.MessagingStyle.Message#getMessagesFromBundleArray(android.os.Parcelable[]) parameter #0:
@@ -453,6 +453,10 @@
+GenericException: android.content.res.loader.ResourcesProvider#finalize():
+ Methods must not throw generic exceptions (`java.lang.Throwable`)
+
+
HiddenSuperclass: android.content.res.ColorStateList:
HiddenSuperclass: android.graphics.Canvas:
@@ -528,9 +532,9 @@
MissingNullability: android.icu.util.VersionInfo#UNICODE_12_1:
MissingNullability: android.media.MediaMetadataRetriever#getFrameAtTime(long, int, android.media.MediaMetadataRetriever.BitmapParams):
- Missing nullability on method `getFrameAtTime` return
+
MissingNullability: android.media.MediaMetadataRetriever#getScaledFrameAtTime(long, int, int, int, android.media.MediaMetadataRetriever.BitmapParams):
- Missing nullability on method `getScaledFrameAtTime` return
+
RequiresPermission: android.accounts.AccountManager#getAccountsByTypeAndFeatures(String, String[], android.accounts.AccountManagerCallback<android.accounts.Account[]>, android.os.Handler):
@@ -1159,6 +1163,10 @@
+StreamFiles: android.content.res.loader.DirectoryResourceLoader#DirectoryResourceLoader(java.io.File):
+ Methods accepting `File` should also accept `FileDescriptor` or streams: constructor android.content.res.loader.DirectoryResourceLoader(java.io.File)
+
+
Todo: android.hardware.camera2.params.StreamConfigurationMap:
Todo: android.hardware.camera2.params.StreamConfigurationMap#getOutputMinFrameDuration(Class<T>, android.util.Size):
diff --git a/api/system-current.txt b/api/system-current.txt
index ed379a4..46413d3 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1359,7 +1359,7 @@
public abstract class Context {
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public boolean bindServiceAsUser(@RequiresPermission android.content.Intent, android.content.ServiceConnection, int, android.os.UserHandle);
- method @NonNull public android.content.Context createContextAsUser(@NonNull android.os.UserHandle);
+ method @NonNull public android.content.Context createContextAsUser(@NonNull android.os.UserHandle, int);
method public abstract android.content.Context createCredentialProtectedStorageContext();
method @NonNull public android.content.Context createPackageContextAsUser(@NonNull String, int, @NonNull android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
method @Nullable public abstract java.io.File getPreloadsFileCache();
@@ -1754,7 +1754,9 @@
field public static final int PROTECTION_FLAG_INCIDENT_REPORT_APPROVER = 1048576; // 0x100000
field public static final int PROTECTION_FLAG_OEM = 16384; // 0x4000
field public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 65536; // 0x10000
+ field public static final int PROTECTION_FLAG_TELEPHONY = 4194304; // 0x400000
field public static final int PROTECTION_FLAG_WELLBEING = 131072; // 0x20000
+ field public static final int PROTECTION_FLAG_WIFI = 8388608; // 0x800000
field @Nullable public final String backgroundPermission;
field @StringRes public int requestRes;
}
@@ -3420,7 +3422,9 @@
public class Location implements android.os.Parcelable {
method public boolean isComplete();
method public void makeComplete();
+ method public void setExtraLocation(@Nullable String, @Nullable android.location.Location);
method public void setIsFromMockProvider(boolean);
+ field public static final String EXTRA_NO_GPS_LOCATION = "noGPSLocation";
}
public class LocationManager {
@@ -7312,7 +7316,7 @@
public abstract class CellBroadcastService extends android.app.Service {
ctor public CellBroadcastService();
method @CallSuper public android.os.IBinder onBind(android.content.Intent);
- method public abstract void onCdmaCellBroadcastSms(int, byte[]);
+ method public abstract void onCdmaCellBroadcastSms(int, byte[], int);
method public abstract void onGsmCellBroadcastSms(int, byte[]);
field public static final String CELL_BROADCAST_SERVICE_INTERFACE = "android.telephony.CellBroadcastService";
}
@@ -9758,9 +9762,10 @@
method public final long getUserActivityTimeout();
method public final void setUserActivityTimeout(long);
field @RequiresPermission(android.Manifest.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS) public static final int SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS = 524288; // 0x80000
+ field @RequiresPermission(android.Manifest.permission.INTERNAL_SYSTEM_WINDOW) public static final int SYSTEM_FLAG_SHOW_FOR_ALL_USERS = 16; // 0x10
}
- @IntDef(flag=true, prefix={"SYSTEM_FLAG_"}, value={android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface WindowManager.LayoutParams.SystemFlags {
+ @IntDef(flag=true, prefix={"SYSTEM_FLAG_"}, value={android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS, android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface WindowManager.LayoutParams.SystemFlags {
}
}
diff --git a/api/system-lint-baseline.txt b/api/system-lint-baseline.txt
index d4a5152..c4af17e 100644
--- a/api/system-lint-baseline.txt
+++ b/api/system-lint-baseline.txt
@@ -702,7 +702,7 @@
AllUpper: android.media.MediaCodecInfo.CodecCapabilities#FEATURE_IntraRefresh:
AllUpper: android.media.MediaCodecInfo.CodecCapabilities#FEATURE_LowLatency:
- Constant field names must be named with only upper case characters: `android.media.MediaCodecInfo.CodecCapabilities#FEATURE_LowLatency`, should be `FEATURE__LOW_LATENCY`?
+
AllUpper: android.media.MediaCodecInfo.CodecCapabilities#FEATURE_MultipleFrames:
AllUpper: android.media.MediaCodecInfo.CodecCapabilities#FEATURE_PartialFrame:
@@ -1648,7 +1648,7 @@
ArrayReturn: android.text.util.Rfc822Tokenizer#tokenize(CharSequence):
ArrayReturn: android.util.ArraySet#ArraySet(E[]) parameter #0:
- Method parameter should be Collection<E> (or subclass) instead of raw array; was `E[]`
+
ArrayReturn: android.util.ArraySet#toArray():
ArrayReturn: android.util.ArraySet#toArray(T[]):
@@ -2430,39 +2430,39 @@
BuilderSetStyle: android.animation.AnimatorSet.Builder:
BuilderSetStyle: android.animation.AnimatorSet.Builder#with(android.animation.Animator):
- Builder methods names should use setFoo() style: method android.animation.AnimatorSet.Builder.with(android.animation.Animator)
+
BuilderSetStyle: android.content.ContentProviderOperation.Builder:
BuilderSetStyle: android.content.ContentProviderOperation.Builder#withExceptionAllowed(boolean):
- Builder methods names should use setFoo() style: method android.content.ContentProviderOperation.Builder.withExceptionAllowed(boolean)
+
BuilderSetStyle: android.content.ContentProviderOperation.Builder#withExpectedCount(int):
- Builder methods names should use setFoo() style: method android.content.ContentProviderOperation.Builder.withExpectedCount(int)
+
BuilderSetStyle: android.content.ContentProviderOperation.Builder#withExtra(String, Object):
- Builder methods names should use setFoo() style: method android.content.ContentProviderOperation.Builder.withExtra(String,Object)
+
BuilderSetStyle: android.content.ContentProviderOperation.Builder#withExtraBackReference(String, int):
- Builder methods names should use setFoo() style: method android.content.ContentProviderOperation.Builder.withExtraBackReference(String,int)
+
BuilderSetStyle: android.content.ContentProviderOperation.Builder#withExtraBackReference(String, int, String):
- Builder methods names should use setFoo() style: method android.content.ContentProviderOperation.Builder.withExtraBackReference(String,int,String)
+
BuilderSetStyle: android.content.ContentProviderOperation.Builder#withExtras(android.os.Bundle):
- Builder methods names should use setFoo() style: method android.content.ContentProviderOperation.Builder.withExtras(android.os.Bundle)
+
BuilderSetStyle: android.content.ContentProviderOperation.Builder#withSelection(String, String[]):
- Builder methods names should use setFoo() style: method android.content.ContentProviderOperation.Builder.withSelection(String,String[])
+
BuilderSetStyle: android.content.ContentProviderOperation.Builder#withSelectionBackReference(int, int):
- Builder methods names should use setFoo() style: method android.content.ContentProviderOperation.Builder.withSelectionBackReference(int,int)
+
BuilderSetStyle: android.content.ContentProviderOperation.Builder#withSelectionBackReference(int, int, String):
- Builder methods names should use setFoo() style: method android.content.ContentProviderOperation.Builder.withSelectionBackReference(int,int,String)
+
BuilderSetStyle: android.content.ContentProviderOperation.Builder#withValue(String, Object):
- Builder methods names should use setFoo() style: method android.content.ContentProviderOperation.Builder.withValue(String,Object)
+
BuilderSetStyle: android.content.ContentProviderOperation.Builder#withValueBackReference(String, int):
- Builder methods names should use setFoo() style: method android.content.ContentProviderOperation.Builder.withValueBackReference(String,int)
+
BuilderSetStyle: android.content.ContentProviderOperation.Builder#withValueBackReference(String, int, String):
- Builder methods names should use setFoo() style: method android.content.ContentProviderOperation.Builder.withValueBackReference(String,int,String)
+
BuilderSetStyle: android.content.ContentProviderOperation.Builder#withValueBackReferences(android.content.ContentValues):
- Builder methods names should use setFoo() style: method android.content.ContentProviderOperation.Builder.withValueBackReferences(android.content.ContentValues)
+
BuilderSetStyle: android.content.ContentProviderOperation.Builder#withValues(android.content.ContentValues):
- Builder methods names should use setFoo() style: method android.content.ContentProviderOperation.Builder.withValues(android.content.ContentValues)
+
BuilderSetStyle: android.content.ContentProviderOperation.Builder#withYieldAllowed(boolean):
- Builder methods names should use setFoo() style: method android.content.ContentProviderOperation.Builder.withYieldAllowed(boolean)
+
CallbackInterface: android.accounts.AccountManagerCallback:
@@ -2680,7 +2680,7 @@
ConcreteCollection: android.util.ArraySet#ArraySet():
ConcreteCollection: android.util.ArraySet#ArraySet(E[]):
- Return type is concrete collection (`android.util.ArraySet`); must be higher-level interface
+
ConcreteCollection: android.util.ArraySet#ArraySet(android.util.ArraySet<E>):
ConcreteCollection: android.util.ArraySet#ArraySet(android.util.ArraySet<E>) parameter #0:
@@ -3439,6 +3439,8 @@
GenericException: android.content.res.AssetManager.AssetInputStream#finalize():
+GenericException: android.content.res.loader.ResourcesProvider#finalize():
+ Methods must not throw generic exceptions (`java.lang.Throwable`)
GenericException: android.database.CursorWindow#finalize():
GenericException: android.database.sqlite.SQLiteDatabase#finalize():
@@ -18114,7 +18116,7 @@
MissingNullability: android.media.MediaMetadataRetriever#getFrameAtTime(long, int):
MissingNullability: android.media.MediaMetadataRetriever#getFrameAtTime(long, int, android.media.MediaMetadataRetriever.BitmapParams):
- Missing nullability on method `getFrameAtTime` return
+
MissingNullability: android.media.MediaMetadataRetriever#getImageAtIndex(int):
MissingNullability: android.media.MediaMetadataRetriever#getImageAtIndex(int, android.media.MediaMetadataRetriever.BitmapParams):
@@ -18126,7 +18128,7 @@
MissingNullability: android.media.MediaMetadataRetriever#getScaledFrameAtTime(long, int, int, int):
MissingNullability: android.media.MediaMetadataRetriever#getScaledFrameAtTime(long, int, int, int, android.media.MediaMetadataRetriever.BitmapParams):
- Missing nullability on method `getScaledFrameAtTime` return
+
MissingNullability: android.media.MediaMetadataRetriever#setDataSource(String) parameter #0:
MissingNullability: android.media.MediaMetadataRetriever#setDataSource(String, java.util.Map<java.lang.String,java.lang.String>) parameter #0:
@@ -35886,7 +35888,7 @@
MissingNullability: android.view.SurfaceView#getSurfaceControl():
MissingNullability: android.view.SurfaceView#setClipBounds(android.graphics.Rect) parameter #0:
- Missing nullability on parameter `clipBounds` in method `setClipBounds`
+
MissingNullability: android.view.TextureView#TextureView(android.content.Context) parameter #0:
MissingNullability: android.view.TextureView#TextureView(android.content.Context, android.util.AttributeSet) parameter #0:
@@ -47988,79 +47990,79 @@
SetterReturnsThis: android.database.sqlite.SQLiteQueryBuilder:
SetterReturnsThis: android.database.sqlite.SQLiteQueryBuilder#setCursorFactory(android.database.sqlite.SQLiteDatabase.CursorFactory):
- Methods must return the builder object (return type android.database.sqlite.SQLiteQueryBuilder instead of void): method android.database.sqlite.SQLiteQueryBuilder.setCursorFactory(android.database.sqlite.SQLiteDatabase.CursorFactory)
+
SetterReturnsThis: android.database.sqlite.SQLiteQueryBuilder#setDistinct(boolean):
- Methods must return the builder object (return type android.database.sqlite.SQLiteQueryBuilder instead of void): method android.database.sqlite.SQLiteQueryBuilder.setDistinct(boolean)
+
SetterReturnsThis: android.database.sqlite.SQLiteQueryBuilder#setProjectionMap(java.util.Map<java.lang.String,java.lang.String>):
- Methods must return the builder object (return type android.database.sqlite.SQLiteQueryBuilder instead of void): method android.database.sqlite.SQLiteQueryBuilder.setProjectionMap(java.util.Map<java.lang.String,java.lang.String>)
+
SetterReturnsThis: android.database.sqlite.SQLiteQueryBuilder#setStrict(boolean):
- Methods must return the builder object (return type android.database.sqlite.SQLiteQueryBuilder instead of void): method android.database.sqlite.SQLiteQueryBuilder.setStrict(boolean)
+
SetterReturnsThis: android.database.sqlite.SQLiteQueryBuilder#setStrictColumns(boolean):
- Methods must return the builder object (return type android.database.sqlite.SQLiteQueryBuilder instead of void): method android.database.sqlite.SQLiteQueryBuilder.setStrictColumns(boolean)
+
SetterReturnsThis: android.database.sqlite.SQLiteQueryBuilder#setStrictGrammar(boolean):
- Methods must return the builder object (return type android.database.sqlite.SQLiteQueryBuilder instead of void): method android.database.sqlite.SQLiteQueryBuilder.setStrictGrammar(boolean)
+
SetterReturnsThis: android.database.sqlite.SQLiteQueryBuilder#setTables(String):
- Methods must return the builder object (return type android.database.sqlite.SQLiteQueryBuilder instead of void): method android.database.sqlite.SQLiteQueryBuilder.setTables(String)
+
SetterReturnsThis: android.hardware.camera2.CaptureRequest.Builder:
SetterReturnsThis: android.hardware.camera2.CaptureRequest.Builder#set(android.hardware.camera2.CaptureRequest.Key<T>, T):
- Methods must return the builder object (return type android.hardware.camera2.CaptureRequest.Builder instead of void): method android.hardware.camera2.CaptureRequest.Builder.set(android.hardware.camera2.CaptureRequest.Key<T>,T)
+
SetterReturnsThis: android.hardware.camera2.CaptureRequest.Builder#setTag(Object):
- Methods must return the builder object (return type android.hardware.camera2.CaptureRequest.Builder instead of void): method android.hardware.camera2.CaptureRequest.Builder.setTag(Object)
+
SetterReturnsThis: android.print.PrintJobInfo.Builder:
SetterReturnsThis: android.print.PrintJobInfo.Builder#setAttributes(android.print.PrintAttributes):
- Methods must return the builder object (return type android.print.PrintJobInfo.Builder instead of void): method android.print.PrintJobInfo.Builder.setAttributes(android.print.PrintAttributes)
+
SetterReturnsThis: android.print.PrintJobInfo.Builder#setCopies(int):
- Methods must return the builder object (return type android.print.PrintJobInfo.Builder instead of void): method android.print.PrintJobInfo.Builder.setCopies(int)
+
SetterReturnsThis: android.print.PrintJobInfo.Builder#setPages(android.print.PageRange[]):
- Methods must return the builder object (return type android.print.PrintJobInfo.Builder instead of void): method android.print.PrintJobInfo.Builder.setPages(android.print.PageRange[])
+
SetterReturnsThis: android.renderscript.Sampler.Builder:
SetterReturnsThis: android.renderscript.Sampler.Builder#setAnisotropy(float):
- Methods must return the builder object (return type android.renderscript.Sampler.Builder instead of void): method android.renderscript.Sampler.Builder.setAnisotropy(float)
+
SetterReturnsThis: android.renderscript.Sampler.Builder#setMagnification(android.renderscript.Sampler.Value):
- Methods must return the builder object (return type android.renderscript.Sampler.Builder instead of void): method android.renderscript.Sampler.Builder.setMagnification(android.renderscript.Sampler.Value)
+
SetterReturnsThis: android.renderscript.Sampler.Builder#setMinification(android.renderscript.Sampler.Value):
- Methods must return the builder object (return type android.renderscript.Sampler.Builder instead of void): method android.renderscript.Sampler.Builder.setMinification(android.renderscript.Sampler.Value)
+
SetterReturnsThis: android.renderscript.Sampler.Builder#setWrapS(android.renderscript.Sampler.Value):
- Methods must return the builder object (return type android.renderscript.Sampler.Builder instead of void): method android.renderscript.Sampler.Builder.setWrapS(android.renderscript.Sampler.Value)
+
SetterReturnsThis: android.renderscript.Sampler.Builder#setWrapT(android.renderscript.Sampler.Value):
- Methods must return the builder object (return type android.renderscript.Sampler.Builder instead of void): method android.renderscript.Sampler.Builder.setWrapT(android.renderscript.Sampler.Value)
+
SetterReturnsThis: android.text.SpannableStringBuilder:
SetterReturnsThis: android.text.SpannableStringBuilder#setFilters(android.text.InputFilter[]):
- Methods must return the builder object (return type android.text.SpannableStringBuilder instead of void): method android.text.SpannableStringBuilder.setFilters(android.text.InputFilter[])
+
SetterReturnsThis: android.text.SpannableStringBuilder#setSpan(Object, int, int, int):
- Methods must return the builder object (return type android.text.SpannableStringBuilder instead of void): method android.text.SpannableStringBuilder.setSpan(Object,int,int,int)
+
SetterReturnsThis: android.text.style.TtsSpan.Builder:
SetterReturnsThis: android.text.style.TtsSpan.Builder#setIntArgument(String, int):
- Methods must return the builder object (return type android.text.style.TtsSpan.Builder<C> instead of C): method android.text.style.TtsSpan.Builder.setIntArgument(String,int)
+
SetterReturnsThis: android.text.style.TtsSpan.Builder#setLongArgument(String, long):
- Methods must return the builder object (return type android.text.style.TtsSpan.Builder<C> instead of C): method android.text.style.TtsSpan.Builder.setLongArgument(String,long)
+
SetterReturnsThis: android.text.style.TtsSpan.Builder#setStringArgument(String, String):
- Methods must return the builder object (return type android.text.style.TtsSpan.Builder<C> instead of C): method android.text.style.TtsSpan.Builder.setStringArgument(String,String)
+
SetterReturnsThis: android.view.textclassifier.TextClassifierEvent.Builder:
SetterReturnsThis: android.view.textclassifier.TextClassifierEvent.Builder#setActionIndices(int...):
- Methods must return the builder object (return type android.view.textclassifier.TextClassifierEvent.Builder<T> instead of T): method android.view.textclassifier.TextClassifierEvent.Builder.setActionIndices(int...)
+
SetterReturnsThis: android.view.textclassifier.TextClassifierEvent.Builder#setEntityTypes(java.lang.String...):
- Methods must return the builder object (return type android.view.textclassifier.TextClassifierEvent.Builder<T> instead of T): method android.view.textclassifier.TextClassifierEvent.Builder.setEntityTypes(java.lang.String...)
+
SetterReturnsThis: android.view.textclassifier.TextClassifierEvent.Builder#setEventContext(android.view.textclassifier.TextClassificationContext):
- Methods must return the builder object (return type android.view.textclassifier.TextClassifierEvent.Builder<T> instead of T): method android.view.textclassifier.TextClassifierEvent.Builder.setEventContext(android.view.textclassifier.TextClassificationContext)
+
SetterReturnsThis: android.view.textclassifier.TextClassifierEvent.Builder#setEventIndex(int):
- Methods must return the builder object (return type android.view.textclassifier.TextClassifierEvent.Builder<T> instead of T): method android.view.textclassifier.TextClassifierEvent.Builder.setEventIndex(int)
+
SetterReturnsThis: android.view.textclassifier.TextClassifierEvent.Builder#setExtras(android.os.Bundle):
- Methods must return the builder object (return type android.view.textclassifier.TextClassifierEvent.Builder<T> instead of T): method android.view.textclassifier.TextClassifierEvent.Builder.setExtras(android.os.Bundle)
+
SetterReturnsThis: android.view.textclassifier.TextClassifierEvent.Builder#setLocale(android.icu.util.ULocale):
- Methods must return the builder object (return type android.view.textclassifier.TextClassifierEvent.Builder<T> instead of T): method android.view.textclassifier.TextClassifierEvent.Builder.setLocale(android.icu.util.ULocale)
+
SetterReturnsThis: android.view.textclassifier.TextClassifierEvent.Builder#setModelName(String):
- Methods must return the builder object (return type android.view.textclassifier.TextClassifierEvent.Builder<T> instead of T): method android.view.textclassifier.TextClassifierEvent.Builder.setModelName(String)
+
SetterReturnsThis: android.view.textclassifier.TextClassifierEvent.Builder#setResultId(String):
- Methods must return the builder object (return type android.view.textclassifier.TextClassifierEvent.Builder<T> instead of T): method android.view.textclassifier.TextClassifierEvent.Builder.setResultId(String)
+
SetterReturnsThis: android.view.textclassifier.TextClassifierEvent.Builder#setScores(float...):
- Methods must return the builder object (return type android.view.textclassifier.TextClassifierEvent.Builder<T> instead of T): method android.view.textclassifier.TextClassifierEvent.Builder.setScores(float...)
+
SingletonConstructor: android.text.Editable.Factory#Factory():
@@ -48265,6 +48267,8 @@
StreamFiles: android.app.backup.BackupAgent#fullBackupFile(java.io.File, android.app.backup.FullBackupDataOutput):
+StreamFiles: android.content.res.loader.DirectoryResourceLoader#DirectoryResourceLoader(java.io.File):
+ Methods accepting `File` should also accept `FileDescriptor` or streams: constructor android.content.res.loader.DirectoryResourceLoader(java.io.File)
StreamFiles: android.database.sqlite.SQLiteDatabase#deleteDatabase(java.io.File):
StreamFiles: android.database.sqlite.SQLiteDatabase#openDatabase(java.io.File, android.database.sqlite.SQLiteDatabase.OpenParams):
diff --git a/api/test-current.txt b/api/test-current.txt
index 6715f4a..9b85b96 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -659,7 +659,7 @@
}
public abstract class Context {
- method @NonNull public android.content.Context createContextAsUser(@NonNull android.os.UserHandle);
+ method @NonNull public android.content.Context createContextAsUser(@NonNull android.os.UserHandle, int);
method @NonNull public android.content.Context createPackageContextAsUser(@NonNull String, int, @NonNull android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
method public abstract android.view.Display getDisplay();
method public abstract int getDisplayId();
@@ -731,6 +731,7 @@
method @RequiresPermission(anyOf={"android.permission.GRANT_RUNTIME_PERMISSIONS", "android.permission.REVOKE_RUNTIME_PERMISSIONS", "android.permission.GET_RUNTIME_PERMISSIONS"}) public abstract int getPermissionFlags(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
method @NonNull public abstract String getServicesSystemSharedLibraryPackageName();
method @NonNull public abstract String getSharedSystemSharedLibraryPackageName();
+ method @Nullable public String[] getTelephonyPackageNames();
method @Nullable public String getWellbeingPackageName();
method @RequiresPermission("android.permission.GRANT_RUNTIME_PERMISSIONS") public abstract void grantRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
method @RequiresPermission("android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS") public abstract void removeOnPermissionsChangeListener(@NonNull android.content.pm.PackageManager.OnPermissionsChangedListener);
@@ -769,8 +770,10 @@
field public static final int PROTECTION_FLAG_INCIDENT_REPORT_APPROVER = 1048576; // 0x100000
field public static final int PROTECTION_FLAG_OEM = 16384; // 0x4000
field public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 65536; // 0x10000
+ field public static final int PROTECTION_FLAG_TELEPHONY = 4194304; // 0x400000
field public static final int PROTECTION_FLAG_VENDOR_PRIVILEGED = 32768; // 0x8000
field public static final int PROTECTION_FLAG_WELLBEING = 131072; // 0x20000
+ field public static final int PROTECTION_FLAG_WIFI = 8388608; // 0x800000
field @Nullable public final String backgroundPermission;
}
@@ -1091,11 +1094,14 @@
public class Location implements android.os.Parcelable {
method public void makeComplete();
+ method public void setExtraLocation(@Nullable String, @Nullable android.location.Location);
+ field public static final String EXTRA_NO_GPS_LOCATION = "noGPSLocation";
}
public class LocationManager {
method @NonNull public String[] getBackgroundThrottlingWhitelist();
method @NonNull public String[] getIgnoreSettingsWhitelist();
+ method @Nullable @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public java.util.List<java.lang.String> getProviderPackages(@NonNull String);
method @NonNull public java.util.List<android.location.LocationRequest> getTestProviderCurrentRequests(String);
method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.location.LocationListener, @Nullable android.os.Looper);
method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.LocationListener);
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index cb27325..8af925a 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -230,6 +230,7 @@
"tests/e2e/Anomaly_duration_sum_e2e_test.cpp",
"tests/e2e/Attribution_e2e_test.cpp",
"tests/e2e/ConfigTtl_e2e_test.cpp",
+ "tests/e2e/CountMetric_e2e_test.cpp",
"tests/e2e/DurationMetric_e2e_test.cpp",
"tests/e2e/GaugeMetric_e2e_pull_test.cpp",
"tests/e2e/GaugeMetric_e2e_push_test.cpp",
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index ff7416c..6c3dff2 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -16,24 +16,26 @@
#define DEBUG false // STOPSHIP if true
#include "Log.h"
-#include "statslog.h"
+
+#include "StatsLogProcessor.h"
#include <android-base/file.h>
#include <dirent.h>
#include <frameworks/base/cmds/statsd/src/active_config_list.pb.h>
-#include "StatsLogProcessor.h"
+#include <log/log_event_list.h>
+#include <utils/Errors.h>
+#include <utils/SystemClock.h>
+
#include "android-base/stringprintf.h"
#include "external/StatsPullerManager.h"
#include "guardrail/StatsdStats.h"
#include "metrics/CountMetricProducer.h"
+#include "state/StateManager.h"
#include "stats_log_util.h"
#include "stats_util.h"
+#include "statslog.h"
#include "storage/StorageManager.h"
-#include <log/log_event_list.h>
-#include <utils/Errors.h>
-#include <utils/SystemClock.h>
-
using namespace android;
using android::base::StringPrintf;
using android::util::FIELD_COUNT_REPEATED;
@@ -218,6 +220,8 @@
onIsolatedUidChangedEventLocked(*event);
}
+ StateManager::getInstance().onLogEvent(*event);
+
if (mMetricsManagers.empty()) {
return;
}
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index 3d002d2..8292a3a 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -262,6 +262,10 @@
FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation);
FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations);
+ FRIEND_TEST(CountMetricE2eTest, TestWithSimpleState);
+ FRIEND_TEST(CountMetricE2eTest, TestWithMappedState);
+ FRIEND_TEST(CountMetricE2eTest, TestWithMultipleStates);
+
FRIEND_TEST(DurationMetricE2eTest, TestOneBucket);
FRIEND_TEST(DurationMetricE2eTest, TestTwoBuckets);
FRIEND_TEST(DurationMetricE2eTest, TestWithActivation);
diff --git a/cmds/statsd/src/anomaly/subscriber_util.cpp b/cmds/statsd/src/anomaly/subscriber_util.cpp
index e09d575..4c30c4c 100644
--- a/cmds/statsd/src/anomaly/subscriber_util.cpp
+++ b/cmds/statsd/src/anomaly/subscriber_util.cpp
@@ -40,7 +40,7 @@
for (const Subscription& subscription : subscriptions) {
if (subscription.probability_of_informing() < 1
- && ((float)rand() / RAND_MAX) >= subscription.probability_of_informing()) {
+ && ((float)rand() / (float)RAND_MAX) >= subscription.probability_of_informing()) {
// Note that due to float imprecision, 0.0 and 1.0 might not truly mean never/always.
// The config writer was advised to use -0.1 and 1.1 for never/always.
ALOGI("Fate decided that a subscriber would not be informed.");
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index b5c8e35..4a06387 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -18,13 +18,15 @@
#include "Log.h"
#include "CountMetricProducer.h"
-#include "guardrail/StatsdStats.h"
-#include "stats_util.h"
-#include "stats_log_util.h"
+#include <inttypes.h>
#include <limits.h>
#include <stdlib.h>
+#include "guardrail/StatsdStats.h"
+#include "stats_log_util.h"
+#include "stats_util.h"
+
using android::util::FIELD_COUNT_REPEATED;
using android::util::FIELD_TYPE_BOOL;
using android::util::FIELD_TYPE_FLOAT;
@@ -65,16 +67,16 @@
const int FIELD_ID_START_BUCKET_ELAPSED_MILLIS = 5;
const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 6;
-CountMetricProducer::CountMetricProducer(const ConfigKey& key, const CountMetric& metric,
- const int conditionIndex,
- const sp<ConditionWizard>& wizard,
- const int64_t timeBaseNs, const int64_t startTimeNs,
- const unordered_map<int, shared_ptr<Activation>>&
- eventActivationMap,
- const unordered_map<int, vector<shared_ptr<Activation>>>&
- eventDeactivationMap)
+CountMetricProducer::CountMetricProducer(
+ const ConfigKey& key, const CountMetric& metric, const int conditionIndex,
+ const sp<ConditionWizard>& wizard, const int64_t timeBaseNs, const int64_t startTimeNs,
+
+ const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
+ const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap,
+ const vector<int>& slicedStateAtoms,
+ const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
: MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard, eventActivationMap,
- eventDeactivationMap) {
+ eventDeactivationMap, slicedStateAtoms, stateGroupMap) {
if (metric.has_bucket()) {
mBucketSizeNs =
TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket()) * 1000000;
@@ -100,6 +102,8 @@
mConditionSliced = true;
}
+ // TODO(tsaichristine): b/142124705 handle metric state links
+
flushIfNeededLocked(startTimeNs);
// Adjust start for partial bucket
mCurrentBucketStartTimeNs = startTimeNs;
@@ -112,6 +116,12 @@
VLOG("~CountMetricProducer() called");
}
+void CountMetricProducer::onStateChanged(int atomId, const HashableDimensionKey& primaryKey,
+ int oldState, int newState) {
+ VLOG("CountMetric %lld onStateChanged State%d, key %s, %d -> %d", (long long)mMetricId, atomId,
+ primaryKey.toString().c_str(), oldState, newState);
+}
+
void CountMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const {
if (mCurrentSlicedCounter == nullptr ||
mCurrentSlicedCounter->size() == 0) {
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
index 61913c7..61e0892 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -17,15 +17,16 @@
#ifndef COUNT_METRIC_PRODUCER_H
#define COUNT_METRIC_PRODUCER_H
-#include <unordered_map>
-
#include <android/util/ProtoOutputStream.h>
#include <gtest/gtest_prod.h>
-#include "../anomaly/AnomalyTracker.h"
-#include "../condition/ConditionTracker.h"
-#include "../matchers/matcher_util.h"
+
+#include <unordered_map>
+
#include "MetricProducer.h"
+#include "anomaly/AnomalyTracker.h"
+#include "condition/ConditionTracker.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include "matchers/matcher_util.h"
#include "stats_util.h"
namespace android {
@@ -40,16 +41,20 @@
class CountMetricProducer : public MetricProducer {
public:
- CountMetricProducer(const ConfigKey& key, const CountMetric& countMetric,
- const int conditionIndex, const sp<ConditionWizard>& wizard,
- const int64_t timeBaseNs, const int64_t startTimeNs,
- const std::unordered_map<int, std::shared_ptr<Activation>>&
- eventActivationMap = {},
- const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
- eventDeactivationMap = {});
+ CountMetricProducer(
+ const ConfigKey& key, const CountMetric& countMetric, const int conditionIndex,
+ const sp<ConditionWizard>& wizard, const int64_t timeBaseNs, const int64_t startTimeNs,
+ const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap = {},
+ const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
+ eventDeactivationMap = {},
+ const vector<int>& slicedStateAtoms = {},
+ const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap = {});
virtual ~CountMetricProducer();
+ void onStateChanged(int atomId, const HashableDimensionKey& primaryKey, int oldState,
+ int newState) override;
+
protected:
void onMatchedLogEventInternalLocked(
const size_t matcherIndex, const MetricDimensionKey& eventKey,
@@ -106,6 +111,7 @@
FRIEND_TEST(CountMetricProducerTest, TestEventWithAppUpgrade);
FRIEND_TEST(CountMetricProducerTest, TestEventWithAppUpgradeInNextBucket);
FRIEND_TEST(CountMetricProducerTest, TestFirstBucket);
+ FRIEND_TEST(CountMetricProducerTest, TestOneWeekTimeUnit);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 31b90f3..ab2a1c3 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -69,9 +69,11 @@
const bool nesting, const sp<ConditionWizard>& wizard,
const FieldMatcher& internalDimensions, const int64_t timeBaseNs, const int64_t startTimeNs,
const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
- const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap)
+ const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap,
+ const vector<int>& slicedStateAtoms,
+ const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
: MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard, eventActivationMap,
- eventDeactivationMap),
+ eventDeactivationMap, slicedStateAtoms, stateGroupMap),
mAggregationType(metric.aggregation_type()),
mStartIndex(startIndex),
mStopIndex(stopIndex),
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index 0592b18..7457d7f 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -38,16 +38,16 @@
class DurationMetricProducer : public MetricProducer {
public:
- DurationMetricProducer(const ConfigKey& key, const DurationMetric& durationMetric,
- const int conditionIndex, const size_t startIndex,
- const size_t stopIndex, const size_t stopAllIndex, const bool nesting,
- const sp<ConditionWizard>& wizard,
- const FieldMatcher& internalDimensions, const int64_t timeBaseNs,
- const int64_t startTimeNs,
- const unordered_map<int, shared_ptr<Activation>>&
- eventActivationMap = {},
- const unordered_map<int, vector<shared_ptr<Activation>>>&
- eventDeactivationMap = {});
+ DurationMetricProducer(
+ const ConfigKey& key, const DurationMetric& durationMetric, const int conditionIndex,
+ const size_t startIndex, const size_t stopIndex, const size_t stopAllIndex,
+ const bool nesting, const sp<ConditionWizard>& wizard,
+ const FieldMatcher& internalDimensions, const int64_t timeBaseNs,
+ const int64_t startTimeNs,
+ const unordered_map<int, shared_ptr<Activation>>& eventActivationMap = {},
+ const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap = {},
+ const vector<int>& slicedStateAtoms = {},
+ const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap = {});
virtual ~DurationMetricProducer();
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index a60a916..32eb077 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -52,16 +52,15 @@
const int FIELD_ID_ELAPSED_TIMESTAMP_NANOS = 1;
const int FIELD_ID_ATOMS = 2;
-EventMetricProducer::EventMetricProducer(const ConfigKey& key, const EventMetric& metric,
- const int conditionIndex,
- const sp<ConditionWizard>& wizard,
- const int64_t startTimeNs,
- const unordered_map<int, shared_ptr<Activation>>&
- eventActivationMap,
- const unordered_map<int, vector<shared_ptr<Activation>>>&
- eventDeactivationMap)
+EventMetricProducer::EventMetricProducer(
+ const ConfigKey& key, const EventMetric& metric, const int conditionIndex,
+ const sp<ConditionWizard>& wizard, const int64_t startTimeNs,
+ const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
+ const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap,
+ const vector<int>& slicedStateAtoms,
+ const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
: MetricProducer(metric.id(), key, startTimeNs, conditionIndex, wizard, eventActivationMap,
- eventDeactivationMap) {
+ eventDeactivationMap, slicedStateAtoms, stateGroupMap) {
if (metric.links().size() > 0) {
for (const auto& link : metric.links()) {
Metric2Condition mc;
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h
index aab53c8..dca37e8 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.h
+++ b/cmds/statsd/src/metrics/EventMetricProducer.h
@@ -33,13 +33,14 @@
class EventMetricProducer : public MetricProducer {
public:
- EventMetricProducer(const ConfigKey& key, const EventMetric& eventMetric,
- const int conditionIndex, const sp<ConditionWizard>& wizard,
- const int64_t startTimeNs,
- const std::unordered_map<int, std::shared_ptr<Activation>>&
- eventActivationMap = {},
- const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
- eventDeactivationMap = {});
+ EventMetricProducer(
+ const ConfigKey& key, const EventMetric& eventMetric, const int conditionIndex,
+ const sp<ConditionWizard>& wizard, const int64_t startTimeNs,
+ const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap = {},
+ const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
+ eventDeactivationMap = {},
+ const vector<int>& slicedStateAtoms = {},
+ const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap = {});
virtual ~EventMetricProducer();
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index e409b6fb..d0f88a8 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -74,9 +74,11 @@
const int atomId, const int64_t timeBaseNs, const int64_t startTimeNs,
const sp<StatsPullerManager>& pullerManager,
const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
- const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap)
+ const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap,
+ const vector<int>& slicedStateAtoms,
+ const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
: MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard, eventActivationMap,
- eventDeactivationMap),
+ eventDeactivationMap, slicedStateAtoms, stateGroupMap),
mWhatMatcherIndex(whatMatcherIndex),
mEventMatcherWizard(matcherWizard),
mPullerManager(pullerManager),
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index dfe1d56..640a02a 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -56,16 +56,17 @@
// producer always reports the guage at the earliest time of the bucket when the condition is met.
class GaugeMetricProducer : public virtual MetricProducer, public virtual PullDataReceiver {
public:
- GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& gaugeMetric,
- const int conditionIndex, const sp<ConditionWizard>& conditionWizard,
- const int whatMatcherIndex,const sp<EventMatcherWizard>& matcherWizard,
- const int pullTagId, const int triggerAtomId, const int atomId,
- const int64_t timeBaseNs, const int64_t startTimeNs,
- const sp<StatsPullerManager>& pullerManager,
- const std::unordered_map<int, std::shared_ptr<Activation>>&
- eventActivationMap = {},
- const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
- eventDeactivationMap = {});
+ GaugeMetricProducer(
+ const ConfigKey& key, const GaugeMetric& gaugeMetric, const int conditionIndex,
+ const sp<ConditionWizard>& conditionWizard, const int whatMatcherIndex,
+ const sp<EventMatcherWizard>& matcherWizard, const int pullTagId,
+ const int triggerAtomId, const int atomId, const int64_t timeBaseNs,
+ const int64_t startTimeNs, const sp<StatsPullerManager>& pullerManager,
+ const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap = {},
+ const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
+ eventDeactivationMap = {},
+ const vector<int>& slicedStateAtoms = {},
+ const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap = {});
virtual ~GaugeMetricProducer();
diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp
index 3426a19..2a700ef 100644
--- a/cmds/statsd/src/metrics/MetricProducer.cpp
+++ b/cmds/statsd/src/metrics/MetricProducer.cpp
@@ -45,23 +45,27 @@
const int conditionIndex, const sp<ConditionWizard>& wizard,
const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap,
const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
- eventDeactivationMap)
- : mMetricId(metricId),
- mConfigKey(key),
- mTimeBaseNs(timeBaseNs),
- mCurrentBucketStartTimeNs(timeBaseNs),
- mCurrentBucketNum(0),
- mCondition(initialCondition(conditionIndex)),
- mConditionTrackerIndex(conditionIndex),
- mConditionSliced(false),
- mWizard(wizard),
- mContainANYPositionInDimensionsInWhat(false),
- mSliceByPositionALL(false),
- mHasLinksToAllConditionDimensionsInTracker(false),
- mEventActivationMap(eventActivationMap),
- mEventDeactivationMap(eventDeactivationMap),
- mIsActive(mEventActivationMap.empty()) {
- }
+ eventDeactivationMap,
+ const vector<int>& slicedStateAtoms,
+ const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
+ : mMetricId(metricId),
+ mConfigKey(key),
+ mTimeBaseNs(timeBaseNs),
+ mCurrentBucketStartTimeNs(timeBaseNs),
+ mCurrentBucketNum(0),
+ mCondition(initialCondition(conditionIndex)),
+ mConditionTrackerIndex(conditionIndex),
+ mConditionSliced(false),
+ mWizard(wizard),
+ mContainANYPositionInDimensionsInWhat(false),
+ mSliceByPositionALL(false),
+ mHasLinksToAllConditionDimensionsInTracker(false),
+ mEventActivationMap(eventActivationMap),
+ mEventDeactivationMap(eventDeactivationMap),
+ mIsActive(mEventActivationMap.empty()),
+ mSlicedStateAtoms(slicedStateAtoms),
+ mStateGroupMap(stateGroupMap) {
+}
void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) {
if (!mIsActive) {
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 1e1eb69..a72de22 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -17,19 +17,19 @@
#ifndef METRIC_PRODUCER_H
#define METRIC_PRODUCER_H
-#include <shared_mutex>
-
#include <frameworks/base/cmds/statsd/src/active_config_list.pb.h>
+#include <log/logprint.h>
+#include <utils/RefBase.h>
+
+#include <unordered_map>
+
#include "HashableDimensionKey.h"
#include "anomaly/AnomalyTracker.h"
#include "condition/ConditionWizard.h"
#include "config/ConfigKey.h"
#include "matchers/matcher_util.h"
#include "packages/PackageInfoListener.h"
-
-#include <log/logprint.h>
-#include <utils/RefBase.h>
-#include <unordered_map>
+#include "state/StateListener.h"
namespace android {
namespace os {
@@ -86,13 +86,15 @@
// writing the report to dropbox. MetricProducers should respond to package changes as required in
// PackageInfoListener, but if none of the metrics are slicing by package name, then the update can
// be a no-op.
-class MetricProducer : public virtual PackageInfoListener {
+class MetricProducer : public virtual PackageInfoListener, public virtual StateListener {
public:
MetricProducer(const int64_t& metricId, const ConfigKey& key, const int64_t timeBaseNs,
const int conditionIndex, const sp<ConditionWizard>& wizard,
const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap,
const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
- eventDeactivationMap);
+ eventDeactivationMap,
+ const vector<int>& slicedStateAtoms,
+ const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap);
virtual ~MetricProducer(){};
@@ -151,6 +153,9 @@
return mConditionSliced;
};
+ void onStateChanged(int atomId, const HashableDimensionKey& primaryKey, int oldState,
+ int newState){};
+
// Output the metrics data to [protoOutput]. All metrics reports end with the same timestamp.
// This method clears all the past buckets.
void onDumpReport(const int64_t dumpTimeNs,
@@ -230,6 +235,11 @@
return mBucketSizeNs;
}
+ inline const std::vector<int> getSlicedStateAtoms() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return mSlicedStateAtoms;
+ }
+
/* If alert is valid, adds an AnomalyTracker and returns it. If invalid, returns nullptr. */
virtual sp<AnomalyTracker> addAnomalyTracker(const Alert &alert,
const sp<AlarmMonitor>& anomalyAlarmMonitor) {
@@ -381,6 +391,16 @@
bool mIsActive;
+ // The slice_by_state atom ids defined in statsd_config.
+ std::vector<int> mSlicedStateAtoms;
+
+ // Maps atom ids and state values to group_ids (<atom_id, <value, group_id>>).
+ std::unordered_map<int, std::unordered_map<int, int64_t>> mStateGroupMap;
+
+ FRIEND_TEST(CountMetricE2eTest, TestWithSimpleState);
+ FRIEND_TEST(CountMetricE2eTest, TestWithMappedState);
+ FRIEND_TEST(CountMetricE2eTest, TestWithMultipleStates);
+
FRIEND_TEST(DurationMetricE2eTest, TestOneBucket);
FRIEND_TEST(DurationMetricE2eTest, TestTwoBuckets);
FRIEND_TEST(DurationMetricE2eTest, TestWithActivation);
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index 963205e..7bae4b9 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -15,8 +15,12 @@
*/
#define DEBUG false // STOPSHIP if true
#include "Log.h"
+
#include "MetricsManager.h"
-#include "statslog.h"
+
+#include <log/logprint.h>
+#include <private/android_filesystem_config.h>
+#include <utils/SystemClock.h>
#include "CountMetricProducer.h"
#include "condition/CombinationConditionTracker.h"
@@ -25,12 +29,10 @@
#include "matchers/CombinationLogMatchingTracker.h"
#include "matchers/SimpleLogMatchingTracker.h"
#include "metrics_manager_util.h"
-#include "stats_util.h"
+#include "state/StateManager.h"
#include "stats_log_util.h"
-
-#include <log/logprint.h>
-#include <private/android_filesystem_config.h>
-#include <utils/SystemClock.h>
+#include "stats_util.h"
+#include "statslog.h"
using android::util::FIELD_COUNT_REPEATED;
using android::util::FIELD_TYPE_INT32;
@@ -149,6 +151,12 @@
}
MetricsManager::~MetricsManager() {
+ for (auto it : mAllMetricProducers) {
+ for (int atomId : it->getSlicedStateAtoms()) {
+ StateManager::getInstance().unregisterListener(atomId, it);
+ }
+ }
+
VLOG("~MetricsManager()");
}
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index 3dad614..d184121 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -282,6 +282,10 @@
TestActivationOnBootMultipleActivationsDifferentActivationTypes);
FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart);
+ FRIEND_TEST(CountMetricE2eTest, TestWithSimpleState);
+ FRIEND_TEST(CountMetricE2eTest, TestWithMappedState);
+ FRIEND_TEST(CountMetricE2eTest, TestWithMultipleStates);
+
FRIEND_TEST(DurationMetricE2eTest, TestOneBucket);
FRIEND_TEST(DurationMetricE2eTest, TestTwoBuckets);
FRIEND_TEST(DurationMetricE2eTest, TestWithActivation);
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index 7fe5a83..6fd0327 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -83,9 +83,11 @@
const sp<EventMatcherWizard>& matcherWizard, const int pullTagId, const int64_t timeBaseNs,
const int64_t startTimeNs, const sp<StatsPullerManager>& pullerManager,
const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
- const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap)
+ const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap,
+ const vector<int>& slicedStateAtoms,
+ const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
: MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, conditionWizard,
- eventActivationMap, eventDeactivationMap),
+ eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap),
mWhatMatcherIndex(whatMatcherIndex),
mEventMatcherWizard(matcherWizard),
mPullerManager(pullerManager),
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index d7cd397..206e602 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -52,15 +52,17 @@
// - an alarm set to the end of the bucket
class ValueMetricProducer : public virtual MetricProducer, public virtual PullDataReceiver {
public:
- ValueMetricProducer(const ConfigKey& key, const ValueMetric& valueMetric,
- const int conditionIndex, const sp<ConditionWizard>& conditionWizard,
- const int whatMatcherIndex, const sp<EventMatcherWizard>& matcherWizard,
- const int pullTagId, const int64_t timeBaseNs, const int64_t startTimeNs,
- const sp<StatsPullerManager>& pullerManager,
- const std::unordered_map<int, std::shared_ptr<Activation>>&
- eventActivationMap = {},
- const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
- eventDeactivationMap = {});
+ ValueMetricProducer(
+ const ConfigKey& key, const ValueMetric& valueMetric, const int conditionIndex,
+ const sp<ConditionWizard>& conditionWizard, const int whatMatcherIndex,
+ const sp<EventMatcherWizard>& matcherWizard, const int pullTagId,
+ const int64_t timeBaseNs, const int64_t startTimeNs,
+ const sp<StatsPullerManager>& pullerManager,
+ const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap = {},
+ const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
+ eventDeactivationMap = {},
+ const vector<int>& slicedStateAtoms = {},
+ const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap = {});
virtual ~ValueMetricProducer();
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index 0fee71e..33e162e 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -20,24 +20,24 @@
#include "metrics_manager_util.h"
#include "MetricProducer.h"
-#include "../condition/CombinationConditionTracker.h"
-#include "../condition/SimpleConditionTracker.h"
-#include "../condition/StateConditionTracker.h"
-#include "../external/StatsPullerManager.h"
-#include "../matchers/CombinationLogMatchingTracker.h"
-#include "../matchers/SimpleLogMatchingTracker.h"
-#include "../matchers/EventMatcherWizard.h"
-#include "../metrics/CountMetricProducer.h"
-#include "../metrics/DurationMetricProducer.h"
-#include "../metrics/EventMetricProducer.h"
-#include "../metrics/GaugeMetricProducer.h"
-#include "../metrics/ValueMetricProducer.h"
+#include <inttypes.h>
+#include "condition/CombinationConditionTracker.h"
+#include "condition/SimpleConditionTracker.h"
+#include "condition/StateConditionTracker.h"
+#include "external/StatsPullerManager.h"
+#include "matchers/CombinationLogMatchingTracker.h"
+#include "matchers/EventMatcherWizard.h"
+#include "matchers/SimpleLogMatchingTracker.h"
+#include "metrics/CountMetricProducer.h"
+#include "metrics/DurationMetricProducer.h"
+#include "metrics/EventMetricProducer.h"
+#include "metrics/GaugeMetricProducer.h"
+#include "metrics/ValueMetricProducer.h"
+#include "state/StateManager.h"
#include "stats_util.h"
#include "statslog.h"
-#include <inttypes.h>
-
using std::set;
using std::string;
using std::unordered_map;
@@ -138,6 +138,41 @@
return true;
}
+// Initializes state data structures for a metric.
+// input:
+// [config]: the input config
+// [stateIds]: the slice_by_state ids for this metric
+// [stateAtomIdMap]: this map contains the mapping from all state ids to atom ids
+// [allStateGroupMaps]: this map contains the mapping from state ids and state
+// values to state group ids for all states
+// output:
+// [slicedStateAtoms]: a vector of atom ids of all the slice_by_states
+// [stateGroupMap]: this map should contain the mapping from states ids and state
+// values to state group ids for all states that this metric
+// is interested in
+bool handleMetricWithStates(
+ const StatsdConfig& config, const ::google::protobuf::RepeatedField<int64_t>& stateIds,
+ const unordered_map<int64_t, int>& stateAtomIdMap,
+ const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
+ vector<int>& slicedStateAtoms,
+ unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap) {
+ for (const auto& stateId : stateIds) {
+ auto it = stateAtomIdMap.find(stateId);
+ if (it == stateAtomIdMap.end()) {
+ ALOGW("cannot find State %" PRId64 " in the config", stateId);
+ return false;
+ }
+ int atomId = it->second;
+ slicedStateAtoms.push_back(atomId);
+
+ auto stateIt = allStateGroupMaps.find(stateId);
+ if (stateIt != allStateGroupMaps.end()) {
+ stateGroupMap[atomId] = stateIt->second;
+ }
+ }
+ return true;
+}
+
// Validates a metricActivation and populates state.
// EventActivationMap and EventDeactivationMap are supplied to a MetricProducer
// to provide the producer with state about its activators and deactivators.
@@ -342,12 +377,32 @@
return true;
}
+bool initStates(const StatsdConfig& config, unordered_map<int64_t, int>& stateAtomIdMap,
+ unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps) {
+ for (int i = 0; i < config.state_size(); i++) {
+ const State& state = config.state(i);
+ const int64_t stateId = state.id();
+ stateAtomIdMap[stateId] = state.atom_id();
+
+ const StateMap& stateMap = state.map();
+ for (auto group : stateMap.group()) {
+ for (auto value : group.value()) {
+ allStateGroupMaps[stateId][value] = group.group_id();
+ }
+ }
+ }
+
+ return true;
+}
+
bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseTimeNs,
const int64_t currentTimeNs, UidMap& uidMap,
const sp<StatsPullerManager>& pullerManager,
const unordered_map<int64_t, int>& logTrackerMap,
const unordered_map<int64_t, int>& conditionTrackerMap,
const vector<sp<LogMatchingTracker>>& allAtomMatchers,
+ const unordered_map<int64_t, int>& stateAtomIdMap,
+ const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
vector<sp<ConditionTracker>>& allConditionTrackers,
vector<sp<MetricProducer>>& allMetricProducers,
unordered_map<int, vector<int>>& conditionToMetricMap,
@@ -398,10 +453,9 @@
int conditionIndex = -1;
if (metric.has_condition()) {
- bool good = handleMetricWithConditions(
- metric.condition(), metricIndex, conditionTrackerMap, metric.links(),
- allConditionTrackers, conditionIndex, conditionToMetricMap);
- if (!good) {
+ if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
+ metric.links(), allConditionTrackers, conditionIndex,
+ conditionToMetricMap)) {
return false;
}
} else {
@@ -411,6 +465,18 @@
}
}
+ std::vector<int> slicedStateAtoms;
+ unordered_map<int, unordered_map<int, int64_t>> stateGroupMap;
+ if (metric.slice_by_state_size() > 0) {
+ if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap,
+ allStateGroupMaps, slicedStateAtoms, stateGroupMap)) {
+ return false;
+ }
+ }
+
+ // TODO(tsaichristine): add check for unequal number of MetricStateLinks
+ // and slice_by_states
+
unordered_map<int, shared_ptr<Activation>> eventActivationMap;
unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
bool success = handleMetricActivation(config, metric.id(), metricIndex,
@@ -421,7 +487,7 @@
sp<MetricProducer> countProducer = new CountMetricProducer(
key, metric, conditionIndex, wizard, timeBaseTimeNs, currentTimeNs,
- eventActivationMap, eventDeactivationMap);
+ eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap);
allMetricProducers.push_back(countProducer);
}
@@ -721,6 +787,13 @@
}
for (const auto& it : allMetricProducers) {
uidMap.addListener(it);
+
+ // Register metrics to StateTrackers
+ for (int atomId : it->getSlicedStateAtoms()) {
+ if (!StateManager::getInstance().registerListener(atomId, it)) {
+ return false;
+ }
+ }
}
return true;
}
@@ -846,6 +919,8 @@
unordered_map<int64_t, int> logTrackerMap;
unordered_map<int64_t, int> conditionTrackerMap;
unordered_map<int64_t, int> metricProducerMap;
+ unordered_map<int64_t, int> stateAtomIdMap;
+ unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps;
if (!initLogTrackers(config, uidMap, logTrackerMap, allAtomMatchers, allTagIds)) {
ALOGE("initLogMatchingTrackers failed");
@@ -858,9 +933,13 @@
ALOGE("initConditionTrackers failed");
return false;
}
-
+ if (!initStates(config, stateAtomIdMap, allStateGroupMaps)) {
+ ALOGE("initStates failed");
+ return false;
+ }
if (!initMetrics(key, config, timeBaseNs, currentTimeNs, uidMap, pullerManager, logTrackerMap,
- conditionTrackerMap, allAtomMatchers, allConditionTrackers, allMetricProducers,
+ conditionTrackerMap, allAtomMatchers, stateAtomIdMap, allStateGroupMaps,
+ allConditionTrackers, allMetricProducers,
conditionToMetricMap, trackerToMetricMap, metricProducerMap,
noReportMetricIds, activationAtomTrackerToMetricMap,
deactivationAtomTrackerToMetricMap, metricsWithActivation)) {
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h
index 3802948..95b2ab8 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.h
+++ b/cmds/statsd/src/metrics/metrics_manager_util.h
@@ -68,6 +68,17 @@
std::unordered_map<int, std::vector<int>>& trackerToConditionMap,
std::unordered_map<int, std::vector<MetricConditionLink>>& eventConditionLinks);
+// Initialize State maps using State protos in the config. These maps will
+// eventually be passed to MetricProducers to initialize their state info.
+// input:
+// [config]: the input config
+// output:
+// [stateAtomIdMap]: this map should contain the mapping from state ids to atom ids
+// [allStateGroupMaps]: this map should contain the mapping from states ids and state
+// values to state group ids for all states
+bool initStates(const StatsdConfig& config, unordered_map<int64_t, int>& stateAtomIdMap,
+ unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps);
+
// Initialize MetricProducers.
// input:
// [key]: the config key that this config belongs to
@@ -75,6 +86,9 @@
// [timeBaseSec]: start time base for all metrics
// [logTrackerMap]: LogMatchingTracker name to index mapping from previous step.
// [conditionTrackerMap]: condition name to index mapping
+// [stateAtomIdMap]: contains the mapping from state ids to atom ids
+// [allStateGroupMaps]: contains the mapping from atom ids and state values to
+// state group ids for all states
// output:
// [allMetricProducers]: contains the list of sp to the MetricProducers created.
// [conditionToMetricMap]: contains the mapping from condition tracker index to
@@ -87,6 +101,8 @@
const std::unordered_map<int64_t, int>& conditionTrackerMap,
const std::unordered_map<int, std::vector<MetricConditionLink>>& eventConditionLinks,
const vector<sp<LogMatchingTracker>>& allAtomMatchers,
+ const unordered_map<int64_t, int>& stateAtomIdMap,
+ const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
vector<sp<ConditionTracker>>& allConditionTrackers,
std::vector<sp<MetricProducer>>& allMetricProducers,
std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
diff --git a/cmds/statsd/src/state/StateManager.cpp b/cmds/statsd/src/state/StateManager.cpp
index a3059c5..95b2c76 100644
--- a/cmds/statsd/src/state/StateManager.cpp
+++ b/cmds/statsd/src/state/StateManager.cpp
@@ -35,26 +35,25 @@
}
}
-bool StateManager::registerListener(int stateAtomId, wp<StateListener> listener) {
+bool StateManager::registerListener(int atomId, wp<StateListener> listener) {
std::lock_guard<std::mutex> lock(mMutex);
// Check if state tracker already exists
- if (mStateTrackers.find(stateAtomId) == mStateTrackers.end()) {
+ if (mStateTrackers.find(atomId) == mStateTrackers.end()) {
// Create a new state tracker iff atom is a state atom
- auto it = android::util::AtomsInfo::kStateAtomsFieldOptions.find(stateAtomId);
+ auto it = android::util::AtomsInfo::kStateAtomsFieldOptions.find(atomId);
if (it != android::util::AtomsInfo::kStateAtomsFieldOptions.end()) {
- mStateTrackers[stateAtomId] = new StateTracker(stateAtomId, it->second);
+ mStateTrackers[atomId] = new StateTracker(atomId, it->second);
} else {
- ALOGE("StateManager cannot register listener, Atom %d is not a state atom",
- stateAtomId);
+ ALOGE("StateManager cannot register listener, Atom %d is not a state atom", atomId);
return false;
}
}
- mStateTrackers[stateAtomId]->registerListener(listener);
+ mStateTrackers[atomId]->registerListener(listener);
return true;
}
-void StateManager::unregisterListener(int stateAtomId, wp<StateListener> listener) {
+void StateManager::unregisterListener(int atomId, wp<StateListener> listener) {
std::unique_lock<std::mutex> lock(mMutex);
// Hold the sp<> until the lock is released so that ~StateTracker() is
@@ -62,7 +61,7 @@
sp<StateTracker> toRemove;
// Unregister listener from correct StateTracker
- auto it = mStateTrackers.find(stateAtomId);
+ auto it = mStateTrackers.find(atomId);
if (it != mStateTrackers.end()) {
it->second->unregisterListener(listener);
@@ -73,15 +72,15 @@
}
} else {
ALOGE("StateManager cannot unregister listener, StateTracker for atom %d does not exist",
- stateAtomId);
+ atomId);
}
lock.unlock();
}
-int StateManager::getState(int stateAtomId, const HashableDimensionKey& key) {
+int StateManager::getStateValue(int atomId, const HashableDimensionKey& key) {
std::lock_guard<std::mutex> lock(mMutex);
- if (mStateTrackers.find(stateAtomId) != mStateTrackers.end()) {
- return mStateTrackers[stateAtomId]->getState(key);
+ if (mStateTrackers.find(atomId) != mStateTrackers.end()) {
+ return mStateTrackers[atomId]->getStateValue(key);
}
return StateTracker::kStateUnknown;
diff --git a/cmds/statsd/src/state/StateManager.h b/cmds/statsd/src/state/StateManager.h
index ce60f14..89ee6c0 100644
--- a/cmds/statsd/src/state/StateManager.h
+++ b/cmds/statsd/src/state/StateManager.h
@@ -15,10 +15,11 @@
*/
#pragma once
-//#include <utils/Log.h>
+#include <gtest/gtest_prod.h>
+#include <inttypes.h>
#include <utils/RefBase.h>
-#include "HashableDimensionKey.h"
+#include "HashableDimensionKey.h"
#include "state/StateListener.h"
#include "state/StateTracker.h"
@@ -38,29 +39,29 @@
// Notifies the correct StateTracker of an event.
void onLogEvent(const LogEvent& event);
- // Returns true if stateAtomId is the id of a state atom and notifies the
- // correct StateTracker to register the listener. If the correct
- // StateTracker does not exist, a new StateTracker is created.
- bool registerListener(int stateAtomId, wp<StateListener> listener);
+ // Returns true if atomId is being tracked and is associated with a state
+ // atom. StateManager notifies the correct StateTracker to register listener.
+ // If the correct StateTracker does not exist, a new StateTracker is created.
+ bool registerListener(int atomId, wp<StateListener> listener);
// Notifies the correct StateTracker to unregister a listener
// and removes the tracker if it no longer has any listeners.
- void unregisterListener(int stateAtomId, wp<StateListener> listener);
+ void unregisterListener(int atomId, wp<StateListener> listener);
- // Queries the correct StateTracker for the state that is mapped to the given
- // query key.
+ // Queries the correct StateTracker for the original/un-mapped state value
+ // that is mapped to the given query key.
// If the StateTracker doesn't exist, returns StateTracker::kStateUnknown.
- int getState(int stateAtomId, const HashableDimensionKey& queryKey);
+ int getStateValue(int atomId, const HashableDimensionKey& queryKey);
inline int getStateTrackersCount() {
std::lock_guard<std::mutex> lock(mMutex);
return mStateTrackers.size();
}
- inline int getListenersCount(int stateAtomId) {
+ inline int getListenersCount(int atomId) {
std::lock_guard<std::mutex> lock(mMutex);
- if (mStateTrackers.find(stateAtomId) != mStateTrackers.end()) {
- return mStateTrackers[stateAtomId]->getListenersCount();
+ if (mStateTrackers.find(atomId) != mStateTrackers.end()) {
+ return mStateTrackers[atomId]->getListenersCount();
}
return -1;
}
diff --git a/cmds/statsd/src/state/StateTracker.cpp b/cmds/statsd/src/state/StateTracker.cpp
index 5a91950..323fc0e 100644
--- a/cmds/statsd/src/state/StateTracker.cpp
+++ b/cmds/statsd/src/state/StateTracker.cpp
@@ -30,13 +30,13 @@
: mAtomId(atomId),
mStateField(getSimpleMatcher(atomId, stateAtomInfo.exclusiveField)) {
// create matcher for each primary field
- // TODO(tsaichristine): handle when primary field is first uid in chain
+ // TODO(tsaichristine): b/142108433 handle when primary field is first uid in chain
for (const auto& primary : stateAtomInfo.primaryFields) {
Matcher matcher = getSimpleMatcher(atomId, primary);
mPrimaryFields.push_back(matcher);
}
- // TODO(tsaichristine): set default state, reset state, and nesting
+ // TODO(tsaichristine): b/142108433 set default state, reset state, and nesting
}
void StateTracker::onLogEvent(const LogEvent& event) {
@@ -96,7 +96,7 @@
mListeners.erase(listener);
}
-int StateTracker::getState(const HashableDimensionKey& queryKey) const {
+int StateTracker::getStateValue(const HashableDimensionKey& queryKey) const {
if (queryKey.getValues().size() == mPrimaryFields.size()) {
auto it = mStateMap.find(queryKey);
if (it != mStateMap.end()) {
diff --git a/cmds/statsd/src/state/StateTracker.h b/cmds/statsd/src/state/StateTracker.h
index f22706c..cfa9fd8 100644
--- a/cmds/statsd/src/state/StateTracker.h
+++ b/cmds/statsd/src/state/StateTracker.h
@@ -48,7 +48,7 @@
// Returns the state value mapped to the given query key.
// If the key isn't mapped to a state or the key size doesn't match the
// primary key size, the default state is returned.
- int getState(const HashableDimensionKey& queryKey) const;
+ int getStateValue(const HashableDimensionKey& queryKey) const;
inline int getListenersCount() const {
return mListeners.size();
diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp
index 67625eb..c22e3cc 100644
--- a/cmds/statsd/src/stats_log_util.cpp
+++ b/cmds/statsd/src/stats_log_util.cpp
@@ -445,6 +445,8 @@
return 12 * 60 * 60 * 1000LL;
case ONE_DAY:
return 24 * 60 * 60 * 1000LL;
+ case ONE_WEEK:
+ return 7 * 24 * 60 * 60 * 1000LL;
case CTS:
return 1000;
case TIME_UNIT_UNSPECIFIED:
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index c107397..c98b2cf 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -44,6 +44,7 @@
SIX_HOURS = 7;
TWELVE_HOURS = 8;
ONE_DAY = 9;
+ ONE_WEEK = 10;
CTS = 1000;
}
diff --git a/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp b/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp
new file mode 100644
index 0000000..6591d69
--- /dev/null
+++ b/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp
@@ -0,0 +1,197 @@
+/*
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+
+#include "src/StatsLogProcessor.h"
+#include "src/state/StateManager.h"
+#include "tests/statsd_test_util.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+#ifdef __ANDROID__
+
+TEST(CountMetricE2eTest, TestWithSimpleState) {
+ // Initialize config
+ StatsdConfig config;
+ config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+
+ auto syncStartMatcher = CreateSyncStartAtomMatcher();
+ *config.add_atom_matcher() = syncStartMatcher;
+
+ auto state = CreateScreenState();
+ *config.add_state() = state;
+
+ // Create count metric that slices by screen state
+ int64_t metricId = 123456;
+ auto countMetric = config.add_count_metric();
+ countMetric->set_id(metricId);
+ countMetric->set_what(syncStartMatcher.id());
+ countMetric->set_bucket(TimeUnit::ONE_MINUTE);
+ countMetric->add_slice_by_state(state.id());
+
+ // Initialize StatsLogProcessor
+ const int64_t baseTimeNs = 0; // 0:00
+ const int64_t configAddedTimeNs = baseTimeNs + 1 * NS_PER_SEC; // 0:01
+ const int64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL;
+
+ int uid = 12345;
+ int64_t cfgId = 98765;
+ ConfigKey cfgKey(uid, cfgId);
+
+ auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey);
+
+ // Check that StateTrackers were properly initialized.
+ EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
+ EXPECT_EQ(1,
+ StateManager::getInstance().getListenersCount(android::util::SCREEN_STATE_CHANGED));
+
+ // Check that CountMetricProducer was initialized correctly.
+ EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
+ EXPECT_TRUE(metricsManager->isConfigValid());
+ EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
+ sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
+ EXPECT_EQ(metricProducer->mSlicedStateAtoms.size(), 1);
+ EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), android::util::SCREEN_STATE_CHANGED);
+ EXPECT_EQ(metricProducer->mStateGroupMap.size(), 0);
+}
+
+TEST(CountMetricE2eTest, TestWithMappedState) {
+ // Initialize config
+ StatsdConfig config;
+ config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+
+ auto syncStartMatcher = CreateSyncStartAtomMatcher();
+ *config.add_atom_matcher() = syncStartMatcher;
+
+ auto state = CreateScreenStateWithOnOffMap();
+ *config.add_state() = state;
+
+ // Create count metric that slices by screen state with on/off map
+ int64_t metricId = 123456;
+ auto countMetric = config.add_count_metric();
+ countMetric->set_id(metricId);
+ countMetric->set_what(syncStartMatcher.id());
+ countMetric->set_bucket(TimeUnit::ONE_MINUTE);
+ countMetric->add_slice_by_state(state.id());
+
+ // Initialize StatsLogProcessor
+ const int64_t baseTimeNs = 0; // 0:00
+ const int64_t configAddedTimeNs = baseTimeNs + 1 * NS_PER_SEC; // 0:01
+ const int64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL;
+
+ int uid = 12345;
+ int64_t cfgId = 98765;
+ ConfigKey cfgKey(uid, cfgId);
+
+ auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey);
+
+ // Check that StateTrackers were properly initialized.
+ EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
+ EXPECT_EQ(1,
+ StateManager::getInstance().getListenersCount(android::util::SCREEN_STATE_CHANGED));
+
+ // Check that CountMetricProducer was initialized correctly.
+ EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
+ EXPECT_TRUE(metricsManager->isConfigValid());
+ EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
+ sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
+ EXPECT_EQ(metricProducer->mSlicedStateAtoms.size(), 1);
+ EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), android::util::SCREEN_STATE_CHANGED);
+ EXPECT_EQ(metricProducer->mStateGroupMap.size(), 1);
+
+ StateMap map = state.map();
+ for (auto group : map.group()) {
+ for (auto value : group.value()) {
+ EXPECT_EQ(metricProducer->mStateGroupMap[android::util::SCREEN_STATE_CHANGED][value],
+ group.group_id());
+ }
+ }
+}
+
+TEST(CountMetricE2eTest, TestWithMultipleStates) {
+ // Initialize config
+ StatsdConfig config;
+ config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+
+ auto syncStartMatcher = CreateSyncStartAtomMatcher();
+ *config.add_atom_matcher() = syncStartMatcher;
+
+ auto state1 = CreateScreenStateWithOnOffMap();
+ *config.add_state() = state1;
+ auto state2 = CreateUidProcessState();
+ *config.add_state() = state2;
+
+ // Create count metric that slices by screen state with on/off map
+ int64_t metricId = 123456;
+ auto countMetric = config.add_count_metric();
+ countMetric->set_id(metricId);
+ countMetric->set_what(syncStartMatcher.id());
+ countMetric->set_bucket(TimeUnit::ONE_MINUTE);
+ countMetric->add_slice_by_state(state1.id());
+ countMetric->add_slice_by_state(state2.id());
+
+ // Initialize StatsLogProcessor
+ const int64_t baseTimeNs = 0; // 0:00
+ const int64_t configAddedTimeNs = baseTimeNs + 1 * NS_PER_SEC; // 0:01
+ const int64_t bucketSizeNs =
+ TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL;
+
+ int uid = 12345;
+ int64_t cfgId = 98765;
+ ConfigKey cfgKey(uid, cfgId);
+
+ auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey);
+
+ // Check that StateTrackers were properly initialized.
+ EXPECT_EQ(2, StateManager::getInstance().getStateTrackersCount());
+ EXPECT_EQ(1,
+ StateManager::getInstance().getListenersCount(android::util::SCREEN_STATE_CHANGED));
+ EXPECT_EQ(1, StateManager::getInstance().getListenersCount(
+ android::util::UID_PROCESS_STATE_CHANGED));
+
+ // Check that CountMetricProducer was initialized correctly.
+ EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+ sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
+ EXPECT_TRUE(metricsManager->isConfigValid());
+ EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
+ sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
+ EXPECT_EQ(metricProducer->mSlicedStateAtoms.size(), 2);
+ EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), android::util::SCREEN_STATE_CHANGED);
+ EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(1), android::util::UID_PROCESS_STATE_CHANGED);
+ EXPECT_EQ(metricProducer->mStateGroupMap.size(), 1);
+
+ StateMap map = state1.map();
+ for (auto group : map.group()) {
+ for (auto value : group.value()) {
+ EXPECT_EQ(metricProducer->mStateGroupMap[android::util::SCREEN_STATE_CHANGED][value],
+ group.group_id());
+ }
+ }
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
index 839daa4..8915c73 100644
--- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
@@ -43,8 +43,8 @@
metric.set_bucket(ONE_MINUTE);
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
- 5, 600 * NS_PER_SEC + NS_PER_SEC/2);
+ CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, 5,
+ 600 * NS_PER_SEC + NS_PER_SEC / 2);
EXPECT_EQ(600500000000, countProducer.mCurrentBucketStartTimeNs);
EXPECT_EQ(10, countProducer.mCurrentBucketNum);
EXPECT_EQ(660000000005, countProducer.getCurrentBucketEndTimeNs());
@@ -131,7 +131,8 @@
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- CountMetricProducer countProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs, bucketStartTimeNs);
+ CountMetricProducer countProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs,
+ bucketStartTimeNs);
countProducer.onConditionChanged(true, bucketStartTimeNs);
countProducer.onMatchedLogEvent(1 /*matcher index*/, event1);
@@ -396,6 +397,26 @@
std::ceil(1.0 * event7.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec));
}
+TEST(CountMetricProducerTest, TestOneWeekTimeUnit) {
+ CountMetric metric;
+ metric.set_id(1);
+ metric.set_bucket(ONE_WEEK);
+
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+ int64_t oneDayNs = 24 * 60 * 60 * 1e9;
+ int64_t fiveWeeksNs = 5 * 7 * oneDayNs;
+
+ CountMetricProducer countProducer(
+ kConfigKey, metric, -1 /* meaning no condition */, wizard, oneDayNs, fiveWeeksNs);
+
+ int64_t fiveWeeksOneDayNs = fiveWeeksNs + oneDayNs;
+
+ EXPECT_EQ(fiveWeeksNs, countProducer.mCurrentBucketStartTimeNs);
+ EXPECT_EQ(4, countProducer.mCurrentBucketNum);
+ EXPECT_EQ(fiveWeeksOneDayNs, countProducer.getCurrentBucketEndTimeNs());
+}
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/tests/state/StateTracker_test.cpp b/cmds/statsd/tests/state/StateTracker_test.cpp
index c89ffea..8d38000 100644
--- a/cmds/statsd/tests/state/StateTracker_test.cpp
+++ b/cmds/statsd/tests/state/StateTracker_test.cpp
@@ -44,7 +44,7 @@
std::vector<Update> updates;
- void onStateChanged(int stateAtomId, const HashableDimensionKey& primaryKey, int oldState,
+ void onStateChanged(int atomId, const HashableDimensionKey& primaryKey, int oldState,
int newState) {
updates.emplace_back(primaryKey, newState);
}
@@ -82,6 +82,7 @@
return event;
}
+// Incorrect event - missing fields
std::shared_ptr<LogEvent> buildIncorrectOverlayEvent(int uid, const std::string& packageName, int state) {
std::shared_ptr<LogEvent> event =
std::make_shared<LogEvent>(android::util::OVERLAY_STATE_CHANGED, 1000 /*timestamp*/);
@@ -91,6 +92,18 @@
event->init();
return event;
}
+
+// Incorrect event - exclusive state has wrong type
+std::shared_ptr<LogEvent> buildOverlayEventBadStateType(int uid, const std::string& packageName) {
+ std::shared_ptr<LogEvent> event =
+ std::make_shared<LogEvent>(android::util::OVERLAY_STATE_CHANGED, 1000 /*timestamp*/);
+ event->write((int32_t)uid);
+ event->write(packageName);
+ event->write(true);
+ event->write("string"); // exclusive state: string instead of int
+ event->init();
+ return event;
+}
// END: build event functions.
// START: get primary key functions
@@ -148,22 +161,22 @@
// Register listener to non-existing StateTracker
EXPECT_EQ(0, mgr.getStateTrackersCount());
- mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener1);
+ EXPECT_TRUE(mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener1));
EXPECT_EQ(1, mgr.getStateTrackersCount());
EXPECT_EQ(1, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED));
// Register listener to existing StateTracker
- mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener2);
+ EXPECT_TRUE(mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener2));
EXPECT_EQ(1, mgr.getStateTrackersCount());
EXPECT_EQ(2, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED));
// Register already registered listener to existing StateTracker
- mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener2);
+ EXPECT_TRUE(mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener2));
EXPECT_EQ(1, mgr.getStateTrackersCount());
EXPECT_EQ(2, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED));
// Register listener to non-state atom
- mgr.registerListener(android::util::BATTERY_LEVEL_CHANGED, listener2);
+ EXPECT_FALSE(mgr.registerListener(android::util::BATTERY_LEVEL_CHANGED, listener2));
EXPECT_EQ(1, mgr.getStateTrackersCount());
}
@@ -227,12 +240,12 @@
// check StateTracker was updated by querying for state
HashableDimensionKey queryKey = DEFAULT_DIMENSION_KEY;
- EXPECT_EQ(2, mgr.getState(android::util::SCREEN_STATE_CHANGED, queryKey));
+ EXPECT_EQ(2, mgr.getStateValue(android::util::SCREEN_STATE_CHANGED, queryKey));
}
/**
* Test StateManager's onLogEvent and StateListener's onStateChanged correctly
- * updates listener for states with primary keys.
+ * updates listener for states with one primary key.
*/
TEST(StateTrackerTest, TestStateChangeOnePrimaryField) {
sp<TestStateListener> listener1 = new TestStateListener();
@@ -240,9 +253,8 @@
mgr.registerListener(android::util::UID_PROCESS_STATE_CHANGED, listener1);
// log event
- std::shared_ptr<LogEvent> event = buildUidProcessEvent(
- 1000,
- android::app::ProcessStateEnum::PROCESS_STATE_TOP); // state value: 1002
+ std::shared_ptr<LogEvent> event =
+ buildUidProcessEvent(1000 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_TOP);
mgr.onLogEvent(*event);
// check listener was updated
@@ -252,23 +264,33 @@
// check StateTracker was updated by querying for state
HashableDimensionKey queryKey;
- getUidProcessKey(1000, &queryKey);
- EXPECT_EQ(1002, mgr.getState(android::util::UID_PROCESS_STATE_CHANGED, queryKey));
+ getUidProcessKey(1000 /* uid */, &queryKey);
+ EXPECT_EQ(1002, mgr.getStateValue(android::util::UID_PROCESS_STATE_CHANGED, queryKey));
}
+/**
+ * Test StateManager's onLogEvent and StateListener's onStateChanged correctly
+ * updates listener for states with multiple primary keys.
+ */
TEST(StateTrackerTest, TestStateChangeMultiplePrimaryFields) {
sp<TestStateListener> listener1 = new TestStateListener();
StateManager mgr;
mgr.registerListener(android::util::OVERLAY_STATE_CHANGED, listener1);
// log event
- std::shared_ptr<LogEvent> event = buildOverlayEvent(1000, "package1", 1); // state: ENTERED
+ std::shared_ptr<LogEvent> event =
+ buildOverlayEvent(1000 /* uid */, "package1", 1); // state: ENTERED
mgr.onLogEvent(*event);
- // check listener update
+ // check listener was updated
EXPECT_EQ(1, listener1->updates.size());
EXPECT_EQ(1000, listener1->updates[0].mKey.getValues()[0].mValue.int_value);
EXPECT_EQ(1, listener1->updates[0].mState);
+
+ // check StateTracker was updated by querying for state
+ HashableDimensionKey queryKey;
+ getOverlayKey(1000 /* uid */, "package1", &queryKey);
+ EXPECT_EQ(1, mgr.getStateValue(android::util::OVERLAY_STATE_CHANGED, queryKey));
}
/**
@@ -282,11 +304,14 @@
mgr.registerListener(android::util::OVERLAY_STATE_CHANGED, listener1);
// log event
- std::shared_ptr<LogEvent> event =
- buildIncorrectOverlayEvent(1000, "package1", 1); // state: ENTERED
- mgr.onLogEvent(*event);
+ std::shared_ptr<LogEvent> event1 =
+ buildIncorrectOverlayEvent(1000 /* uid */, "package1", 1 /* state */);
+ std::shared_ptr<LogEvent> event2 = buildOverlayEventBadStateType(1001 /* uid */, "package2");
- // check listener update
+ // check listener was updated
+ mgr.onLogEvent(*event1);
+ EXPECT_EQ(0, listener1->updates.size());
+ mgr.onLogEvent(*event2);
EXPECT_EQ(0, listener1->updates.size());
}
@@ -301,18 +326,19 @@
std::shared_ptr<LogEvent> event1 = buildUidProcessEvent(
1000,
- android::app::ProcessStateEnum::PROCESS_STATE_TOP); // state value: 1002
+ android::app::ProcessStateEnum::PROCESS_STATE_TOP); // state value: 1002
std::shared_ptr<LogEvent> event2 = buildUidProcessEvent(
1001,
- android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE); // state value: 1003
+ android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE); // state value:
+ // 1003
std::shared_ptr<LogEvent> event3 = buildUidProcessEvent(
1002,
- android::app::ProcessStateEnum::PROCESS_STATE_PERSISTENT); // state value: 1000
+ android::app::ProcessStateEnum::PROCESS_STATE_PERSISTENT); // state value: 1000
std::shared_ptr<LogEvent> event4 = buildUidProcessEvent(
1001,
- android::app::ProcessStateEnum::PROCESS_STATE_TOP); // state value: 1002
+ android::app::ProcessStateEnum::PROCESS_STATE_TOP); // state value: 1002
std::shared_ptr<LogEvent> event5 =
- buildScreenEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON); // state value:
+ buildScreenEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON);
std::shared_ptr<LogEvent> event6 = buildOverlayEvent(1000, "package1", 1);
std::shared_ptr<LogEvent> event7 = buildOverlayEvent(1000, "package2", 2);
@@ -327,25 +353,25 @@
// Query for UidProcessState of uid 1001
HashableDimensionKey queryKey1;
getUidProcessKey(1001, &queryKey1);
- EXPECT_EQ(1003, mgr.getState(android::util::UID_PROCESS_STATE_CHANGED, queryKey1));
+ EXPECT_EQ(1003, mgr.getStateValue(android::util::UID_PROCESS_STATE_CHANGED, queryKey1));
// Query for UidProcessState of uid 1004 - not in state map
HashableDimensionKey queryKey2;
getUidProcessKey(1004, &queryKey2);
- EXPECT_EQ(-1,
- mgr.getState(android::util::UID_PROCESS_STATE_CHANGED, queryKey2)); // default state
+ EXPECT_EQ(-1, mgr.getStateValue(android::util::UID_PROCESS_STATE_CHANGED,
+ queryKey2)); // default state
// Query for UidProcessState of uid 1001 - after change in state
mgr.onLogEvent(*event4);
- EXPECT_EQ(1002, mgr.getState(android::util::UID_PROCESS_STATE_CHANGED, queryKey1));
+ EXPECT_EQ(1002, mgr.getStateValue(android::util::UID_PROCESS_STATE_CHANGED, queryKey1));
// Query for ScreenState
- EXPECT_EQ(2, mgr.getState(android::util::SCREEN_STATE_CHANGED, DEFAULT_DIMENSION_KEY));
+ EXPECT_EQ(2, mgr.getStateValue(android::util::SCREEN_STATE_CHANGED, DEFAULT_DIMENSION_KEY));
// Query for OverlayState of uid 1000, package name "package2"
HashableDimensionKey queryKey3;
getOverlayKey(1000, "package2", &queryKey3);
- EXPECT_EQ(2, mgr.getState(android::util::OVERLAY_STATE_CHANGED, queryKey3));
+ EXPECT_EQ(2, mgr.getStateValue(android::util::OVERLAY_STATE_CHANGED, queryKey3));
}
} // namespace statsd
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
index 2c4f3c7..38c22ab 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -251,6 +251,101 @@
return predicate;
}
+State CreateScreenState() {
+ State state;
+ state.set_id(StringToId("ScreenState"));
+ state.set_atom_id(29);
+ return state;
+}
+
+State CreateUidProcessState() {
+ State state;
+ state.set_id(StringToId("UidProcessState"));
+ state.set_atom_id(27);
+ return state;
+}
+
+State CreateOverlayState() {
+ State state;
+ state.set_id(StringToId("OverlayState"));
+ state.set_atom_id(59);
+ return state;
+}
+
+State CreateScreenStateWithOnOffMap() {
+ State state;
+ state.set_id(StringToId("ScreenStateOnOff"));
+ state.set_atom_id(29);
+
+ auto map = CreateScreenStateOnOffMap();
+ *state.mutable_map() = map;
+
+ return state;
+}
+
+State CreateScreenStateWithInDozeMap() {
+ State state;
+ state.set_id(StringToId("ScreenStateInDoze"));
+ state.set_atom_id(29);
+
+ auto map = CreateScreenStateInDozeMap();
+ *state.mutable_map() = map;
+
+ return state;
+}
+
+StateMap_StateGroup CreateScreenStateOnGroup() {
+ StateMap_StateGroup group;
+ group.set_group_id(StringToId("SCREEN_ON"));
+ group.add_value(2);
+ group.add_value(5);
+ group.add_value(6);
+ return group;
+}
+
+StateMap_StateGroup CreateScreenStateOffGroup() {
+ StateMap_StateGroup group;
+ group.set_group_id(StringToId("SCREEN_OFF"));
+ group.add_value(0);
+ group.add_value(1);
+ group.add_value(3);
+ group.add_value(4);
+ return group;
+}
+
+StateMap CreateScreenStateOnOffMap() {
+ StateMap map;
+ *map.add_group() = CreateScreenStateOnGroup();
+ *map.add_group() = CreateScreenStateOffGroup();
+ return map;
+}
+
+StateMap_StateGroup CreateScreenStateInDozeGroup() {
+ StateMap_StateGroup group;
+ group.set_group_id(StringToId("SCREEN_DOZE"));
+ group.add_value(3);
+ group.add_value(4);
+ return group;
+}
+
+StateMap_StateGroup CreateScreenStateNotDozeGroup() {
+ StateMap_StateGroup group;
+ group.set_group_id(StringToId("SCREEN_NOT_DOZE"));
+ group.add_value(0);
+ group.add_value(1);
+ group.add_value(2);
+ group.add_value(5);
+ group.add_value(6);
+ return group;
+}
+
+StateMap CreateScreenStateInDozeMap() {
+ StateMap map;
+ *map.add_group() = CreateScreenStateInDozeGroup();
+ *map.add_group() = CreateScreenStateNotDozeGroup();
+ return map;
+}
+
void addPredicateToPredicateCombination(const Predicate& predicate,
Predicate* combinationPredicate) {
combinationPredicate->mutable_combination()->add_predicate(predicate.id());
diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h
index 635c583..c026105 100644
--- a/cmds/statsd/tests/statsd_test_util.h
+++ b/cmds/statsd/tests/statsd_test_util.h
@@ -104,6 +104,37 @@
// Create a Predicate proto for app is in background.
Predicate CreateIsInBackgroundPredicate();
+// Create State proto for screen state atom.
+State CreateScreenState();
+
+// Create State proto for uid process state atom.
+State CreateUidProcessState();
+
+// Create State proto for overlay state atom.
+State CreateOverlayState();
+
+State CreateScreenStateWithOnOffMap();
+
+State CreateScreenStateWithInDozeMap();
+
+// Create StateGroup proto for ScreenState ON group
+StateMap_StateGroup CreateScreenStateOnGroup();
+
+// Create StateGroup proto for ScreenState OFF group
+StateMap_StateGroup CreateScreenStateOffGroup();
+
+// Create StateMap proto for ScreenState ON/OFF map
+StateMap CreateScreenStateOnOffMap();
+
+// Create StateGroup proto for ScreenState IN DOZE group
+StateMap_StateGroup CreateScreenStateInDozeGroup();
+
+// Create StateGroup proto for ScreenState NOT IN DOZE group
+StateMap_StateGroup CreateScreenStateNotDozeGroup();
+
+// Create StateMap proto for ScreenState IN DOZE map
+StateMap CreateScreenStateInDozeMap();
+
// Add a predicate to the predicate combination.
void addPredicateToPredicateCombination(const Predicate& predicate, Predicate* combination);
@@ -319,4 +350,4 @@
}
} // namespace statsd
} // namespace os
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 86bf20a..03ef286 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -3160,6 +3160,15 @@
}
@Override
+ public String[] getTelephonyPackageNames() {
+ try {
+ return mPM.getTelephonyPackageNames();
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
public String getSystemCaptionsServicePackageName() {
try {
return mPM.getSystemCaptionsServicePackageName();
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 39fab63..d5bc9b0 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2212,9 +2212,9 @@
}
@Override
- public Context createContextAsUser(UserHandle user) {
+ public Context createContextAsUser(UserHandle user, @CreatePackageOptions int flags) {
try {
- return createPackageContextAsUser(getPackageName(), mFlags, user);
+ return createPackageContextAsUser(getPackageName(), flags, user);
} catch (NameNotFoundException e) {
throw new IllegalStateException("Own package not found: package=" + getPackageName());
}
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 316cab8..58f6741 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -997,8 +997,13 @@
}
/**
- * @hide
+ * <p>
+ * Gets the currently applied notification policy. If {@link #getCurrentInterruptionFilter}
+ * is equal to {@link #INTERRUPTION_FILTER_ALL}, then the consolidated notification policy
+ * will match the default notification policy returned by {@link #getNotificationPolicy}.
+ * </p>
*/
+ @Nullable
public NotificationManager.Policy getConsolidatedNotificationPolicy() {
INotificationManager service = getService();
try {
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 9e6054c..68ab89c 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -884,21 +884,24 @@
* @param activityToken optional token to clean up Activity resources
*/
private void cleanupReferences(IBinder activityToken) {
- if (activityToken != null) {
- ActivityResources activityResources = mActivityResourceReferences.get(activityToken);
- if (activityResources != null) {
- ArrayUtils.unstableRemoveIf(activityResources.activityResources,
- sEmptyReferencePredicate);
+ synchronized (this) {
+ if (activityToken != null) {
+ ActivityResources activityResources = mActivityResourceReferences.get(
+ activityToken);
+ if (activityResources != null) {
+ ArrayUtils.unstableRemoveIf(activityResources.activityResources,
+ sEmptyReferencePredicate);
+ }
+ } else {
+ ArrayUtils.unstableRemoveIf(mResourceReferences, sEmptyReferencePredicate);
}
- } else {
- ArrayUtils.unstableRemoveIf(mResourceReferences, sEmptyReferencePredicate);
- }
- for (int index = mResourcesWithLoaders.size() - 1; index >= 0; index--) {
- ResourcesWithLoaders resourcesWithLoaders = mResourcesWithLoaders.get(index);
- Resources resources = resourcesWithLoaders.resources();
- if (resources == null) {
- mResourcesWithLoaders.remove(index);
+ for (int index = mResourcesWithLoaders.size() - 1; index >= 0; index--) {
+ ResourcesWithLoaders resourcesWithLoaders = mResourcesWithLoaders.get(index);
+ Resources resources = resourcesWithLoaders.resources();
+ if (resources == null) {
+ mResourcesWithLoaders.remove(index);
+ }
}
}
}
diff --git a/core/java/android/app/admin/PasswordMetrics.java b/core/java/android/app/admin/PasswordMetrics.java
index 9929855..464f75c 100644
--- a/core/java/android/app/admin/PasswordMetrics.java
+++ b/core/java/android/app/admin/PasswordMetrics.java
@@ -27,9 +27,6 @@
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
-import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
-import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
-
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.app.admin.DevicePolicyManager.PasswordComplexity;
@@ -37,8 +34,7 @@
import android.os.Parcelable;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.Preconditions;
-import com.android.internal.widget.LockPatternUtils.CredentialType;
+import com.android.internal.widget.LockscreenCredential;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -226,21 +222,21 @@
};
/**
- * Returnsthe {@code PasswordMetrics} for a given credential.
+ * Returns the {@code PasswordMetrics} for a given credential.
*
* If the credential is a pin or a password, equivalent to {@link #computeForPassword(byte[])}.
* {@code credential} cannot be null when {@code type} is
* {@link com.android.internal.widget.LockPatternUtils#CREDENTIAL_TYPE_PASSWORD}.
*/
- public static PasswordMetrics computeForCredential(
- @CredentialType int type, byte[] credential) {
- if (type == CREDENTIAL_TYPE_PASSWORD) {
- Preconditions.checkNotNull(credential, "credential cannot be null");
- return PasswordMetrics.computeForPassword(credential);
- } else if (type == CREDENTIAL_TYPE_PATTERN) {
+ public static PasswordMetrics computeForCredential(LockscreenCredential credential) {
+ if (credential.isPassword()) {
+ return PasswordMetrics.computeForPassword(credential.getCredential());
+ } else if (credential.isPattern()) {
return new PasswordMetrics(PASSWORD_QUALITY_SOMETHING);
- } else /* if (type == CREDENTIAL_TYPE_NONE) */ {
+ } else if (credential.isNone()) {
return new PasswordMetrics(PASSWORD_QUALITY_UNSPECIFIED);
+ } else {
+ throw new IllegalArgumentException("Unknown credential type " + credential.getType());
}
}
diff --git a/core/java/android/app/slice/SliceManager.java b/core/java/android/app/slice/SliceManager.java
index 955093d..90ecce2 100644
--- a/core/java/android/app/slice/SliceManager.java
+++ b/core/java/android/app/slice/SliceManager.java
@@ -390,6 +390,8 @@
}
Bundle extras = new Bundle();
extras.putParcelable(SliceProvider.EXTRA_INTENT, intent);
+ extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
+ new ArrayList<>(supportedSpecs));
final Bundle res = provider.call(SliceProvider.METHOD_MAP_INTENT, null, extras);
if (res == null) {
return null;
diff --git a/core/java/android/app/timedetector/TimeSignal.java b/core/java/android/app/timedetector/TimeSignal.java
index da21794..b494260 100644
--- a/core/java/android/app/timedetector/TimeSignal.java
+++ b/core/java/android/app/timedetector/TimeSignal.java
@@ -56,8 +56,7 @@
private static TimeSignal createFromParcel(Parcel in) {
String sourceId = in.readString();
- TimestampedValue<Long> utcTime =
- TimestampedValue.readFromParcel(in, null /* classLoader */, Long.class);
+ TimestampedValue<Long> utcTime = in.readParcelable(null /* classLoader */);
return new TimeSignal(sourceId, utcTime);
}
@@ -69,7 +68,7 @@
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeString(mSourceId);
- TimestampedValue.writeToParcel(dest, mUtcTime);
+ dest.writeParcelable(mUtcTime, 0);
}
@NonNull
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 227684b..e7e278f 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -5244,7 +5244,7 @@
@SystemApi
@TestApi
@NonNull
- public Context createContextAsUser(@NonNull UserHandle user) {
+ public Context createContextAsUser(@NonNull UserHandle user, @CreatePackageOptions int flags) {
if (Build.IS_ENG) {
throw new IllegalStateException("createContextAsUser not overridden!");
}
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index f7cd51e..7993ea1 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -885,8 +885,8 @@
/** @hide */
@Override
- public Context createContextAsUser(UserHandle user) {
- return mBase.createContextAsUser(user);
+ public Context createContextAsUser(UserHandle user, @CreatePackageOptions int flags) {
+ return mBase.createContextAsUser(user, flags);
}
/** @hide */
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 19d8edf..1d78e2c 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -682,6 +682,8 @@
String getWellbeingPackageName();
+ String[] getTelephonyPackageNames();
+
String getAppPredictionServicePackageName();
String getSystemCaptionsServicePackageName();
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 9513ce8..7509065 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2858,6 +2858,14 @@
/**
* Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+ * The device does not have slices implementation.
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_SLICES_DISABLED = "android.software.slices_disabled";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
* The device supports device-unique Keystore attestations. Only available on devices that
* also support {@link #FEATURE_STRONGBOX_KEYSTORE}, and can only be used by device owner
* apps (see {@link android.app.admin.DevicePolicyManager#generateKeyPair}).
@@ -7416,6 +7424,18 @@
}
/**
+ * @return the system defined telephony package names, or null if there's none.
+ *
+ * @hide
+ */
+ @Nullable
+ @TestApi
+ public String[] getTelephonyPackageNames() {
+ throw new UnsupportedOperationException(
+ "getTelephonyPackageNames not implemented in subclass");
+ }
+
+ /**
* @return the system defined content capture service package name, or null if there's none.
*
* @hide
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 0b157fa..cf21e96 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -577,8 +577,6 @@
*/
public interface Callback {
boolean hasFeature(String feature);
- String[] getOverlayPaths(String targetPackageName, String targetPath);
- String[] getOverlayApks(String targetPackageName);
}
/**
@@ -595,14 +593,6 @@
@Override public boolean hasFeature(String feature) {
return mPm.hasSystemFeature(feature);
}
-
- @Override public String[] getOverlayPaths(String targetPackageName, String targetPath) {
- return null;
- }
-
- @Override public String[] getOverlayApks(String targetPackageName) {
- return null;
- }
}
/**
@@ -1195,19 +1185,7 @@
}
final byte[] bytes = IoUtils.readFileAsByteArray(cacheFile.getAbsolutePath());
- Package p = fromCacheEntry(bytes);
- if (mCallback != null) {
- String[] overlayApks = mCallback.getOverlayApks(p.packageName);
- if (overlayApks != null && overlayApks.length > 0) {
- for (String overlayApk : overlayApks) {
- // If a static RRO is updated, return null.
- if (!isCacheUpToDate(new File(overlayApk), cacheFile)) {
- return null;
- }
- }
- }
- }
- return p;
+ return fromCacheEntry(bytes);
} catch (Throwable e) {
Slog.w(TAG, "Error reading package cache: ", e);
@@ -1381,7 +1359,7 @@
final Resources res = new Resources(assets, mMetrics, null);
final String[] outError = new String[1];
- final Package pkg = parseBaseApk(apkPath, res, parser, flags, outError);
+ final Package pkg = parseBaseApk(res, parser, flags, outError);
if (pkg == null) {
throw new PackageParserException(mParseError,
apkPath + " (at " + parser.getPositionDescription() + "): " + outError[0]);
@@ -1944,7 +1922,6 @@
* need to consider whether they should be supported by split APKs and child
* packages.
*
- * @param apkPath The package apk file path
* @param res The resources from which to resolve values
* @param parser The manifest parser
* @param flags Flags how to parse
@@ -1954,8 +1931,7 @@
* @throws XmlPullParserException
* @throws IOException
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
- private Package parseBaseApk(String apkPath, Resources res, XmlResourceParser parser, int flags,
+ private Package parseBaseApk(Resources res, XmlResourceParser parser, int flags,
String[] outError) throws XmlPullParserException, IOException {
final String splitName;
final String pkgName;
@@ -1975,15 +1951,6 @@
return null;
}
- if (mCallback != null) {
- String[] overlayPaths = mCallback.getOverlayPaths(pkgName, apkPath);
- if (overlayPaths != null && overlayPaths.length > 0) {
- for (String overlayPath : overlayPaths) {
- res.getAssets().addOverlayPath(overlayPath);
- }
- }
- }
-
final Package pkg = new Package(pkgName);
TypedArray sa = res.obtainAttributes(parser,
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index dd5c6a5..aa6f58e 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -237,6 +237,28 @@
@TestApi
public static final int PROTECTION_FLAG_APP_PREDICTOR = 0x200000;
+ /**
+ * Additional flag for {@link #protectionLevel}, corresponding
+ * to the <code>telephony</code> value of
+ * {@link android.R.attr#protectionLevel}.
+ *
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public static final int PROTECTION_FLAG_TELEPHONY = 0x400000;
+
+ /**
+ * Additional flag for {@link #protectionLevel}, corresponding
+ * to the <code>wifi</code> value of
+ * {@link android.R.attr#protectionLevel}.
+ *
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public static final int PROTECTION_FLAG_WIFI = 0x800000;
+
/** @hide */
@IntDef(flag = true, prefix = { "PROTECTION_FLAG_" }, value = {
PROTECTION_FLAG_PRIVILEGED,
@@ -258,6 +280,8 @@
PROTECTION_FLAG_CONFIGURATOR,
PROTECTION_FLAG_INCIDENT_REPORT_APPROVER,
PROTECTION_FLAG_APP_PREDICTOR,
+ PROTECTION_FLAG_TELEPHONY,
+ PROTECTION_FLAG_WIFI,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ProtectionFlags {}
@@ -501,6 +525,12 @@
if ((level & PermissionInfo.PROTECTION_FLAG_APP_PREDICTOR) != 0) {
protLevel += "|appPredictor";
}
+ if ((level & PermissionInfo.PROTECTION_FLAG_TELEPHONY) != 0) {
+ protLevel += "|telephony";
+ }
+ if ((level & PermissionInfo.PROTECTION_FLAG_WIFI) != 0) {
+ protLevel += "|wifi";
+ }
return protLevel;
}
diff --git a/core/java/android/net/INetworkPolicyListener.aidl b/core/java/android/net/INetworkPolicyListener.aidl
index 106b7be..fe9141c 100644
--- a/core/java/android/net/INetworkPolicyListener.aidl
+++ b/core/java/android/net/INetworkPolicyListener.aidl
@@ -15,6 +15,7 @@
*/
package android.net;
+import android.telephony.SubscriptionPlan;
/** {@hide} */
oneway interface INetworkPolicyListener {
@@ -22,5 +23,6 @@
void onMeteredIfacesChanged(in String[] meteredIfaces);
void onRestrictBackgroundChanged(boolean restrictBackground);
void onUidPoliciesChanged(int uid, int uidPolicies);
- void onSubscriptionOverride(int subId, int overrideMask, int overrideValue, long networkTypeMask);
+ void onSubscriptionOverride(int subId, int overrideMask, int overrideValue);
+ void onSubscriptionPlansChanged(int subId, in SubscriptionPlan[] plans);
}
diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl
index 90327663..385cb1d 100644
--- a/core/java/android/net/INetworkPolicyManager.aidl
+++ b/core/java/android/net/INetworkPolicyManager.aidl
@@ -76,7 +76,7 @@
SubscriptionPlan[] getSubscriptionPlans(int subId, String callingPackage);
void setSubscriptionPlans(int subId, in SubscriptionPlan[] plans, String callingPackage);
String getSubscriptionPlansOwner(int subId);
- void setSubscriptionOverride(int subId, int overrideMask, int overrideValue, long networkTypeMask, long timeoutMillis, String callingPackage);
+ void setSubscriptionOverride(int subId, int overrideMask, int overrideValue, long timeoutMillis, String callingPackage);
void factoryReset(String subscriber);
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 628dcd2..9150aae 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -31,6 +31,7 @@
import android.os.Build;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.telephony.SubscriptionPlan;
import android.util.DebugUtils;
import android.util.Pair;
import android.util.Range;
@@ -380,7 +381,8 @@
@Override public void onMeteredIfacesChanged(String[] meteredIfaces) { }
@Override public void onRestrictBackgroundChanged(boolean restrictBackground) { }
@Override public void onUidPoliciesChanged(int uid, int uidPolicies) { }
- @Override public void onSubscriptionOverride(int subId, int overrideMask, int overrideValue,
- long networkTypeMask) { }
+ @Override public void onSubscriptionOverride(int subId, int overrideMask,
+ int overrideValue) { }
+ @Override public void onSubscriptionPlansChanged(int subId, SubscriptionPlan[] plans) { }
}
}
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 2c9333b..ef3afab 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -1085,4 +1085,13 @@
StrictMode.clearGatheredViolations();
return res;
}
+
+ /**
+ * Returns the specified service from servicemanager. If the service is not running,
+ * servicemanager will attempt to start it, and this function will wait for it to be ready.
+ * Returns nullptr only if there are permission problems or fatal errors.
+ * @hide
+ */
+ public static final native @Nullable IBinder waitForService(@NonNull String serviceName)
+ throws RemoteException;
}
diff --git a/core/java/android/print/TEST_MAPPING b/core/java/android/print/TEST_MAPPING
new file mode 100644
index 0000000..4fa8822
--- /dev/null
+++ b/core/java/android/print/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsPrintTestCases",
+ "options": [
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ }
+ ]
+ }
+ ]
+}
diff --git a/core/java/android/print/pdf/TEST_MAPPING b/core/java/android/print/pdf/TEST_MAPPING
new file mode 100644
index 0000000..d763598
--- /dev/null
+++ b/core/java/android/print/pdf/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsPdfTestCases"
+ }
+ ]
+}
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index fd81178..eb09930 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -363,15 +363,22 @@
* <p>
* Type: INTEGER (int)
*
- * @see #FLAG_SUPPORTS_WRITE
- * @see #FLAG_SUPPORTS_DELETE
- * @see #FLAG_SUPPORTS_THUMBNAIL
+ * @see #FLAG_DIR_BLOCKS_TREE
* @see #FLAG_DIR_PREFERS_GRID
* @see #FLAG_DIR_PREFERS_LAST_MODIFIED
- * @see #FLAG_VIRTUAL_DOCUMENT
+ * @see #FLAG_DIR_SUPPORTS_CREATE
+ * @see #FLAG_PARTIAL
* @see #FLAG_SUPPORTS_COPY
+ * @see #FLAG_SUPPORTS_DELETE
+ * @see #FLAG_SUPPORTS_METADATA
* @see #FLAG_SUPPORTS_MOVE
* @see #FLAG_SUPPORTS_REMOVE
+ * @see #FLAG_SUPPORTS_RENAME
+ * @see #FLAG_SUPPORTS_SETTINGS
+ * @see #FLAG_SUPPORTS_THUMBNAIL
+ * @see #FLAG_SUPPORTS_WRITE
+ * @see #FLAG_VIRTUAL_DOCUMENT
+ * @see #FLAG_WEB_LINKABLE
*/
public static final String COLUMN_FLAGS = "flags";
@@ -542,6 +549,23 @@
* @see DocumentsContract#getDocumentMetadata(ContentInterface, Uri)
*/
public static final int FLAG_SUPPORTS_METADATA = 1 << 14;
+
+ /**
+ * Flag indicating that a document is a directory that wants to block itself
+ * from being selected when the user launches an {@link Intent#ACTION_OPEN_DOCUMENT_TREE}
+ * intent. Only valid when {@link #COLUMN_MIME_TYPE} is {@link #MIME_TYPE_DIR}.
+ * <p>
+ * Note that this flag <em>only</em> applies to the single directory to which it is
+ * applied. It does <em>not</em> block the user from selecting either a parent or
+ * child directory during an {@link Intent#ACTION_OPEN_DOCUMENT_TREE} request.
+ * In particular, the only way to guarantee that a specific directory can never
+ * be granted via an {@link Intent#ACTION_OPEN_DOCUMENT_TREE} request is to ensure
+ * that both it and <em>all of its parent directories</em> have set this flag.
+ *
+ * @see Intent#ACTION_OPEN_DOCUMENT_TREE
+ * @see #COLUMN_FLAGS
+ */
+ public static final int FLAG_DIR_BLOCKS_TREE = 1 << 15;
}
/**
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 493f9a2..a1333df 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -39,7 +39,6 @@
import android.content.Intent;
import android.content.UriPermission;
import android.database.Cursor;
-import android.database.DatabaseUtils;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.ImageDecoder;
@@ -70,15 +69,19 @@
import com.android.internal.annotations.GuardedBy;
+import libcore.util.HexEncoding;
+
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.text.Collator;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
+import java.util.Locale;
import java.util.Objects;
import java.util.Set;
import java.util.regex.Pattern;
@@ -2059,7 +2062,17 @@
/**
* A non human readable key calculated from the TITLE, used for
* searching, sorting and grouping
+ *
+ * @see Audio#keyFor(String)
+ * @deprecated These keys are generated using
+ * {@link java.util.Locale#ROOT}, which means they don't
+ * reflect locale-specific sorting preferences. To apply
+ * locale-specific sorting preferences, use
+ * {@link ContentResolver#QUERY_ARG_SQL_SORT_ORDER} with
+ * {@code COLLATE LOCALIZED}, or
+ * {@link ContentResolver#QUERY_ARG_SORT_LOCALE}.
*/
+ @Deprecated
@Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
public static final String TITLE_KEY = "title_key";
@@ -2104,7 +2117,17 @@
/**
* A non human readable key calculated from the ARTIST, used for
* searching, sorting and grouping
+ *
+ * @see Audio#keyFor(String)
+ * @deprecated These keys are generated using
+ * {@link java.util.Locale#ROOT}, which means they don't
+ * reflect locale-specific sorting preferences. To apply
+ * locale-specific sorting preferences, use
+ * {@link ContentResolver#QUERY_ARG_SQL_SORT_ORDER} with
+ * {@code COLLATE LOCALIZED}, or
+ * {@link ContentResolver#QUERY_ARG_SORT_LOCALE}.
*/
+ @Deprecated
@Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
public static final String ARTIST_KEY = "artist_key";
@@ -2129,7 +2152,17 @@
/**
* A non human readable key calculated from the ALBUM, used for
* searching, sorting and grouping
+ *
+ * @see Audio#keyFor(String)
+ * @deprecated These keys are generated using
+ * {@link java.util.Locale#ROOT}, which means they don't
+ * reflect locale-specific sorting preferences. To apply
+ * locale-specific sorting preferences, use
+ * {@link ContentResolver#QUERY_ARG_SQL_SORT_ORDER} with
+ * {@code COLLATE LOCALIZED}, or
+ * {@link ContentResolver#QUERY_ARG_SORT_LOCALE}.
*/
+ @Deprecated
@Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
public static final String ALBUM_KEY = "album_key";
@@ -2186,91 +2219,89 @@
public static final String IS_AUDIOBOOK = "is_audiobook";
/**
- * The genre of the audio file, if any
- * Does not exist in the database - only used by the media scanner for inserts.
- * @hide
+ * The id of the genre the audio file is from, if any
*/
- @Deprecated
- // @Column(Cursor.FIELD_TYPE_STRING)
+ @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
+ public static final String GENRE_ID = "genre_id";
+
+ /**
+ * The genre of the audio file, if any.
+ */
+ @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
public static final String GENRE = "genre";
/**
- * The resource URI of a localized title, if any
+ * A non human readable key calculated from the GENRE, used for
+ * searching, sorting and grouping
+ *
+ * @see Audio#keyFor(String)
+ * @deprecated These keys are generated using
+ * {@link java.util.Locale#ROOT}, which means they don't
+ * reflect locale-specific sorting preferences. To apply
+ * locale-specific sorting preferences, use
+ * {@link ContentResolver#QUERY_ARG_SQL_SORT_ORDER} with
+ * {@code COLLATE LOCALIZED}, or
+ * {@link ContentResolver#QUERY_ARG_SORT_LOCALE}.
+ */
+ @Deprecated
+ @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
+ public static final String GENRE_KEY = "genre_key";
+
+ /**
+ * The resource URI of a localized title, if any.
+ * <p>
* Conforms to this pattern:
- * Scheme: {@link ContentResolver.SCHEME_ANDROID_RESOURCE}
- * Authority: Package Name of ringtone title provider
- * First Path Segment: Type of resource (must be "string")
- * Second Path Segment: Resource ID of title
- * @hide
+ * <ul>
+ * <li>Scheme: {@link ContentResolver#SCHEME_ANDROID_RESOURCE}
+ * <li>Authority: Package Name of ringtone title provider
+ * <li>First Path Segment: Type of resource (must be "string")
+ * <li>Second Path Segment: Resource ID of title
+ * </ul>
*/
@Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
public static final String TITLE_RESOURCE_URI = "title_resource_uri";
}
+ private static final Pattern PATTERN_TRIM_BEFORE = Pattern.compile(
+ "(?i)(^(the|an|a) |,\\s*(the|an|a)$|[^\\w\\s]|^\\s+|\\s+$)");
+ private static final Pattern PATTERN_TRIM_AFTER = Pattern.compile(
+ "(^(00)+|(00)+$)");
+
/**
- * Converts a name to a "key" that can be used for grouping, sorting
- * and searching.
- * The rules that govern this conversion are:
- * - remove 'special' characters like ()[]'!?.,
- * - remove leading/trailing spaces
- * - convert everything to lowercase
- * - remove leading "the ", "an " and "a "
- * - remove trailing ", the|an|a"
- * - remove accents. This step leaves us with CollationKey data,
- * which is not human readable
+ * Converts a user-visible string into a "key" that can be used for
+ * grouping, sorting, and searching.
*
- * @param name The artist or album name to convert
- * @return The "key" for the given name.
+ * @return Opaque token that should not be parsed or displayed to users.
+ * @deprecated These keys are generated using
+ * {@link java.util.Locale#ROOT}, which means they don't
+ * reflect locale-specific sorting preferences. To apply
+ * locale-specific sorting preferences, use
+ * {@link ContentResolver#QUERY_ARG_SQL_SORT_ORDER} with
+ * {@code COLLATE LOCALIZED}, or
+ * {@link ContentResolver#QUERY_ARG_SORT_LOCALE}.
*/
- public static String keyFor(String name) {
- if (name != null) {
- boolean sortfirst = false;
- if (name.equals(UNKNOWN_STRING)) {
- return "\001";
- }
- // Check if the first character is \001. We use this to
- // force sorting of certain special files, like the silent ringtone.
- if (name.startsWith("\001")) {
- sortfirst = true;
- }
- name = name.trim().toLowerCase();
- if (name.startsWith("the ")) {
- name = name.substring(4);
- }
- if (name.startsWith("an ")) {
- name = name.substring(3);
- }
- if (name.startsWith("a ")) {
- name = name.substring(2);
- }
- if (name.endsWith(", the") || name.endsWith(",the") ||
- name.endsWith(", an") || name.endsWith(",an") ||
- name.endsWith(", a") || name.endsWith(",a")) {
- name = name.substring(0, name.lastIndexOf(','));
- }
- name = name.replaceAll("[\\[\\]\\(\\)\"'.,?!]", "").trim();
- if (name.length() > 0) {
- // Insert a separator between the characters to avoid
- // matches on a partial character. If we ever change
- // to start-of-word-only matches, this can be removed.
- StringBuilder b = new StringBuilder();
- b.append('.');
- int nl = name.length();
- for (int i = 0; i < nl; i++) {
- b.append(name.charAt(i));
- b.append('.');
- }
- name = b.toString();
- String key = DatabaseUtils.getCollationKey(name);
- if (sortfirst) {
- key = "\001" + key;
- }
- return key;
- } else {
- return "";
- }
+ @Deprecated
+ public static @Nullable String keyFor(@Nullable String name) {
+ if (TextUtils.isEmpty(name)) return null;
+
+ if (UNKNOWN_STRING.equals(name)) {
+ return "01";
}
- return null;
+
+ final boolean sortFirst = name.startsWith("\001");
+
+ name = PATTERN_TRIM_BEFORE.matcher(name).replaceAll("");
+ if (TextUtils.isEmpty(name)) return null;
+
+ final Collator c = Collator.getInstance(Locale.ROOT);
+ c.setStrength(Collator.PRIMARY);
+ name = HexEncoding.encodeToString(c.getCollationKey(name).toByteArray(), false);
+
+ name = PATTERN_TRIM_AFTER.matcher(name).replaceAll("");
+ if (sortFirst) {
+ name = "01" + name;
+ }
+ return name;
}
public static final class Media implements AudioColumns {
@@ -2632,7 +2663,17 @@
/**
* A non human readable key calculated from the ARTIST, used for
* searching, sorting and grouping
+ *
+ * @see Audio#keyFor(String)
+ * @deprecated These keys are generated using
+ * {@link java.util.Locale#ROOT}, which means they don't
+ * reflect locale-specific sorting preferences. To apply
+ * locale-specific sorting preferences, use
+ * {@link ContentResolver#QUERY_ARG_SQL_SORT_ORDER} with
+ * {@code COLLATE LOCALIZED}, or
+ * {@link ContentResolver#QUERY_ARG_SORT_LOCALE}.
*/
+ @Deprecated
@Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
public static final String ARTIST_KEY = "artist_key";
@@ -2736,6 +2777,23 @@
public static final String ARTIST = "artist";
/**
+ * A non human readable key calculated from the ARTIST, used for
+ * searching, sorting and grouping
+ *
+ * @see Audio#keyFor(String)
+ * @deprecated These keys are generated using
+ * {@link java.util.Locale#ROOT}, which means they don't
+ * reflect locale-specific sorting preferences. To apply
+ * locale-specific sorting preferences, use
+ * {@link ContentResolver#QUERY_ARG_SQL_SORT_ORDER} with
+ * {@code COLLATE LOCALIZED}, or
+ * {@link ContentResolver#QUERY_ARG_SORT_LOCALE}.
+ */
+ @Deprecated
+ @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
+ public static final String ARTIST_KEY = "artist_key";
+
+ /**
* The number of songs on this album
*/
@Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
@@ -2770,7 +2828,17 @@
/**
* A non human readable key calculated from the ALBUM, used for
* searching, sorting and grouping
+ *
+ * @see Audio#keyFor(String)
+ * @deprecated These keys are generated using
+ * {@link java.util.Locale#ROOT}, which means they don't
+ * reflect locale-specific sorting preferences. To apply
+ * locale-specific sorting preferences, use
+ * {@link ContentResolver#QUERY_ARG_SQL_SORT_ORDER} with
+ * {@code COLLATE LOCALIZED}, or
+ * {@link ContentResolver#QUERY_ARG_SORT_LOCALE}.
*/
+ @Deprecated
@Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
public static final String ALBUM_KEY = "album_key";
diff --git a/core/java/android/util/DebugUtils.java b/core/java/android/util/DebugUtils.java
index 20e0d14..bc5edf8 100644
--- a/core/java/android/util/DebugUtils.java
+++ b/core/java/android/util/DebugUtils.java
@@ -255,7 +255,7 @@
if (value == 0 && flagsWasZero) {
return constNameWithoutPrefix(prefix, field);
}
- if ((flags & value) == value) {
+ if (value != 0 && (flags & value) == value) {
flags &= ~value;
res.append(constNameWithoutPrefix(prefix, field)).append('|');
}
diff --git a/core/java/android/util/TimestampedValue.java b/core/java/android/util/TimestampedValue.java
index 1289e4d..4505673 100644
--- a/core/java/android/util/TimestampedValue.java
+++ b/core/java/android/util/TimestampedValue.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
+import android.os.Parcelable;
import android.os.SystemClock;
import java.util.Objects;
@@ -30,14 +31,14 @@
* If a suitable clock is used the reference time can be used to identify the age of a value or
* ordering between values.
*
- * <p>To read and write a timestamped value from / to a Parcel see
- * {@link #readFromParcel(Parcel, ClassLoader, Class)} and
- * {@link #writeToParcel(Parcel, TimestampedValue)}.
+ * <p>This class implements {@link Parcelable} for convenience but instances will only actually be
+ * parcelable if the value type held is {@code null}, {@link Parcelable}, or one of the other types
+ * supported by {@link Parcel#writeValue(Object)} / {@link Parcel#readValue(ClassLoader)}.
*
* @param <T> the type of the value with an associated timestamp
* @hide
*/
-public final class TimestampedValue<T> {
+public final class TimestampedValue<T> implements Parcelable {
private final long mReferenceTimeMillis;
private final T mValue;
@@ -81,57 +82,43 @@
}
/**
- * Read a {@link TimestampedValue} from a parcel that was stored using
- * {@link #writeToParcel(Parcel, TimestampedValue)}.
- *
- * <p>The marshalling/unmarshalling of the value relies upon {@link Parcel#writeValue(Object)}
- * and {@link Parcel#readValue(ClassLoader)} and so this method can only be used with types
- * supported by those methods.
- *
- * @param in the Parcel to read from
- * @param classLoader the ClassLoader to pass to {@link Parcel#readValue(ClassLoader)}
- * @param valueClass the expected type of the value, typically the same as {@code <T>} but can
- * also be a subclass
- * @throws RuntimeException if the value read is not compatible with {@code valueClass} or the
- * object could not be read
- */
- @SuppressWarnings("unchecked")
- @NonNull
- public static <T> TimestampedValue<T> readFromParcel(
- @NonNull Parcel in, @Nullable ClassLoader classLoader, Class<? extends T> valueClass) {
- long referenceTimeMillis = in.readLong();
- T value = (T) in.readValue(classLoader);
- // Equivalent to static code: if (!(value.getClass() instanceof {valueClass})) {
- if (value != null && !valueClass.isAssignableFrom(value.getClass())) {
- throw new RuntimeException("Value was of type " + value.getClass()
- + " is not assignable to " + valueClass);
- }
- return new TimestampedValue<>(referenceTimeMillis, value);
- }
-
- /**
- * Write a {@link TimestampedValue} to a parcel so that it can be read using
- * {@link #readFromParcel(Parcel, ClassLoader, Class)}.
- *
- * <p>The marshalling/unmarshalling of the value relies upon {@link Parcel#writeValue(Object)}
- * and {@link Parcel#readValue(ClassLoader)} and so this method can only be used with types
- * supported by those methods.
- *
- * @param dest the Parcel
- * @param timestampedValue the value
- * @throws RuntimeException if the value could not be written to the Parcel
- */
- public static void writeToParcel(
- @NonNull Parcel dest, @NonNull TimestampedValue<?> timestampedValue) {
- dest.writeLong(timestampedValue.mReferenceTimeMillis);
- dest.writeValue(timestampedValue.mValue);
- }
-
- /**
* Returns the difference in milliseconds between two instance's reference times.
*/
public static long referenceTimeDifference(
@NonNull TimestampedValue<?> one, @NonNull TimestampedValue<?> two) {
return one.mReferenceTimeMillis - two.mReferenceTimeMillis;
}
+
+ public static final @NonNull Parcelable.Creator<TimestampedValue<?>> CREATOR =
+ new Parcelable.ClassLoaderCreator<TimestampedValue<?>>() {
+
+ @Override
+ public TimestampedValue<?> createFromParcel(@NonNull Parcel source) {
+ return createFromParcel(source, null);
+ }
+
+ @Override
+ public TimestampedValue<?> createFromParcel(
+ @NonNull Parcel source, @Nullable ClassLoader classLoader) {
+ long referenceTimeMillis = source.readLong();
+ Object value = source.readValue(classLoader);
+ return new TimestampedValue<>(referenceTimeMillis, value);
+ }
+
+ @Override
+ public TimestampedValue[] newArray(int size) {
+ return new TimestampedValue[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeLong(mReferenceTimeMillis);
+ dest.writeValue(mValue);
+ }
}
diff --git a/core/java/android/view/IRecentsAnimationController.aidl b/core/java/android/view/IRecentsAnimationController.aidl
index 955be8d..762366e 100644
--- a/core/java/android/view/IRecentsAnimationController.aidl
+++ b/core/java/android/view/IRecentsAnimationController.aidl
@@ -120,4 +120,10 @@
* @see IRecentsAnimationRunner#onCancelled
*/
void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot);
+
+ /**
+ * Sets a state for controller to decide which surface is the destination when the recents
+ * animation is cancelled through fail safe mechanism.
+ */
+ void setWillFinishToHome(boolean willFinishToHome);
}
diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl
index 4b872d3..8bf99ec 100644
--- a/core/java/android/view/IWindow.aidl
+++ b/core/java/android/view/IWindow.aidl
@@ -81,6 +81,14 @@
*/
void showInsets(int types, boolean fromIme);
+ /**
+ * Called when a set of insets source window should be hidden by policy.
+ *
+ * @param types internal inset types (WindowInsets.Type.InsetType) to hide
+ * @param fromIme true if this request originated from IME (InputMethodService).
+ */
+ void hideInsets(int types, boolean fromIme);
+
void moved(int newX, int newY);
void dispatchAppVisibility(boolean visible);
void dispatchGetNewSurface();
diff --git a/core/java/android/view/InputMonitor.java b/core/java/android/view/InputMonitor.java
index 1a1d7e6..ad1f201 100644
--- a/core/java/android/view/InputMonitor.java
+++ b/core/java/android/view/InputMonitor.java
@@ -40,8 +40,6 @@
private static final boolean DEBUG = false;
@NonNull
- private final String mName;
- @NonNull
private final InputChannel mInputChannel;
@NonNull
private final IInputMonitorHost mHost;
@@ -81,23 +79,19 @@
- // Code below generated by codegen v1.0.1.
+ // Code below generated by codegen v1.0.7.
//
// DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
//
// To regenerate run:
// $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/InputMonitor.java
- //
- // CHECKSTYLE:OFF Generated code
+
@DataClass.Generated.Member
public InputMonitor(
- @NonNull String name,
@NonNull InputChannel inputChannel,
@NonNull IInputMonitorHost host) {
- this.mName = name;
- com.android.internal.util.AnnotationValidations.validate(
- NonNull.class, null, mName);
this.mInputChannel = inputChannel;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mInputChannel);
@@ -109,11 +103,6 @@
}
@DataClass.Generated.Member
- public @NonNull String getName() {
- return mName;
- }
-
- @DataClass.Generated.Member
public @NonNull InputChannel getInputChannel() {
return mInputChannel;
}
@@ -130,7 +119,6 @@
// String fieldNameToString() { ... }
return "InputMonitor { " +
- "name = " + mName + ", " +
"inputChannel = " + mInputChannel + ", " +
"host = " + mHost +
" }";
@@ -142,7 +130,6 @@
// You can override field parcelling by defining methods like:
// void parcelFieldName(Parcel dest, int flags) { ... }
- dest.writeString(mName);
dest.writeTypedObject(mInputChannel, flags);
dest.writeStrongInterface(mHost);
}
@@ -151,6 +138,26 @@
@DataClass.Generated.Member
public int describeContents() { return 0; }
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ InputMonitor(Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ InputChannel inputChannel = (InputChannel) in.readTypedObject(InputChannel.CREATOR);
+ IInputMonitorHost host = IInputMonitorHost.Stub.asInterface(in.readStrongBinder());
+
+ this.mInputChannel = inputChannel;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mInputChannel);
+ this.mHost = host;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mHost);
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
@DataClass.Generated.Member
public static final @NonNull Parcelable.Creator<InputMonitor> CREATOR
= new Parcelable.Creator<InputMonitor>() {
@@ -160,26 +167,16 @@
}
@Override
- @SuppressWarnings({"unchecked", "RedundantCast"})
public InputMonitor createFromParcel(Parcel in) {
- // You can override field unparcelling by defining methods like:
- // static FieldType unparcelFieldName(Parcel in) { ... }
-
- String name = in.readString();
- InputChannel inputChannel = (InputChannel) in.readTypedObject(InputChannel.CREATOR);
- IInputMonitorHost host = IInputMonitorHost.Stub.asInterface(in.readStrongBinder());
- return new InputMonitor(
- name,
- inputChannel,
- host);
+ return new InputMonitor(in);
}
};
@DataClass.Generated(
- time = 1569871940995L,
- codegenVersion = "1.0.1",
+ time = 1571177265149L,
+ codegenVersion = "1.0.7",
sourceFile = "frameworks/base/core/java/android/view/InputMonitor.java",
- inputSignatures = "private static final java.lang.String TAG\nprivate static final boolean DEBUG\nprivate final @android.annotation.NonNull java.lang.String mName\nprivate final @android.annotation.NonNull android.view.InputChannel mInputChannel\nprivate final @android.annotation.NonNull android.view.IInputMonitorHost mHost\npublic void pilferPointers()\npublic void dispose()\nclass InputMonitor extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true)")
+ inputSignatures = "private static final java.lang.String TAG\nprivate static final boolean DEBUG\nprivate final @android.annotation.NonNull android.view.InputChannel mInputChannel\nprivate final @android.annotation.NonNull android.view.IInputMonitorHost mHost\npublic void pilferPointers()\npublic void dispose()\nclass InputMonitor extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index 341c214..e4deffa 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -229,6 +229,10 @@
final InsetsSourceConsumer consumer = items.valueAt(i);
final InsetsSource source = mInitialInsetsState.getSource(consumer.getType());
final InsetsSourceControl control = consumer.getControl();
+ if (control == null) {
+ // Control may not be available for consumer yet or revoked.
+ continue;
+ }
final SurfaceControl leash = consumer.getControl().getLeash();
mTmpMatrix.setTranslate(control.getSurfacePosition().x, control.getSurfacePosition().y);
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 5a8636d..5bb4f63 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -251,6 +251,10 @@
@Override
public void hide(@InsetType int types) {
+ hide(types, false /* fromIme */);
+ }
+
+ void hide(@InsetType int types, boolean fromIme) {
int typesReady = 0;
final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
for (int i = internalTypes.size() - 1; i >= 0; i--) {
@@ -265,7 +269,7 @@
}
typesReady |= InsetsState.toPublicType(consumer.getType());
}
- applyAnimation(typesReady, false /* show */, false /* fromIme */);
+ applyAnimation(typesReady, false /* show */, fromIme /* fromIme */);
}
@Override
@@ -331,42 +335,35 @@
boolean isReady = true;
for (int i = internalTypes.size() - 1; i >= 0; i--) {
InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i));
- // Double check for IME that IME target window has focus.
- if (consumer.getType() != TYPE_IME || consumer.hasWindowFocus()) {
- boolean setVisible = !consumer.isVisible();
- if (setVisible) {
- // Show request
- switch(consumer.requestShow(fromIme)) {
- case ShowResult.SHOW_IMMEDIATELY:
- typesReady |= InsetsState.toPublicType(consumer.getType());
- break;
- case ShowResult.SHOW_DELAYED:
- isReady = false;
- break;
- case ShowResult.SHOW_FAILED:
- // IME cannot be shown (since it didn't have focus), proceed
- // with animation of other types.
- if (mPendingTypesToShow != 0) {
- // remove IME from pending because view no longer has focus.
- mPendingTypesToShow &= ~InsetsState.toPublicType(TYPE_IME);
- }
- break;
- }
- } else {
- // Hide request
- // TODO: Move notifyHidden() to beginning of the hide animation
- // (when visibility actually changes using hideDirectly()).
- consumer.notifyHidden();
- typesReady |= InsetsState.toPublicType(consumer.getType());
+ boolean setVisible = !consumer.isVisible();
+ if (setVisible) {
+ // Show request
+ switch(consumer.requestShow(fromIme)) {
+ case ShowResult.SHOW_IMMEDIATELY:
+ typesReady |= InsetsState.toPublicType(consumer.getType());
+ break;
+ case ShowResult.SHOW_DELAYED:
+ isReady = false;
+ break;
+ case ShowResult.SHOW_FAILED:
+ // IME cannot be shown (since it didn't have focus), proceed
+ // with animation of other types.
+ if (mPendingTypesToShow != 0) {
+ // remove IME from pending because view no longer has focus.
+ mPendingTypesToShow &= ~InsetsState.toPublicType(TYPE_IME);
+ }
+ break;
}
- consumers.put(consumer.getType(), consumer);
} else {
- // window doesnt have focus, no-op.
- isReady = false;
- // TODO: Let the calling app know that window has lost focus and
- // show()/hide()/controlWindowInsetsAnimation requests will be ignored.
- typesReady &= ~InsetsState.toPublicType(consumer.getType());
+ // Hide request
+ // TODO: Move notifyHidden() to beginning of the hide animation
+ // (when visibility actually changes using hideDirectly()).
+ if (!fromIme) {
+ consumer.notifyHidden();
+ }
+ typesReady |= InsetsState.toPublicType(consumer.getType());
}
+ consumers.put(consumer.getType(), consumer);
}
return new Pair<>(typesReady, isReady);
}
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 8a1fd62..ad59ae5 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -32,6 +32,7 @@
import android.os.Debug;
import android.os.Handler;
import android.os.Looper;
+import android.os.Message;
import android.os.RemoteException;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -46,6 +47,7 @@
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
+import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -53,12 +55,12 @@
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.util.ArrayDeque;
-import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.concurrent.Callable;
-import java.util.concurrent.CancellationException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
@@ -68,6 +70,7 @@
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;
+import java.util.stream.Stream;
/**
* Various debugging/tracing tools related to {@link View} and the view hierarchy.
@@ -331,11 +334,83 @@
public View findHierarchyView(String className, int hashCode);
}
- private static HashMap<Class<?>, Method[]> mCapturedViewMethodsForClasses = null;
- private static HashMap<Class<?>, Field[]> mCapturedViewFieldsForClasses = null;
+ private abstract static class PropertyInfo<T extends Annotation,
+ R extends AccessibleObject & Member> {
+
+ public final R member;
+ public final T property;
+ public final String name;
+ public final Class<?> returnType;
+
+ public String entrySuffix = "";
+ public String valueSuffix = "";
+
+ PropertyInfo(Class<T> property, R member, Class<?> returnType) {
+ this.member = member;
+ this.name = member.getName();
+ this.property = member.getAnnotation(property);
+ this.returnType = returnType;
+ }
+
+ public abstract Object invoke(Object target) throws Exception;
+
+ static <T extends Annotation> PropertyInfo<T, ?> forMethod(Method method,
+ Class<T> property) {
+ // Ensure the method return and parameter types can be resolved.
+ try {
+ if ((method.getReturnType() == Void.class)
+ || (method.getParameterTypes().length != 0)) {
+ return null;
+ }
+ } catch (NoClassDefFoundError e) {
+ return null;
+ }
+ if (!method.isAnnotationPresent(property)) {
+ return null;
+ }
+ method.setAccessible(true);
+
+ PropertyInfo info = new MethodPI(method, property);
+ info.entrySuffix = "()";
+ info.valueSuffix = ";";
+ return info;
+ }
+
+ static <T extends Annotation> PropertyInfo<T, ?> forField(Field field, Class<T> property) {
+ if (!field.isAnnotationPresent(property)) {
+ return null;
+ }
+ field.setAccessible(true);
+ return new FieldPI<>(field, property);
+ }
+ }
+
+ private static class MethodPI<T extends Annotation> extends PropertyInfo<T, Method> {
+
+ MethodPI(Method method, Class<T> property) {
+ super(property, method, method.getReturnType());
+ }
+
+ @Override
+ public Object invoke(Object target) throws Exception {
+ return member.invoke(target);
+ }
+ }
+
+ private static class FieldPI<T extends Annotation> extends PropertyInfo<T, Field> {
+
+ FieldPI(Field field, Class<T> property) {
+ super(property, field, field.getType());
+ }
+
+ @Override
+ public Object invoke(Object target) throws Exception {
+ return member.get(target);
+ }
+ }
// Maximum delay in ms after which we stop trying to capture a View's drawing
- private static final int CAPTURE_TIMEOUT = 4000;
+ private static final int CAPTURE_TIMEOUT = 6000;
private static final String REMOTE_COMMAND_CAPTURE = "CAPTURE";
private static final String REMOTE_COMMAND_DUMP = "DUMP";
@@ -346,9 +421,9 @@
private static final String REMOTE_COMMAND_CAPTURE_LAYERS = "CAPTURE_LAYERS";
private static final String REMOTE_COMMAND_OUTPUT_DISPLAYLIST = "OUTPUT_DISPLAYLIST";
- private static HashMap<Class<?>, Field[]> sFieldsForClasses;
- private static HashMap<Class<?>, Method[]> sMethodsForClasses;
- private static HashMap<AccessibleObject, ExportedProperty> sAnnotations;
+ private static HashMap<Class<?>, PropertyInfo<ExportedProperty, ?>[]> sExportProperties;
+ private static HashMap<Class<?>, PropertyInfo<CapturedViewProperty, ?>[]>
+ sCapturedViewProperties;
/**
* @deprecated This enum is now unused
@@ -1157,6 +1232,69 @@
private static void dumpViewHierarchy(Context context, ViewGroup group,
BufferedWriter out, int level, boolean skipChildren, boolean includeProperties) {
+ cacheExportedProperties(group.getClass());
+ if (!skipChildren) {
+ cacheExportedPropertiesForChildren(group);
+ }
+ // Try to use the handler provided by the view
+ Handler handler = group.getHandler();
+ // Fall back on using the main thread
+ if (handler == null) {
+ handler = new Handler(Looper.getMainLooper());
+ }
+
+ if (handler.getLooper() == Looper.myLooper()) {
+ dumpViewHierarchyOnUIThread(context, group, out, level, skipChildren,
+ includeProperties);
+ } else {
+ FutureTask task = new FutureTask(() ->
+ dumpViewHierarchyOnUIThread(context, group, out, level, skipChildren,
+ includeProperties), null);
+ Message msg = Message.obtain(handler, task);
+ msg.setAsynchronous(true);
+ handler.sendMessage(msg);
+ while (true) {
+ try {
+ task.get(CAPTURE_TIMEOUT, java.util.concurrent.TimeUnit.MILLISECONDS);
+ return;
+ } catch (InterruptedException e) {
+ // try again
+ } catch (ExecutionException | TimeoutException e) {
+ // Something unexpected happened.
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ }
+
+ private static void cacheExportedPropertiesForChildren(ViewGroup group) {
+ final int count = group.getChildCount();
+ for (int i = 0; i < count; i++) {
+ final View view = group.getChildAt(i);
+ cacheExportedProperties(view.getClass());
+ if (view instanceof ViewGroup) {
+ cacheExportedPropertiesForChildren((ViewGroup) view);
+ }
+ }
+ }
+
+ private static void cacheExportedProperties(Class<?> klass) {
+ if (sExportProperties != null && sExportProperties.containsKey(klass)) {
+ return;
+ }
+ do {
+ for (PropertyInfo<ExportedProperty, ?> info : getExportedProperties(klass)) {
+ if (!info.returnType.isPrimitive() && info.property.deepExport()) {
+ cacheExportedProperties(info.returnType);
+ }
+ }
+ klass = klass.getSuperclass();
+ } while (klass != Object.class);
+ }
+
+
+ private static void dumpViewHierarchyOnUIThread(Context context, ViewGroup group,
+ BufferedWriter out, int level, boolean skipChildren, boolean includeProperties) {
if (!dumpView(context, group, out, level, includeProperties)) {
return;
}
@@ -1169,16 +1307,16 @@
for (int i = 0; i < count; i++) {
final View view = group.getChildAt(i);
if (view instanceof ViewGroup) {
- dumpViewHierarchy(context, (ViewGroup) view, out, level + 1, skipChildren,
- includeProperties);
+ dumpViewHierarchyOnUIThread(context, (ViewGroup) view, out, level + 1,
+ skipChildren, includeProperties);
} else {
dumpView(context, view, out, level + 1, includeProperties);
}
if (view.mOverlay != null) {
ViewOverlay overlay = view.getOverlay();
ViewGroup overlayContainer = overlay.mOverlayViewGroup;
- dumpViewHierarchy(context, overlayContainer, out, level + 2, skipChildren,
- includeProperties);
+ dumpViewHierarchyOnUIThread(context, overlayContainer, out, level + 2,
+ skipChildren, includeProperties);
}
}
if (group instanceof HierarchyHandler) {
@@ -1212,81 +1350,28 @@
return true;
}
- private static Field[] getExportedPropertyFields(Class<?> klass) {
- if (sFieldsForClasses == null) {
- sFieldsForClasses = new HashMap<Class<?>, Field[]>();
- }
- if (sAnnotations == null) {
- sAnnotations = new HashMap<AccessibleObject, ExportedProperty>(512);
- }
-
- final HashMap<Class<?>, Field[]> map = sFieldsForClasses;
-
- Field[] fields = map.get(klass);
- if (fields != null) {
- return fields;
- }
-
- try {
- final Field[] declaredFields = klass.getDeclaredFieldsUnchecked(false);
- final ArrayList<Field> foundFields = new ArrayList<Field>();
- for (final Field field : declaredFields) {
- // Fields which can't be resolved have a null type.
- if (field.getType() != null && field.isAnnotationPresent(ExportedProperty.class)) {
- field.setAccessible(true);
- foundFields.add(field);
- sAnnotations.put(field, field.getAnnotation(ExportedProperty.class));
- }
- }
- fields = foundFields.toArray(new Field[foundFields.size()]);
- map.put(klass, fields);
- } catch (NoClassDefFoundError e) {
- throw new AssertionError(e);
- }
-
- return fields;
+ private static <T extends Annotation> PropertyInfo<T, ?>[] convertToPropertyInfos(
+ Method[] methods, Field[] fields, Class<T> property) {
+ return Stream.of(Arrays.stream(methods).map(m -> PropertyInfo.forMethod(m, property)),
+ Arrays.stream(fields).map(f -> PropertyInfo.forField(f, property)))
+ .flatMap(Function.identity())
+ .filter(i -> i != null)
+ .toArray(PropertyInfo[]::new);
}
- private static Method[] getExportedPropertyMethods(Class<?> klass) {
- if (sMethodsForClasses == null) {
- sMethodsForClasses = new HashMap<Class<?>, Method[]>(100);
+ private static PropertyInfo<ExportedProperty, ?>[] getExportedProperties(Class<?> klass) {
+ if (sExportProperties == null) {
+ sExportProperties = new HashMap<>();
}
- if (sAnnotations == null) {
- sAnnotations = new HashMap<AccessibleObject, ExportedProperty>(512);
+ final HashMap<Class<?>, PropertyInfo<ExportedProperty, ?>[]> map = sExportProperties;
+ PropertyInfo<ExportedProperty, ?>[] properties = sExportProperties.get(klass);
+
+ if (properties == null) {
+ properties = convertToPropertyInfos(klass.getDeclaredMethodsUnchecked(false),
+ klass.getDeclaredFieldsUnchecked(false), ExportedProperty.class);
+ map.put(klass, properties);
}
-
- final HashMap<Class<?>, Method[]> map = sMethodsForClasses;
-
- Method[] methods = map.get(klass);
- if (methods != null) {
- return methods;
- }
-
- methods = klass.getDeclaredMethodsUnchecked(false);
-
- final ArrayList<Method> foundMethods = new ArrayList<Method>();
- for (final Method method : methods) {
- // Ensure the method return and parameter types can be resolved.
- try {
- method.getReturnType();
- method.getParameterTypes();
- } catch (NoClassDefFoundError e) {
- continue;
- }
-
- if (method.getParameterTypes().length == 0 &&
- method.isAnnotationPresent(ExportedProperty.class) &&
- method.getReturnType() != Void.class) {
- method.setAccessible(true);
- foundMethods.add(method);
- sAnnotations.put(method, method.getAnnotation(ExportedProperty.class));
- }
- }
-
- methods = foundMethods.toArray(new Method[foundMethods.size()]);
- map.put(klass, methods);
-
- return methods;
+ return properties;
}
private static void dumpViewProperties(Context context, Object view,
@@ -1305,233 +1390,97 @@
Class<?> klass = view.getClass();
do {
- exportFields(context, view, out, klass, prefix);
- exportMethods(context, view, out, klass, prefix);
+ writeExportedProperties(context, view, out, klass, prefix);
klass = klass.getSuperclass();
} while (klass != Object.class);
}
- private static Object callMethodOnAppropriateTheadBlocking(final Method method,
- final Object object) throws IllegalAccessException, InvocationTargetException,
- TimeoutException {
- if (!(object instanceof View)) {
- return method.invoke(object, (Object[]) null);
- }
-
- final View view = (View) object;
- Callable<Object> callable = new Callable<Object>() {
- @Override
- public Object call() throws IllegalAccessException, InvocationTargetException {
- return method.invoke(view, (Object[]) null);
- }
- };
- FutureTask<Object> future = new FutureTask<Object>(callable);
- // Try to use the handler provided by the view
- Handler handler = view.getHandler();
- // Fall back on using the main thread
- if (handler == null) {
- handler = new Handler(android.os.Looper.getMainLooper());
- }
- handler.post(future);
- while (true) {
- try {
- return future.get(CAPTURE_TIMEOUT, java.util.concurrent.TimeUnit.MILLISECONDS);
- } catch (ExecutionException e) {
- Throwable t = e.getCause();
- if (t instanceof IllegalAccessException) {
- throw (IllegalAccessException)t;
- }
- if (t instanceof InvocationTargetException) {
- throw (InvocationTargetException)t;
- }
- throw new RuntimeException("Unexpected exception", t);
- } catch (InterruptedException e) {
- // Call get again
- } catch (CancellationException e) {
- throw new RuntimeException("Unexpected cancellation exception", e);
- }
- }
- }
-
private static String formatIntToHexString(int value) {
return "0x" + Integer.toHexString(value).toUpperCase();
}
- private static void exportMethods(Context context, Object view, BufferedWriter out,
+ private static void writeExportedProperties(Context context, Object view, BufferedWriter out,
Class<?> klass, String prefix) throws IOException {
-
- final Method[] methods = getExportedPropertyMethods(klass);
- int count = methods.length;
- for (int i = 0; i < count; i++) {
- final Method method = methods[i];
+ for (PropertyInfo<ExportedProperty, ?> info : getExportedProperties(klass)) {
//noinspection EmptyCatchBlock
+ Object value;
try {
- Object methodValue = callMethodOnAppropriateTheadBlocking(method, view);
- final Class<?> returnType = method.getReturnType();
- final ExportedProperty property = sAnnotations.get(method);
- String categoryPrefix =
- property.category().length() != 0 ? property.category() + ":" : "";
-
- if (returnType == int.class) {
- if (property.resolveId() && context != null) {
- final int id = (Integer) methodValue;
- methodValue = resolveId(context, id);
- } else {
- final FlagToString[] flagsMapping = property.flagMapping();
- if (flagsMapping.length > 0) {
- final int intValue = (Integer) methodValue;
- final String valuePrefix =
- categoryPrefix + prefix + method.getName() + '_';
- exportUnrolledFlags(out, flagsMapping, intValue, valuePrefix);
- }
-
- final IntToString[] mapping = property.mapping();
- if (mapping.length > 0) {
- final int intValue = (Integer) methodValue;
- boolean mapped = false;
- int mappingCount = mapping.length;
- for (int j = 0; j < mappingCount; j++) {
- final IntToString mapper = mapping[j];
- if (mapper.from() == intValue) {
- methodValue = mapper.to();
- mapped = true;
- break;
- }
- }
-
- if (!mapped) {
- methodValue = intValue;
- }
- }
- }
- } else if (returnType == int[].class) {
- final int[] array = (int[]) methodValue;
- final String valuePrefix = categoryPrefix + prefix + method.getName() + '_';
- final String suffix = "()";
-
- exportUnrolledArray(context, out, property, array, valuePrefix, suffix);
-
- continue;
- } else if (returnType == String[].class) {
- final String[] array = (String[]) methodValue;
- if (property.hasAdjacentMapping() && array != null) {
- for (int j = 0; j < array.length; j += 2) {
- if (array[j] != null) {
- writeEntry(out, categoryPrefix + prefix, array[j], "()",
- array[j + 1] == null ? "null" : array[j + 1]);
- }
-
- }
- }
-
- continue;
- } else if (!returnType.isPrimitive()) {
- if (property.deepExport()) {
- dumpViewProperties(context, methodValue, out, prefix + property.prefix());
- continue;
- }
- }
-
- writeEntry(out, categoryPrefix + prefix, method.getName(), "()", methodValue);
- } catch (IllegalAccessException e) {
- } catch (InvocationTargetException e) {
- } catch (TimeoutException e) {
+ value = info.invoke(view);
+ } catch (Exception e) {
+ // ignore
+ continue;
}
- }
- }
- private static void exportFields(Context context, Object view, BufferedWriter out,
- Class<?> klass, String prefix) throws IOException {
+ String categoryPrefix =
+ info.property.category().length() != 0 ? info.property.category() + ":" : "";
- final Field[] fields = getExportedPropertyFields(klass);
+ if (info.returnType == int.class || info.returnType == byte.class) {
+ if (info.property.resolveId() && context != null) {
+ final int id = (Integer) value;
+ value = resolveId(context, id);
- int count = fields.length;
- for (int i = 0; i < count; i++) {
- final Field field = fields[i];
-
- //noinspection EmptyCatchBlock
- try {
- Object fieldValue = null;
- final Class<?> type = field.getType();
- final ExportedProperty property = sAnnotations.get(field);
- String categoryPrefix =
- property.category().length() != 0 ? property.category() + ":" : "";
-
- if (type == int.class || type == byte.class) {
- if (property.resolveId() && context != null) {
- final int id = field.getInt(view);
- fieldValue = resolveId(context, id);
- } else {
- final FlagToString[] flagsMapping = property.flagMapping();
- if (flagsMapping.length > 0) {
- final int intValue = field.getInt(view);
- final String valuePrefix =
- categoryPrefix + prefix + field.getName() + '_';
- exportUnrolledFlags(out, flagsMapping, intValue, valuePrefix);
- }
-
- final IntToString[] mapping = property.mapping();
- if (mapping.length > 0) {
- final int intValue = field.getInt(view);
- int mappingCount = mapping.length;
- for (int j = 0; j < mappingCount; j++) {
- final IntToString mapped = mapping[j];
- if (mapped.from() == intValue) {
- fieldValue = mapped.to();
- break;
- }
- }
-
- if (fieldValue == null) {
- fieldValue = intValue;
- }
- }
-
- if (property.formatToHexString()) {
- fieldValue = field.get(view);
- if (type == int.class) {
- fieldValue = formatIntToHexString((Integer) fieldValue);
- } else if (type == byte.class) {
- fieldValue = "0x"
- + HexEncoding.encodeToString((Byte) fieldValue, true);
- }
- }
+ } else if (info.property.formatToHexString()) {
+ if (info.returnType == int.class) {
+ value = formatIntToHexString((Integer) value);
+ } else if (info.returnType == byte.class) {
+ value = "0x"
+ + HexEncoding.encodeToString((Byte) value, true);
}
- } else if (type == int[].class) {
- final int[] array = (int[]) field.get(view);
- final String valuePrefix = categoryPrefix + prefix + field.getName() + '_';
- final String suffix = "";
-
- exportUnrolledArray(context, out, property, array, valuePrefix, suffix);
-
- continue;
- } else if (type == String[].class) {
- final String[] array = (String[]) field.get(view);
- if (property.hasAdjacentMapping() && array != null) {
- for (int j = 0; j < array.length; j += 2) {
- if (array[j] != null) {
- writeEntry(out, categoryPrefix + prefix, array[j], "",
- array[j + 1] == null ? "null" : array[j + 1]);
- }
- }
+ } else {
+ final ViewDebug.FlagToString[] flagsMapping = info.property.flagMapping();
+ if (flagsMapping.length > 0) {
+ final int intValue = (Integer) value;
+ final String valuePrefix =
+ categoryPrefix + prefix + info.name + '_';
+ exportUnrolledFlags(out, flagsMapping, intValue, valuePrefix);
}
- continue;
- } else if (!type.isPrimitive()) {
- if (property.deepExport()) {
- dumpViewProperties(context, field.get(view), out, prefix +
- property.prefix());
- continue;
+ final ViewDebug.IntToString[] mapping = info.property.mapping();
+ if (mapping.length > 0) {
+ final int intValue = (Integer) value;
+ boolean mapped = false;
+ int mappingCount = mapping.length;
+ for (int j = 0; j < mappingCount; j++) {
+ final ViewDebug.IntToString mapper = mapping[j];
+ if (mapper.from() == intValue) {
+ value = mapper.to();
+ mapped = true;
+ break;
+ }
+ }
+
+ if (!mapped) {
+ value = intValue;
+ }
+ }
+ }
+ } else if (info.returnType == int[].class) {
+ final int[] array = (int[]) value;
+ final String valuePrefix = categoryPrefix + prefix + info.name + '_';
+ exportUnrolledArray(context, out, info.property, array, valuePrefix,
+ info.entrySuffix);
+
+ continue;
+ } else if (info.returnType == String[].class) {
+ final String[] array = (String[]) value;
+ if (info.property.hasAdjacentMapping() && array != null) {
+ for (int j = 0; j < array.length; j += 2) {
+ if (array[j] != null) {
+ writeEntry(out, categoryPrefix + prefix, array[j],
+ info.entrySuffix, array[j + 1] == null ? "null" : array[j + 1]);
+ }
}
}
- if (fieldValue == null) {
- fieldValue = field.get(view);
+ continue;
+ } else if (!info.returnType.isPrimitive()) {
+ if (info.property.deepExport()) {
+ dumpViewProperties(context, value, out, prefix + info.property.prefix());
+ continue;
}
-
- writeEntry(out, categoryPrefix + prefix, field.getName(), "", fieldValue);
- } catch (IllegalAccessException e) {
}
+
+ writeEntry(out, categoryPrefix + prefix, info.name, info.entrySuffix, value);
}
}
@@ -1721,91 +1670,40 @@
}
}
- private static Field[] capturedViewGetPropertyFields(Class<?> klass) {
- if (mCapturedViewFieldsForClasses == null) {
- mCapturedViewFieldsForClasses = new HashMap<Class<?>, Field[]>();
+ private static PropertyInfo<CapturedViewProperty, ?>[] getCapturedViewProperties(
+ Class<?> klass) {
+ if (sCapturedViewProperties == null) {
+ sCapturedViewProperties = new HashMap<>();
}
- final HashMap<Class<?>, Field[]> map = mCapturedViewFieldsForClasses;
+ final HashMap<Class<?>, PropertyInfo<CapturedViewProperty, ?>[]> map =
+ sCapturedViewProperties;
- Field[] fields = map.get(klass);
- if (fields != null) {
- return fields;
+ PropertyInfo<CapturedViewProperty, ?>[] infos = map.get(klass);
+ if (infos == null) {
+ infos = convertToPropertyInfos(klass.getMethods(), klass.getFields(),
+ CapturedViewProperty.class);
+ map.put(klass, infos);
}
-
- final ArrayList<Field> foundFields = new ArrayList<Field>();
- fields = klass.getFields();
-
- int count = fields.length;
- for (int i = 0; i < count; i++) {
- final Field field = fields[i];
- if (field.isAnnotationPresent(CapturedViewProperty.class)) {
- field.setAccessible(true);
- foundFields.add(field);
- }
- }
-
- fields = foundFields.toArray(new Field[foundFields.size()]);
- map.put(klass, fields);
-
- return fields;
+ return infos;
}
- private static Method[] capturedViewGetPropertyMethods(Class<?> klass) {
- if (mCapturedViewMethodsForClasses == null) {
- mCapturedViewMethodsForClasses = new HashMap<Class<?>, Method[]>();
- }
- final HashMap<Class<?>, Method[]> map = mCapturedViewMethodsForClasses;
-
- Method[] methods = map.get(klass);
- if (methods != null) {
- return methods;
- }
-
- final ArrayList<Method> foundMethods = new ArrayList<Method>();
- methods = klass.getMethods();
-
- int count = methods.length;
- for (int i = 0; i < count; i++) {
- final Method method = methods[i];
- if (method.getParameterTypes().length == 0 &&
- method.isAnnotationPresent(CapturedViewProperty.class) &&
- method.getReturnType() != Void.class) {
- method.setAccessible(true);
- foundMethods.add(method);
- }
- }
-
- methods = foundMethods.toArray(new Method[foundMethods.size()]);
- map.put(klass, methods);
-
- return methods;
- }
-
- private static String capturedViewExportMethods(Object obj, Class<?> klass,
- String prefix) {
-
+ private static String exportCapturedViewProperties(Object obj, Class<?> klass, String prefix) {
if (obj == null) {
return "null";
}
StringBuilder sb = new StringBuilder();
- final Method[] methods = capturedViewGetPropertyMethods(klass);
- int count = methods.length;
- for (int i = 0; i < count; i++) {
- final Method method = methods[i];
+ for (PropertyInfo<CapturedViewProperty, ?> pi : getCapturedViewProperties(klass)) {
try {
- Object methodValue = method.invoke(obj, (Object[]) null);
- final Class<?> returnType = method.getReturnType();
+ Object methodValue = pi.invoke(obj);
- CapturedViewProperty property = method.getAnnotation(CapturedViewProperty.class);
- if (property.retrieveReturn()) {
+ if (pi.property.retrieveReturn()) {
//we are interested in the second level data only
- sb.append(capturedViewExportMethods(methodValue, returnType, method.getName() + "#"));
+ sb.append(exportCapturedViewProperties(methodValue, pi.returnType,
+ pi.name + "#"));
} else {
- sb.append(prefix);
- sb.append(method.getName());
- sb.append("()=");
+ sb.append(prefix).append(pi.name).append(pi.entrySuffix).append("=");
if (methodValue != null) {
final String value = methodValue.toString().replace("\n", "\\n");
@@ -1813,47 +1711,10 @@
} else {
sb.append("null");
}
- sb.append("; ");
+ sb.append(pi.valueSuffix).append(" ");
}
- } catch (IllegalAccessException e) {
- //Exception IllegalAccess, it is OK here
- //we simply ignore this method
- } catch (InvocationTargetException e) {
- //Exception InvocationTarget, it is OK here
- //we simply ignore this method
- }
- }
- return sb.toString();
- }
-
- private static String capturedViewExportFields(Object obj, Class<?> klass, String prefix) {
- if (obj == null) {
- return "null";
- }
-
- StringBuilder sb = new StringBuilder();
- final Field[] fields = capturedViewGetPropertyFields(klass);
-
- int count = fields.length;
- for (int i = 0; i < count; i++) {
- final Field field = fields[i];
- try {
- Object fieldValue = field.get(obj);
-
- sb.append(prefix);
- sb.append(field.getName());
- sb.append("=");
-
- if (fieldValue != null) {
- final String value = fieldValue.toString().replace("\n", "\\n");
- sb.append(value);
- } else {
- sb.append("null");
- }
- sb.append(' ');
- } catch (IllegalAccessException e) {
- //Exception IllegalAccess, it is OK here
- //we simply ignore this field
+ } catch (Exception e) {
+ //It is OK here, we simply ignore this property
}
}
return sb.toString();
@@ -1869,8 +1730,7 @@
public static void dumpCapturedView(String tag, Object view) {
Class<?> klass = view.getClass();
StringBuilder sb = new StringBuilder(klass.getName() + ": ");
- sb.append(capturedViewExportFields(view, klass, ""));
- sb.append(capturedViewExportMethods(view, klass, ""));
+ sb.append(exportCapturedViewProperties(view, klass, ""));
Log.d(tag, sb.toString());
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 9ddd84f..20dc234 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -4570,6 +4570,7 @@
private static final int MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED = 32;
private static final int MSG_LOCATION_IN_PARENT_DISPLAY_CHANGED = 33;
private static final int MSG_SHOW_INSETS = 34;
+ private static final int MSG_HIDE_INSETS = 35;
final class ViewRootHandler extends Handler {
@@ -4636,6 +4637,8 @@
return "MSG_LOCATION_IN_PARENT_DISPLAY_CHANGED";
case MSG_SHOW_INSETS:
return "MSG_SHOW_INSETS";
+ case MSG_HIDE_INSETS:
+ return "MSG_HIDE_INSETS";
}
return super.getMessageName(message);
}
@@ -4754,6 +4757,10 @@
mInsetsController.show(msg.arg1, msg.arg2 == 1);
break;
}
+ case MSG_HIDE_INSETS: {
+ mInsetsController.hide(msg.arg1, msg.arg2 == 1);
+ break;
+ }
case MSG_WINDOW_MOVED:
if (mAdded) {
final int w = mWinFrame.width();
@@ -7559,6 +7566,10 @@
mHandler.obtainMessage(MSG_SHOW_INSETS, types, fromIme ? 1 : 0).sendToTarget();
}
+ private void hideInsets(@InsetType int types, boolean fromIme) {
+ mHandler.obtainMessage(MSG_HIDE_INSETS, types, fromIme ? 1 : 0).sendToTarget();
+ }
+
public void dispatchMoved(int newX, int newY) {
if (DEBUG_LAYOUT) Log.v(mTag, "Window moved " + this + ": newX=" + newX + " newY=" + newY);
if (mTranslator != null) {
@@ -8682,6 +8693,14 @@
}
@Override
+ public void hideInsets(@InsetType int types, boolean fromIme) {
+ final ViewRootImpl viewAncestor = mViewAncestor.get();
+ if (viewAncestor != null) {
+ viewAncestor.hideInsets(types, fromIme);
+ }
+ }
+
+ @Override
public void moved(int newX, int newY) {
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 0b42cd9..db76bb6 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1680,8 +1680,9 @@
* to determine its default behavior.
*
* {@hide} */
- @UnsupportedAppUsage
- public static final int PRIVATE_FLAG_SHOW_FOR_ALL_USERS = 0x00000010;
+ @SystemApi
+ @RequiresPermission(permission.INTERNAL_SYSTEM_WINDOW)
+ public static final int SYSTEM_FLAG_SHOW_FOR_ALL_USERS = 0x00000010;
/**
* Never animate position changes of the window.
@@ -1842,6 +1843,7 @@
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true, prefix = { "SYSTEM_FLAG_" }, value = {
SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS,
+ SYSTEM_FLAG_SHOW_FOR_ALL_USERS,
})
public @interface SystemFlags {}
@@ -1863,8 +1865,8 @@
equals = PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS,
name = "WANTS_OFFSET_NOTIFICATIONS"),
@ViewDebug.FlagToString(
- mask = PRIVATE_FLAG_SHOW_FOR_ALL_USERS,
- equals = PRIVATE_FLAG_SHOW_FOR_ALL_USERS,
+ mask = SYSTEM_FLAG_SHOW_FOR_ALL_USERS,
+ equals = SYSTEM_FLAG_SHOW_FOR_ALL_USERS,
name = "SHOW_FOR_ALL_USERS"),
@ViewDebug.FlagToString(
mask = PRIVATE_FLAG_NO_MOVE_ANIMATION,
diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
index 925a589..0b15cd0 100644
--- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
+++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
@@ -246,7 +246,7 @@
Toast warningToast = mFrameworkObjectProvider.makeToastFromText(
mContext, toastMessage, Toast.LENGTH_LONG);
warningToast.getWindowParams().privateFlags |=
- WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
warningToast.show();
}
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 407a85f..068056f 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -388,21 +388,24 @@
mResolverDrawerLayout.setPadding(mSystemWindowInsets.left, mSystemWindowInsets.top,
mSystemWindowInsets.right, 0);
- View emptyView = findViewById(R.id.empty);
- if (emptyView != null) {
- emptyView.setPadding(0, 0, 0, mSystemWindowInsets.bottom
- + getResources().getDimensionPixelSize(
- R.dimen.chooser_edge_margin_normal) * 2);
- }
-
- if (mFooterSpacer == null) {
- mFooterSpacer = new Space(getApplicationContext());
+ // Need extra padding so the list can fully scroll up
+ if (useLayoutWithDefault()) {
+ if (mFooterSpacer == null) {
+ mFooterSpacer = new Space(getApplicationContext());
+ } else {
+ ((ListView) mAdapterView).removeFooterView(mFooterSpacer);
+ }
+ mFooterSpacer.setLayoutParams(new AbsListView.LayoutParams(LayoutParams.MATCH_PARENT,
+ mSystemWindowInsets.bottom));
+ ((ListView) mAdapterView).addFooterView(mFooterSpacer);
} else {
- ((ListView) mAdapterView).removeFooterView(mFooterSpacer);
+ View emptyView = findViewById(R.id.empty);
+ if (emptyView != null) {
+ emptyView.setPadding(0, 0, 0, mSystemWindowInsets.bottom
+ + getResources().getDimensionPixelSize(
+ R.dimen.chooser_edge_margin_normal) * 2);
+ }
}
- mFooterSpacer.setLayoutParams(new AbsListView.LayoutParams(LayoutParams.MATCH_PARENT,
- mSystemWindowInsets.bottom));
- ((ListView) mAdapterView).addFooterView(mFooterSpacer);
resetButtonBar();
@@ -561,7 +564,7 @@
intent.getData().getHost(),
mAdapter.getFilteredItem().getDisplayLabel());
} else if (mAdapter.areAllTargetsBrowsers()) {
- dialogTitle = getString(ActionTitle.BROWSABLE_TITLE_RES);
+ dialogTitle = getString(ActionTitle.BROWSABLE_TITLE_RES);
} else {
dialogTitle = getString(ActionTitle.BROWSABLE_HOST_TITLE_RES,
intent.getData().getHost());
@@ -1304,6 +1307,7 @@
// In case this method is called again (due to activity recreation), avoid adding a new
// header if one is already present.
if (useHeader && listView != null && listView.getHeaderViewsCount() == 0) {
+ listView.setHeaderDividersEnabled(true);
listView.addHeaderView(LayoutInflater.from(this).inflate(
R.layout.resolver_different_item_header, listView, false));
}
@@ -1346,11 +1350,13 @@
final ViewGroup buttonLayout = findViewById(R.id.button_bar);
if (buttonLayout != null) {
buttonLayout.setVisibility(View.VISIBLE);
- int inset = mSystemWindowInsets != null ? mSystemWindowInsets.bottom : 0;
- buttonLayout.setPadding(buttonLayout.getPaddingLeft(), buttonLayout.getPaddingTop(),
- buttonLayout.getPaddingRight(), getResources().getDimensionPixelSize(
- R.dimen.resolver_button_bar_spacing) + inset);
+ if (!useLayoutWithDefault()) {
+ int inset = mSystemWindowInsets != null ? mSystemWindowInsets.bottom : 0;
+ buttonLayout.setPadding(buttonLayout.getPaddingLeft(), buttonLayout.getPaddingTop(),
+ buttonLayout.getPaddingRight(), getResources().getDimensionPixelSize(
+ R.dimen.resolver_button_bar_spacing) + inset);
+ }
mOnceButton = (Button) buttonLayout.findViewById(R.id.button_once);
mAlwaysButton = (Button) buttonLayout.findViewById(R.id.button_always);
@@ -2057,7 +2063,9 @@
CharSequence subLabel = info.getExtendedInfo();
if (TextUtils.equals(label, subLabel)) subLabel = null;
- if (!TextUtils.equals(holder.text2.getText(), subLabel)) {
+ if (!TextUtils.equals(holder.text2.getText(), subLabel)
+ && !TextUtils.isEmpty(subLabel)) {
+ holder.text2.setVisibility(View.VISIBLE);
holder.text2.setText(subLabel);
}
diff --git a/core/java/com/android/internal/app/procstats/AssociationState.java b/core/java/com/android/internal/app/procstats/AssociationState.java
index 0ed2524..1d4239f 100644
--- a/core/java/com/android/internal/app/procstats/AssociationState.java
+++ b/core/java/com/android/internal/app/procstats/AssociationState.java
@@ -950,6 +950,14 @@
proto.write(PackageAssociationProcessStatsProto.COMPONENT_NAME, mName);
+ proto.write(PackageAssociationProcessStatsProto.TOTAL_COUNT, mTotalCount);
+ proto.write(PackageAssociationProcessStatsProto.TOTAL_DURATION_MS, getTotalDuration(now));
+ if (mTotalActiveCount != 0) {
+ proto.write(PackageAssociationProcessStatsProto.ACTIVE_COUNT, mTotalActiveCount);
+ proto.write(PackageAssociationProcessStatsProto.ACTIVE_DURATION_MS,
+ getActiveDuration(now));
+ }
+
final int NSRC = mSources.size();
for (int isrc = 0; isrc < NSRC; isrc++) {
final SourceKey key = mSources.keyAt(isrc);
diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java
index cdb79ab..f5708a5c 100644
--- a/core/java/com/android/internal/content/FileSystemProvider.java
+++ b/core/java/com/android/internal/content/FileSystemProvider.java
@@ -17,6 +17,7 @@
package com.android.internal.content;
import android.annotation.CallSuper;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ContentResolver;
import android.content.Intent;
@@ -552,6 +553,11 @@
flags |= Document.FLAG_SUPPORTS_DELETE;
flags |= Document.FLAG_SUPPORTS_RENAME;
flags |= Document.FLAG_SUPPORTS_MOVE;
+
+ if (shouldBlockFromTree(docId)) {
+ flags |= Document.FLAG_DIR_BLOCKS_TREE;
+ }
+
} else {
flags |= Document.FLAG_SUPPORTS_WRITE;
flags |= Document.FLAG_SUPPORTS_DELETE;
@@ -592,6 +598,10 @@
return row;
}
+ protected boolean shouldBlockFromTree(@NonNull String docId) {
+ return false;
+ }
+
protected boolean typeSupportsMetadata(String mimeType) {
return MetadataReader.isSupportedMimeType(mimeType)
|| Document.MIME_TYPE_DIR.equals(mimeType);
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index a845b58..659134a 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -17,6 +17,7 @@
package com.android.internal.statusbar;
import android.app.Notification;
+import android.net.Uri;
import android.content.ComponentName;
import android.graphics.Rect;
import android.os.Bundle;
@@ -77,6 +78,7 @@
void onNotificationSettingsViewed(String key);
void setSystemUiVisibility(int displayId, int vis, int mask, String cause);
void onNotificationBubbleChanged(String key, boolean isBubble);
+ void grantInlineReplyUriPermission(String key, in Uri uri);
void onGlobalActionsShown();
void onGlobalActionsHidden();
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index 0e078dd..7e1f13a 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -80,6 +80,10 @@
}
@Override
+ public void hideInsets(@InsetType int types, boolean fromIme) throws RemoteException {
+ }
+
+ @Override
public void moved(int newX, int newY) {
}
diff --git a/core/java/com/android/internal/widget/LockPatternChecker.java b/core/java/com/android/internal/widget/LockPatternChecker.java
index 09bc28c..85a45fd 100644
--- a/core/java/com/android/internal/widget/LockPatternChecker.java
+++ b/core/java/com/android/internal/widget/LockPatternChecker.java
@@ -1,13 +1,9 @@
package com.android.internal.widget;
-import android.annotation.UnsupportedAppUsage;
import android.os.AsyncTask;
import com.android.internal.widget.LockPatternUtils.RequestThrottledException;
-import java.util.ArrayList;
-import java.util.List;
-
/**
* Helper class to check/verify PIN/Password/Pattern asynchronously.
*/
@@ -53,34 +49,28 @@
}
/**
- * Verify a pattern asynchronously.
+ * Verify a lockscreen credential asynchronously.
*
* @param utils The LockPatternUtils instance to use.
- * @param pattern The pattern to check.
- * @param challenge The challenge to verify against the pattern.
- * @param userId The user to check against the pattern.
+ * @param credential The credential to check.
+ * @param challenge The challenge to verify against the credential.
+ * @param userId The user to check against the credential.
* @param callback The callback to be invoked with the verification result.
*/
- public static AsyncTask<?, ?, ?> verifyPattern(final LockPatternUtils utils,
- final List<LockPatternView.Cell> pattern,
+ public static AsyncTask<?, ?, ?> verifyCredential(final LockPatternUtils utils,
+ final LockscreenCredential credential,
final long challenge,
final int userId,
final OnVerifyCallback callback) {
+ // Create a copy of the credential since checking credential is asynchrounous.
+ final LockscreenCredential credentialCopy = credential.duplicate();
AsyncTask<Void, Void, byte[]> task = new AsyncTask<Void, Void, byte[]>() {
private int mThrottleTimeout;
- private List<LockPatternView.Cell> patternCopy;
-
- @Override
- protected void onPreExecute() {
- // Make a copy of the pattern to prevent race conditions.
- // No need to clone the individual cells because they are immutable.
- patternCopy = new ArrayList(pattern);
- }
@Override
protected byte[] doInBackground(Void... args) {
try {
- return utils.verifyPattern(patternCopy, challenge, userId);
+ return utils.verifyCredential(credentialCopy, challenge, userId);
} catch (RequestThrottledException ex) {
mThrottleTimeout = ex.getTimeoutMs();
return null;
@@ -90,6 +80,12 @@
@Override
protected void onPostExecute(byte[] result) {
callback.onVerified(result, mThrottleTimeout);
+ credentialCopy.zeroize();
+ }
+
+ @Override
+ protected void onCancelled() {
+ credentialCopy.zeroize();
}
};
task.execute();
@@ -97,32 +93,26 @@
}
/**
- * Checks a pattern asynchronously.
+ * Checks a lockscreen credential asynchronously.
*
* @param utils The LockPatternUtils instance to use.
- * @param pattern The pattern to check.
- * @param userId The user to check against the pattern.
+ * @param credential The credential to check.
+ * @param userId The user to check against the credential.
* @param callback The callback to be invoked with the check result.
*/
- public static AsyncTask<?, ?, ?> checkPattern(final LockPatternUtils utils,
- final List<LockPatternView.Cell> pattern,
+ public static AsyncTask<?, ?, ?> checkCredential(final LockPatternUtils utils,
+ final LockscreenCredential credential,
final int userId,
final OnCheckCallback callback) {
+ // Create a copy of the credential since checking credential is asynchrounous.
+ final LockscreenCredential credentialCopy = credential.duplicate();
AsyncTask<Void, Void, Boolean> task = new AsyncTask<Void, Void, Boolean>() {
private int mThrottleTimeout;
- private List<LockPatternView.Cell> patternCopy;
-
- @Override
- protected void onPreExecute() {
- // Make a copy of the pattern to prevent race conditions.
- // No need to clone the individual cells because they are immutable.
- patternCopy = new ArrayList(pattern);
- }
@Override
protected Boolean doInBackground(Void... args) {
try {
- return utils.checkPattern(patternCopy, userId, callback::onEarlyMatched);
+ return utils.checkCredential(credentialCopy, userId, callback::onEarlyMatched);
} catch (RequestThrottledException ex) {
mThrottleTimeout = ex.getTimeoutMs();
return false;
@@ -132,11 +122,13 @@
@Override
protected void onPostExecute(Boolean result) {
callback.onChecked(result, mThrottleTimeout);
+ credentialCopy.zeroize();
}
@Override
protected void onCancelled() {
callback.onCancelled();
+ credentialCopy.zeroize();
}
};
task.execute();
@@ -144,84 +136,29 @@
}
/**
- * Verify a password asynchronously.
+ * Perform a lockscreen credential verification explicitly on a managed profile with unified
+ * challenge, using the parent user's credential.
*
* @param utils The LockPatternUtils instance to use.
- * @param password The password to check.
- * @param challenge The challenge to verify against the pattern.
- * @param userId The user to check against the pattern.
- * @param callback The callback to be invoked with the verification result.
- *
- * @deprecated Pass the password as a byte array.
- */
- @Deprecated
- public static AsyncTask<?, ?, ?> verifyPassword(final LockPatternUtils utils,
- final String password,
- final long challenge,
- final int userId,
- final OnVerifyCallback callback) {
- byte[] passwordBytes = password != null ? password.getBytes() : null;
- return verifyPassword(utils, passwordBytes, challenge, userId, callback);
- }
-
- /**
- * Verify a password asynchronously.
- *
- * @param utils The LockPatternUtils instance to use.
- * @param password The password to check.
- * @param challenge The challenge to verify against the pattern.
- * @param userId The user to check against the pattern.
- * @param callback The callback to be invoked with the verification result.
- */
- public static AsyncTask<?, ?, ?> verifyPassword(final LockPatternUtils utils,
- final byte[] password,
- final long challenge,
- final int userId,
- final OnVerifyCallback callback) {
- AsyncTask<Void, Void, byte[]> task = new AsyncTask<Void, Void, byte[]>() {
- private int mThrottleTimeout;
-
- @Override
- protected byte[] doInBackground(Void... args) {
- try {
- return utils.verifyPassword(password, challenge, userId);
- } catch (RequestThrottledException ex) {
- mThrottleTimeout = ex.getTimeoutMs();
- return null;
- }
- }
-
- @Override
- protected void onPostExecute(byte[] result) {
- callback.onVerified(result, mThrottleTimeout);
- }
- };
- task.execute();
- return task;
- }
-
- /**
- * Verify a password asynchronously.
- *
- * @param utils The LockPatternUtils instance to use.
- * @param password The password to check.
- * @param challenge The challenge to verify against the pattern.
- * @param userId The user to check against the pattern.
+ * @param credential The credential to check.
+ * @param challenge The challenge to verify against the credential.
+ * @param userId The user to check against the credential.
* @param callback The callback to be invoked with the verification result.
*/
public static AsyncTask<?, ?, ?> verifyTiedProfileChallenge(final LockPatternUtils utils,
- final byte[] password,
- final boolean isPattern,
+ final LockscreenCredential credential,
final long challenge,
final int userId,
final OnVerifyCallback callback) {
+ // Create a copy of the credential since checking credential is asynchrounous.
+ final LockscreenCredential credentialCopy = credential.duplicate();
AsyncTask<Void, Void, byte[]> task = new AsyncTask<Void, Void, byte[]>() {
private int mThrottleTimeout;
@Override
protected byte[] doInBackground(Void... args) {
try {
- return utils.verifyTiedProfileChallenge(password, isPattern, challenge, userId);
+ return utils.verifyTiedProfileChallenge(credentialCopy, challenge, userId);
} catch (RequestThrottledException ex) {
mThrottleTimeout = ex.getTimeoutMs();
return null;
@@ -231,64 +168,12 @@
@Override
protected void onPostExecute(byte[] result) {
callback.onVerified(result, mThrottleTimeout);
- }
- };
- task.execute();
- return task;
- }
-
- /**
- * Checks a password asynchronously.
- *
- * @param utils The LockPatternUtils instance to use.
- * @param password The password to check.
- * @param userId The user to check against the pattern.
- * @param callback The callback to be invoked with the check result.
- * @deprecated Pass passwords as byte[]
- */
- @UnsupportedAppUsage
- @Deprecated
- public static AsyncTask<?, ?, ?> checkPassword(final LockPatternUtils utils,
- final String password,
- final int userId,
- final OnCheckCallback callback) {
- byte[] passwordBytes = password != null ? password.getBytes() : null;
- return checkPassword(utils, passwordBytes, userId, callback);
- }
-
- /**
- * Checks a password asynchronously.
- *
- * @param utils The LockPatternUtils instance to use.
- * @param passwordBytes The password to check.
- * @param userId The user to check against the pattern.
- * @param callback The callback to be invoked with the check result.
- */
- public static AsyncTask<?, ?, ?> checkPassword(final LockPatternUtils utils,
- final byte[] passwordBytes,
- final int userId,
- final OnCheckCallback callback) {
- AsyncTask<Void, Void, Boolean> task = new AsyncTask<Void, Void, Boolean>() {
- private int mThrottleTimeout;
-
- @Override
- protected Boolean doInBackground(Void... args) {
- try {
- return utils.checkPassword(passwordBytes, userId, callback::onEarlyMatched);
- } catch (RequestThrottledException ex) {
- mThrottleTimeout = ex.getTimeoutMs();
- return false;
- }
- }
-
- @Override
- protected void onPostExecute(Boolean result) {
- callback.onChecked(result, mThrottleTimeout);
+ credentialCopy.zeroize();
}
@Override
protected void onCancelled() {
- callback.onCancelled();
+ credentialCopy.zeroize();
}
};
task.execute();
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 070121c..1daa25a 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -26,10 +26,10 @@
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.app.admin.DevicePolicyManager;
-import android.app.admin.PasswordMetrics;
import android.app.trust.IStrongAuthTracker;
import android.app.trust.TrustManager;
import android.content.ComponentName;
@@ -365,11 +365,24 @@
null /* componentName */, userId);
}
- private byte[] verifyCredential(byte[] credential, int type, long challenge, int userId)
- throws RequestThrottledException {
+ /**
+ * Check to see if a credential matches the saved one.
+ * If credential matches, return an opaque attestation that the challenge was verified.
+ *
+ * @param credential The credential to check.
+ * @param challenge The challenge to verify against the credential
+ * @return the attestation that the challenge was verified, or null
+ * @param userId The user whose credential is being verified
+ * @throws RequestThrottledException if credential verification is being throttled due to
+ * to many incorrect attempts.
+ * @throws IllegalStateException if called on the main thread.
+ */
+ public byte[] verifyCredential(@NonNull LockscreenCredential credential, long challenge,
+ int userId) throws RequestThrottledException {
+ throwIfCalledOnMainThread();
try {
- VerifyCredentialResponse response = getLockSettings().verifyCredential(credential,
- type, challenge, userId);
+ VerifyCredentialResponse response = getLockSettings().verifyCredential(
+ credential.getCredential(), credential.getType(), challenge, userId);
if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
return response.getPayload();
} else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) {
@@ -382,11 +395,24 @@
}
}
- private boolean checkCredential(byte[] credential, int type, int userId,
+ /**
+ * Check to see if a credential matches the saved one.
+ *
+ * @param credential The credential to check.
+ * @param userId The user whose credential is being checked
+ * @param progressCallback callback to deliver early signal that the credential matches
+ * @return {@code true} if credential matches, {@code false} otherwise
+ * @throws RequestThrottledException if credential verification is being throttled due to
+ * to many incorrect attempts.
+ * @throws IllegalStateException if called on the main thread.
+ */
+ public boolean checkCredential(@NonNull LockscreenCredential credential, int userId,
@Nullable CheckCredentialProgressCallback progressCallback)
throws RequestThrottledException {
+ throwIfCalledOnMainThread();
try {
- VerifyCredentialResponse response = getLockSettings().checkCredential(credential, type,
+ VerifyCredentialResponse response = getLockSettings().checkCredential(
+ credential.getCredential(), credential.getType(),
userId, wrapCallback(progressCallback));
if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
@@ -402,79 +428,26 @@
}
/**
- * Check to see if a pattern matches the saved pattern.
- * If pattern matches, return an opaque attestation that the challenge
- * was verified.
+ * Check if the credential of a managed profile with unified challenge matches. In this context,
+ * The credential should be the parent user's lockscreen password. If credential matches,
+ * return an opaque attestation associated with the managed profile that the challenge was
+ * verified.
*
- * @param pattern The pattern to check.
- * @param challenge The challenge to verify against the pattern
- * @return the attestation that the challenge was verified, or null.
+ * @param credential The parent user's credential to check.
+ * @param challenge The challenge to verify against the credential
+ * @return the attestation that the challenge was verified, or null
+ * @param userId The managed profile user id
+ * @throws RequestThrottledException if credential verification is being throttled due to
+ * to many incorrect attempts.
+ * @throws IllegalStateException if called on the main thread.
*/
- public byte[] verifyPattern(List<LockPatternView.Cell> pattern, long challenge, int userId)
- throws RequestThrottledException {
- throwIfCalledOnMainThread();
- return verifyCredential(patternToByteArray(pattern), CREDENTIAL_TYPE_PATTERN, challenge,
- userId);
- }
-
- /**
- * Check to see if a pattern matches the saved pattern. If no pattern exists,
- * always returns true.
- * @param pattern The pattern to check.
- * @return Whether the pattern matches the stored one.
- */
- public boolean checkPattern(List<LockPatternView.Cell> pattern, int userId)
- throws RequestThrottledException {
- return checkPattern(pattern, userId, null /* progressCallback */);
- }
-
- /**
- * Check to see if a pattern matches the saved pattern. If no pattern exists,
- * always returns true.
- * @param pattern The pattern to check.
- * @return Whether the pattern matches the stored one.
- */
- public boolean checkPattern(List<LockPatternView.Cell> pattern, int userId,
- @Nullable CheckCredentialProgressCallback progressCallback)
- throws RequestThrottledException {
- throwIfCalledOnMainThread();
- return checkCredential(patternToByteArray(pattern), CREDENTIAL_TYPE_PATTERN, userId,
- progressCallback);
- }
-
- /**
- * Check to see if a password matches the saved password.
- * If password matches, return an opaque attestation that the challenge
- * was verified.
- *
- * @param password The password to check.
- * @param challenge The challenge to verify against the password
- * @return the attestation that the challenge was verified, or null.
- */
- public byte[] verifyPassword(byte[] password, long challenge, int userId)
- throws RequestThrottledException {
- throwIfCalledOnMainThread();
- return verifyCredential(password, CREDENTIAL_TYPE_PASSWORD, challenge, userId);
- }
-
-
- /**
- * Check to see if a password matches the saved password.
- * If password matches, return an opaque attestation that the challenge
- * was verified.
- *
- * @param password The password to check.
- * @param challenge The challenge to verify against the password
- * @return the attestation that the challenge was verified, or null.
- */
- public byte[] verifyTiedProfileChallenge(byte[] password, boolean isPattern, long challenge,
- int userId) throws RequestThrottledException {
+ public byte[] verifyTiedProfileChallenge(@NonNull LockscreenCredential credential,
+ long challenge, int userId) throws RequestThrottledException {
throwIfCalledOnMainThread();
try {
VerifyCredentialResponse response =
- getLockSettings().verifyTiedProfileChallenge(password,
- isPattern ? CREDENTIAL_TYPE_PATTERN : CREDENTIAL_TYPE_PASSWORD, challenge,
- userId);
+ getLockSettings().verifyTiedProfileChallenge(
+ credential.getCredential(), credential.getType(), challenge, userId);
if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
return response.getPayload();
@@ -489,61 +462,6 @@
}
/**
- *
- * Check to see if a password matches the saved password. If no password exists,
- * always returns true.
- * @param password The password to check.
- * @return Whether the password matches the stored one.
- */
- @UnsupportedAppUsage
- public boolean checkPassword(String password, int userId) throws RequestThrottledException {
- byte[] passwordBytes = password != null ? password.getBytes() : null;
- return checkPassword(passwordBytes, userId, null /* progressCallback */);
- }
-
-
- /**
- *
- * Check to see if a password matches the saved password. If no password exists,
- * always returns true.
- * @param password The password to check.
- * @return Whether the password matches the stored one.
- */
- public boolean checkPassword(byte[] password, int userId) throws RequestThrottledException {
- return checkPassword(password, userId, null /* progressCallback */);
- }
-
- // TODO(b/120484642): This method is necessary for vendor/qcom code and is a hidden api
- /* *
- * Check to see if a password matches the saved password. If no password exists,
- * always returns true.
- * @param password The password to check.
- * @return Whether the password matches the stored one.
- */
- public boolean checkPassword(String password, int userId,
- @Nullable CheckCredentialProgressCallback progressCallback)
- throws RequestThrottledException {
- byte[] passwordBytes = password != null ? password.getBytes() : null;
- throwIfCalledOnMainThread();
- return checkCredential(passwordBytes, CREDENTIAL_TYPE_PASSWORD, userId, progressCallback);
-
- }
-
- /**
- * Check to see if a password matches the saved password. If no password exists,
- * always returns true.
- * @param password The password to check.
- * @return Whether the password matches the stored one.
- */
-
- public boolean checkPassword(byte[] password, int userId,
- @Nullable CheckCredentialProgressCallback progressCallback)
- throws RequestThrottledException {
- throwIfCalledOnMainThread();
- return checkCredential(password, CREDENTIAL_TYPE_PASSWORD, userId, progressCallback);
- }
-
- /**
* Check to see if vold already has the password.
* Note that this also clears vold's copy of the password.
* @return Whether the vold password matches or not.
@@ -560,9 +478,10 @@
* Returns the password history hash factor, needed to check new password against password
* history with {@link #checkPasswordHistory(byte[], byte[], int)}
*/
- public byte[] getPasswordHistoryHashFactor(byte[] currentPassword, int userId) {
+ public byte[] getPasswordHistoryHashFactor(@NonNull LockscreenCredential currentPassword,
+ int userId) {
try {
- return getLockSettings().getHashFactor(currentPassword, userId);
+ return getLockSettings().getHashFactor(currentPassword.getCredential(), userId);
} catch (RemoteException e) {
Log.e(TAG, "failed to get hash factor", e);
return null;
@@ -679,57 +598,6 @@
}
/**
- * Clear any lock pattern or password.
-
- * <p> This method will fail (returning {@code false}) if the previously
- * saved password provided is incorrect, or if the lockscreen verification
- * is still being throttled.
- *
- * @param savedCredential The previously saved credential
- * @param userHandle the user whose pattern is to be saved.
- * @return whether this was successful or not.
- * @throws RuntimeException if password change encountered an unrecoverable error.
- */
- public boolean clearLock(byte[] savedCredential, int userHandle) {
- return clearLock(savedCredential, userHandle, false);
- }
-
- /**
- * Clear any lock pattern or password, with the option to ignore incorrect existing credential.
- * <p> This method will fail (returning {@code false}) if the previously
- * saved password provided is incorrect, or if the lockscreen verification
- * is still being throttled.
- *
- * @param savedCredential The previously saved credential
- * @param userHandle the user whose pattern is to be saved.
- * @return whether this was successful or not.
- * @throws RuntimeException if password change encountered an unrecoverable error.
- */
- public boolean clearLock(byte[] savedCredential, int userHandle, boolean allowUntrustedChange) {
- final int currentQuality = getKeyguardStoredPasswordQuality(userHandle);
- setKeyguardStoredPasswordQuality(PASSWORD_QUALITY_UNSPECIFIED, userHandle);
-
- try {
- if (!getLockSettings().setLockCredential(null, CREDENTIAL_TYPE_NONE, savedCredential,
- PASSWORD_QUALITY_UNSPECIFIED, userHandle, allowUntrustedChange)) {
- return false;
- }
- } catch (RemoteException | RuntimeException e) {
- setKeyguardStoredPasswordQuality(currentQuality, userHandle);
- throw new RuntimeException("Failed to clear lock", e);
- }
-
- if (userHandle == UserHandle.USER_SYSTEM) {
- // Set the encryption password to default.
- updateEncryptionPassword(StorageManager.CRYPT_TYPE_DEFAULT, null);
- setCredentialRequiredToDecrypt(false);
- }
-
- onAfterChangingPassword(userHandle);
- return true;
- }
-
- /**
* Disable showing lock screen at all for a given user.
* This is only meaningful if pattern, pin or password are not set.
*
@@ -762,75 +630,88 @@
|| isDemoUser;
}
- /**
- * Save a lock pattern.
- *
- * <p> This method will fail (returning {@code false}) if the previously saved pattern provided
- * is incorrect, or if the lockscreen verification is still being throttled.
- *
- * @param pattern The new pattern to save.
- * @param savedPattern The previously saved pattern, converted to byte[] format
- * @param userId the user whose pattern is to be saved.
- * @return whether this was successful or not.
- * @throws RuntimeException if password change encountered an unrecoverable error.
- */
- public boolean saveLockPattern(List<LockPatternView.Cell> pattern, byte[] savedPattern,
- int userId) {
- return saveLockPattern(pattern, savedPattern, userId, false);
+ /** Returns if the given quality maps to an alphabetic password */
+ public static boolean isQualityAlphabeticPassword(int quality) {
+ return quality >= PASSWORD_QUALITY_ALPHABETIC;
+ }
+
+ /** Returns if the given quality maps to an numeric pin */
+ public static boolean isQualityNumericPin(int quality) {
+ return quality == PASSWORD_QUALITY_NUMERIC || quality == PASSWORD_QUALITY_NUMERIC_COMPLEX;
}
/**
- * Save a lock pattern.
+ * Save a new lockscreen credential.
*
- * <p> This method will fail (returning {@code false}) if the previously saved pattern provided
- * is incorrect, or if the lockscreen verification is still being throttled.
+ * <p> This method will fail (returning {@code false}) if the previously saved credential
+ * provided is incorrect, or if the lockscreen verification is still being throttled.
*
- * @param pattern The new pattern to save.
- * @param savedPattern The previously saved pattern, converted to byte[] format
- * @param userId the user whose pattern is to be saved.
- * @param allowUntrustedChange whether we want to allow saving a new password if the existing
- * password being provided is incorrect.
- * @return whether this was successful or not.
+ * @param newCredential The new credential to save
+ * @param savedCredential The current credential
+ * @param userId the user whose lockscreen credential is to be changed
+ *
+ * @return whether this method saved the new password successfully or not. This flow will fail
+ * and return false if the given credential is wrong.
* @throws RuntimeException if password change encountered an unrecoverable error.
*/
- public boolean saveLockPattern(List<LockPatternView.Cell> pattern, byte[] savedPattern,
- int userId, boolean allowUntrustedChange) {
+ public boolean setLockCredential(@NonNull LockscreenCredential newCredential,
+ @NonNull LockscreenCredential savedCredential, int userId) {
+ return setLockCredential(newCredential, savedCredential, userId, false);
+ }
+
+ /**
+ * Save a new lockscreen credential.
+ * <p> This method will fail (returning {@code false}) if the previously saved pattern provided
+ * is incorrect and allowUntrustedChange is false, or if the lockscreen verification is still
+ * being throttled.
+ * @param newCredential The new credential to save
+ * @param savedCredential The current credential
+ * @param userHandle the user whose lockscreen credential is to be changed
+ * @param allowUntrustedChange whether we want to allow saving a new pattern if the existing
+ * credentialt being provided is incorrect.
+ *
+ * @return whether this method saved the new password successfully or not. This flow will fail
+ * and return false if the given credential is wrong and allowUntrustedChange is false.
+ * @throws RuntimeException if password change encountered an unrecoverable error.
+ */
+ public boolean setLockCredential(@NonNull LockscreenCredential newCredential,
+ @NonNull LockscreenCredential savedCredential, int userHandle,
+ boolean allowUntrustedChange) {
if (!hasSecureLockScreen()) {
throw new UnsupportedOperationException(
"This operation requires the lock screen feature.");
}
- if (pattern == null || pattern.size() < MIN_LOCK_PATTERN_SIZE) {
- throw new IllegalArgumentException("pattern must not be null and at least "
- + MIN_LOCK_PATTERN_SIZE + " dots long.");
- }
+ newCredential.checkLength();
- final byte[] bytePattern = patternToByteArray(pattern);
- final int currentQuality = getKeyguardStoredPasswordQuality(userId);
- setKeyguardStoredPasswordQuality(PASSWORD_QUALITY_SOMETHING, userId);
+ final int currentQuality = getKeyguardStoredPasswordQuality(userHandle);
+ setKeyguardStoredPasswordQuality(newCredential.getQuality(), userHandle);
+
try {
- if (!getLockSettings().setLockCredential(bytePattern, CREDENTIAL_TYPE_PATTERN,
- savedPattern, PASSWORD_QUALITY_SOMETHING, userId, allowUntrustedChange)) {
+ if (!getLockSettings().setLockCredential(
+ newCredential.getCredential(), newCredential.getType(),
+ savedCredential.getCredential(),
+ newCredential.getQuality(), userHandle, allowUntrustedChange)) {
+ setKeyguardStoredPasswordQuality(currentQuality, userHandle);
return false;
}
} catch (RemoteException | RuntimeException e) {
- setKeyguardStoredPasswordQuality(currentQuality, userId);
- throw new RuntimeException("Couldn't save lock pattern", e);
- }
- // Update the device encryption password.
- if (userId == UserHandle.USER_SYSTEM
- && LockPatternUtils.isDeviceEncryptionEnabled()) {
- if (!shouldEncryptWithCredentials(true)) {
- clearEncryptionPassword();
- } else {
- updateEncryptionPassword(StorageManager.CRYPT_TYPE_PATTERN, bytePattern);
- }
+ setKeyguardStoredPasswordQuality(currentQuality, userHandle);
+ throw new RuntimeException("Unable to save lock password", e);
}
- reportPatternWasChosen(userId);
- onAfterChangingPassword(userId);
+ onPostPasswordChanged(newCredential, userHandle);
return true;
}
+ private void onPostPasswordChanged(LockscreenCredential newCredential, int userHandle) {
+ updateEncryptionPasswordIfNeeded(newCredential, userHandle);
+ if (newCredential.isPattern()) {
+ reportPatternWasChosen(userHandle);
+ }
+ updatePasswordHistory(newCredential, userHandle);
+ reportEnabledTrustAgentsChanged(userHandle);
+ }
+
private void updateCryptoUserInfo(int userId) {
if (userId != UserHandle.USER_SYSTEM) {
return;
@@ -929,149 +810,35 @@
}
/**
- * Save a lock password. Does not ensure that the password is as good
- * as the requested mode, but will adjust the mode to be as good as the
- * password.
- *
- * <p> This method will fail (returning {@code false}) if the previously
- * saved password provided is incorrect, or if the lockscreen verification
- * is still being throttled.
- *
- * @param password The password to save
- * @param savedPassword The previously saved lock password, or null if none
- * @param requestedQuality {@see DevicePolicyManager#getPasswordQuality(
- * android.content.ComponentName)}
- * @param userHandle The userId of the user to change the password for
- * @return whether this was successful or not.
- * @throws RuntimeException if password change encountered an unrecoverable error.
- * @deprecated Pass password as a byte array
- */
- @Deprecated
- public boolean saveLockPassword(String password, String savedPassword, int requestedQuality,
- int userHandle) {
- byte[] passwordBytes = password != null ? password.getBytes() : null;
- byte[] savedPasswordBytes = savedPassword != null ? savedPassword.getBytes() : null;
- return saveLockPassword(passwordBytes, savedPasswordBytes, requestedQuality, userHandle);
- }
-
- /**
- * Save a lock password. Does not ensure that the password is as good
- * as the requested mode, but will adjust the mode to be as good as the
- * password.
- *
- * <p> This method will fail (returning {@code false}) if the previously
- * saved password provided is incorrect, or if the lockscreen verification
- * is still being throttled.
- *
- * @param password The password to save
- * @param savedPassword The previously saved lock password, or null if none
- * @param requestedQuality {@see DevicePolicyManager#getPasswordQuality(
- * android.content.ComponentName)}
- * @param userHandle The userId of the user to change the password for
- * @return whether this was successful or not.
- * @throws RuntimeException if password change encountered an unrecoverable error.
- */
- public boolean saveLockPassword(byte[] password, byte[] savedPassword, int requestedQuality,
- int userHandle) {
- return saveLockPassword(password, savedPassword, requestedQuality,
- userHandle, false);
- }
-
- /**
- * Save a lock password. Does not ensure that the password is as good
- * as the requested mode, but will adjust the mode to be as good as the
- * password.
- *
- * <p> This method will fail (returning {@code false}) if the previously
- * saved password provided is incorrect, or if the lockscreen verification
- * is still being throttled.
- *
- * @param password The password to save
- * @param savedPassword The previously saved lock password, or null if none
- * @param requestedQuality {@see DevicePolicyManager#getPasswordQuality(
- * android.content.ComponentName)}
- * @param userHandle The userId of the user to change the password for
- * @param allowUntrustedChange whether we want to allow saving a new password if the existing
- * password being provided is incorrect.
- * @return whether this method saved the new password successfully or not. This flow will fail
- * and return false if the given credential is wrong and allowUntrustedChange is false.
- * @throws RuntimeException if password change encountered an unrecoverable error.
- */
- public boolean saveLockPassword(byte[] password, byte[] savedPassword,
- int requestedQuality, int userHandle, boolean allowUntrustedChange) {
- if (!hasSecureLockScreen()) {
- throw new UnsupportedOperationException(
- "This operation requires the lock screen feature.");
- }
- if (password == null || password.length < MIN_LOCK_PASSWORD_SIZE) {
- throw new IllegalArgumentException("password must not be null and at least "
- + "of length " + MIN_LOCK_PASSWORD_SIZE);
- }
-
- if (requestedQuality < PASSWORD_QUALITY_NUMERIC) {
- throw new IllegalArgumentException("quality must be at least NUMERIC, but was "
- + requestedQuality);
- }
-
- final int currentQuality = getKeyguardStoredPasswordQuality(userHandle);
- final int passwordQuality = PasswordMetrics.computeForPassword(password).quality;
- final int newKeyguardQuality =
- computeKeyguardQuality(CREDENTIAL_TYPE_PASSWORD, requestedQuality, passwordQuality);
- setKeyguardStoredPasswordQuality(newKeyguardQuality, userHandle);
- try {
- getLockSettings().setLockCredential(password, CREDENTIAL_TYPE_PASSWORD, savedPassword,
- requestedQuality, userHandle, allowUntrustedChange);
- } catch (RemoteException | RuntimeException e) {
- setKeyguardStoredPasswordQuality(currentQuality, userHandle);
- throw new RuntimeException("Unable to save lock password", e);
- }
-
- updateEncryptionPasswordIfNeeded(password, passwordQuality, userHandle);
- updatePasswordHistory(password, userHandle);
- onAfterChangingPassword(userHandle);
- return true;
- }
-
- /**
- * Compute keyguard credential quality to store in PASSWORD_TYPE_KEY by computing max between
- * them so that digit-only password is distinguished from PIN.
- *
- * TODO: remove this method and make CREDENTIAL_TYPE distinguish between PIN and password, so
- * that this quality is no longer needs to be persisted.
- */
- private int computeKeyguardQuality(
- @CredentialType int credentialType, int requestedQuality, int passwordQuality) {
- return credentialType == CREDENTIAL_TYPE_PASSWORD
- ? Math.max(passwordQuality, requestedQuality) : passwordQuality;
- }
-
- /**
* Update device encryption password if calling user is USER_SYSTEM and device supports
* encryption.
*/
- private void updateEncryptionPasswordIfNeeded(byte[] password, int quality, int userHandle) {
+ private void updateEncryptionPasswordIfNeeded(LockscreenCredential credential, int userHandle) {
// Update the device encryption password.
- if (userHandle == UserHandle.USER_SYSTEM
- && LockPatternUtils.isDeviceEncryptionEnabled()) {
- if (!shouldEncryptWithCredentials(true)) {
- clearEncryptionPassword();
- } else {
- boolean numeric = quality == PASSWORD_QUALITY_NUMERIC;
- boolean numericComplex = quality == PASSWORD_QUALITY_NUMERIC_COMPLEX;
- int type = numeric || numericComplex ? StorageManager.CRYPT_TYPE_PIN
- : StorageManager.CRYPT_TYPE_PASSWORD;
- updateEncryptionPassword(type, password);
- }
+ if (userHandle != UserHandle.USER_SYSTEM || !isDeviceEncryptionEnabled()) {
+ return;
}
+ if (!shouldEncryptWithCredentials(true)) {
+ updateEncryptionPassword(StorageManager.CRYPT_TYPE_DEFAULT, null);
+ return;
+ }
+ if (credential.isNone()) {
+ // Set the encryption password to default.
+ setCredentialRequiredToDecrypt(false);
+ }
+ updateEncryptionPassword(credential.getStorageCryptType(), credential.getCredential());
}
/**
* Store the hash of the *current* password in the password history list, if device policy
* enforces password history requirement.
*/
- private void updatePasswordHistory(byte[] password, int userHandle) {
- if (password == null || password.length == 0) {
- Log.e(TAG, "checkPasswordHistory: empty password");
+ private void updatePasswordHistory(LockscreenCredential password, int userHandle) {
+ if (password.isNone()) {
+ return;
+ }
+ if (password.isPattern()) {
+ // Do not keep track of historical patterns
return;
}
// Add the password to the password history. We assume all
@@ -1085,10 +852,10 @@
passwordHistory = "";
} else {
final byte[] hashFactor = getPasswordHistoryHashFactor(password, userHandle);
- String hash = passwordToHistoryHash(password, hashFactor, userHandle);
+ String hash = passwordToHistoryHash(password.getCredential(), hashFactor, userHandle);
if (hash == null) {
Log.e(TAG, "Compute new style password hash failed, fallback to legacy style");
- hash = legacyPasswordToHash(password, userHandle);
+ hash = legacyPasswordToHash(password.getCredential(), userHandle);
}
if (TextUtils.isEmpty(passwordHistory)) {
passwordHistory = hash;
@@ -1132,8 +899,8 @@
}
/**
- * Retrieves the quality mode for {@param userHandle}.
- * {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)}
+ * Retrieves the quality mode for {@code userHandle}.
+ * @see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)
*
* @return stored password quality
*/
@@ -1147,37 +914,37 @@
}
/**
- * Enables/disables the Separate Profile Challenge for this {@param userHandle}. This is a no-op
+ * Enables/disables the Separate Profile Challenge for this {@code userHandle}. This is a no-op
* for user handles that do not belong to a managed profile.
*
* @param userHandle Managed profile user id
* @param enabled True if separate challenge is enabled
- * @param managedUserPassword Managed profile previous password. Null when {@param enabled} is
+ * @param managedUserPassword Managed profile previous password. Null when {@code enabled} is
* true
*/
public void setSeparateProfileChallengeEnabled(int userHandle, boolean enabled,
- byte[] managedUserPassword) {
+ LockscreenCredential managedUserPassword) {
if (!isManagedProfile(userHandle)) {
return;
}
try {
getLockSettings().setSeparateProfileChallengeEnabled(userHandle, enabled,
- managedUserPassword);
- onAfterChangingPassword(userHandle);
+ managedUserPassword.getCredential());
+ reportEnabledTrustAgentsChanged(userHandle);
} catch (RemoteException e) {
Log.e(TAG, "Couldn't update work profile challenge enabled");
}
}
/**
- * Returns true if {@param userHandle} is a managed profile with separate challenge.
+ * Returns true if {@code userHandle} is a managed profile with separate challenge.
*/
public boolean isSeparateProfileChallengeEnabled(int userHandle) {
return isManagedProfile(userHandle) && hasSeparateChallenge(userHandle);
}
/**
- * Returns true if {@param userHandle} is a managed profile with unified challenge.
+ * Returns true if {@code userHandle} is a managed profile with unified challenge.
*/
public boolean isManagedProfileWithUnifiedChallenge(int userHandle) {
return isManagedProfile(userHandle) && !hasSeparateChallenge(userHandle);
@@ -1218,20 +985,6 @@
/**
* Deserialize a pattern.
- * @param string The pattern serialized with {@link #patternToString}
- * @return The pattern.
- * @deprecated Pass patterns as byte[] and use byteArrayToPattern
- */
- @Deprecated
- public static List<LockPatternView.Cell> stringToPattern(String string) {
- if (string == null) {
- return null;
- }
- return byteArrayToPattern(string.getBytes());
- }
-
- /**
- * Deserialize a pattern.
* @param bytes The pattern serialized with {@link #patternToByteArray}
* @return The pattern.
*/
@@ -1252,19 +1005,6 @@
/**
* Serialize a pattern.
* @param pattern The pattern.
- * @return The pattern in string form.
- * @deprecated Use patternToByteArray instead.
- */
- @UnsupportedAppUsage
- @Deprecated
- public static String patternToString(List<LockPatternView.Cell> pattern) {
- return new String(patternToByteArray(pattern));
- }
-
-
- /**
- * Serialize a pattern.
- * @param pattern The pattern.
* @return The pattern in byte array form.
*/
public static byte[] patternToByteArray(List<LockPatternView.Cell> pattern) {
@@ -1281,34 +1021,6 @@
return res;
}
- /*
- * Generate an SHA-1 hash for the pattern. Not the most secure, but it is
- * at least a second level of protection. First level is that the file
- * is in a location only readable by the system process.
- * @param pattern the gesture pattern.
- * @return the hash of the pattern in a byte array.
- */
- @UnsupportedAppUsage
- public static byte[] patternToHash(List<LockPatternView.Cell> pattern) {
- if (pattern == null) {
- return null;
- }
-
- final int patternSize = pattern.size();
- byte[] res = new byte[patternSize];
- for (int i = 0; i < patternSize; i++) {
- LockPatternView.Cell cell = pattern.get(i);
- res[i] = (byte) (cell.getRow() * 3 + cell.getColumn());
- }
- try {
- MessageDigest md = MessageDigest.getInstance("SHA-1");
- byte[] hash = md.digest(res);
- return hash;
- } catch (NoSuchAlgorithmException nsa) {
- return res;
- }
- }
-
private String getSalt(int userId) {
long salt = getLong(LOCK_PASSWORD_SALT_KEY, 0, userId);
if (salt == 0) {
@@ -1332,6 +1044,7 @@
* @param password the gesture pattern.
*
* @return the hash of the pattern in a byte array.
+ * TODO: move to LockscreenCredential class
*/
public String legacyPasswordToHash(byte[] password, int userId) {
if (password == null || password.length == 0) {
@@ -1362,6 +1075,7 @@
/**
* Hash the password for password history check purpose.
+ * TODO: move to LockscreenCredential class
*/
private String passwordToHistoryHash(byte[] passwordToHash, byte[] hashFactor, int userId) {
if (passwordToHash == null || passwordToHash.length == 0 || hashFactor == null) {
@@ -1629,7 +1343,7 @@
}
/**
- * Disable trust until credentials have been entered for user {@param userId}.
+ * Disable trust until credentials have been entered for user {@code userId}.
*
* Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission.
*
@@ -1640,7 +1354,7 @@
}
/**
- * Requests strong authentication for user {@param userId}.
+ * Requests strong authentication for user {@code userId}.
*
* Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission.
*
@@ -1657,7 +1371,7 @@
}
}
- private void onAfterChangingPassword(int userHandle) {
+ private void reportEnabledTrustAgentsChanged(int userHandle) {
getTrustManager().reportEnabledTrustAgentsChanged(userHandle);
}
@@ -1823,54 +1537,36 @@
* <p>This method is only available to code running in the system server process itself.
*
* @param credential The new credential to be set
- * @param type Credential type: password / pattern / none.
- * @param requestedQuality the requested password quality by DevicePolicyManager.
- * See {@link DevicePolicyManager#getPasswordQuality(android.content.ComponentName)}
* @param tokenHandle Handle of the escrow token
* @param token Escrow token
- * @param userId The user who's lock credential to be changed
+ * @param userHandle The user who's lock credential to be changed
* @return {@code true} if the operation is successful.
*/
- public boolean setLockCredentialWithToken(byte[] credential, int type, int requestedQuality,
- long tokenHandle, byte[] token, int userId) {
+ public boolean setLockCredentialWithToken(LockscreenCredential credential, long tokenHandle,
+ byte[] token, int userHandle) {
if (!hasSecureLockScreen()) {
throw new UnsupportedOperationException(
"This operation requires the lock screen feature.");
}
+ credential.checkLength();
LockSettingsInternal localService = getLockSettingsInternal();
- if (type != CREDENTIAL_TYPE_NONE) {
- if (credential == null || credential.length < MIN_LOCK_PASSWORD_SIZE) {
- throw new IllegalArgumentException("password must not be null and at least "
- + "of length " + MIN_LOCK_PASSWORD_SIZE);
- }
- final int quality = PasswordMetrics.computeForCredential(type, credential).quality;
- final int keyguardQuality = computeKeyguardQuality(type, quality, requestedQuality);
- if (!localService.setLockCredentialWithToken(credential, type, tokenHandle, token,
- keyguardQuality, userId)) {
+
+ final int currentQuality = getKeyguardStoredPasswordQuality(userHandle);
+ setKeyguardStoredPasswordQuality(credential.getQuality(), userHandle);
+
+ try {
+ if (!localService.setLockCredentialWithToken(credential.getCredential(),
+ credential.getType(),
+ tokenHandle, token, credential.getType(), userHandle)) {
+ setKeyguardStoredPasswordQuality(currentQuality, userHandle);
return false;
}
- setKeyguardStoredPasswordQuality(quality, userId);
-
- updateEncryptionPasswordIfNeeded(credential, quality, userId);
- updatePasswordHistory(credential, userId);
- onAfterChangingPassword(userId);
- } else {
- if (!(credential == null || credential.length == 0)) {
- throw new IllegalArgumentException("password must be emtpy for NONE type");
- }
- if (!localService.setLockCredentialWithToken(null, CREDENTIAL_TYPE_NONE, tokenHandle,
- token, PASSWORD_QUALITY_UNSPECIFIED, userId)) {
- return false;
- }
- setKeyguardStoredPasswordQuality(PASSWORD_QUALITY_UNSPECIFIED, userId);
-
- if (userId == UserHandle.USER_SYSTEM) {
- // Set the encryption password to default.
- updateEncryptionPassword(StorageManager.CRYPT_TYPE_DEFAULT, null);
- setCredentialRequiredToDecrypt(false);
- }
+ } catch (RuntimeException e) {
+ setKeyguardStoredPasswordQuality(currentQuality, userHandle);
+ throw new RuntimeException("Unable to save lock credential", e);
}
- onAfterChangingPassword(userId);
+
+ onPostPasswordChanged(credential, userHandle);
return true;
}
@@ -1997,7 +1693,7 @@
}
/**
- * @return true if unlocking with trust alone is allowed for {@param userId} by the current
+ * @return true if unlocking with trust alone is allowed for {@code userId} by the current
* strong authentication requirements.
*/
public boolean isTrustAllowedForUser(int userId) {
@@ -2005,7 +1701,7 @@
}
/**
- * @return true if unlocking with a biometric method alone is allowed for {@param userId}
+ * @return true if unlocking with a biometric method alone is allowed for {@code userId}
* by the current strong authentication requirements.
*/
public boolean isBiometricAllowedForUser(int userId) {
@@ -2013,7 +1709,7 @@
}
/**
- * Called when the strong authentication requirements for {@param userId} changed.
+ * Called when the strong authentication requirements for {@code userId} changed.
*/
public void onStrongAuthRequiredChanged(int userId) {
}
@@ -2102,22 +1798,4 @@
return FRP_CREDENTIAL_ENABLED && context.getResources().getBoolean(
com.android.internal.R.bool.config_enableCredentialFactoryResetProtection);
}
-
- /**
- * Converts a CharSequence to a byte array without requiring a toString(), which creates an
- * additional copy.
- *
- * @param chars The CharSequence to convert
- * @return A byte array representing the input
- */
- public static byte[] charSequenceToByteArray(CharSequence chars) {
- if (chars == null) {
- return null;
- }
- byte[] bytes = new byte[chars.length()];
- for (int i = 0; i < chars.length(); i++) {
- bytes[i] = (byte) chars.charAt(i);
- }
- return bytes;
- }
}
diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java
index 3f6c4d4..74a0aa3 100644
--- a/core/java/com/android/internal/widget/LockPatternView.java
+++ b/core/java/com/android/internal/widget/LockPatternView.java
@@ -1318,7 +1318,7 @@
super.onRestoreInstanceState(ss.getSuperState());
setPattern(
DisplayMode.Correct,
- LockPatternUtils.stringToPattern(ss.getSerializedPattern()));
+ LockPatternUtils.byteArrayToPattern(ss.getSerializedPattern().getBytes()));
mPatternDisplayMode = DisplayMode.values()[ss.getDisplayMode()];
mInputEnabled = ss.isInputEnabled();
mInStealthMode = ss.isInStealthMode();
diff --git a/core/java/com/android/internal/widget/LockscreenCredential.java b/core/java/com/android/internal/widget/LockscreenCredential.java
new file mode 100644
index 0000000..19e6d97
--- /dev/null
+++ b/core/java/com/android/internal/widget/LockscreenCredential.java
@@ -0,0 +1,339 @@
+/*
+ * 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.internal.widget;
+
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.storage.StorageManager;
+import android.text.TextUtils;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * A class representing a lockscreen credential. It can be either an empty password, a pattern
+ * or a password (or PIN).
+ *
+ * <p> As required by some security certification, the framework tries its best to
+ * remove copies of the lockscreen credential bytes from memory. In this regard, this class
+ * abuses the {@link AutoCloseable} interface for sanitizing memory. This
+ * presents a nice syntax to auto-zeroize memory with the try-with-resource statement:
+ * <pre>
+ * try {LockscreenCredential credential = LockscreenCredential.createPassword(...) {
+ * // Process the credential in some way
+ * }
+ * </pre>
+ * With this construct, we can garantee that there will be no copies of the password left in
+ * memory when the credential goes out of scope. This should help mitigate certain class of
+ * attacks where the attcker gains read-only access to full device memory (cold boot attack,
+ * unsecured software/hardware memory dumping interfaces such as JTAG).
+ */
+public class LockscreenCredential implements Parcelable, AutoCloseable {
+
+ private final int mType;
+ // Stores raw credential bytes, or null if credential has been zeroized. An empty password
+ // is represented as a byte array of length 0.
+ private byte[] mCredential;
+ // Store the quality of the password, this is used to distinguish between pin
+ // (PASSWORD_QUALITY_NUMERIC) and password (PASSWORD_QUALITY_ALPHABETIC).
+ private final int mQuality;
+
+ /**
+ * Private constructor, use static builder methods instead.
+ *
+ * <p> Builder methods should create a private copy of the credential bytes and pass in here.
+ * LockscreenCredential will only store the reference internally without copying. This is to
+ * minimize the number of extra copies introduced.
+ */
+ private LockscreenCredential(int type, int quality, byte[] credential) {
+ Preconditions.checkNotNull(credential);
+ if (type == CREDENTIAL_TYPE_NONE) {
+ Preconditions.checkArgument(credential.length == 0);
+ } else {
+ Preconditions.checkArgument(credential.length > 0);
+ }
+ mType = type;
+ mQuality = quality;
+ mCredential = credential;
+ }
+
+ /**
+ * Creates a LockscreenCredential object representing empty password.
+ */
+ public static LockscreenCredential createNone() {
+ return new LockscreenCredential(CREDENTIAL_TYPE_NONE, PASSWORD_QUALITY_UNSPECIFIED,
+ new byte[0]);
+ }
+
+ /**
+ * Creates a LockscreenCredential object representing the given pattern.
+ */
+ public static LockscreenCredential createPattern(@NonNull List<LockPatternView.Cell> pattern) {
+ return new LockscreenCredential(CREDENTIAL_TYPE_PATTERN,
+ PASSWORD_QUALITY_SOMETHING,
+ LockPatternUtils.patternToByteArray(pattern));
+ }
+
+ /**
+ * Creates a LockscreenCredential object representing the given alphabetic password.
+ */
+ public static LockscreenCredential createPassword(@NonNull CharSequence password) {
+ return new LockscreenCredential(CREDENTIAL_TYPE_PASSWORD,
+ PASSWORD_QUALITY_ALPHABETIC,
+ charSequenceToByteArray(password));
+ }
+
+ /**
+ * Creates a LockscreenCredential object representing the given numeric PIN.
+ */
+ public static LockscreenCredential createPin(@NonNull CharSequence pin) {
+ return new LockscreenCredential(CREDENTIAL_TYPE_PASSWORD,
+ PASSWORD_QUALITY_NUMERIC,
+ charSequenceToByteArray(pin));
+ }
+
+ /**
+ * Creates a LockscreenCredential object representing the given alphabetic password.
+ * If the supplied password is empty, create an empty credential object.
+ */
+ public static LockscreenCredential createPasswordOrNone(@Nullable CharSequence password) {
+ if (TextUtils.isEmpty(password)) {
+ return createNone();
+ } else {
+ return createPassword(password);
+ }
+ }
+
+ /**
+ * Creates a LockscreenCredential object representing the given numeric PIN.
+ * If the supplied password is empty, create an empty credential object.
+ */
+ public static LockscreenCredential createPinOrNone(@Nullable CharSequence pin) {
+ if (TextUtils.isEmpty(pin)) {
+ return createNone();
+ } else {
+ return createPin(pin);
+ }
+ }
+
+ /**
+ * Create a LockscreenCredential object based on raw credential and type
+ * TODO: Remove once LSS.setUserPasswordMetrics accepts a LockscreenCredential
+ */
+ public static LockscreenCredential createRaw(int type, byte[] credential) {
+ if (type == CREDENTIAL_TYPE_NONE) {
+ return createNone();
+ } else {
+ return new LockscreenCredential(type, PASSWORD_QUALITY_UNSPECIFIED, credential);
+ }
+ }
+
+ private void ensureNotZeroized() {
+ Preconditions.checkState(mCredential != null, "Credential is already zeroized");
+ }
+ /**
+ * Returns the type of this credential. Can be one of {@link #CREDENTIAL_TYPE_NONE},
+ * {@link #CREDENTIAL_TYPE_PATTERN} or {@link #CREDENTIAL_TYPE_PASSWORD}.
+ *
+ * TODO: Remove once credential type is internal. Callers should use {@link #isNone},
+ * {@link #isPattern} and {@link #isPassword} instead.
+ */
+ public int getType() {
+ ensureNotZeroized();
+ return mType;
+ }
+
+ /**
+ * Returns the quality type of the credential
+ */
+ public int getQuality() {
+ ensureNotZeroized();
+ return mQuality;
+ }
+
+ /**
+ * Returns the credential bytes. This is a direct reference of the internal field so
+ * callers should not modify it.
+ *
+ */
+ public byte[] getCredential() {
+ ensureNotZeroized();
+ return mCredential;
+ }
+
+ /**
+ * Returns the credential type recognized by {@link StorageManager}. Can be one of
+ * {@link StorageManager#CRYPT_TYPE_DEFAULT}, {@link StorageManager#CRYPT_TYPE_PATTERN},
+ * {@link StorageManager#CRYPT_TYPE_PIN} or {@link StorageManager#CRYPT_TYPE_PASSWORD}.
+ */
+ public int getStorageCryptType() {
+ if (isNone()) {
+ return StorageManager.CRYPT_TYPE_DEFAULT;
+ }
+ if (isPattern()) {
+ return StorageManager.CRYPT_TYPE_PATTERN;
+ }
+ if (isPassword()) {
+ return mQuality == PASSWORD_QUALITY_NUMERIC
+ ? StorageManager.CRYPT_TYPE_PIN : StorageManager.CRYPT_TYPE_PASSWORD;
+ }
+ throw new IllegalStateException("Unhandled credential type");
+ }
+
+ /** Returns whether this is an empty credential */
+ public boolean isNone() {
+ ensureNotZeroized();
+ return mType == CREDENTIAL_TYPE_NONE;
+ }
+
+ /** Returns whether this is a pattern credential */
+ public boolean isPattern() {
+ ensureNotZeroized();
+ return mType == CREDENTIAL_TYPE_PATTERN;
+ }
+
+ /** Returns whether this is a password credential */
+ public boolean isPassword() {
+ ensureNotZeroized();
+ return mType == CREDENTIAL_TYPE_PASSWORD;
+ }
+
+ /** Returns the length of the credential */
+ public int size() {
+ ensureNotZeroized();
+ return mCredential.length;
+ }
+
+ /** Create a copy of the credential */
+ public LockscreenCredential duplicate() {
+ return new LockscreenCredential(mType, mQuality,
+ mCredential != null ? Arrays.copyOf(mCredential, mCredential.length) : null);
+ }
+
+ /**
+ * Zeroize the credential bytes.
+ */
+ public void zeroize() {
+ if (mCredential != null) {
+ Arrays.fill(mCredential, (byte) 0);
+ mCredential = null;
+ }
+ }
+
+ /**
+ * Check if the credential meets minimal length requirement.
+ *
+ * @throws IllegalArgumentException if the credential is too short.
+ */
+ public void checkLength() {
+ if (isNone()) {
+ return;
+ }
+ if (isPattern()) {
+ if (size() < LockPatternUtils.MIN_LOCK_PATTERN_SIZE) {
+ throw new IllegalArgumentException("pattern must not be null and at least "
+ + LockPatternUtils.MIN_LOCK_PATTERN_SIZE + " dots long.");
+ }
+ return;
+ }
+ if (isPassword()) {
+ if (size() < LockPatternUtils.MIN_LOCK_PASSWORD_SIZE) {
+ throw new IllegalArgumentException("password must not be null and at least "
+ + "of length " + LockPatternUtils.MIN_LOCK_PASSWORD_SIZE);
+ }
+ return;
+ }
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mType);
+ dest.writeInt(mQuality);
+ dest.writeByteArray(mCredential);
+ }
+
+ public static final Parcelable.Creator<LockscreenCredential> CREATOR =
+ new Parcelable.Creator<LockscreenCredential>() {
+
+ @Override
+ public LockscreenCredential createFromParcel(Parcel source) {
+ return new LockscreenCredential(source.readInt(), source.readInt(),
+ source.createByteArray());
+ }
+
+ @Override
+ public LockscreenCredential[] newArray(int size) {
+ return new LockscreenCredential[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void close() {
+ zeroize();
+ }
+
+ @Override
+ public int hashCode() {
+ // Effective Java — Item 9
+ return ((17 + mType) * 31 + mQuality) * 31 + mCredential.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) return true;
+ if (!(o instanceof LockscreenCredential)) return false;
+ final LockscreenCredential other = (LockscreenCredential) o;
+ return mType == other.mType && mQuality == other.mQuality
+ && Arrays.equals(mCredential, other.mCredential);
+ }
+
+ /**
+ * Converts a CharSequence to a byte array without requiring a toString(), which creates an
+ * additional copy.
+ *
+ * @param chars The CharSequence to convert
+ * @return A byte array representing the input
+ */
+ private static byte[] charSequenceToByteArray(CharSequence chars) {
+ if (chars == null) {
+ return new byte[0];
+ }
+ byte[] bytes = new byte[chars.length()];
+ for (int i = 0; i < chars.length(); i++) {
+ bytes[i] = (byte) chars.charAt(i);
+ }
+ return bytes;
+ }
+}
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 697825d..ea0389f 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -168,6 +168,10 @@
// These are the permitted backup transport service components
final ArraySet<ComponentName> mBackupTransportWhitelist = new ArraySet<>();
+ // These are packages mapped to maps of component class name to default enabled state.
+ final ArrayMap<String, ArrayMap<String, Boolean>> mPackageComponentEnabledState =
+ new ArrayMap<>();
+
// Package names that are exempted from private API blacklisting
final ArraySet<String> mHiddenApiPackageWhitelist = new ArraySet<>();
@@ -301,6 +305,10 @@
return mBackupTransportWhitelist;
}
+ public ArrayMap<String, Boolean> getComponentsEnabledStates(String packageName) {
+ return mPackageComponentEnabledState.get(packageName);
+ }
+
public ArraySet<String> getDisabledUntilUsedPreinstalledCarrierApps() {
return mDisabledUntilUsedPreinstalledCarrierApps;
}
@@ -846,6 +854,14 @@
}
XmlUtils.skipCurrentTag(parser);
} break;
+ case "component-override": {
+ if (allowAppConfigs) {
+ readComponentOverrides(parser, permFile);
+ } else {
+ logNotAllowedInPartition(name, permFile, parser);
+ }
+ XmlUtils.skipCurrentTag(parser);
+ } break;
case "backup-transport-whitelisted-service": {
if (allowFeatures) {
String serviceName = parser.getAttributeValue(null, "service");
@@ -1269,6 +1285,54 @@
}
}
+ private void readComponentOverrides(XmlPullParser parser, File permFile)
+ throws IOException, XmlPullParserException {
+ String pkgname = parser.getAttributeValue(null, "package");
+ if (pkgname == null) {
+ Slog.w(TAG, "<component-override> without package in "
+ + permFile + " at " + parser.getPositionDescription());
+ return;
+ }
+
+ pkgname = pkgname.intern();
+
+ final int depth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, depth)) {
+ String name = parser.getName();
+ if ("component".equals(name)) {
+ String clsname = parser.getAttributeValue(null, "class");
+ String enabled = parser.getAttributeValue(null, "enabled");
+ if (clsname == null) {
+ Slog.w(TAG, "<component> without class in "
+ + permFile + " at " + parser.getPositionDescription());
+ return;
+ } else if (enabled == null) {
+ Slog.w(TAG, "<component> without enabled in "
+ + permFile + " at " + parser.getPositionDescription());
+ return;
+ }
+
+ if (clsname.startsWith(".")) {
+ clsname = pkgname + clsname;
+ }
+
+ clsname = clsname.intern();
+
+ ArrayMap<String, Boolean> componentEnabledStates =
+ mPackageComponentEnabledState.get(pkgname);
+ if (componentEnabledStates == null) {
+ componentEnabledStates = new ArrayMap<>();
+ mPackageComponentEnabledState.put(pkgname,
+ componentEnabledStates);
+ }
+
+ componentEnabledStates.put(clsname, !"false".equals(enabled));
+ } else {
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+ }
+
private static boolean isSystemProcess() {
return Process.myUid() == Process.SYSTEM_UID;
}
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 9445319..d62d2d9 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -139,8 +139,8 @@
"nativePrivateClean", "nativeSharedClean", "nativeSwappedOut", "nativeSwappedOutPss" }
};
-jfieldID otherStats_field;
-jfieldID hasSwappedOutPss_field;
+static jfieldID otherStats_field;
+static jfieldID hasSwappedOutPss_field;
struct stats_t {
int pss;
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index af47cb6..c7b36d0 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -108,7 +108,7 @@
jmethodID put;
} gArrayMapOffsets;
-jclass g_stringClass = nullptr;
+static jclass g_stringClass = nullptr;
// ----------------------------------------------------------------------------
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index e406e22..2232393 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -992,6 +992,31 @@
return IPCThreadState::self()->blockUntilThreadAvailable();
}
+static jobject android_os_Binder_waitForService(
+ JNIEnv *env,
+ jclass /* clazzObj */,
+ jstring serviceNameObj) {
+
+ const jchar* serviceName = env->GetStringCritical(serviceNameObj, nullptr);
+ if (!serviceName) {
+ signalExceptionForError(env, nullptr, BAD_VALUE, true /*canThrowRemoteException*/);
+ return nullptr;
+ }
+ String16 nameCopy = String16(reinterpret_cast<const char16_t *>(serviceName),
+ env->GetStringLength(serviceNameObj));
+ env->ReleaseStringCritical(serviceNameObj, serviceName);
+
+ auto sm = android::defaultServiceManager();
+ sp<IBinder> service = sm->waitForService(nameCopy);
+
+ if (!service) {
+ signalExceptionForError(env, nullptr, NAME_NOT_FOUND, true /*canThrowRemoteException*/);
+ return nullptr;
+ }
+
+ return javaObjectForIBinder(env, service);
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod gBinderMethods[] = {
@@ -1019,7 +1044,8 @@
{ "flushPendingCommands", "()V", (void*)android_os_Binder_flushPendingCommands },
{ "getNativeBBinderHolder", "()J", (void*)android_os_Binder_getNativeBBinderHolder },
{ "getNativeFinalizer", "()J", (void*)android_os_Binder_getNativeFinalizer },
- { "blockUntilThreadAvailable", "()V", (void*)android_os_Binder_blockUntilThreadAvailable }
+ { "blockUntilThreadAvailable", "()V", (void*)android_os_Binder_blockUntilThreadAvailable },
+ { "waitForService", "(Ljava/lang/String;)Landroid/os/IBinder;", (void*)android_os_Binder_waitForService }
};
const char* const kBinderPathName = "android/os/Binder";
diff --git a/core/jni/android_view_InputChannel.cpp b/core/jni/android_view_InputChannel.cpp
index af34e7b7..bf1cea8 100644
--- a/core/jni/android_view_InputChannel.cpp
+++ b/core/jni/android_view_InputChannel.cpp
@@ -112,7 +112,9 @@
}
static jobject android_view_InputChannel_createInputChannel(JNIEnv* env,
- std::unique_ptr<NativeInputChannel> nativeInputChannel) {
+ sp<InputChannel> inputChannel) {
+ std::unique_ptr<NativeInputChannel> nativeInputChannel =
+ std::make_unique<NativeInputChannel>(inputChannel);
jobject inputChannelObj = env->NewObject(gInputChannelClassInfo.clazz,
gInputChannelClassInfo.ctor);
if (inputChannelObj) {
@@ -143,14 +145,12 @@
return nullptr;
}
- jobject serverChannelObj = android_view_InputChannel_createInputChannel(env,
- std::make_unique<NativeInputChannel>(serverChannel));
+ jobject serverChannelObj = android_view_InputChannel_createInputChannel(env, serverChannel);
if (env->ExceptionCheck()) {
return nullptr;
}
- jobject clientChannelObj = android_view_InputChannel_createInputChannel(env,
- std::make_unique<NativeInputChannel>(clientChannel));
+ jobject clientChannelObj = android_view_InputChannel_createInputChannel(env, clientChannel);
if (env->ExceptionCheck()) {
return nullptr;
}
diff --git a/core/proto/android/service/procstats.proto b/core/proto/android/service/procstats.proto
index f49a044..ad7299d 100644
--- a/core/proto/android/service/procstats.proto
+++ b/core/proto/android/service/procstats.proto
@@ -241,12 +241,25 @@
repeated StateStats active_state_stats = 6;
}
-// Next Tag: 3
+// Next Tag: 7
message PackageAssociationProcessStatsProto {
option (android.msg_privacy).dest = DEST_AUTOMATIC;
// Name of the target component.
optional string component_name = 1;
+
+ // Total count of the times this association appeared.
+ optional int32 total_count = 3;
+
+ // Millisecond uptime total duration this association was around.
+ optional int64 total_duration_ms = 4;
+
+ // Total count of the times this association became actively impacting its target process.
+ optional int32 active_count = 5;
+
+ // Millisecond uptime total duration this association was around.
+ optional int64 active_duration_ms = 6;
+
// Information on one source in this association.
repeated PackageAssociationSourceProcessStatsProto sources = 2;
}
diff --git a/core/proto/android/stats/devicepolicy/device_policy_enums.proto b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
index 0821d14..15813a1 100644
--- a/core/proto/android/stats/devicepolicy/device_policy_enums.proto
+++ b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
@@ -113,9 +113,9 @@
PROVISIONING_PREPROVISIONING_ACTIVITY_TIME_MS = 87;
PROVISIONING_ENCRYPT_DEVICE_ACTIVITY_TIME_MS = 88;
PROVISIONING_WEB_ACTIVITY_TIME_MS = 89;
- PROVISIONING_TRAMPOLINE_ACTIVITY_TIME_MS = 90;
- PROVISIONING_POST_ENCRYPTION_ACTIVITY_TIME_MS = 91;
- PROVISIONING_FINALIZATION_ACTIVITY_TIME_MS = 92;
+ PROVISIONING_TRAMPOLINE_ACTIVITY_TIME_MS = 90 [deprecated=true];
+ PROVISIONING_POST_ENCRYPTION_ACTIVITY_TIME_MS = 91 [deprecated=true];
+ PROVISIONING_FINALIZATION_ACTIVITY_TIME_MS = 92 [deprecated=true];
PROVISIONING_NETWORK_TYPE = 93;
PROVISIONING_ACTION = 94;
PROVISIONING_EXTRAS = 95;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index b030b33..5c1e13b 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1624,7 +1624,7 @@
@hide This should only be used by Settings and SystemUI.
-->
<permission android:name="android.permission.NETWORK_SETTINGS"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|telephony" />
<!-- Allows SetupWizard to call methods in Networking services
<p>Not for use by any other third-party or privileged applications.
@@ -2138,12 +2138,12 @@
<!-- Must be required by a telephony data service to ensure that only the
system can bind to it.
- <p>Protection level: signature
+ <p>Protection level: signature|telephony
@SystemApi
@hide
-->
<permission android:name="android.permission.BIND_TELEPHONY_DATA_SERVICE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|telephony" />
<!-- Must be required by a NetworkService to ensure that only the
system can bind to it.
@@ -2164,11 +2164,11 @@
<!-- @SystemApi Must be required by an EuiccService to ensure that only the system can bind to
it.
- <p>Protection level: signature
+ <p>Protection level: signature|telephony
@hide
-->
<permission android:name="android.permission.BIND_EUICC_SERVICE"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|telephony" />
<!-- ================================== -->
<!-- Permissions for sdcard interaction -->
@@ -2955,7 +2955,7 @@
@hide
-->
<permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|telephony|wifi" />
<!-- @SystemApi Allows an application to use
{@link android.view.WindowManager.LayoutsParams#SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS}
@@ -3740,7 +3740,7 @@
@hide
-->
<permission android:name="android.permission.DEVICE_POWER"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|telephony" />
<!-- Allows toggling battery saver on the system.
Superseded by DEVICE_POWER permission. @hide @SystemApi
@@ -3775,13 +3775,13 @@
<p>Not for use by third-party applications.
-->
<permission android:name="android.permission.BROADCAST_SMS"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|telephony" />
<!-- Allows an application to broadcast a WAP PUSH receipt notification.
<p>Not for use by third-party applications.
-->
<permission android:name="android.permission.BROADCAST_WAP_PUSH"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|telephony" />
<!-- @SystemApi Allows an application to broadcast privileged networking requests.
<p>Not for use by third-party applications.
@@ -4396,13 +4396,13 @@
{@link android.provider.BlockedNumberContract}.
@hide -->
<permission android:name="android.permission.READ_BLOCKED_NUMBERS"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|telephony" />
<!-- Allows the holder to write blocked numbers. See
{@link android.provider.BlockedNumberContract}.
@hide -->
<permission android:name="android.permission.WRITE_BLOCKED_NUMBERS"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|telephony" />
<!-- Must be required by an {@link android.service.vr.VrListenerService}, to ensure that only
the system can bind to it.
diff --git a/core/res/res/layout/resolve_list_item.xml b/core/res/res/layout/resolve_list_item.xml
index 0bdb25a..4857095 100644
--- a/core/res/res/layout/resolve_list_item.xml
+++ b/core/res/res/layout/resolve_list_item.xml
@@ -22,8 +22,6 @@
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:minHeight="?attr/listPreferredItemHeightSmall"
- android:paddingTop="4dp"
- android:paddingBottom="4dp"
android:background="?attr/activatedBackgroundIndicator">
<!-- Activity icon when presenting dialog
@@ -32,7 +30,8 @@
android:layout_width="@dimen/resolver_icon_size"
android:layout_height="@dimen/resolver_icon_size"
android:layout_gravity="start|center_vertical"
- android:layout_marginStart="?attr/listPreferredItemPaddingStart"
+ android:layout_marginStart="@dimen/resolver_icon_margin"
+ android:layout_marginEnd="@dimen/resolver_icon_margin"
android:layout_marginTop="12dp"
android:layout_marginBottom="12dp"
android:scaleType="fitCenter" />
@@ -40,8 +39,7 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:gravity="start|center_vertical"
android:orientation="vertical"
- android:paddingStart="?attr/listPreferredItemPaddingStart"
- android:paddingEnd="?attr/listPreferredItemPaddingEnd"
+ android:paddingEnd="@dimen/resolver_edge_margin"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_gravity="start|center_vertical">
@@ -49,14 +47,20 @@
<TextView android:id="@android:id/text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textAppearance="?attr/textAppearanceMedium"
- android:textColor="?attr/textColorPrimary"
+ android:layout_gravity="start|center_vertical"
+ android:textColor="?android:attr/textColorPrimary"
+ android:fontFamily="@android:string/config_bodyFontFamily"
+ android:textSize="16sp"
android:minLines="1"
android:maxLines="1"
android:ellipsize="marquee" />
<!-- Extended activity info to distinguish between duplicate activity names -->
<TextView android:id="@android:id/text2"
- android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorSecondary"
+ android:fontFamily="@android:string/config_bodyFontFamily"
+ android:layout_gravity="start|center_vertical"
+ android:textSize="14sp"
+ android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minLines="1"
diff --git a/core/res/res/layout/resolver_different_item_header.xml b/core/res/res/layout/resolver_different_item_header.xml
index 7d9ffd7..0a35edc 100644
--- a/core/res/res/layout/resolver_different_item_header.xml
+++ b/core/res/res/layout/resolver_different_item_header.xml
@@ -22,12 +22,12 @@
android:layout_height="wrap_content"
android:layout_alwaysShow="true"
android:text="@string/use_a_different_app"
- android:minHeight="56dp"
- android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="?android:attr/textColorPrimary"
+ android:fontFamily="@android:string/config_headlineFontFamilyMedium"
+ android:textSize="16sp"
android:gravity="start|center_vertical"
- android:paddingStart="16dp"
- android:paddingEnd="16dp"
- android:paddingTop="8dp"
- android:paddingBottom="8dp"
- android:elevation="8dp"
- />
+ android:paddingStart="@dimen/resolver_edge_margin"
+ android:paddingEnd="@dimen/resolver_edge_margin"
+ android:paddingTop="@dimen/resolver_small_margin"
+ android:paddingBottom="@dimen/resolver_edge_margin"
+ android:elevation="1dp" />
diff --git a/core/res/res/layout/resolver_list.xml b/core/res/res/layout/resolver_list.xml
index 1dd4207..6e45e7a 100644
--- a/core/res/res/layout/resolver_list.xml
+++ b/core/res/res/layout/resolver_list.xml
@@ -29,16 +29,18 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alwaysShow="true"
- android:elevation="8dp"
- android:background="?attr/colorBackgroundFloating">
+ android:elevation="@dimen/resolver_elevation"
+ android:paddingTop="@dimen/resolver_small_margin"
+ android:paddingStart="@dimen/resolver_edge_margin"
+ android:paddingEnd="@dimen/resolver_edge_margin"
+ android:paddingBottom="@dimen/resolver_edge_margin"
+ android:background="@drawable/bottomsheet_background">
<TextView
android:id="@+id/profile_button"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:layout_marginEnd="8dp"
- android:paddingStart="8dp"
- android:paddingEnd="8dp"
android:visibility="gone"
style="?attr/borderlessButtonStyle"
android:textAppearance="?attr/textAppearanceButton"
@@ -50,36 +52,49 @@
<TextView
android:id="@+id/title"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:minHeight="56dp"
- android:textAppearance="?attr/textAppearanceMedium"
- android:gravity="start|center_vertical"
- android:paddingStart="?attr/dialogPreferredPadding"
- android:paddingEnd="?attr/dialogPreferredPadding"
- android:paddingTop="8dp"
android:layout_below="@id/profile_button"
android:layout_alignParentStart="true"
- android:paddingBottom="8dp" />
+ android:textColor="?android:attr/textColorPrimary"
+ android:fontFamily="@android:string/config_headlineFontFamilyMedium"
+ android:textSize="16sp"
+ android:gravity="start|center_vertical" />
</RelativeLayout>
+ <View
+ android:layout_alwaysShow="true"
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="?attr/colorBackgroundFloating"
+ android:foreground="?attr/dividerVertical" />
<ListView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/resolver_list"
android:clipToPadding="false"
- android:scrollbarStyle="outsideOverlay"
android:background="?attr/colorBackgroundFloating"
- android:elevation="8dp"
+ android:elevation="@dimen/resolver_elevation"
android:nestedScrollingEnabled="true"
+ android:scrollbarStyle="outsideOverlay"
android:scrollIndicators="top|bottom"
- android:divider="@null" />
+ android:divider="?attr/dividerVertical"
+ android:footerDividersEnabled="false"
+ android:headerDividersEnabled="false"
+ android:dividerHeight="1dp" />
+ <View
+ android:layout_alwaysShow="true"
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="?attr/colorBackgroundFloating"
+ android:foreground="?attr/dividerVertical" />
+
<TextView android:id="@+id/empty"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorBackgroundFloating"
- android:elevation="8dp"
+ android:elevation="@dimen/resolver_elevation"
android:layout_alwaysShow="true"
android:text="@string/noApplications"
android:padding="32dp"
@@ -102,18 +117,19 @@
android:background="?attr/colorBackgroundFloating"
android:paddingTop="@dimen/resolver_button_bar_spacing"
android:paddingBottom="@dimen/resolver_button_bar_spacing"
- android:paddingStart="12dp"
- android:paddingEnd="12dp"
- android:elevation="8dp">
+ android:paddingStart="@dimen/resolver_edge_margin"
+ android:paddingEnd="@dimen/resolver_small_margin"
+ android:elevation="@dimen/resolver_elevation">
<Button
android:id="@+id/button_once"
android:layout_width="wrap_content"
android:layout_gravity="start"
android:maxLines="2"
- style="?attr/buttonBarNegativeButtonStyle"
- android:minHeight="@dimen/alert_dialog_button_bar_height"
+ style="?attr/buttonBarButtonStyle"
+ android:fontFamily="@android:string/config_headlineFontFamilyMedium"
android:layout_height="wrap_content"
+ android:textAllCaps="false"
android:enabled="false"
android:text="@string/activity_resolver_use_once"
android:onClick="onButtonClick" />
@@ -123,8 +139,9 @@
android:layout_width="wrap_content"
android:layout_gravity="end"
android:maxLines="2"
- android:minHeight="@dimen/alert_dialog_button_bar_height"
- style="?attr/buttonBarPositiveButtonStyle"
+ style="?attr/buttonBarButtonStyle"
+ android:fontFamily="@android:string/config_headlineFontFamilyMedium"
+ android:textAllCaps="false"
android:layout_height="wrap_content"
android:enabled="false"
android:text="@string/activity_resolver_use_always"
diff --git a/core/res/res/layout/resolver_list_with_default.xml b/core/res/res/layout/resolver_list_with_default.xml
index 740a7eb..dbba0b7 100644
--- a/core/res/res/layout/resolver_list_with_default.xml
+++ b/core/res/res/layout/resolver_list_with_default.xml
@@ -29,22 +29,22 @@
android:layout_height="wrap_content"
android:layout_alwaysShow="true"
android:orientation="vertical"
- android:background="?attr/colorBackgroundFloating"
- android:elevation="8dp">
+ android:background="@drawable/bottomsheet_background"
+ android:paddingTop="@dimen/resolver_small_margin"
+ android:elevation="@dimen/resolver_elevation">
<LinearLayout
android:layout_width="match_parent"
- android:layout_height="64dp"
- android:orientation="horizontal">
-
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:paddingBottom="@dimen/resolver_edge_margin"
+ android:paddingEnd="@dimen/resolver_edge_margin">
<ImageView
android:id="@+id/icon"
- android:layout_width="24dp"
- android:layout_height="24dp"
+ android:layout_width="@dimen/resolver_icon_size"
+ android:layout_height="@dimen/resolver_icon_size"
android:layout_gravity="start|top"
- android:layout_marginStart="16dp"
- android:layout_marginEnd="16dp"
- android:layout_marginTop="20dp"
+ android:layout_marginStart="@dimen/resolver_icon_margin"
android:src="@drawable/resolver_icon_placeholder"
android:scaleType="fitCenter" />
@@ -52,9 +52,11 @@
android:id="@+id/title"
android:layout_width="0dp"
android:layout_weight="1"
- android:layout_height="?attr/listPreferredItemHeight"
- android:layout_marginStart="16dp"
- android:textAppearance="?attr/textAppearanceMedium"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/resolver_icon_margin"
+ android:textColor="?android:attr/textColorPrimary"
+ android:fontFamily="@android:string/config_headlineFontFamilyMedium"
+ android:textSize="16sp"
android:gravity="start|center_vertical"
android:paddingEnd="16dp" />
@@ -107,21 +109,22 @@
android:orientation="horizontal"
android:layoutDirection="locale"
android:measureWithLargestChild="true"
- android:paddingTop="8dp"
- android:paddingBottom="8dp"
- android:paddingStart="12dp"
- android:paddingEnd="12dp"
- android:elevation="8dp">
+ android:paddingTop="@dimen/resolver_button_bar_spacing"
+ android:paddingBottom="@dimen/resolver_button_bar_spacing"
+ android:paddingStart="@dimen/resolver_edge_margin"
+ android:paddingEnd="@dimen/resolver_small_margin"
+ android:elevation="@dimen/resolver_elevation">
<Button
android:id="@+id/button_once"
android:layout_width="wrap_content"
android:layout_gravity="start"
android:maxLines="2"
- style="?attr/buttonBarNegativeButtonStyle"
- android:minHeight="@dimen/alert_dialog_button_bar_height"
+ style="?attr/buttonBarButtonStyle"
+ android:fontFamily="@android:string/config_headlineFontFamilyMedium"
android:layout_height="wrap_content"
android:enabled="false"
+ android:textAllCaps="false"
android:text="@string/activity_resolver_use_once"
android:onClick="onButtonClick" />
@@ -130,29 +133,40 @@
android:layout_width="wrap_content"
android:layout_gravity="end"
android:maxLines="2"
- android:minHeight="@dimen/alert_dialog_button_bar_height"
- style="?attr/buttonBarPositiveButtonStyle"
+ style="?attr/buttonBarButtonStyle"
+ android:fontFamily="@android:string/config_headlineFontFamilyMedium"
android:layout_height="wrap_content"
android:enabled="false"
+ android:textAllCaps="false"
android:text="@string/activity_resolver_use_always"
android:onClick="onButtonClick" />
</LinearLayout>
-
- <View
- android:layout_width="match_parent"
- android:layout_height="1dp"
- android:background="?attr/dividerVertical" />
</LinearLayout>
+ <View
+ android:layout_alwaysShow="true"
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="?attr/colorBackgroundFloating"
+ android:foreground="?attr/dividerVertical" />
<ListView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/resolver_list"
android:clipToPadding="false"
- android:scrollbarStyle="outsideOverlay"
android:background="?attr/colorBackgroundFloating"
- android:elevation="8dp"
+ android:elevation="@dimen/resolver_elevation"
android:nestedScrollingEnabled="true"
- android:divider="@null" />
-
+ android:scrollbarStyle="outsideOverlay"
+ android:scrollIndicators="top|bottom"
+ android:divider="?attr/dividerVertical"
+ android:footerDividersEnabled="false"
+ android:headerDividersEnabled="false"
+ android:dividerHeight="1dp" />
+ <View
+ android:layout_alwaysShow="true"
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="?attr/colorBackgroundFloating"
+ android:foreground="?attr/dividerVertical" />
</com.android.internal.widget.ResolverDrawerLayout>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index a301702..ffcfe43 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -295,6 +295,12 @@
<!-- Additional flag from base permission type: this permission can be automatically
granted to the system app predictor -->
<flag name="appPredictor" value="0x200000" />
+ <!-- Additional flag from base permission type: this permission can be automatically
+ granted to the system telephony apps -->
+ <flag name="telephony" value="0x400000" />
+ <!-- Additional flag from base permission type: this permission can be automatically
+ granted to the system wifi app-->
+ <flag name="wifi" value="0x800000" />
</attr>
<!-- Flags indicating more context for a permission group. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 3fef7a2d..5605246 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3687,6 +3687,21 @@
-->
<string name="config_defaultWellbeingPackage" translatable="false"></string>
+ <!-- The package name for the system telephony apps.
+ This package must be trusted, as it will be granted with permissions with special telephony
+ protection level. Note, framework by default support multiple telephony apps, each package
+ name is separated by comma.
+ Example: "com.android.phone,com.android.stk,com.android.providers.telephony"
+ -->
+ <string name="config_telephonyPackages" translatable="false">"com.android.phone,com.android.stk,com.android.providers.telephony,com.android.ons"</string>
+
+ <!-- The package name for the default system wifi app.
+ This package must be trusted, as it has the permissions to control wifi
+ connectivity on the device.
+ Example: "com.android.wifi"
+ -->
+ <string name="config_wifiPackage" translatable="false">"com.android.wifi"</string>
+
<!-- The component name for the default system attention service.
This service must be trusted, as it can be activated without explicit consent of the user.
See android.attention.AttentionManagerService.
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 609659b..a01bbe3 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -750,7 +750,7 @@
<dimen name="seekbar_thumb_exclusion_max_size">48dp</dimen>
- <!-- chooser (sharesheet) spacing -->
+ <!-- chooser/resolver (sharesheet) spacing -->
<dimen name="chooser_corner_radius">8dp</dimen>
<dimen name="chooser_row_text_option_translate">25dp</dimen>
<dimen name="chooser_view_spacing">18dp</dimen>
@@ -759,11 +759,15 @@
<dimen name="chooser_preview_image_font_size">20sp</dimen>
<dimen name="chooser_preview_image_border">1dp</dimen>
<dimen name="chooser_preview_width">-1px</dimen>
- <dimen name="resolver_icon_size">42dp</dimen>
- <dimen name="resolver_button_bar_spacing">8dp</dimen>
- <dimen name="resolver_badge_size">18dp</dimen>
<dimen name="chooser_target_width">90dp</dimen>
<dimen name="chooser_header_scroll_elevation">4dp</dimen>
<dimen name="chooser_max_collapsed_height">288dp</dimen>
<dimen name="chooser_direct_share_label_placeholder_max_width">72dp</dimen>
+ <dimen name="resolver_icon_size">32dp</dimen>
+ <dimen name="resolver_button_bar_spacing">8dp</dimen>
+ <dimen name="resolver_badge_size">18dp</dimen>
+ <dimen name="resolver_icon_margin">16dp</dimen>
+ <dimen name="resolver_small_margin">18dp</dimen>
+ <dimen name="resolver_edge_margin">24dp</dimen>
+ <dimen name="resolver_elevation">1dp</dimen>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 363bc9d..42cd2cd 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3467,6 +3467,8 @@
<java-symbol type="string" name="config_defaultAutofillService" />
<java-symbol type="string" name="config_defaultTextClassifierPackage" />
<java-symbol type="string" name="config_defaultWellbeingPackage" />
+ <java-symbol type="string" name="config_telephonyPackages" />
+ <java-symbol type="string" name="config_wifiPackage" />
<java-symbol type="string" name="config_defaultContentCaptureService" />
<java-symbol type="string" name="config_defaultAugmentedAutofillService" />
<java-symbol type="string" name="config_defaultAppPredictionService" />
@@ -3819,6 +3821,10 @@
<java-symbol type="dimen" name="resolver_icon_size"/>
<java-symbol type="dimen" name="resolver_badge_size"/>
<java-symbol type="dimen" name="resolver_button_bar_spacing"/>
+ <java-symbol type="dimen" name="resolver_icon_margin"/>
+ <java-symbol type="dimen" name="resolver_small_margin"/>
+ <java-symbol type="dimen" name="resolver_edge_margin"/>
+ <java-symbol type="dimen" name="resolver_elevation"/>
<!-- For DropBox -->
<java-symbol type="integer" name="config_dropboxLowPriorityBroadcastRateLimitPeriod" />
diff --git a/core/tests/coretests/src/android/util/TimestampedValueTest.java b/core/tests/coretests/src/android/util/TimestampedValueTest.java
index 6e3ab79..6fc2400 100644
--- a/core/tests/coretests/src/android/util/TimestampedValueTest.java
+++ b/core/tests/coretests/src/android/util/TimestampedValueTest.java
@@ -55,12 +55,12 @@
TimestampedValue<String> stringValue = new TimestampedValue<>(1000, "Hello");
Parcel parcel = Parcel.obtain();
try {
- TimestampedValue.writeToParcel(parcel, stringValue);
+ parcel.writeParcelable(stringValue, 0);
parcel.setDataPosition(0);
TimestampedValue<String> stringValueCopy =
- TimestampedValue.readFromParcel(parcel, null /* classLoader */, String.class);
+ parcel.readParcelable(null /* classLoader */);
assertEquals(stringValue, stringValueCopy);
} finally {
parcel.recycle();
@@ -72,12 +72,12 @@
TimestampedValue<String> stringValue = new TimestampedValue<>(1000, "Hello");
Parcel parcel = Parcel.obtain();
try {
- TimestampedValue.writeToParcel(parcel, stringValue);
+ parcel.writeParcelable(stringValue, 0);
parcel.setDataPosition(0);
- TimestampedValue<Object> stringValueCopy =
- TimestampedValue.readFromParcel(parcel, null /* classLoader */, Object.class);
+ TimestampedValue<String> stringValueCopy =
+ parcel.readParcelable(null /* classLoader */);
assertEquals(stringValue, stringValueCopy);
} finally {
parcel.recycle();
@@ -85,15 +85,15 @@
}
@Test
- public void testParceling_valueClassIncompatible() {
- TimestampedValue<String> stringValue = new TimestampedValue<>(1000, "Hello");
+ public void testParceling_valueClassNotParcelable() {
+ // This class is not one supported by Parcel.writeValue().
+ class NotParcelable {}
+
+ TimestampedValue<NotParcelable> notParcelableValue =
+ new TimestampedValue<>(1000, new NotParcelable());
Parcel parcel = Parcel.obtain();
try {
- TimestampedValue.writeToParcel(parcel, stringValue);
-
- parcel.setDataPosition(0);
-
- TimestampedValue.readFromParcel(parcel, null /* classLoader */, Double.class);
+ parcel.writeParcelable(notParcelableValue, 0);
fail();
} catch (RuntimeException expected) {
} finally {
@@ -106,12 +106,11 @@
TimestampedValue<String> nullValue = new TimestampedValue<>(1000, null);
Parcel parcel = Parcel.obtain();
try {
- TimestampedValue.writeToParcel(parcel, nullValue);
+ parcel.writeParcelable(nullValue, 0);
parcel.setDataPosition(0);
- TimestampedValue<Object> nullValueCopy =
- TimestampedValue.readFromParcel(parcel, null /* classLoader */, String.class);
+ TimestampedValue<String> nullValueCopy = parcel.readParcelable(null /* classLoader */);
assertEquals(nullValue, nullValueCopy);
} finally {
parcel.recycle();
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
index abee1da2..7b405434 100644
--- a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
@@ -345,9 +345,9 @@
accessibilityShortcutController.performAccessibilityShortcut();
accessibilityShortcutController.performAccessibilityShortcut();
verify(mToast).show();
- assertEquals(WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS,
+ assertEquals(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS,
mLayoutParams.privateFlags
- & WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS);
+ & WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS);
verify(mAccessibilityManagerService, times(1)).performAccessibilityShortcut();
}
diff --git a/core/tests/coretests/src/com/android/internal/widget/LockscreenCredentialTest.java b/core/tests/coretests/src/com/android/internal/widget/LockscreenCredentialTest.java
new file mode 100644
index 0000000..5eec91c
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/widget/LockscreenCredentialTest.java
@@ -0,0 +1,155 @@
+/*
+ * 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.internal.widget;
+
+
+import android.test.AndroidTestCase;
+
+import java.util.Arrays;
+
+
+public class LockscreenCredentialTest extends AndroidTestCase {
+
+ public void testEmptyCredential() {
+ LockscreenCredential empty = LockscreenCredential.createNone();
+
+ assertTrue(empty.isNone());
+ assertEquals(0, empty.size());
+ assertNotNull(empty.getCredential());
+
+ assertFalse(empty.isPassword());
+ assertFalse(empty.isPattern());
+ }
+
+ public void testPasswordCredential() {
+ LockscreenCredential password = LockscreenCredential.createPassword("password");
+
+ assertTrue(password.isPassword());
+ assertEquals(8, password.size());
+ assertTrue(Arrays.equals("password".getBytes(), password.getCredential()));
+
+ assertFalse(password.isNone());
+ assertFalse(password.isPattern());
+ }
+
+ public void testPatternCredential() {
+ LockscreenCredential pattern = LockscreenCredential.createPattern(Arrays.asList(
+ LockPatternView.Cell.of(0, 0),
+ LockPatternView.Cell.of(0, 1),
+ LockPatternView.Cell.of(0, 2),
+ LockPatternView.Cell.of(1, 2),
+ LockPatternView.Cell.of(2, 2)
+ ));
+
+ assertTrue(pattern.isPattern());
+ assertEquals(5, pattern.size());
+ assertTrue(Arrays.equals("12369".getBytes(), pattern.getCredential()));
+
+ assertFalse(pattern.isNone());
+ assertFalse(pattern.isPassword());
+ }
+
+ public void testPasswordOrNoneCredential() {
+ assertEquals(LockscreenCredential.createNone(),
+ LockscreenCredential.createPasswordOrNone(null));
+ assertEquals(LockscreenCredential.createNone(),
+ LockscreenCredential.createPasswordOrNone(""));
+ assertEquals(LockscreenCredential.createPassword("abcd"),
+ LockscreenCredential.createPasswordOrNone("abcd"));
+ }
+
+ public void testSanitize() {
+ LockscreenCredential password = LockscreenCredential.createPassword("password");
+ password.zeroize();
+
+ try {
+ password.isNone();
+ fail("Sanitized credential still accessible");
+ } catch (IllegalStateException expected) { }
+
+ try {
+ password.isPattern();
+ fail("Sanitized credential still accessible");
+ } catch (IllegalStateException expected) { }
+ try {
+ password.isPassword();
+ fail("Sanitized credential still accessible");
+ } catch (IllegalStateException expected) { }
+ try {
+ password.size();
+ fail("Sanitized credential still accessible");
+ } catch (IllegalStateException expected) { }
+ try {
+ password.getCredential();
+ fail("Sanitized credential still accessible");
+ } catch (IllegalStateException expected) { }
+ }
+
+ public void testEquals() {
+ assertEquals(LockscreenCredential.createNone(), LockscreenCredential.createNone());
+ assertEquals(LockscreenCredential.createPassword("1234"),
+ LockscreenCredential.createPassword("1234"));
+ assertEquals(LockscreenCredential.createPin("4321"),
+ LockscreenCredential.createPin("4321"));
+ assertEquals(createPattern("1234"), createPattern("1234"));
+
+ assertNotSame(LockscreenCredential.createPassword("1234"),
+ LockscreenCredential.createNone());
+ assertNotSame(LockscreenCredential.createPassword("1234"),
+ LockscreenCredential.createPassword("4321"));
+ assertNotSame(LockscreenCredential.createPassword("1234"),
+ createPattern("1234"));
+ assertNotSame(LockscreenCredential.createPassword("1234"),
+ LockscreenCredential.createPin("1234"));
+
+ assertNotSame(LockscreenCredential.createPin("1111"),
+ LockscreenCredential.createNone());
+ assertNotSame(LockscreenCredential.createPin("1111"),
+ LockscreenCredential.createPin("2222"));
+ assertNotSame(LockscreenCredential.createPin("1111"),
+ createPattern("1111"));
+ assertNotSame(LockscreenCredential.createPin("1111"),
+ LockscreenCredential.createPassword("1111"));
+
+ assertNotSame(createPattern("5678"),
+ LockscreenCredential.createNone());
+ assertNotSame(createPattern("5678"),
+ createPattern("1234"));
+ assertNotSame(createPattern("5678"),
+ LockscreenCredential.createPassword("5678"));
+ assertNotSame(createPattern("5678"),
+ LockscreenCredential.createPin("5678"));
+ }
+
+ public void testDuplicate() {
+ LockscreenCredential credential;
+
+ credential = LockscreenCredential.createNone();
+ assertEquals(credential, credential.duplicate());
+ credential = LockscreenCredential.createPassword("abcd");
+ assertEquals(credential, credential.duplicate());
+ credential = LockscreenCredential.createPin("1234");
+ assertEquals(credential, credential.duplicate());
+ credential = createPattern("5678");
+ assertEquals(credential, credential.duplicate());
+ }
+
+ private LockscreenCredential createPattern(String patternString) {
+ return LockscreenCredential.createPattern(LockPatternUtils.byteArrayToPattern(
+ patternString.getBytes()));
+ }
+}
diff --git a/data/etc/car/com.google.android.car.kitchensink.xml b/data/etc/car/com.google.android.car.kitchensink.xml
index d36a826..61281ee 100644
--- a/data/etc/car/com.google.android.car.kitchensink.xml
+++ b/data/etc/car/com.google.android.car.kitchensink.xml
@@ -16,17 +16,30 @@
-->
<permissions>
<privapp-permissions package="com.google.android.car.kitchensink">
+ <permission name="android.permission.ACCESS_NETWORK_STATE"/>
+ <permission name="android.permission.ACCESS_WIFI_STATE"/>
+ <permission name="android.permission.ACTIVITY_EMBEDDING"/>
+ <permission name="android.permission.INJECT_EVENTS"/>
+ <!-- use for CarServiceUnitTest and CarServiceTest -->
+ <permission name="android.permission.INTERACT_ACROSS_USERS"/>
+ <!-- use for CarServiceUnitTest -->
+ <permission name="android.permission.INTERACT_ACROSS_USERS_FULL"/>
<permission name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"/>
<permission name="android.permission.LOCATION_HARDWARE"/>
<permission name="android.permission.MANAGE_USB"/>
<permission name="android.permission.MANAGE_USERS"/>
+ <!-- use for CarServiceTest -->
+ <permission name="android.permission.MEDIA_CONTENT_CONTROL"/>
<permission name="android.permission.MODIFY_AUDIO_ROUTING"/>
<permission name="android.permission.MODIFY_DAY_NIGHT_MODE"/>
<permission name="android.permission.MODIFY_PHONE_STATE"/>
<permission name="android.permission.PROVIDE_TRUST_AGENT"/>
+ <permission name="android.permission.OVERRIDE_WIFI_CONFIG"/>
<permission name="android.permission.REAL_GET_TASKS"/>
<permission name="android.permission.READ_LOGS"/>
<permission name="android.permission.REBOOT"/>
+ <!-- use for CarServiceTest -->
+ <permission name="android.permission.SET_ACTIVITY_WATCHER"/>
<permission name="android.permission.WRITE_SECURE_SETTINGS"/>
</privapp-permissions>
</permissions>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 10f6b69..4b4e416 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -121,6 +121,7 @@
<permission name="android.permission.APPROVE_INCIDENT_REPORTS"/>
<permission name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
<permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME" />
+ <permission name="android.permission.PACKAGE_USAGE_STATS" />
</privapp-permissions>
<privapp-permissions package="com.android.phone">
@@ -359,6 +360,7 @@
<permission name="android.permission.CONNECTIVITY_INTERNAL"/>
<permission name="android.permission.DUMP"/>
<permission name="android.permission.INTERACT_ACROSS_USERS"/>
+ <permission name="android.permission.INTERNAL_SYSTEM_WINDOW"/>
<permission name="android.permission.LOCAL_MAC_ADDRESS"/>
<permission name="android.permission.MANAGE_USERS"/>
<permission name="android.permission.PACKAGE_USAGE_STATS"/>
diff --git a/data/fonts/Android.mk b/data/fonts/Android.mk
index 454dceb..4226e08 100644
--- a/data/fonts/Android.mk
+++ b/data/fonts/Android.mk
@@ -83,8 +83,8 @@
################################
# Copies the font configuration file into system/etc for the product as fonts.xml.
-# In the case where $(ADDITIONAL_FONTS_FILE) is defined, the content of $(ADDITIONAL_FONTS_FILE)
-# is added to the $(AOSP_FONTS_FILE).
+# Additional fonts should be installed to /product/fonts/ alongside a corresponding
+# fonts_customiztion.xml in /product/etc/
include $(CLEAR_VARS)
LOCAL_MODULE := fonts.xml
diff --git a/graphics/java/android/graphics/pdf/TEST_MAPPING b/graphics/java/android/graphics/pdf/TEST_MAPPING
new file mode 100644
index 0000000..d763598
--- /dev/null
+++ b/graphics/java/android/graphics/pdf/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsPdfTestCases"
+ }
+ ]
+}
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index ae90f11..61b72cf 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -26,8 +26,10 @@
// a problem
"-Wno-free-nonheap-object",
- // clang's warning is broken, see: https://llvm.org/bugs/show_bug.cgi?id=21629
- "-Wno-missing-braces",
+ // Clang is producing non-determistic binary when the new pass manager is
+ // enabled. Disable the new PM as a temporary workaround.
+ // b/142372146
+ "-fno-experimental-new-pass-manager",
],
include_dirs: [
diff --git a/libs/hwui/DamageAccumulator.h b/libs/hwui/DamageAccumulator.h
index 7d0b687..030a20f 100644
--- a/libs/hwui/DamageAccumulator.h
+++ b/libs/hwui/DamageAccumulator.h
@@ -27,7 +27,7 @@
// Smaller than INT_MIN/INT_MAX because we offset these values
// and thus don't want to be adding offsets to INT_MAX, that's bad
#define DIRTY_MIN (-0x7ffffff - 1)
-#define DIRTY_MAX (0x7ffffff)
+#define DIRTY_MAX (0x8000000)
namespace android {
namespace uirenderer {
diff --git a/location/java/android/location/AbstractListenerManager.java b/location/java/android/location/AbstractListenerManager.java
index c41023e..944ebf9 100644
--- a/location/java/android/location/AbstractListenerManager.java
+++ b/location/java/android/location/AbstractListenerManager.java
@@ -42,8 +42,8 @@
@Nullable private volatile T mListener;
private Registration(Executor executor, T listener) {
- Preconditions.checkArgument(listener != null);
- Preconditions.checkArgument(executor != null);
+ Preconditions.checkArgument(listener != null, "invalid null listener/callback");
+ Preconditions.checkArgument(executor != null, "invalid null executor");
mExecutor = executor;
mListener = listener;
}
@@ -83,16 +83,18 @@
return addInternal(listener, executor);
}
- protected final boolean addInternal(Object listener, Handler handler) throws RemoteException {
+ protected final boolean addInternal(@NonNull Object listener, @NonNull Handler handler)
+ throws RemoteException {
return addInternal(listener, new HandlerExecutor(handler));
}
- protected final boolean addInternal(Object listener, Executor executor) throws RemoteException {
+ protected final boolean addInternal(@NonNull Object listener, @NonNull Executor executor)
+ throws RemoteException {
+ Preconditions.checkArgument(listener != null, "invalid null listener/callback");
return addInternal(listener, new Registration<>(executor, convertKey(listener)));
}
private boolean addInternal(Object key, Registration<T> registration) throws RemoteException {
- Preconditions.checkNotNull(key);
Preconditions.checkNotNull(registration);
synchronized (mListeners) {
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index d06ba12..daa2e08 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -42,11 +42,11 @@
interface ILocationManager
{
void requestLocationUpdates(in LocationRequest request, in ILocationListener listener,
- in PendingIntent intent, String packageName);
+ in PendingIntent intent, String packageName, String listenerIdentifier);
void removeUpdates(in ILocationListener listener, in PendingIntent intent, String packageName);
void requestGeofence(in LocationRequest request, in Geofence geofence,
- in PendingIntent intent, String packageName);
+ in PendingIntent intent, String packageName, String listenerIdentifier);
void removeGeofence(in Geofence fence, in PendingIntent intent, String packageName);
Location getLastLocation(in LocationRequest request, String packageName);
@@ -64,22 +64,23 @@
boolean sendNiResponse(int notifId, int userResponse);
- boolean addGnssMeasurementsListener(in IGnssMeasurementsListener listener, in String packageName);
+ boolean addGnssMeasurementsListener(in IGnssMeasurementsListener listener,
+ String packageName, String listenerIdentifier);
void injectGnssMeasurementCorrections(in GnssMeasurementCorrections corrections,
in String packageName);
long getGnssCapabilities(in String packageName);
void removeGnssMeasurementsListener(in IGnssMeasurementsListener listener);
- boolean addGnssNavigationMessageListener(
- in IGnssNavigationMessageListener listener,
- in String packageName);
+ boolean addGnssNavigationMessageListener(in IGnssNavigationMessageListener listener,
+ String packageName, String listenerIdentifier);
void removeGnssNavigationMessageListener(in IGnssNavigationMessageListener listener);
int getGnssYearOfHardware();
String getGnssHardwareModelName();
int getGnssBatchSize(String packageName);
- boolean addGnssBatchingCallback(in IBatchedLocationCallback callback, String packageName);
+ boolean addGnssBatchingCallback(in IBatchedLocationCallback callback, String packageName,
+ String listenerIdentifier);
void removeGnssBatchingCallback();
boolean startGnssBatch(long periodNanos, boolean wakeOnFifoFull, String packageName);
void flushGnssBatch(String packageName);
@@ -92,6 +93,7 @@
String getBestProvider(in Criteria criteria, boolean enabledOnly);
ProviderProperties getProviderProperties(String provider);
boolean isProviderPackage(String packageName);
+ List<String> getProviderPackages(String provider);
void setExtraLocationControllerPackage(String packageName);
String getExtraLocationControllerPackage();
diff --git a/location/java/android/location/Location.java b/location/java/android/location/Location.java
index 9c36d76..6824be8 100644
--- a/location/java/android/location/Location.java
+++ b/location/java/android/location/Location.java
@@ -16,6 +16,7 @@
package android.location;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
@@ -79,6 +80,8 @@
*
* @hide
*/
+ @TestApi
+ @SystemApi
public static final String EXTRA_NO_GPS_LOCATION = "noGPSLocation";
/**
@@ -1214,8 +1217,9 @@
* @param value the Location to attach
* @hide
*/
- @UnsupportedAppUsage
- public void setExtraLocation(String key, Location value) {
+ @TestApi
+ @SystemApi
+ public void setExtraLocation(@Nullable String key, @Nullable Location value) {
if (mExtras == null) {
mExtras = new Bundle();
}
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 90e29df..0b3e1c3 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -51,6 +51,7 @@
import com.android.internal.location.ProviderProperties;
import com.android.internal.util.Preconditions;
+import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.RejectedExecutionException;
@@ -542,6 +543,23 @@
}
/**
+ * Create a string that allows an app to identify a listener
+ *
+ * @param listener The listener
+ *
+ * @return A identifying string
+ */
+ private static String getListenerIdentifier(@NonNull Object listener) {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append(listener.getClass().getName());
+ sb.append('@');
+ sb.append(Integer.toHexString(System.identityHashCode(listener)));
+
+ return sb.toString();
+ }
+
+ /**
* Register for a single location update using the named provider and
* a callback.
*
@@ -981,7 +999,7 @@
boolean registered = false;
try {
mService.requestLocationUpdates(locationRequest, transport, null,
- mContext.getPackageName());
+ mContext.getPackageName(), getListenerIdentifier(listener));
registered = true;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -1026,7 +1044,7 @@
try {
mService.requestLocationUpdates(locationRequest, null, pendingIntent,
- mContext.getPackageName());
+ mContext.getPackageName(), getListenerIdentifier(pendingIntent));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1235,6 +1253,24 @@
}
/**
+ * Returns a list of packages associated with the given provider,
+ * and an empty list if no package is associated with the provider.
+ *
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
+ @Nullable
+ public List<String> getProviderPackages(@NonNull String provider) {
+ try {
+ return mService.getProviderPackages(provider);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ return Collections.emptyList();
+ }
+ }
+
+ /**
* Sends additional commands to a location provider. Can be used to support provider specific
* extensions to the Location Manager API.
*
@@ -1471,7 +1507,8 @@
Geofence fence = Geofence.createCircle(latitude, longitude, radius);
LocationRequest request = new LocationRequest().setExpireIn(expiration);
try {
- mService.requestGeofence(request, fence, intent, mContext.getPackageName());
+ mService.requestGeofence(request, fence, intent, mContext.getPackageName(),
+ getListenerIdentifier(intent));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1548,7 +1585,8 @@
Preconditions.checkArgument(fence != null, "invalid null geofence");
try {
- mService.requestGeofence(request, fence, intent, mContext.getPackageName());
+ mService.requestGeofence(request, fence, intent, mContext.getPackageName(),
+ getListenerIdentifier(intent));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1762,6 +1800,7 @@
* @param handler a handler with a looper that the callback runs on
* @return true if the listener was successfully added
*
+ * @throws IllegalArgumentException if callback is null
* @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
*/
@RequiresPermission(ACCESS_FINE_LOCATION)
@@ -1781,10 +1820,12 @@
/**
* Registers a GNSS status callback.
*
- * @param callback GNSS status callback object to register
* @param executor the executor that the callback runs on
+ * @param callback GNSS status callback object to register
* @return true if the listener was successfully added
*
+ * @throws IllegalArgumentException if executor is null
+ * @throws IllegalArgumentException if callback is null
* @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
*/
@RequiresPermission(ACCESS_FINE_LOCATION)
@@ -1853,6 +1894,8 @@
* @param listener a {@link OnNmeaMessageListener} object to register
* @param handler a handler with the looper that the listener runs on.
* @return true if the listener was successfully added
+ *
+ * @throws IllegalArgumentException if listener is null
* @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
*/
@RequiresPermission(ACCESS_FINE_LOCATION)
@@ -1874,6 +1917,9 @@
* @param listener a {@link OnNmeaMessageListener} object to register
* @param executor the {@link Executor} that the listener runs on.
* @return true if the listener was successfully added
+ *
+ * @throws IllegalArgumentException if executor is null
+ * @throws IllegalArgumentException if listener is null
* @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
*/
@RequiresPermission(ACCESS_FINE_LOCATION)
@@ -1946,6 +1992,9 @@
* @param callback a {@link GnssMeasurementsEvent.Callback} object to register.
* @param handler the handler that the callback runs on.
* @return {@code true} if the callback was added successfully, {@code false} otherwise.
+ *
+ * @throws IllegalArgumentException if callback is null
+ * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
*/
@RequiresPermission(ACCESS_FINE_LOCATION)
public boolean registerGnssMeasurementsCallback(
@@ -1966,6 +2015,10 @@
* @param callback a {@link GnssMeasurementsEvent.Callback} object to register.
* @param executor the executor that the callback runs on.
* @return {@code true} if the callback was added successfully, {@code false} otherwise.
+ *
+ * @throws IllegalArgumentException if executor is null
+ * @throws IllegalArgumentException if callback is null
+ * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
*/
@RequiresPermission(ACCESS_FINE_LOCATION)
public boolean registerGnssMeasurementsCallback(
@@ -1983,6 +2036,9 @@
*
* @param measurementCorrections a {@link GnssMeasurementCorrections} object with the GNSS
* measurement corrections to be injected into the GNSS chipset.
+ *
+ * @throws IllegalArgumentException if measurementCorrections is null
+ * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
* @hide
*/
@SystemApi
@@ -2057,6 +2113,9 @@
* @param callback a {@link GnssNavigationMessage.Callback} object to register.
* @param handler the handler that the callback runs on.
* @return {@code true} if the callback was added successfully, {@code false} otherwise.
+ *
+ * @throws IllegalArgumentException if callback is null
+ * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
*/
@RequiresPermission(ACCESS_FINE_LOCATION)
public boolean registerGnssNavigationMessageCallback(
@@ -2078,6 +2137,10 @@
* @param callback a {@link GnssNavigationMessage.Callback} object to register.
* @param executor the looper that the callback runs on.
* @return {@code true} if the callback was added successfully, {@code false} otherwise.
+ *
+ * @throws IllegalArgumentException if executor is null
+ * @throws IllegalArgumentException if callback is null
+ * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
*/
@RequiresPermission(ACCESS_FINE_LOCATION)
public boolean registerGnssNavigationMessageCallback(
@@ -2538,7 +2601,7 @@
mListenerTransport = new GnssMeasurementsListener();
return mService.addGnssMeasurementsListener(mListenerTransport,
- mContext.getPackageName());
+ mContext.getPackageName(), "gnss measurement callback");
}
@Override
@@ -2574,7 +2637,7 @@
mListenerTransport = new GnssNavigationMessageListener();
return mService.addGnssNavigationMessageListener(mListenerTransport,
- mContext.getPackageName());
+ mContext.getPackageName(), "gnss navigation callback");
}
@Override
@@ -2609,7 +2672,8 @@
Preconditions.checkState(mListenerTransport == null);
mListenerTransport = new BatchedLocationCallback();
- return mService.addGnssBatchingCallback(mListenerTransport, mContext.getPackageName());
+ return mService.addGnssBatchingCallback(mListenerTransport, mContext.getPackageName(),
+ "batched location callback");
}
@Override
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index 5341d07..33ddfa7 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -2776,7 +2776,7 @@
updateImageSizeValues(in, IFD_TYPE_THUMBNAIL);
// Check if each image data is in valid position.
- validateImages(in);
+ validateImages();
if (mMimeType == IMAGE_TYPE_PEF) {
// PEF files contain a MakerNote data, which contains the data for ColorSpace tag.
@@ -3143,8 +3143,11 @@
// 2.1. Integers and byte order
in.setByteOrder(ByteOrder.BIG_ENDIAN);
+ int bytesRead = 0;
+
// Skip the signature bytes
in.seek(PNG_SIGNATURE.length);
+ bytesRead += PNG_SIGNATURE.length;
try {
while (true) {
@@ -3159,12 +3162,14 @@
// See PNG (Portable Network Graphics) Specification, Version 1.2,
// 3.2. Chunk layout
int length = in.readInt();
+ bytesRead += 4;
byte[] type = new byte[PNG_CHUNK_LENGTH_BYTE_LENGTH];
if (in.read(type) != type.length) {
throw new IOException("Encountered invalid length while parsing PNG chunk"
+ "type");
}
+ bytesRead += PNG_CHUNK_LENGTH_BYTE_LENGTH;
if (Arrays.equals(type, PNG_CHUNK_TYPE_IEND)) {
// IEND marks the end of the image.
@@ -3177,12 +3182,17 @@
+ "type: " + byteArrayToHexString(type));
}
readExifSegment(data, IFD_TYPE_PRIMARY);
+
+ validateImages();
break;
} else {
// Skip to next chunk
in.skipBytes(length + PNG_CHUNK_CRC_BYTE_LENGTH);
+ bytesRead += length + PNG_CHUNK_CRC_BYTE_LENGTH;
}
}
+ // Save offset values for handleThumbnailFromJfif() function
+ mExifOffset = bytesRead;
} catch (EOFException e) {
// Should not reach here. Will only reach here if the file is corrupted or
// does not follow the PNG specifications
@@ -3675,7 +3685,7 @@
int thumbnailLength = jpegInterchangeFormatLengthAttribute.getIntValue(mExifByteOrder);
if (mMimeType == IMAGE_TYPE_JPEG || mMimeType == IMAGE_TYPE_RAF
- || mMimeType == IMAGE_TYPE_RW2) {
+ || mMimeType == IMAGE_TYPE_RW2 || mMimeType == IMAGE_TYPE_PNG) {
thumbnailOffset += mExifOffset;
} else if (mMimeType == IMAGE_TYPE_ORF) {
// Update offset value since RAF files have IFD data preceding MakerNote data.
@@ -3819,12 +3829,13 @@
}
// Validate primary, preview, thumbnail image data by comparing image size
- private void validateImages(InputStream in) throws IOException {
+ private void validateImages() throws IOException {
// Swap images based on size (primary > preview > thumbnail)
swapBasedOnImageSize(IFD_TYPE_PRIMARY, IFD_TYPE_PREVIEW);
swapBasedOnImageSize(IFD_TYPE_PRIMARY, IFD_TYPE_THUMBNAIL);
swapBasedOnImageSize(IFD_TYPE_PREVIEW, IFD_TYPE_THUMBNAIL);
+ // TODO (b/142296453): Revise image width/height setting logic
// Check if image has PixelXDimension/PixelYDimension tags, which contain valid image
// sizes, excluding padding at the right end or bottom end of the image to make sure that
// the values are multiples of 64. See JEITA CP-3451C Table 5 and Section 4.8.1. B.
diff --git a/media/java/android/media/IMediaRoute2Provider.aidl b/media/java/android/media/IMediaRoute2Provider.aidl
index f132cef..66764c7 100644
--- a/media/java/android/media/IMediaRoute2Provider.aidl
+++ b/media/java/android/media/IMediaRoute2Provider.aidl
@@ -27,4 +27,6 @@
void selectRoute(String packageName, String id);
void unselectRoute(String packageName, String id);
void notifyControlRequestSent(String id, in Intent request);
+ void requestSetVolume(String id, int volume);
+ void requestUpdateVolume(String id, int delta);
}
diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl
index 81213b9..7b7a34e 100644
--- a/media/java/android/media/IMediaRouterService.aidl
+++ b/media/java/android/media/IMediaRouterService.aidl
@@ -46,6 +46,8 @@
void registerClient2(IMediaRouter2Client client, String packageName);
void unregisterClient2(IMediaRouter2Client client);
void sendControlRequest(IMediaRouter2Client client, in MediaRoute2Info route, in Intent request);
+ void requestSetVolume2(IMediaRouter2Client client, in MediaRoute2Info route, int volume);
+ void requestUpdateVolume2(IMediaRouter2Client client, in MediaRoute2Info route, int direction);
/**
* Changes the selected route of the client.
*
@@ -66,4 +68,9 @@
*/
void selectClientRoute2(IMediaRouter2Manager manager, String packageName,
in @nullable MediaRoute2Info route);
+
+ void requestSetVolume2Manager(IMediaRouter2Manager manager,
+ in MediaRoute2Info route, int volume);
+ void requestUpdateVolume2Manager(IMediaRouter2Manager manager,
+ in MediaRoute2Info route, int direction);
}
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 26e7936..91d644b 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -21,6 +21,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.annotation.UnsupportedAppUsage;
import android.os.Build;
@@ -3750,8 +3751,11 @@
public static final int DolbyVisionProfileDvheStn = 0x20;
public static final int DolbyVisionProfileDvheDth = 0x40;
public static final int DolbyVisionProfileDvheDtb = 0x80;
- public static final int DolbyVisionProfileDvheSt = 0x100;
- public static final int DolbyVisionProfileDvavSe = 0x200;
+ public static final int DolbyVisionProfileDvheSt = 0x100;
+ public static final int DolbyVisionProfileDvavSe = 0x200;
+ /** Dolby Vision AV1 profile */
+ @SuppressLint("AllUpper")
+ public static final int DolbyVisionProfileDvav110 = 0x400;
public static final int DolbyVisionLevelHd24 = 0x1;
public static final int DolbyVisionLevelHd30 = 0x2;
diff --git a/media/java/android/media/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java
index e8e0f82..58deff2 100644
--- a/media/java/android/media/MediaRoute2ProviderService.java
+++ b/media/java/android/media/MediaRoute2ProviderService.java
@@ -81,6 +81,20 @@
public abstract void onControlRequest(String routeId, Intent request);
/**
+ * Called when requestSetVolume is called on a route of the provider
+ * @param routeId the id of the route
+ * @param volume the target volume
+ */
+ public abstract void onSetVolume(String routeId, int volume);
+
+ /**
+ * Called when requestUpdateVolume is called on a route of the provider
+ * @param routeId id of the route
+ * @param delta the delta to add to the current volume
+ */
+ public abstract void onUpdateVolume(String routeId, int delta);
+
+ /**
* Updates provider info and publishes routes
*/
public final void setProviderInfo(MediaRoute2ProviderInfo info) {
@@ -130,5 +144,17 @@
mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onControlRequest,
MediaRoute2ProviderService.this, id, request));
}
+
+ @Override
+ public void requestSetVolume(String id, int volume) {
+ mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onSetVolume,
+ MediaRoute2ProviderService.this, id, volume));
+ }
+
+ @Override
+ public void requestUpdateVolume(String id, int delta) {
+ mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onUpdateVolume,
+ MediaRoute2ProviderService.this, id, delta));
+ }
}
}
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index ed35ef6..aca40d8 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -265,6 +265,54 @@
}
}
+ /**
+ * Requests a volume change for the route asynchronously.
+ * <p>
+ * It may have no effect if the route is currently not selected.
+ * </p>
+ *
+ * @param volume The new volume value between 0 and {@link MediaRoute2Info#getVolumeMax}.
+ */
+ public void requestSetVolume(@NonNull MediaRoute2Info route, int volume) {
+ Objects.requireNonNull(route, "route must not be null");
+
+ Client client;
+ synchronized (sLock) {
+ client = mClient;
+ }
+ if (client != null) {
+ try {
+ mMediaRouterService.requestSetVolume2(client, route, volume);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Unable to send control request.", ex);
+ }
+ }
+ }
+
+ /**
+ * Requests an incremental volume update for the route asynchronously.
+ * <p>
+ * It may have no effect if the route is currently not selected.
+ * </p>
+ *
+ * @param delta The delta to add to the current volume.
+ */
+ public void requestUpdateVolume(@NonNull MediaRoute2Info route, int delta) {
+ Objects.requireNonNull(route, "route must not be null");
+
+ Client client;
+ synchronized (sLock) {
+ client = mClient;
+ }
+ if (client != null) {
+ try {
+ mMediaRouterService.requestUpdateVolume2(client, route, delta);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Unable to send control request.", ex);
+ }
+ }
+ }
+
@GuardedBy("mCallbackRecords")
private int findCallbackRecordIndexLocked(Callback callback) {
final int count = mCallbackRecords.size();
@@ -310,6 +358,7 @@
List<MediaRoute2Info> outRoutes) {
if (provider == null || !provider.isValid()) {
Log.w(TAG, "Ignoring invalid provider : " + provider);
+ return;
}
final Collection<MediaRoute2Info> routes = provider.getRoutes();
@@ -321,10 +370,21 @@
if (!route.supportsControlCategory(controlCategories)) {
continue;
}
+ MediaRoute2Info preRoute = findRouteById(route.getId());
+ if (!route.equals(preRoute)) {
+ notifyRouteChanged(route);
+ }
outRoutes.add(route);
}
}
+ MediaRoute2Info findRouteById(String id) {
+ for (MediaRoute2Info route : mRoutes) {
+ if (route.getId().equals(id)) return route;
+ }
+ return null;
+ }
+
void notifyRouteListChanged(List<MediaRoute2Info> routes) {
for (CallbackRecord record: mCallbackRecords) {
record.mExecutor.execute(
@@ -332,10 +392,18 @@
}
}
+ void notifyRouteChanged(MediaRoute2Info route) {
+ for (CallbackRecord record: mCallbackRecords) {
+ record.mExecutor.execute(
+ () -> record.mCallback.onRouteChanged(route));
+ }
+ }
+
/**
* Interface for receiving events about media routing changes.
*/
public static class Callback {
+ //TODO: clean up these callbacks
/**
* Called when a route is added.
*/
@@ -369,7 +437,7 @@
void notifyRoutes() {
final List<MediaRoute2Info> routes = mRoutes;
// notify only when bound to media router service.
- //TODO: Correct the condition when control category, default rotue, .. are finalized.
+ //TODO: Correct the condition when control category, default route, .. are finalized.
if (routes.size() > 0) {
mExecutor.execute(() -> mCallback.onRoutesChanged(routes));
}
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index 0b64569..4f2a295 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -234,6 +234,54 @@
}
}
+ /**
+ * Requests a volume change for the route asynchronously.
+ * <p>
+ * It may have no effect if the route is currently not selected.
+ * </p>
+ *
+ * @param volume The new volume value between 0 and {@link MediaRoute2Info#getVolumeMax}.
+ */
+ public void requestSetVolume(@NonNull MediaRoute2Info route, int volume) {
+ Objects.requireNonNull(route, "route must not be null");
+
+ Client client;
+ synchronized (sLock) {
+ client = mClient;
+ }
+ if (client != null) {
+ try {
+ mMediaRouterService.requestSetVolume2Manager(client, route, volume);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Unable to send control request.", ex);
+ }
+ }
+ }
+
+ /**
+ * Requests an incremental volume update for the route asynchronously.
+ * <p>
+ * It may have no effect if the route is currently not selected.
+ * </p>
+ *
+ * @param delta The delta to add to the current volume.
+ */
+ public void requestUpdateVolume(@NonNull MediaRoute2Info route, int delta) {
+ Objects.requireNonNull(route, "route must not be null");
+
+ Client client;
+ synchronized (sLock) {
+ client = mClient;
+ }
+ if (client != null) {
+ try {
+ mMediaRouterService.requestUpdateVolume2Manager(client, route, delta);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Unable to send control request.", ex);
+ }
+ }
+ }
+
int findProviderIndex(MediaRoute2ProviderInfo provider) {
final int count = mProviders.size();
for (int i = 0; i < count; i++) {
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 771628c..ca96c9a 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -17,106 +17,14 @@
package android.media;
import android.annotation.UnsupportedAppUsage;
-import android.content.ContentProviderClient;
-import android.content.ContentResolver;
-import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
-import android.content.SharedPreferences;
-import android.database.Cursor;
-import android.database.SQLException;
-import android.drm.DrmManagerClient;
-import android.graphics.BitmapFactory;
-import android.mtp.MtpConstants;
import android.net.Uri;
import android.os.Build;
-import android.os.Environment;
import android.os.RemoteException;
-import android.os.SystemProperties;
-import android.provider.MediaStore;
-import android.provider.MediaStore.Audio;
-import android.provider.MediaStore.Audio.Playlists;
-import android.provider.MediaStore.Files;
-import android.provider.MediaStore.Files.FileColumns;
-import android.provider.MediaStore.Images;
-import android.provider.MediaStore.Video;
-import android.provider.Settings;
-import android.provider.Settings.SettingNotFoundException;
-import android.sax.Element;
-import android.sax.ElementListener;
-import android.sax.RootElement;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.text.TextUtils;
-import android.util.Log;
-import android.util.Xml;
-
-import dalvik.system.CloseGuard;
-
-import org.xml.sax.Attributes;
-import org.xml.sax.ContentHandler;
-import org.xml.sax.SAXException;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Locale;
-import java.util.TimeZone;
-import java.util.concurrent.atomic.AtomicBoolean;
/**
- * Internal service helper that no-one should use directly.
- *
- * The way the scan currently works is:
- * - The Java MediaScannerService creates a MediaScanner (this class), and calls
- * MediaScanner.scanDirectories on it.
- * - scanDirectories() calls the native processDirectory() for each of the specified directories.
- * - the processDirectory() JNI method wraps the provided mediascanner client in a native
- * 'MyMediaScannerClient' class, then calls processDirectory() on the native MediaScanner
- * object (which got created when the Java MediaScanner was created).
- * - native MediaScanner.processDirectory() calls
- * doProcessDirectory(), which recurses over the folder, and calls
- * native MyMediaScannerClient.scanFile() for every file whose extension matches.
- * - native MyMediaScannerClient.scanFile() calls back on Java MediaScannerClient.scanFile,
- * which calls doScanFile, which after some setup calls back down to native code, calling
- * MediaScanner.processFile().
- * - MediaScanner.processFile() calls one of several methods, depending on the type of the
- * file: parseMP3, parseMP4, parseMidi, parseOgg or parseWMA.
- * - each of these methods gets metadata key/value pairs from the file, and repeatedly
- * calls native MyMediaScannerClient.handleStringTag, which calls back up to its Java
- * counterparts in this file.
- * - Java handleStringTag() gathers the key/value pairs that it's interested in.
- * - once processFile returns and we're back in Java code in doScanFile(), it calls
- * Java MyMediaScannerClient.endFile(), which takes all the data that's been
- * gathered and inserts an entry in to the database.
- *
- * In summary:
- * Java MediaScannerService calls
- * Java MediaScanner scanDirectories, which calls
- * Java MediaScanner processDirectory (native method), which calls
- * native MediaScanner processDirectory, which calls
- * native MyMediaScannerClient scanFile, which calls
- * Java MyMediaScannerClient scanFile, which calls
- * Java MediaScannerClient doScanFile, which calls
- * Java MediaScanner processFile (native method), which calls
- * native MediaScanner processFile, which calls
- * native parseMP3, parseMP4, parseMidi, parseOgg or parseWMA, which calls
- * native MyMediaScanner handleStringTag, which calls
- * Java MyMediaScanner handleStringTag.
- * Once MediaScanner processFile returns, an entry is inserted in to the database.
- *
- * The MediaScanner class is not thread-safe, so it should only be used in a single threaded manner.
- *
- * {@hide}
- *
+ * @hide
* @deprecated this media scanner has served faithfully for many years, but it's
* become tedious to test and maintain, mainly due to the way it
* weaves obscurely between managed and native code. It's been
@@ -125,1876 +33,182 @@
*/
@Deprecated
public class MediaScanner implements AutoCloseable {
- static {
- System.loadLibrary("media_jni");
- native_init();
- }
-
- private final static String TAG = "MediaScanner";
-
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private static final String[] FILES_PRESCAN_PROJECTION = new String[] {
- Files.FileColumns._ID, // 0
- Files.FileColumns.DATA, // 1
- Files.FileColumns.FORMAT, // 2
- Files.FileColumns.DATE_MODIFIED, // 3
- Files.FileColumns.MEDIA_TYPE, // 4
};
- private static final String[] ID_PROJECTION = new String[] {
- Files.FileColumns._ID,
- };
-
- private static final int FILES_PRESCAN_ID_COLUMN_INDEX = 0;
- private static final int FILES_PRESCAN_PATH_COLUMN_INDEX = 1;
- private static final int FILES_PRESCAN_FORMAT_COLUMN_INDEX = 2;
- private static final int FILES_PRESCAN_DATE_MODIFIED_COLUMN_INDEX = 3;
- private static final int FILES_PRESCAN_MEDIA_TYPE_COLUMN_INDEX = 4;
-
- private static final String[] PLAYLIST_MEMBERS_PROJECTION = new String[] {
- Audio.Playlists.Members.PLAYLIST_ID, // 0
- };
-
- private static final int ID_PLAYLISTS_COLUMN_INDEX = 0;
- private static final int PATH_PLAYLISTS_COLUMN_INDEX = 1;
- private static final int DATE_MODIFIED_PLAYLISTS_COLUMN_INDEX = 2;
-
- private static final String RINGTONES_DIR = "/ringtones/";
- private static final String NOTIFICATIONS_DIR = "/notifications/";
- private static final String ALARMS_DIR = "/alarms/";
- private static final String MUSIC_DIR = "/music/";
- private static final String PODCASTS_DIR = "/podcasts/";
- private static final String AUDIOBOOKS_DIR = "/audiobooks/";
-
- public static final String SCANNED_BUILD_PREFS_NAME = "MediaScanBuild";
- public static final String LAST_INTERNAL_SCAN_FINGERPRINT = "lastScanFingerprint";
- private static final String SYSTEM_SOUNDS_DIR = Environment.getRootDirectory() + "/media/audio";
- private static final String OEM_SOUNDS_DIR = Environment.getOemDirectory() + "/media/audio";
- private static final String PRODUCT_SOUNDS_DIR = Environment.getProductDirectory() + "/media/audio";
- private static String sLastInternalScanFingerprint;
-
- private static final String[] ID3_GENRES = {
- // ID3v1 Genres
- "Blues",
- "Classic Rock",
- "Country",
- "Dance",
- "Disco",
- "Funk",
- "Grunge",
- "Hip-Hop",
- "Jazz",
- "Metal",
- "New Age",
- "Oldies",
- "Other",
- "Pop",
- "R&B",
- "Rap",
- "Reggae",
- "Rock",
- "Techno",
- "Industrial",
- "Alternative",
- "Ska",
- "Death Metal",
- "Pranks",
- "Soundtrack",
- "Euro-Techno",
- "Ambient",
- "Trip-Hop",
- "Vocal",
- "Jazz+Funk",
- "Fusion",
- "Trance",
- "Classical",
- "Instrumental",
- "Acid",
- "House",
- "Game",
- "Sound Clip",
- "Gospel",
- "Noise",
- "AlternRock",
- "Bass",
- "Soul",
- "Punk",
- "Space",
- "Meditative",
- "Instrumental Pop",
- "Instrumental Rock",
- "Ethnic",
- "Gothic",
- "Darkwave",
- "Techno-Industrial",
- "Electronic",
- "Pop-Folk",
- "Eurodance",
- "Dream",
- "Southern Rock",
- "Comedy",
- "Cult",
- "Gangsta",
- "Top 40",
- "Christian Rap",
- "Pop/Funk",
- "Jungle",
- "Native American",
- "Cabaret",
- "New Wave",
- "Psychadelic",
- "Rave",
- "Showtunes",
- "Trailer",
- "Lo-Fi",
- "Tribal",
- "Acid Punk",
- "Acid Jazz",
- "Polka",
- "Retro",
- "Musical",
- "Rock & Roll",
- "Hard Rock",
- // The following genres are Winamp extensions
- "Folk",
- "Folk-Rock",
- "National Folk",
- "Swing",
- "Fast Fusion",
- "Bebob",
- "Latin",
- "Revival",
- "Celtic",
- "Bluegrass",
- "Avantgarde",
- "Gothic Rock",
- "Progressive Rock",
- "Psychedelic Rock",
- "Symphonic Rock",
- "Slow Rock",
- "Big Band",
- "Chorus",
- "Easy Listening",
- "Acoustic",
- "Humour",
- "Speech",
- "Chanson",
- "Opera",
- "Chamber Music",
- "Sonata",
- "Symphony",
- "Booty Bass",
- "Primus",
- "Porn Groove",
- "Satire",
- "Slow Jam",
- "Club",
- "Tango",
- "Samba",
- "Folklore",
- "Ballad",
- "Power Ballad",
- "Rhythmic Soul",
- "Freestyle",
- "Duet",
- "Punk Rock",
- "Drum Solo",
- "A capella",
- "Euro-House",
- "Dance Hall",
- // The following ones seem to be fairly widely supported as well
- "Goa",
- "Drum & Bass",
- "Club-House",
- "Hardcore",
- "Terror",
- "Indie",
- "Britpop",
- null,
- "Polsk Punk",
- "Beat",
- "Christian Gangsta",
- "Heavy Metal",
- "Black Metal",
- "Crossover",
- "Contemporary Christian",
- "Christian Rock",
- "Merengue",
- "Salsa",
- "Thrash Metal",
- "Anime",
- "JPop",
- "Synthpop",
- // 148 and up don't seem to have been defined yet.
- };
-
- private long mNativeContext;
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private final Context mContext;
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private final String mPackageName;
- private final String mVolumeName;
- private final ContentProviderClient mMediaProvider;
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private final Uri mAudioUri;
- private final Uri mVideoUri;
- private final Uri mImagesUri;
- private final Uri mPlaylistsUri;
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private final Uri mFilesUri;
- private final Uri mFilesFullUri;
- private final boolean mProcessPlaylists;
- private final boolean mProcessGenres;
- private int mMtpObjectHandle;
- private final AtomicBoolean mClosed = new AtomicBoolean();
- private final CloseGuard mCloseGuard = CloseGuard.get();
-
- /** whether to use bulk inserts or individual inserts for each item */
- private static final boolean ENABLE_BULK_INSERTS = true;
-
- // used when scanning the image database so we know whether we have to prune
- // old thumbnail files
- private int mOriginalCount;
- /** Whether the scanner has set a default sound for the ringer ringtone. */
- private boolean mDefaultRingtoneSet;
- /** Whether the scanner has set a default sound for the notification ringtone. */
- private boolean mDefaultNotificationSet;
- /** Whether the scanner has set a default sound for the alarm ringtone. */
- private boolean mDefaultAlarmSet;
- /** The filename for the default sound for the ringer ringtone. */
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private String mDefaultRingtoneFilename;
- /** The filename for the default sound for the notification ringtone. */
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private String mDefaultNotificationFilename;
- /** The filename for the default sound for the alarm ringtone. */
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private String mDefaultAlarmAlertFilename;
- /**
- * The prefix for system properties that define the default sound for
- * ringtones. Concatenate the name of the setting from Settings
- * to get the full system property.
- */
- private static final String DEFAULT_RINGTONE_PROPERTY_PREFIX = "ro.config.";
-
- private final BitmapFactory.Options mBitmapOptions = new BitmapFactory.Options();
private static class FileEntry {
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
long mRowId;
- String mPath;
- long mLastModified;
- int mFormat;
- int mMediaType;
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
boolean mLastModifiedChanged;
- /** @deprecated kept intact for lame apps using reflection */
@Deprecated
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
FileEntry(long rowId, String path, long lastModified, int format) {
- this(rowId, path, lastModified, format, FileColumns.MEDIA_TYPE_NONE);
- }
-
- FileEntry(long rowId, String path, long lastModified, int format, int mediaType) {
- mRowId = rowId;
- mPath = path;
- mLastModified = lastModified;
- mFormat = format;
- mMediaType = mediaType;
- mLastModifiedChanged = false;
- }
-
- @Override
- public String toString() {
- return mPath + " mRowId: " + mRowId;
+ throw new UnsupportedOperationException();
}
}
- private static class PlaylistEntry {
- String path;
- long bestmatchid;
- int bestmatchlevel;
- }
-
- private final ArrayList<PlaylistEntry> mPlaylistEntries = new ArrayList<>();
- private final ArrayList<FileEntry> mPlayLists = new ArrayList<>();
-
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private MediaInserter mMediaInserter;
- private DrmManagerClient mDrmManagerClient = null;
-
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
public MediaScanner(Context c, String volumeName) {
- native_setup();
- mContext = c;
- mPackageName = c.getPackageName();
- mVolumeName = volumeName;
-
- mBitmapOptions.inSampleSize = 1;
- mBitmapOptions.inJustDecodeBounds = true;
-
- setDefaultRingtoneFileNames();
-
- mMediaProvider = mContext.getContentResolver()
- .acquireContentProviderClient(MediaStore.AUTHORITY);
-
- if (sLastInternalScanFingerprint == null) {
- final SharedPreferences scanSettings =
- mContext.getSharedPreferences(SCANNED_BUILD_PREFS_NAME, Context.MODE_PRIVATE);
- sLastInternalScanFingerprint =
- scanSettings.getString(LAST_INTERNAL_SCAN_FINGERPRINT, new String());
- }
-
- mAudioUri = Audio.Media.getContentUri(volumeName);
- mVideoUri = Video.Media.getContentUri(volumeName);
- mImagesUri = Images.Media.getContentUri(volumeName);
- mFilesUri = Files.getContentUri(volumeName);
-
- Uri filesFullUri = mFilesUri.buildUpon().appendQueryParameter("nonotify", "1").build();
- filesFullUri = MediaStore.setIncludePending(filesFullUri);
- filesFullUri = MediaStore.setIncludeTrashed(filesFullUri);
- mFilesFullUri = filesFullUri;
-
- if (!volumeName.equals("internal")) {
- // we only support playlists on external media
- mProcessPlaylists = true;
- mProcessGenres = true;
- mPlaylistsUri = Playlists.getContentUri(volumeName);
- } else {
- mProcessPlaylists = false;
- mProcessGenres = false;
- mPlaylistsUri = null;
- }
-
- final Locale locale = mContext.getResources().getConfiguration().locale;
- if (locale != null) {
- String language = locale.getLanguage();
- String country = locale.getCountry();
- if (language != null) {
- if (country != null) {
- setLocale(language + "_" + country);
- } else {
- setLocale(language);
- }
- }
- }
-
- mCloseGuard.open("close");
+ throw new UnsupportedOperationException();
}
- private void setDefaultRingtoneFileNames() {
- mDefaultRingtoneFilename = SystemProperties.get(DEFAULT_RINGTONE_PROPERTY_PREFIX
- + Settings.System.RINGTONE);
- mDefaultNotificationFilename = SystemProperties.get(DEFAULT_RINGTONE_PROPERTY_PREFIX
- + Settings.System.NOTIFICATION_SOUND);
- mDefaultAlarmAlertFilename = SystemProperties.get(DEFAULT_RINGTONE_PROPERTY_PREFIX
- + Settings.System.ALARM_ALERT);
- }
-
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private final MyMediaScannerClient mClient = new MyMediaScannerClient();
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private boolean isDrmEnabled() {
- String prop = SystemProperties.get("drm.service.enabled");
- return prop != null && prop.equals("true");
+ throw new UnsupportedOperationException();
}
private class MyMediaScannerClient implements MediaScannerClient {
-
- private final SimpleDateFormat mDateFormatter;
-
- private String mArtist;
- private String mAlbumArtist; // use this if mArtist is missing
- private String mAlbum;
- private String mTitle;
- private String mComposer;
- private String mGenre;
- @UnsupportedAppUsage
- private String mMimeType;
- /** @deprecated file types no longer exist */
@Deprecated
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
+ private String mMimeType;
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private int mFileType;
- private int mTrack;
- private int mYear;
- private int mDuration;
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private String mPath;
- private long mDate;
- private long mLastModified;
- private long mFileSize;
- private String mWriter;
- private int mCompilation;
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private boolean mIsDrm;
- @UnsupportedAppUsage
- private boolean mNoMedia; // flag to suppress file from appearing in media tables
- private boolean mScanSuccess;
- private int mWidth;
- private int mHeight;
- private int mColorStandard;
- private int mColorTransfer;
- private int mColorRange;
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
+ private boolean mNoMedia;
public MyMediaScannerClient() {
- mDateFormatter = new SimpleDateFormat("yyyyMMdd'T'HHmmss");
- mDateFormatter.setTimeZone(TimeZone.getTimeZone("UTC"));
+ throw new UnsupportedOperationException();
}
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
public FileEntry beginFile(String path, String mimeType, long lastModified,
long fileSize, boolean isDirectory, boolean noMedia) {
- mMimeType = mimeType;
- mFileSize = fileSize;
- mIsDrm = false;
- mScanSuccess = true;
-
- if (!isDirectory) {
- if (!noMedia && isNoMediaFile(path)) {
- noMedia = true;
- }
- mNoMedia = noMedia;
-
- // if mimeType was not specified, compute file type based on file extension.
- if (mMimeType == null) {
- mMimeType = MediaFile.getMimeTypeForFile(path);
- }
-
- if (isDrmEnabled() && MediaFile.isDrmMimeType(mMimeType)) {
- getMimeTypeFromDrm(path);
- }
- }
-
- FileEntry entry = makeEntryFor(path);
- // add some slack to avoid a rounding error
- long delta = (entry != null) ? (lastModified - entry.mLastModified) : 0;
- boolean wasModified = delta > 1 || delta < -1;
- if (entry == null || wasModified) {
- if (wasModified) {
- entry.mLastModified = lastModified;
- } else {
- entry = new FileEntry(0, path, lastModified,
- (isDirectory ? MtpConstants.FORMAT_ASSOCIATION : 0),
- FileColumns.MEDIA_TYPE_NONE);
- }
- entry.mLastModifiedChanged = true;
- }
-
- if (mProcessPlaylists && MediaFile.isPlayListMimeType(mMimeType)) {
- mPlayLists.add(entry);
- // we don't process playlists in the main scan, so return null
- return null;
- }
-
- // clear all the metadata
- mArtist = null;
- mAlbumArtist = null;
- mAlbum = null;
- mTitle = null;
- mComposer = null;
- mGenre = null;
- mTrack = 0;
- mYear = 0;
- mDuration = 0;
- mPath = path;
- mDate = 0;
- mLastModified = lastModified;
- mWriter = null;
- mCompilation = 0;
- mWidth = 0;
- mHeight = 0;
- mColorStandard = -1;
- mColorTransfer = -1;
- mColorRange = -1;
-
- return entry;
+ throw new UnsupportedOperationException();
}
- @Override
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
public void scanFile(String path, long lastModified, long fileSize,
boolean isDirectory, boolean noMedia) {
- // This is the callback funtion from native codes.
- // Log.v(TAG, "scanFile: "+path);
- doScanFile(path, null, lastModified, fileSize, isDirectory, false, noMedia);
+ throw new UnsupportedOperationException();
}
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
public Uri doScanFile(String path, String mimeType, long lastModified,
long fileSize, boolean isDirectory, boolean scanAlways, boolean noMedia) {
- Uri result = null;
-// long t1 = System.currentTimeMillis();
- try {
- FileEntry entry = beginFile(path, mimeType, lastModified,
- fileSize, isDirectory, noMedia);
-
- if (entry == null) {
- return null;
- }
-
- // if this file was just inserted via mtp, set the rowid to zero
- // (even though it already exists in the database), to trigger
- // the correct code path for updating its entry
- if (mMtpObjectHandle != 0) {
- entry.mRowId = 0;
- }
-
- if (entry.mPath != null) {
- if (((!mDefaultNotificationSet &&
- doesPathHaveFilename(entry.mPath, mDefaultNotificationFilename))
- || (!mDefaultRingtoneSet &&
- doesPathHaveFilename(entry.mPath, mDefaultRingtoneFilename))
- || (!mDefaultAlarmSet &&
- doesPathHaveFilename(entry.mPath, mDefaultAlarmAlertFilename)))) {
- Log.w(TAG, "forcing rescan of " + entry.mPath +
- "since ringtone setting didn't finish");
- scanAlways = true;
- } else if (isSystemSoundWithMetadata(entry.mPath)
- && !Build.FINGERPRINT.equals(sLastInternalScanFingerprint)) {
- // file is located on the system partition where the date cannot be trusted:
- // rescan if the build fingerprint has changed since the last scan.
- Log.i(TAG, "forcing rescan of " + entry.mPath
- + " since build fingerprint changed");
- scanAlways = true;
- }
- }
-
- // rescan for metadata if file was modified since last scan
- if (entry != null && (entry.mLastModifiedChanged || scanAlways)) {
- if (noMedia) {
- result = endFile(entry, false, false, false, false, false, false);
- } else {
- boolean isaudio = MediaFile.isAudioMimeType(mMimeType);
- boolean isvideo = MediaFile.isVideoMimeType(mMimeType);
- boolean isimage = MediaFile.isImageMimeType(mMimeType);
-
- if (isaudio || isvideo || isimage) {
- path = Environment.maybeTranslateEmulatedPathToInternal(new File(path))
- .getAbsolutePath();
- }
-
- // we only extract metadata for audio and video files
- if (isaudio || isvideo) {
- mScanSuccess = processFile(path, mimeType, this);
- }
-
- if (isimage) {
- mScanSuccess = processImageFile(path);
- }
-
- String lowpath = path.toLowerCase(Locale.ROOT);
- boolean ringtones = mScanSuccess && (lowpath.indexOf(RINGTONES_DIR) > 0);
- boolean notifications = mScanSuccess &&
- (lowpath.indexOf(NOTIFICATIONS_DIR) > 0);
- boolean alarms = mScanSuccess && (lowpath.indexOf(ALARMS_DIR) > 0);
- boolean podcasts = mScanSuccess && (lowpath.indexOf(PODCASTS_DIR) > 0);
- boolean audiobooks = mScanSuccess && (lowpath.indexOf(AUDIOBOOKS_DIR) > 0);
- boolean music = mScanSuccess && ((lowpath.indexOf(MUSIC_DIR) > 0) ||
- (!ringtones && !notifications && !alarms && !podcasts && !audiobooks));
-
- result = endFile(entry, ringtones, notifications, alarms, podcasts,
- audiobooks, music);
- }
- }
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in MediaScanner.scanFile()", e);
- }
-// long t2 = System.currentTimeMillis();
-// Log.v(TAG, "scanFile: " + path + " took " + (t2-t1));
- return result;
+ throw new UnsupportedOperationException();
}
- private long parseDate(String date) {
- try {
- return mDateFormatter.parse(date).getTime();
- } catch (ParseException e) {
- return 0;
- }
- }
-
- private int parseSubstring(String s, int start, int defaultValue) {
- int length = s.length();
- if (start == length) return defaultValue;
-
- char ch = s.charAt(start++);
- // return defaultValue if we have no integer at all
- if (ch < '0' || ch > '9') return defaultValue;
-
- int result = ch - '0';
- while (start < length) {
- ch = s.charAt(start++);
- if (ch < '0' || ch > '9') return result;
- result = result * 10 + (ch - '0');
- }
-
- return result;
- }
-
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
public void handleStringTag(String name, String value) {
- if (name.equalsIgnoreCase("title") || name.startsWith("title;")) {
- // Don't trim() here, to preserve the special \001 character
- // used to force sorting. The media provider will trim() before
- // inserting the title in to the database.
- mTitle = value;
- } else if (name.equalsIgnoreCase("artist") || name.startsWith("artist;")) {
- mArtist = value.trim();
- } else if (name.equalsIgnoreCase("albumartist") || name.startsWith("albumartist;")
- || name.equalsIgnoreCase("band") || name.startsWith("band;")) {
- mAlbumArtist = value.trim();
- } else if (name.equalsIgnoreCase("album") || name.startsWith("album;")) {
- mAlbum = value.trim();
- } else if (name.equalsIgnoreCase("composer") || name.startsWith("composer;")) {
- mComposer = value.trim();
- } else if (mProcessGenres &&
- (name.equalsIgnoreCase("genre") || name.startsWith("genre;"))) {
- mGenre = getGenreName(value);
- } else if (name.equalsIgnoreCase("year") || name.startsWith("year;")) {
- mYear = parseSubstring(value, 0, 0);
- } else if (name.equalsIgnoreCase("tracknumber") || name.startsWith("tracknumber;")) {
- // track number might be of the form "2/12"
- // we just read the number before the slash
- int num = parseSubstring(value, 0, 0);
- mTrack = (mTrack / 1000) * 1000 + num;
- } else if (name.equalsIgnoreCase("discnumber") ||
- name.equals("set") || name.startsWith("set;")) {
- // set number might be of the form "1/3"
- // we just read the number before the slash
- int num = parseSubstring(value, 0, 0);
- mTrack = (num * 1000) + (mTrack % 1000);
- } else if (name.equalsIgnoreCase("duration")) {
- mDuration = parseSubstring(value, 0, 0);
- } else if (name.equalsIgnoreCase("writer") || name.startsWith("writer;")) {
- mWriter = value.trim();
- } else if (name.equalsIgnoreCase("compilation")) {
- mCompilation = parseSubstring(value, 0, 0);
- } else if (name.equalsIgnoreCase("isdrm")) {
- mIsDrm = (parseSubstring(value, 0, 0) == 1);
- } else if (name.equalsIgnoreCase("date")) {
- mDate = parseDate(value);
- } else if (name.equalsIgnoreCase("width")) {
- mWidth = parseSubstring(value, 0, 0);
- } else if (name.equalsIgnoreCase("height")) {
- mHeight = parseSubstring(value, 0, 0);
- } else if (name.equalsIgnoreCase("colorstandard")) {
- mColorStandard = parseSubstring(value, 0, -1);
- } else if (name.equalsIgnoreCase("colortransfer")) {
- mColorTransfer = parseSubstring(value, 0, -1);
- } else if (name.equalsIgnoreCase("colorrange")) {
- mColorRange = parseSubstring(value, 0, -1);
- } else {
- //Log.v(TAG, "unknown tag: " + name + " (" + mProcessGenres + ")");
- }
+ throw new UnsupportedOperationException();
}
- private boolean convertGenreCode(String input, String expected) {
- String output = getGenreName(input);
- if (output.equals(expected)) {
- return true;
- } else {
- Log.d(TAG, "'" + input + "' -> '" + output + "', expected '" + expected + "'");
- return false;
- }
- }
- private void testGenreNameConverter() {
- convertGenreCode("2", "Country");
- convertGenreCode("(2)", "Country");
- convertGenreCode("(2", "(2");
- convertGenreCode("2 Foo", "Country");
- convertGenreCode("(2) Foo", "Country");
- convertGenreCode("(2 Foo", "(2 Foo");
- convertGenreCode("2Foo", "2Foo");
- convertGenreCode("(2)Foo", "Country");
- convertGenreCode("200 Foo", "Foo");
- convertGenreCode("(200) Foo", "Foo");
- convertGenreCode("200Foo", "200Foo");
- convertGenreCode("(200)Foo", "Foo");
- convertGenreCode("200)Foo", "200)Foo");
- convertGenreCode("200) Foo", "200) Foo");
- }
-
- public String getGenreName(String genreTagValue) {
-
- if (genreTagValue == null) {
- return null;
- }
- final int length = genreTagValue.length();
-
- if (length > 0) {
- boolean parenthesized = false;
- StringBuffer number = new StringBuffer();
- int i = 0;
- for (; i < length; ++i) {
- char c = genreTagValue.charAt(i);
- if (i == 0 && c == '(') {
- parenthesized = true;
- } else if (Character.isDigit(c)) {
- number.append(c);
- } else {
- break;
- }
- }
- char charAfterNumber = i < length ? genreTagValue.charAt(i) : ' ';
- if ((parenthesized && charAfterNumber == ')')
- || !parenthesized && Character.isWhitespace(charAfterNumber)) {
- try {
- short genreIndex = Short.parseShort(number.toString());
- if (genreIndex >= 0) {
- if (genreIndex < ID3_GENRES.length && ID3_GENRES[genreIndex] != null) {
- return ID3_GENRES[genreIndex];
- } else if (genreIndex == 0xFF) {
- return null;
- } else if (genreIndex < 0xFF && (i + 1) < length) {
- // genre is valid but unknown,
- // if there is a string after the value we take it
- if (parenthesized && charAfterNumber == ')') {
- i++;
- }
- String ret = genreTagValue.substring(i).trim();
- if (ret.length() != 0) {
- return ret;
- }
- } else {
- // else return the number, without parentheses
- return number.toString();
- }
- }
- } catch (NumberFormatException e) {
- }
- }
- }
-
- return genreTagValue;
- }
-
- private boolean processImageFile(String path) {
- try {
- mBitmapOptions.outWidth = 0;
- mBitmapOptions.outHeight = 0;
- BitmapFactory.decodeFile(path, mBitmapOptions);
- mWidth = mBitmapOptions.outWidth;
- mHeight = mBitmapOptions.outHeight;
- return mWidth > 0 && mHeight > 0;
- } catch (Throwable th) {
- // ignore;
- }
- return false;
- }
-
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
public void setMimeType(String mimeType) {
- if ("audio/mp4".equals(mMimeType) &&
- mimeType.startsWith("video")) {
- // for feature parity with Donut, we force m4a files to keep the
- // audio/mp4 mimetype, even if they are really "enhanced podcasts"
- // with a video track
- return;
- }
- mMimeType = mimeType;
+ throw new UnsupportedOperationException();
}
- /**
- * Formats the data into a values array suitable for use with the Media
- * Content Provider.
- *
- * @return a map of values
- */
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private ContentValues toValues() {
- ContentValues map = new ContentValues();
-
- map.put(MediaStore.MediaColumns.DATA, mPath);
- map.put(MediaStore.MediaColumns.TITLE, mTitle);
- map.put(MediaStore.MediaColumns.DATE_MODIFIED, mLastModified);
- map.put(MediaStore.MediaColumns.SIZE, mFileSize);
- map.put(MediaStore.MediaColumns.MIME_TYPE, mMimeType);
- map.put(MediaStore.MediaColumns.IS_DRM, mIsDrm);
- map.putNull(MediaStore.MediaColumns.HASH);
-
- String resolution = null;
- if (mWidth > 0 && mHeight > 0) {
- map.put(MediaStore.MediaColumns.WIDTH, mWidth);
- map.put(MediaStore.MediaColumns.HEIGHT, mHeight);
- resolution = mWidth + "x" + mHeight;
- }
-
- if (!mNoMedia) {
- if (MediaFile.isVideoMimeType(mMimeType)) {
- map.put(Video.Media.ARTIST, (mArtist != null && mArtist.length() > 0
- ? mArtist : MediaStore.UNKNOWN_STRING));
- map.put(Video.Media.ALBUM, (mAlbum != null && mAlbum.length() > 0
- ? mAlbum : MediaStore.UNKNOWN_STRING));
- map.put(Video.Media.DURATION, mDuration);
- if (resolution != null) {
- map.put(Video.Media.RESOLUTION, resolution);
- }
- if (mColorStandard >= 0) {
- map.put(Video.Media.COLOR_STANDARD, mColorStandard);
- }
- if (mColorTransfer >= 0) {
- map.put(Video.Media.COLOR_TRANSFER, mColorTransfer);
- }
- if (mColorRange >= 0) {
- map.put(Video.Media.COLOR_RANGE, mColorRange);
- }
- if (mDate > 0) {
- map.put(Video.Media.DATE_TAKEN, mDate);
- }
- } else if (MediaFile.isImageMimeType(mMimeType)) {
- // FIXME - add DESCRIPTION
- } else if (MediaFile.isAudioMimeType(mMimeType)) {
- map.put(Audio.Media.ARTIST, (mArtist != null && mArtist.length() > 0) ?
- mArtist : MediaStore.UNKNOWN_STRING);
- map.put(Audio.Media.ALBUM_ARTIST, (mAlbumArtist != null &&
- mAlbumArtist.length() > 0) ? mAlbumArtist : null);
- map.put(Audio.Media.ALBUM, (mAlbum != null && mAlbum.length() > 0) ?
- mAlbum : MediaStore.UNKNOWN_STRING);
- map.put(Audio.Media.COMPOSER, mComposer);
- map.put(Audio.Media.GENRE, mGenre);
- if (mYear != 0) {
- map.put(Audio.Media.YEAR, mYear);
- }
- map.put(Audio.Media.TRACK, mTrack);
- map.put(Audio.Media.DURATION, mDuration);
- map.put(Audio.Media.COMPILATION, mCompilation);
- }
- }
- return map;
+ throw new UnsupportedOperationException();
}
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private Uri endFile(FileEntry entry, boolean ringtones, boolean notifications,
boolean alarms, boolean podcasts, boolean audiobooks, boolean music)
throws RemoteException {
- // update database
-
- // use album artist if artist is missing
- if (mArtist == null || mArtist.length() == 0) {
- mArtist = mAlbumArtist;
- }
-
- ContentValues values = toValues();
- String title = values.getAsString(MediaStore.MediaColumns.TITLE);
- if (title == null || TextUtils.isEmpty(title.trim())) {
- title = MediaFile.getFileTitle(values.getAsString(MediaStore.MediaColumns.DATA));
- values.put(MediaStore.MediaColumns.TITLE, title);
- }
- String album = values.getAsString(Audio.Media.ALBUM);
- if (MediaStore.UNKNOWN_STRING.equals(album)) {
- album = values.getAsString(MediaStore.MediaColumns.DATA);
- // extract last path segment before file name
- int lastSlash = album.lastIndexOf('/');
- if (lastSlash >= 0) {
- int previousSlash = 0;
- while (true) {
- int idx = album.indexOf('/', previousSlash + 1);
- if (idx < 0 || idx >= lastSlash) {
- break;
- }
- previousSlash = idx;
- }
- if (previousSlash != 0) {
- album = album.substring(previousSlash + 1, lastSlash);
- values.put(Audio.Media.ALBUM, album);
- }
- }
- }
- long rowId = entry.mRowId;
- if (MediaFile.isAudioMimeType(mMimeType) && (rowId == 0 || mMtpObjectHandle != 0)) {
- // Only set these for new entries. For existing entries, they
- // may have been modified later, and we want to keep the current
- // values so that custom ringtones still show up in the ringtone
- // picker.
- values.put(Audio.Media.IS_RINGTONE, ringtones);
- values.put(Audio.Media.IS_NOTIFICATION, notifications);
- values.put(Audio.Media.IS_ALARM, alarms);
- values.put(Audio.Media.IS_MUSIC, music);
- values.put(Audio.Media.IS_PODCAST, podcasts);
- values.put(Audio.Media.IS_AUDIOBOOK, audiobooks);
- } else if (MediaFile.isExifMimeType(mMimeType) && !mNoMedia) {
- ExifInterface exif = null;
- try {
- exif = new ExifInterface(entry.mPath);
- } catch (Exception ex) {
- // exif is null
- }
- if (exif != null) {
- long time = exif.getGpsDateTime();
- if (time != -1) {
- values.put(Images.Media.DATE_TAKEN, time);
- } else {
- // If no time zone information is available, we should consider using
- // EXIF local time as taken time if the difference between file time
- // and EXIF local time is not less than 1 Day, otherwise MediaProvider
- // will use file time as taken time.
- time = exif.getDateTime();
- if (time != -1 && Math.abs(mLastModified * 1000 - time) >= 86400000) {
- values.put(Images.Media.DATE_TAKEN, time);
- }
- }
-
- int orientation = exif.getAttributeInt(
- ExifInterface.TAG_ORIENTATION, -1);
- if (orientation != -1) {
- // We only recognize a subset of orientation tag values.
- int degree;
- switch(orientation) {
- case ExifInterface.ORIENTATION_ROTATE_90:
- degree = 90;
- break;
- case ExifInterface.ORIENTATION_ROTATE_180:
- degree = 180;
- break;
- case ExifInterface.ORIENTATION_ROTATE_270:
- degree = 270;
- break;
- default:
- degree = 0;
- break;
- }
- values.put(Images.Media.ORIENTATION, degree);
- }
- }
- }
-
- Uri tableUri = mFilesUri;
- int mediaType = FileColumns.MEDIA_TYPE_NONE;
- MediaInserter inserter = mMediaInserter;
- if (!mNoMedia) {
- if (MediaFile.isVideoMimeType(mMimeType)) {
- tableUri = mVideoUri;
- mediaType = FileColumns.MEDIA_TYPE_VIDEO;
- } else if (MediaFile.isImageMimeType(mMimeType)) {
- tableUri = mImagesUri;
- mediaType = FileColumns.MEDIA_TYPE_IMAGE;
- } else if (MediaFile.isAudioMimeType(mMimeType)) {
- tableUri = mAudioUri;
- mediaType = FileColumns.MEDIA_TYPE_AUDIO;
- } else if (MediaFile.isPlayListMimeType(mMimeType)) {
- tableUri = mPlaylistsUri;
- mediaType = FileColumns.MEDIA_TYPE_PLAYLIST;
- }
- }
- Uri result = null;
- boolean needToSetSettings = false;
- // Setting a flag in order not to use bulk insert for the file related with
- // notifications, ringtones, and alarms, because the rowId of the inserted file is
- // needed.
- if (notifications && !mDefaultNotificationSet) {
- if (TextUtils.isEmpty(mDefaultNotificationFilename) ||
- doesPathHaveFilename(entry.mPath, mDefaultNotificationFilename)) {
- needToSetSettings = true;
- }
- } else if (ringtones && !mDefaultRingtoneSet) {
- if (TextUtils.isEmpty(mDefaultRingtoneFilename) ||
- doesPathHaveFilename(entry.mPath, mDefaultRingtoneFilename)) {
- needToSetSettings = true;
- }
- } else if (alarms && !mDefaultAlarmSet) {
- if (TextUtils.isEmpty(mDefaultAlarmAlertFilename) ||
- doesPathHaveFilename(entry.mPath, mDefaultAlarmAlertFilename)) {
- needToSetSettings = true;
- }
- }
-
- if (rowId == 0) {
- if (mMtpObjectHandle != 0) {
- values.put(MediaStore.MediaColumns.MEDIA_SCANNER_NEW_OBJECT_ID, mMtpObjectHandle);
- }
- if (tableUri == mFilesUri) {
- int format = entry.mFormat;
- if (format == 0) {
- format = MediaFile.getFormatCode(entry.mPath, mMimeType);
- }
- values.put(Files.FileColumns.FORMAT, format);
- }
- // New file, insert it.
- // Directories need to be inserted before the files they contain, so they
- // get priority when bulk inserting.
- // If the rowId of the inserted file is needed, it gets inserted immediately,
- // bypassing the bulk inserter.
- if (inserter == null || needToSetSettings) {
- if (inserter != null) {
- inserter.flushAll();
- }
- result = mMediaProvider.insert(tableUri, values);
- } else if (entry.mFormat == MtpConstants.FORMAT_ASSOCIATION) {
- inserter.insertwithPriority(tableUri, values);
- } else {
- inserter.insert(tableUri, values);
- }
-
- if (result != null) {
- rowId = ContentUris.parseId(result);
- entry.mRowId = rowId;
- }
- } else {
- // updated file
- result = ContentUris.withAppendedId(tableUri, rowId);
- // path should never change, and we want to avoid replacing mixed cased paths
- // with squashed lower case paths
- values.remove(MediaStore.MediaColumns.DATA);
-
- if (!mNoMedia) {
- // Changing media type must be done as separate update
- if (mediaType != entry.mMediaType) {
- final ContentValues mediaTypeValues = new ContentValues();
- mediaTypeValues.put(FileColumns.MEDIA_TYPE, mediaType);
- mMediaProvider.update(ContentUris.withAppendedId(mFilesUri, rowId),
- mediaTypeValues, null, null);
- }
- }
-
- mMediaProvider.update(result, values, null, null);
- }
-
- if(needToSetSettings) {
- if (notifications) {
- setRingtoneIfNotSet(Settings.System.NOTIFICATION_SOUND, tableUri, rowId);
- mDefaultNotificationSet = true;
- } else if (ringtones) {
- setRingtoneIfNotSet(Settings.System.RINGTONE, tableUri, rowId);
- mDefaultRingtoneSet = true;
- } else if (alarms) {
- setRingtoneIfNotSet(Settings.System.ALARM_ALERT, tableUri, rowId);
- mDefaultAlarmSet = true;
- }
- }
-
- return result;
+ throw new UnsupportedOperationException();
}
- private boolean doesPathHaveFilename(String path, String filename) {
- int pathFilenameStart = path.lastIndexOf(File.separatorChar) + 1;
- int filenameLength = filename.length();
- return path.regionMatches(pathFilenameStart, filename, 0, filenameLength) &&
- pathFilenameStart + filenameLength == path.length();
- }
-
- private void setRingtoneIfNotSet(String settingName, Uri uri, long rowId) {
- if (wasRingtoneAlreadySet(settingName)) {
- return;
- }
-
- ContentResolver cr = mContext.getContentResolver();
- String existingSettingValue = Settings.System.getString(cr, settingName);
- if (TextUtils.isEmpty(existingSettingValue)) {
- final Uri settingUri = Settings.System.getUriFor(settingName);
- final Uri ringtoneUri = ContentUris.withAppendedId(uri, rowId);
- RingtoneManager.setActualDefaultRingtoneUri(mContext,
- RingtoneManager.getDefaultType(settingUri), ringtoneUri);
- }
- Settings.System.putInt(cr, settingSetIndicatorName(settingName), 1);
- }
-
- /** @deprecated file types no longer exist */
@Deprecated
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private int getFileTypeFromDrm(String path) {
- return 0;
- }
-
- private void getMimeTypeFromDrm(String path) {
- mMimeType = null;
-
- if (mDrmManagerClient == null) {
- mDrmManagerClient = new DrmManagerClient(mContext);
- }
-
- if (mDrmManagerClient.canHandle(path, null)) {
- mIsDrm = true;
- mMimeType = mDrmManagerClient.getOriginalMimeType(path);
- }
-
- if (mMimeType == null) {
- mMimeType = ContentResolver.MIME_TYPE_DEFAULT;
- }
- }
-
- }; // end of anonymous MediaScannerClient instance
-
- private static boolean isSystemSoundWithMetadata(String path) {
- if (path.startsWith(SYSTEM_SOUNDS_DIR + ALARMS_DIR)
- || path.startsWith(SYSTEM_SOUNDS_DIR + RINGTONES_DIR)
- || path.startsWith(SYSTEM_SOUNDS_DIR + NOTIFICATIONS_DIR)
- || path.startsWith(OEM_SOUNDS_DIR + ALARMS_DIR)
- || path.startsWith(OEM_SOUNDS_DIR + RINGTONES_DIR)
- || path.startsWith(OEM_SOUNDS_DIR + NOTIFICATIONS_DIR)
- || path.startsWith(PRODUCT_SOUNDS_DIR + ALARMS_DIR)
- || path.startsWith(PRODUCT_SOUNDS_DIR + RINGTONES_DIR)
- || path.startsWith(PRODUCT_SOUNDS_DIR + NOTIFICATIONS_DIR)) {
- return true;
- }
- return false;
- }
-
- private String settingSetIndicatorName(String base) {
- return base + "_set";
- }
-
- private boolean wasRingtoneAlreadySet(String name) {
- ContentResolver cr = mContext.getContentResolver();
- String indicatorName = settingSetIndicatorName(name);
- try {
- return Settings.System.getInt(cr, indicatorName) != 0;
- } catch (SettingNotFoundException e) {
- return false;
+ throw new UnsupportedOperationException();
}
}
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private void prescan(String filePath, boolean prescanFiles) throws RemoteException {
- Cursor c = null;
- String where = null;
- String[] selectionArgs = null;
-
- mPlayLists.clear();
-
- if (filePath != null) {
- // query for only one file
- where = MediaStore.Files.FileColumns._ID + ">?" +
- " AND " + Files.FileColumns.DATA + "=?";
- selectionArgs = new String[] { "", filePath };
- } else {
- where = MediaStore.Files.FileColumns._ID + ">?";
- selectionArgs = new String[] { "" };
- }
-
- mDefaultRingtoneSet = wasRingtoneAlreadySet(Settings.System.RINGTONE);
- mDefaultNotificationSet = wasRingtoneAlreadySet(Settings.System.NOTIFICATION_SOUND);
- mDefaultAlarmSet = wasRingtoneAlreadySet(Settings.System.ALARM_ALERT);
-
- // Tell the provider to not delete the file.
- // If the file is truly gone the delete is unnecessary, and we want to avoid
- // accidentally deleting files that are really there (this may happen if the
- // filesystem is mounted and unmounted while the scanner is running).
- Uri.Builder builder = mFilesUri.buildUpon();
- builder.appendQueryParameter(MediaStore.PARAM_DELETE_DATA, "false");
- MediaBulkDeleter deleter = new MediaBulkDeleter(mMediaProvider, builder.build());
-
- // Build the list of files from the content provider
- try {
- if (prescanFiles) {
- // First read existing files from the files table.
- // Because we'll be deleting entries for missing files as we go,
- // we need to query the database in small batches, to avoid problems
- // with CursorWindow positioning.
- long lastId = Long.MIN_VALUE;
- Uri limitUri = mFilesUri.buildUpon()
- .appendQueryParameter(MediaStore.PARAM_LIMIT, "1000").build();
-
- while (true) {
- selectionArgs[0] = "" + lastId;
- if (c != null) {
- c.close();
- c = null;
- }
- c = mMediaProvider.query(limitUri, FILES_PRESCAN_PROJECTION,
- where, selectionArgs, MediaStore.Files.FileColumns._ID, null);
- if (c == null) {
- break;
- }
-
- int num = c.getCount();
-
- if (num == 0) {
- break;
- }
- while (c.moveToNext()) {
- long rowId = c.getLong(FILES_PRESCAN_ID_COLUMN_INDEX);
- String path = c.getString(FILES_PRESCAN_PATH_COLUMN_INDEX);
- int format = c.getInt(FILES_PRESCAN_FORMAT_COLUMN_INDEX);
- long lastModified = c.getLong(FILES_PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
- lastId = rowId;
-
- // Only consider entries with absolute path names.
- // This allows storing URIs in the database without the
- // media scanner removing them.
- if (path != null && path.startsWith("/")) {
- boolean exists = false;
- try {
- exists = Os.access(path, android.system.OsConstants.F_OK);
- } catch (ErrnoException e1) {
- }
- if (!exists && !MtpConstants.isAbstractObject(format)) {
- // do not delete missing playlists, since they may have been
- // modified by the user.
- // The user can delete them in the media player instead.
- // instead, clear the path and lastModified fields in the row
- String mimeType = MediaFile.getMimeTypeForFile(path);
- if (!MediaFile.isPlayListMimeType(mimeType)) {
- deleter.delete(rowId);
- if (path.toLowerCase(Locale.US).endsWith("/.nomedia")) {
- deleter.flush();
- String parent = new File(path).getParent();
- mMediaProvider.call(MediaStore.UNHIDE_CALL, parent, null);
- }
- }
- }
- }
- }
- }
- }
- }
- finally {
- if (c != null) {
- c.close();
- }
- deleter.flush();
- }
-
- // compute original size of images
- mOriginalCount = 0;
- c = mMediaProvider.query(mImagesUri, ID_PROJECTION, null, null, null, null);
- if (c != null) {
- mOriginalCount = c.getCount();
- c.close();
- }
+ throw new UnsupportedOperationException();
}
- static class MediaBulkDeleter {
- StringBuilder whereClause = new StringBuilder();
- ArrayList<String> whereArgs = new ArrayList<String>(100);
- final ContentProviderClient mProvider;
- final Uri mBaseUri;
-
- public MediaBulkDeleter(ContentProviderClient provider, Uri baseUri) {
- mProvider = provider;
- mBaseUri = baseUri;
- }
-
- public void delete(long id) throws RemoteException {
- if (whereClause.length() != 0) {
- whereClause.append(",");
- }
- whereClause.append("?");
- whereArgs.add("" + id);
- if (whereArgs.size() > 100) {
- flush();
- }
- }
- public void flush() throws RemoteException {
- int size = whereArgs.size();
- if (size > 0) {
- String [] foo = new String [size];
- foo = whereArgs.toArray(foo);
- int numrows = mProvider.delete(mBaseUri,
- MediaStore.MediaColumns._ID + " IN (" +
- whereClause.toString() + ")", foo);
- //Log.i("@@@@@@@@@", "rows deleted: " + numrows);
- whereClause.setLength(0);
- whereArgs.clear();
- }
- }
- }
-
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
private void postscan(final String[] directories) throws RemoteException {
-
- // handle playlists last, after we know what media files are on the storage.
- if (mProcessPlaylists) {
- processPlayLists();
- }
-
- // allow GC to clean up
- mPlayLists.clear();
+ throw new UnsupportedOperationException();
}
- private void releaseResources() {
- // release the DrmManagerClient resources
- if (mDrmManagerClient != null) {
- mDrmManagerClient.close();
- mDrmManagerClient = null;
- }
- }
-
- public void scanDirectories(String[] directories) {
- try {
- long start = System.currentTimeMillis();
- prescan(null, true);
- long prescan = System.currentTimeMillis();
-
- if (ENABLE_BULK_INSERTS) {
- // create MediaInserter for bulk inserts
- mMediaInserter = new MediaInserter(mMediaProvider, 500);
- }
-
- for (int i = 0; i < directories.length; i++) {
- processDirectory(directories[i], mClient);
- }
-
- if (ENABLE_BULK_INSERTS) {
- // flush remaining inserts
- mMediaInserter.flushAll();
- mMediaInserter = null;
- }
-
- long scan = System.currentTimeMillis();
- postscan(directories);
- long end = System.currentTimeMillis();
-
- if (false) {
- Log.d(TAG, " prescan time: " + (prescan - start) + "ms\n");
- Log.d(TAG, " scan time: " + (scan - prescan) + "ms\n");
- Log.d(TAG, "postscan time: " + (end - scan) + "ms\n");
- Log.d(TAG, " total time: " + (end - start) + "ms\n");
- }
- } catch (SQLException e) {
- // this might happen if the SD card is removed while the media scanner is running
- Log.e(TAG, "SQLException in MediaScanner.scan()", e);
- } catch (UnsupportedOperationException e) {
- // this might happen if the SD card is removed while the media scanner is running
- Log.e(TAG, "UnsupportedOperationException in MediaScanner.scan()", e);
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in MediaScanner.scan()", e);
- } finally {
- releaseResources();
- }
- }
-
- // this function is used to scan a single file
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
public Uri scanSingleFile(String path, String mimeType) {
- try {
- prescan(path, true);
-
- File file = new File(path);
- if (!file.exists() || !file.canRead()) {
- return null;
- }
-
- // lastModified is in milliseconds on Files.
- long lastModifiedSeconds = file.lastModified() / 1000;
-
- // always scan the file, so we can return the content://media Uri for existing files
- return mClient.doScanFile(path, mimeType, lastModifiedSeconds, file.length(),
- false, true, MediaScanner.isNoMediaPath(path));
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in MediaScanner.scanFile()", e);
- return null;
- } finally {
- releaseResources();
- }
+ throw new UnsupportedOperationException();
}
- private static boolean isNoMediaFile(String path) {
- File file = new File(path);
- if (file.isDirectory()) return false;
-
- // special case certain file names
- // I use regionMatches() instead of substring() below
- // to avoid memory allocation
- int lastSlash = path.lastIndexOf('/');
- if (lastSlash >= 0 && lastSlash + 2 < path.length()) {
- // ignore those ._* files created by MacOS
- if (path.regionMatches(lastSlash + 1, "._", 0, 2)) {
- return true;
- }
-
- // ignore album art files created by Windows Media Player:
- // Folder.jpg, AlbumArtSmall.jpg, AlbumArt_{...}_Large.jpg
- // and AlbumArt_{...}_Small.jpg
- if (path.regionMatches(true, path.length() - 4, ".jpg", 0, 4)) {
- if (path.regionMatches(true, lastSlash + 1, "AlbumArt_{", 0, 10) ||
- path.regionMatches(true, lastSlash + 1, "AlbumArt.", 0, 9)) {
- return true;
- }
- int length = path.length() - lastSlash - 1;
- if ((length == 17 && path.regionMatches(
- true, lastSlash + 1, "AlbumArtSmall", 0, 13)) ||
- (length == 10
- && path.regionMatches(true, lastSlash + 1, "Folder", 0, 6))) {
- return true;
- }
- }
- }
- return false;
- }
-
- private static HashMap<String,String> mNoMediaPaths = new HashMap<String,String>();
- private static HashMap<String,String> mMediaPaths = new HashMap<String,String>();
-
- /* MediaProvider calls this when a .nomedia file is added or removed */
- public static void clearMediaPathCache(boolean clearMediaPaths, boolean clearNoMediaPaths) {
- synchronized (MediaScanner.class) {
- if (clearMediaPaths) {
- mMediaPaths.clear();
- }
- if (clearNoMediaPaths) {
- mNoMediaPaths.clear();
- }
- }
- }
-
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
public static boolean isNoMediaPath(String path) {
- if (path == null) {
- return false;
- }
- // return true if file or any parent directory has name starting with a dot
- if (path.indexOf("/.") >= 0) {
- return true;
- }
-
- int firstSlash = path.lastIndexOf('/');
- if (firstSlash <= 0) {
- return false;
- }
- String parent = path.substring(0, firstSlash);
-
- synchronized (MediaScanner.class) {
- if (mNoMediaPaths.containsKey(parent)) {
- return true;
- } else if (!mMediaPaths.containsKey(parent)) {
- // check to see if any parent directories have a ".nomedia" file
- // start from 1 so we don't bother checking in the root directory
- int offset = 1;
- while (offset >= 0) {
- int slashIndex = path.indexOf('/', offset);
- if (slashIndex > offset) {
- slashIndex++; // move past slash
- File file = new File(path.substring(0, slashIndex) + ".nomedia");
- if (file.exists()) {
- // we have a .nomedia in one of the parent directories
- mNoMediaPaths.put(parent, "");
- return true;
- }
- }
- offset = slashIndex;
- }
- mMediaPaths.put(parent, "");
- }
- }
-
- return isNoMediaFile(path);
+ throw new UnsupportedOperationException();
}
- public void scanMtpFile(String path, int objectHandle, int format) {
- String mimeType = MediaFile.getMimeType(path, format);
- File file = new File(path);
- long lastModifiedSeconds = file.lastModified() / 1000;
-
- if (!MediaFile.isAudioMimeType(mimeType) && !MediaFile.isVideoMimeType(mimeType) &&
- !MediaFile.isImageMimeType(mimeType) && !MediaFile.isPlayListMimeType(mimeType) &&
- !MediaFile.isDrmMimeType(mimeType)) {
-
- // no need to use the media scanner, but we need to update last modified and file size
- ContentValues values = new ContentValues();
- values.put(Files.FileColumns.SIZE, file.length());
- values.put(Files.FileColumns.DATE_MODIFIED, lastModifiedSeconds);
- try {
- String[] whereArgs = new String[] { Integer.toString(objectHandle) };
- mMediaProvider.update(Files.getMtpObjectsUri(mVolumeName), values,
- "_id=?", whereArgs);
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in scanMtpFile", e);
- }
- return;
- }
-
- mMtpObjectHandle = objectHandle;
- Cursor fileList = null;
- try {
- if (MediaFile.isPlayListMimeType(mimeType)) {
- // build file cache so we can look up tracks in the playlist
- prescan(null, true);
-
- FileEntry entry = makeEntryFor(path);
- if (entry != null) {
- fileList = mMediaProvider.query(mFilesUri,
- FILES_PRESCAN_PROJECTION, null, null, null, null);
- processPlayList(entry, fileList);
- }
- } else {
- // MTP will create a file entry for us so we don't want to do it in prescan
- prescan(path, false);
-
- // always scan the file, so we can return the content://media Uri for existing files
- mClient.doScanFile(path, mimeType, lastModifiedSeconds, file.length(),
- (format == MtpConstants.FORMAT_ASSOCIATION), true, isNoMediaPath(path));
- }
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in MediaScanner.scanFile()", e);
- } finally {
- mMtpObjectHandle = 0;
- if (fileList != null) {
- fileList.close();
- }
- releaseResources();
- }
- }
-
- @UnsupportedAppUsage
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
FileEntry makeEntryFor(String path) {
- String where;
- String[] selectionArgs;
-
- Cursor c = null;
- try {
- where = Files.FileColumns.DATA + "=?";
- selectionArgs = new String[] { path };
- c = mMediaProvider.query(mFilesFullUri, FILES_PRESCAN_PROJECTION,
- where, selectionArgs, null, null);
- if (c != null && c.moveToFirst()) {
- long rowId = c.getLong(FILES_PRESCAN_ID_COLUMN_INDEX);
- long lastModified = c.getLong(FILES_PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
- int format = c.getInt(FILES_PRESCAN_FORMAT_COLUMN_INDEX);
- int mediaType = c.getInt(FILES_PRESCAN_MEDIA_TYPE_COLUMN_INDEX);
- return new FileEntry(rowId, path, lastModified, format, mediaType);
- }
- } catch (RemoteException e) {
- } finally {
- if (c != null) {
- c.close();
- }
- }
- return null;
+ throw new UnsupportedOperationException();
}
- // returns the number of matching file/directory names, starting from the right
- private int matchPaths(String path1, String path2) {
- int result = 0;
- int end1 = path1.length();
- int end2 = path2.length();
-
- while (end1 > 0 && end2 > 0) {
- int slash1 = path1.lastIndexOf('/', end1 - 1);
- int slash2 = path2.lastIndexOf('/', end2 - 1);
- int backSlash1 = path1.lastIndexOf('\\', end1 - 1);
- int backSlash2 = path2.lastIndexOf('\\', end2 - 1);
- int start1 = (slash1 > backSlash1 ? slash1 : backSlash1);
- int start2 = (slash2 > backSlash2 ? slash2 : backSlash2);
- if (start1 < 0) start1 = 0; else start1++;
- if (start2 < 0) start2 = 0; else start2++;
- int length = end1 - start1;
- if (end2 - start2 != length) break;
- if (path1.regionMatches(true, start1, path2, start2, length)) {
- result++;
- end1 = start1 - 1;
- end2 = start2 - 1;
- } else break;
- }
-
- return result;
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "All scanning requests should be performed through {@link android.media.MediaScannerConnection}")
+ private void setLocale(String locale) {
+ throw new UnsupportedOperationException();
}
- private boolean matchEntries(long rowId, String data) {
-
- int len = mPlaylistEntries.size();
- boolean done = true;
- for (int i = 0; i < len; i++) {
- PlaylistEntry entry = mPlaylistEntries.get(i);
- if (entry.bestmatchlevel == Integer.MAX_VALUE) {
- continue; // this entry has been matched already
- }
- done = false;
- if (data.equalsIgnoreCase(entry.path)) {
- entry.bestmatchid = rowId;
- entry.bestmatchlevel = Integer.MAX_VALUE;
- continue; // no need for path matching
- }
-
- int matchLength = matchPaths(data, entry.path);
- if (matchLength > entry.bestmatchlevel) {
- entry.bestmatchid = rowId;
- entry.bestmatchlevel = matchLength;
- }
- }
- return done;
- }
-
- private void cachePlaylistEntry(String line, String playListDirectory) {
- PlaylistEntry entry = new PlaylistEntry();
- // watch for trailing whitespace
- int entryLength = line.length();
- while (entryLength > 0 && Character.isWhitespace(line.charAt(entryLength - 1))) entryLength--;
- // path should be longer than 3 characters.
- // avoid index out of bounds errors below by returning here.
- if (entryLength < 3) return;
- if (entryLength < line.length()) line = line.substring(0, entryLength);
-
- // does entry appear to be an absolute path?
- // look for Unix or DOS absolute paths
- char ch1 = line.charAt(0);
- boolean fullPath = (ch1 == '/' ||
- (Character.isLetter(ch1) && line.charAt(1) == ':' && line.charAt(2) == '\\'));
- // if we have a relative path, combine entry with playListDirectory
- if (!fullPath)
- line = playListDirectory + line;
- entry.path = line;
- //FIXME - should we look for "../" within the path?
-
- mPlaylistEntries.add(entry);
- }
-
- private void processCachedPlaylist(Cursor fileList, ContentValues values, Uri playlistUri) {
- fileList.moveToPosition(-1);
- while (fileList.moveToNext()) {
- long rowId = fileList.getLong(FILES_PRESCAN_ID_COLUMN_INDEX);
- String data = fileList.getString(FILES_PRESCAN_PATH_COLUMN_INDEX);
- if (matchEntries(rowId, data)) {
- break;
- }
- }
-
- int len = mPlaylistEntries.size();
- int index = 0;
- for (int i = 0; i < len; i++) {
- PlaylistEntry entry = mPlaylistEntries.get(i);
- if (entry.bestmatchlevel > 0) {
- try {
- values.clear();
- values.put(MediaStore.Audio.Playlists.Members.PLAY_ORDER, Integer.valueOf(index));
- values.put(MediaStore.Audio.Playlists.Members.AUDIO_ID, Long.valueOf(entry.bestmatchid));
- mMediaProvider.insert(playlistUri, values);
- index++;
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in MediaScanner.processCachedPlaylist()", e);
- return;
- }
- }
- }
- mPlaylistEntries.clear();
- }
-
- private void processM3uPlayList(String path, String playListDirectory, Uri uri,
- ContentValues values, Cursor fileList) {
- BufferedReader reader = null;
- try {
- File f = new File(path);
- if (f.exists()) {
- reader = new BufferedReader(
- new InputStreamReader(new FileInputStream(f)), 8192);
- String line = reader.readLine();
- mPlaylistEntries.clear();
- while (line != null) {
- // ignore comment lines, which begin with '#'
- if (line.length() > 0 && line.charAt(0) != '#') {
- cachePlaylistEntry(line, playListDirectory);
- }
- line = reader.readLine();
- }
-
- processCachedPlaylist(fileList, values, uri);
- }
- } catch (IOException e) {
- Log.e(TAG, "IOException in MediaScanner.processM3uPlayList()", e);
- } finally {
- try {
- if (reader != null)
- reader.close();
- } catch (IOException e) {
- Log.e(TAG, "IOException in MediaScanner.processM3uPlayList()", e);
- }
- }
- }
-
- private void processPlsPlayList(String path, String playListDirectory, Uri uri,
- ContentValues values, Cursor fileList) {
- BufferedReader reader = null;
- try {
- File f = new File(path);
- if (f.exists()) {
- reader = new BufferedReader(
- new InputStreamReader(new FileInputStream(f)), 8192);
- String line = reader.readLine();
- mPlaylistEntries.clear();
- while (line != null) {
- // ignore comment lines, which begin with '#'
- if (line.startsWith("File")) {
- int equals = line.indexOf('=');
- if (equals > 0) {
- cachePlaylistEntry(line.substring(equals + 1), playListDirectory);
- }
- }
- line = reader.readLine();
- }
-
- processCachedPlaylist(fileList, values, uri);
- }
- } catch (IOException e) {
- Log.e(TAG, "IOException in MediaScanner.processPlsPlayList()", e);
- } finally {
- try {
- if (reader != null)
- reader.close();
- } catch (IOException e) {
- Log.e(TAG, "IOException in MediaScanner.processPlsPlayList()", e);
- }
- }
- }
-
- class WplHandler implements ElementListener {
-
- final ContentHandler handler;
- String playListDirectory;
-
- public WplHandler(String playListDirectory, Uri uri, Cursor fileList) {
- this.playListDirectory = playListDirectory;
-
- RootElement root = new RootElement("smil");
- Element body = root.getChild("body");
- Element seq = body.getChild("seq");
- Element media = seq.getChild("media");
- media.setElementListener(this);
-
- this.handler = root.getContentHandler();
- }
-
- @Override
- public void start(Attributes attributes) {
- String path = attributes.getValue("", "src");
- if (path != null) {
- cachePlaylistEntry(path, playListDirectory);
- }
- }
-
- @Override
- public void end() {
- }
-
- ContentHandler getContentHandler() {
- return handler;
- }
- }
-
- private void processWplPlayList(String path, String playListDirectory, Uri uri,
- ContentValues values, Cursor fileList) {
- FileInputStream fis = null;
- try {
- File f = new File(path);
- if (f.exists()) {
- fis = new FileInputStream(f);
-
- mPlaylistEntries.clear();
- Xml.parse(fis, Xml.findEncodingByName("UTF-8"),
- new WplHandler(playListDirectory, uri, fileList).getContentHandler());
-
- processCachedPlaylist(fileList, values, uri);
- }
- } catch (SAXException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- try {
- if (fis != null)
- fis.close();
- } catch (IOException e) {
- Log.e(TAG, "IOException in MediaScanner.processWplPlayList()", e);
- }
- }
- }
-
- private void processPlayList(FileEntry entry, Cursor fileList) throws RemoteException {
- String path = entry.mPath;
- ContentValues values = new ContentValues();
- int lastSlash = path.lastIndexOf('/');
- if (lastSlash < 0) throw new IllegalArgumentException("bad path " + path);
- Uri uri, membersUri;
- long rowId = entry.mRowId;
-
- // make sure we have a name
- String name = values.getAsString(MediaStore.Audio.Playlists.NAME);
- if (name == null) {
- name = values.getAsString(MediaStore.MediaColumns.TITLE);
- if (name == null) {
- // extract name from file name
- int lastDot = path.lastIndexOf('.');
- name = (lastDot < 0 ? path.substring(lastSlash + 1)
- : path.substring(lastSlash + 1, lastDot));
- }
- }
-
- values.put(MediaStore.Audio.Playlists.NAME, name);
- values.put(MediaStore.Audio.Playlists.DATE_MODIFIED, entry.mLastModified);
-
- if (rowId == 0) {
- values.put(MediaStore.Audio.Playlists.DATA, path);
- uri = mMediaProvider.insert(mPlaylistsUri, values);
- rowId = ContentUris.parseId(uri);
- membersUri = Uri.withAppendedPath(uri, Playlists.Members.CONTENT_DIRECTORY);
- } else {
- uri = ContentUris.withAppendedId(mPlaylistsUri, rowId);
- mMediaProvider.update(uri, values, null, null);
-
- // delete members of existing playlist
- membersUri = Uri.withAppendedPath(uri, Playlists.Members.CONTENT_DIRECTORY);
- mMediaProvider.delete(membersUri, null, null);
- }
-
- String playListDirectory = path.substring(0, lastSlash + 1);
- String mimeType = MediaFile.getMimeTypeForFile(path);
- switch (mimeType) {
- case "application/vnd.ms-wpl":
- processWplPlayList(path, playListDirectory, membersUri, values, fileList);
- break;
- case "audio/x-mpegurl":
- processM3uPlayList(path, playListDirectory, membersUri, values, fileList);
- break;
- case "audio/x-scpls":
- processPlsPlayList(path, playListDirectory, membersUri, values, fileList);
- break;
- }
- }
-
- private void processPlayLists() throws RemoteException {
- Iterator<FileEntry> iterator = mPlayLists.iterator();
- Cursor fileList = null;
- try {
- // use the files uri and projection because we need the format column,
- // but restrict the query to just audio files
- fileList = mMediaProvider.query(mFilesUri, FILES_PRESCAN_PROJECTION,
- "media_type=2", null, null, null);
- while (iterator.hasNext()) {
- FileEntry entry = iterator.next();
- // only process playlist files if they are new or have been modified since the last scan
- if (entry.mLastModifiedChanged) {
- processPlayList(entry, fileList);
- }
- }
- } catch (RemoteException e1) {
- } finally {
- if (fileList != null) {
- fileList.close();
- }
- }
- }
-
- private native void processDirectory(String path, MediaScannerClient client);
- private native boolean processFile(String path, String mimeType, MediaScannerClient client);
- @UnsupportedAppUsage
- private native void setLocale(String locale);
-
- public native byte[] extractAlbumArt(FileDescriptor fd);
-
- private static native final void native_init();
- private native final void native_setup();
- private native final void native_finalize();
-
@Override
public void close() {
- mCloseGuard.close();
- if (mClosed.compareAndSet(false, true)) {
- mMediaProvider.close();
- native_finalize();
- }
- }
-
- @Override
- protected void finalize() throws Throwable {
- try {
- if (mCloseGuard != null) {
- mCloseGuard.warnIfOpen();
- }
-
- close();
- } finally {
- super.finalize();
- }
+ throw new UnsupportedOperationException();
}
}
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index 435d8d7..ff40442 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -846,7 +846,7 @@
* Adds an audio file to the list of ringtones.
*
* After making sure the given file is an audio file, copies the file to the ringtone storage,
- * and asks the {@link android.media.MediaScanner} to scan that file. This call will block until
+ * and asks the system to scan that file. This call will block until
* the scan is completed.
*
* The directory where the copied file is stored is the directory that matches the ringtone's
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index 45ee210..bd9ea13 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -17,7 +17,6 @@
"android_media_MediaPlayer.cpp",
"android_media_MediaProfiles.cpp",
"android_media_MediaRecorder.cpp",
- "android_media_MediaScanner.cpp",
"android_media_MediaSync.cpp",
"android_media_ResampleInputStream.cpp",
"android_media_Streams.cpp",
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index d24edc7..94299bc 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -1446,7 +1446,6 @@
extern int register_android_media_MediaMetadataRetriever(JNIEnv *env);
extern int register_android_media_MediaMuxer(JNIEnv *env);
extern int register_android_media_MediaRecorder(JNIEnv *env);
-extern int register_android_media_MediaScanner(JNIEnv *env);
extern int register_android_media_MediaSync(JNIEnv *env);
extern int register_android_media_ResampleInputStream(JNIEnv *env);
extern int register_android_media_MediaProfiles(JNIEnv *env);
@@ -1485,11 +1484,6 @@
goto bail;
}
- if (register_android_media_MediaScanner(env) < 0) {
- ALOGE("ERROR: MediaScanner native registration failed\n");
- goto bail;
- }
-
if (register_android_media_MediaMetadataRetriever(env) < 0) {
ALOGE("ERROR: MediaMetadataRetriever native registration failed\n");
goto bail;
diff --git a/media/jni/android_media_MediaScanner.cpp b/media/jni/android_media_MediaScanner.cpp
deleted file mode 100644
index 58044c0..0000000
--- a/media/jni/android_media_MediaScanner.cpp
+++ /dev/null
@@ -1,468 +0,0 @@
-/*
-**
-** Copyright 2007, 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.
-*/
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "MediaScannerJNI"
-#include <utils/Log.h>
-#include <utils/threads.h>
-#include <media/mediascanner.h>
-#include <media/stagefright/StagefrightMediaScanner.h>
-#include <private/media/VideoFrame.h>
-
-#include "jni.h"
-#include <nativehelper/JNIHelp.h>
-#include "android_runtime/AndroidRuntime.h"
-#include "android_runtime/Log.h"
-#include <android-base/macros.h> // for FALLTHROUGH_INTENDED
-
-using namespace android;
-
-
-static const char* const kClassMediaScannerClient =
- "android/media/MediaScannerClient";
-
-static const char* const kClassMediaScanner =
- "android/media/MediaScanner";
-
-static const char* const kRunTimeException =
- "java/lang/RuntimeException";
-
-static const char* const kIllegalArgumentException =
- "java/lang/IllegalArgumentException";
-
-struct fields_t {
- jfieldID context;
-};
-static fields_t fields;
-
-static status_t checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
- if (env->ExceptionCheck()) {
- ALOGE("An exception was thrown by callback '%s'.", methodName);
- LOGE_EX(env);
- env->ExceptionClear();
- return UNKNOWN_ERROR;
- }
- return OK;
-}
-
-// stolen from dalvik/vm/checkJni.cpp
-static bool isValidUtf8(const char* bytes) {
- while (*bytes != '\0') {
- unsigned char utf8 = *(bytes++);
- // Switch on the high four bits.
- switch (utf8 >> 4) {
- case 0x00:
- case 0x01:
- case 0x02:
- case 0x03:
- case 0x04:
- case 0x05:
- case 0x06:
- case 0x07:
- // Bit pattern 0xxx. No need for any extra bytes.
- break;
- case 0x08:
- case 0x09:
- case 0x0a:
- case 0x0b:
- case 0x0f:
- /*
- * Bit pattern 10xx or 1111, which are illegal start bytes.
- * Note: 1111 is valid for normal UTF-8, but not the
- * modified UTF-8 used here.
- */
- return false;
- case 0x0e:
- // Bit pattern 1110, so there are two additional bytes.
- utf8 = *(bytes++);
- if ((utf8 & 0xc0) != 0x80) {
- return false;
- }
- // Fall through to take care of the final byte.
- FALLTHROUGH_INTENDED;
- case 0x0c:
- case 0x0d:
- // Bit pattern 110x, so there is one additional byte.
- utf8 = *(bytes++);
- if ((utf8 & 0xc0) != 0x80) {
- return false;
- }
- break;
- }
- }
- return true;
-}
-
-class MyMediaScannerClient : public MediaScannerClient
-{
-public:
- MyMediaScannerClient(JNIEnv *env, jobject client)
- : mEnv(env),
- mClient(env->NewGlobalRef(client)),
- mScanFileMethodID(0),
- mHandleStringTagMethodID(0),
- mSetMimeTypeMethodID(0)
- {
- ALOGV("MyMediaScannerClient constructor");
- jclass mediaScannerClientInterface =
- env->FindClass(kClassMediaScannerClient);
-
- if (mediaScannerClientInterface == NULL) {
- ALOGE("Class %s not found", kClassMediaScannerClient);
- } else {
- mScanFileMethodID = env->GetMethodID(
- mediaScannerClientInterface,
- "scanFile",
- "(Ljava/lang/String;JJZZ)V");
-
- mHandleStringTagMethodID = env->GetMethodID(
- mediaScannerClientInterface,
- "handleStringTag",
- "(Ljava/lang/String;Ljava/lang/String;)V");
-
- mSetMimeTypeMethodID = env->GetMethodID(
- mediaScannerClientInterface,
- "setMimeType",
- "(Ljava/lang/String;)V");
- }
- }
-
- virtual ~MyMediaScannerClient()
- {
- ALOGV("MyMediaScannerClient destructor");
- mEnv->DeleteGlobalRef(mClient);
- }
-
- virtual status_t scanFile(const char* path, long long lastModified,
- long long fileSize, bool isDirectory, bool noMedia)
- {
- ALOGV("scanFile: path(%s), time(%lld), size(%lld) and isDir(%d)",
- path, lastModified, fileSize, isDirectory);
-
- jstring pathStr;
- if ((pathStr = mEnv->NewStringUTF(path)) == NULL) {
- mEnv->ExceptionClear();
- return NO_MEMORY;
- }
-
- mEnv->CallVoidMethod(mClient, mScanFileMethodID, pathStr, lastModified,
- fileSize, isDirectory, noMedia);
-
- mEnv->DeleteLocalRef(pathStr);
- return checkAndClearExceptionFromCallback(mEnv, "scanFile");
- }
-
- virtual status_t handleStringTag(const char* name, const char* value)
- {
- ALOGV("handleStringTag: name(%s) and value(%s)", name, value);
- jstring nameStr, valueStr;
- if ((nameStr = mEnv->NewStringUTF(name)) == NULL) {
- mEnv->ExceptionClear();
- return NO_MEMORY;
- }
- char *cleaned = NULL;
- if (!isValidUtf8(value)) {
- cleaned = strdup(value);
- char *chp = cleaned;
- char ch;
- while ((ch = *chp)) {
- if (ch & 0x80) {
- *chp = '?';
- }
- chp++;
- }
- value = cleaned;
- }
- valueStr = mEnv->NewStringUTF(value);
- free(cleaned);
- if (valueStr == NULL) {
- mEnv->DeleteLocalRef(nameStr);
- mEnv->ExceptionClear();
- return NO_MEMORY;
- }
-
- mEnv->CallVoidMethod(
- mClient, mHandleStringTagMethodID, nameStr, valueStr);
-
- mEnv->DeleteLocalRef(nameStr);
- mEnv->DeleteLocalRef(valueStr);
- return checkAndClearExceptionFromCallback(mEnv, "handleStringTag");
- }
-
- virtual status_t setMimeType(const char* mimeType)
- {
- ALOGV("setMimeType: %s", mimeType);
- jstring mimeTypeStr;
- if ((mimeTypeStr = mEnv->NewStringUTF(mimeType)) == NULL) {
- mEnv->ExceptionClear();
- return NO_MEMORY;
- }
-
- mEnv->CallVoidMethod(mClient, mSetMimeTypeMethodID, mimeTypeStr);
-
- mEnv->DeleteLocalRef(mimeTypeStr);
- return checkAndClearExceptionFromCallback(mEnv, "setMimeType");
- }
-
-private:
- JNIEnv *mEnv;
- jobject mClient;
- jmethodID mScanFileMethodID;
- jmethodID mHandleStringTagMethodID;
- jmethodID mSetMimeTypeMethodID;
-};
-
-
-static MediaScanner *getNativeScanner_l(JNIEnv* env, jobject thiz)
-{
- return (MediaScanner *) env->GetLongField(thiz, fields.context);
-}
-
-static void setNativeScanner_l(JNIEnv* env, jobject thiz, MediaScanner *s)
-{
- env->SetLongField(thiz, fields.context, (jlong)s);
-}
-
-static void
-android_media_MediaScanner_processDirectory(
- JNIEnv *env, jobject thiz, jstring path, jobject client)
-{
- ALOGV("processDirectory");
- MediaScanner *mp = getNativeScanner_l(env, thiz);
- if (mp == NULL) {
- jniThrowException(env, kRunTimeException, "No scanner available");
- return;
- }
-
- if (path == NULL) {
- jniThrowException(env, kIllegalArgumentException, NULL);
- return;
- }
-
- const char *pathStr = env->GetStringUTFChars(path, NULL);
- if (pathStr == NULL) { // Out of memory
- return;
- }
-
- MyMediaScannerClient myClient(env, client);
- MediaScanResult result = mp->processDirectory(pathStr, myClient);
- if (result == MEDIA_SCAN_RESULT_ERROR) {
- ALOGE("An error occurred while scanning directory '%s'.", pathStr);
- }
- env->ReleaseStringUTFChars(path, pathStr);
-}
-
-static jboolean
-android_media_MediaScanner_processFile(
- JNIEnv *env, jobject thiz, jstring path,
- jstring mimeType, jobject client)
-{
- ALOGV("processFile");
-
- // Lock already hold by processDirectory
- MediaScanner *mp = getNativeScanner_l(env, thiz);
- if (mp == NULL) {
- jniThrowException(env, kRunTimeException, "No scanner available");
- return false;
- }
-
- if (path == NULL) {
- jniThrowException(env, kIllegalArgumentException, NULL);
- return false;
- }
-
- const char *pathStr = env->GetStringUTFChars(path, NULL);
- if (pathStr == NULL) { // Out of memory
- return false;
- }
-
- const char *mimeTypeStr =
- (mimeType ? env->GetStringUTFChars(mimeType, NULL) : NULL);
- if (mimeType && mimeTypeStr == NULL) { // Out of memory
- // ReleaseStringUTFChars can be called with an exception pending.
- env->ReleaseStringUTFChars(path, pathStr);
- return false;
- }
-
- MyMediaScannerClient myClient(env, client);
- MediaScanResult result = mp->processFile(pathStr, mimeTypeStr, myClient);
- if (result == MEDIA_SCAN_RESULT_ERROR) {
- ALOGE("An error occurred while scanning file '%s'.", pathStr);
- }
- env->ReleaseStringUTFChars(path, pathStr);
- if (mimeType) {
- env->ReleaseStringUTFChars(mimeType, mimeTypeStr);
- }
- return result != MEDIA_SCAN_RESULT_ERROR;
-}
-
-static void
-android_media_MediaScanner_setLocale(
- JNIEnv *env, jobject thiz, jstring locale)
-{
- ALOGV("setLocale");
- MediaScanner *mp = getNativeScanner_l(env, thiz);
- if (mp == NULL) {
- jniThrowException(env, kRunTimeException, "No scanner available");
- return;
- }
-
- if (locale == NULL) {
- jniThrowException(env, kIllegalArgumentException, NULL);
- return;
- }
- const char *localeStr = env->GetStringUTFChars(locale, NULL);
- if (localeStr == NULL) { // Out of memory
- return;
- }
- mp->setLocale(localeStr);
-
- env->ReleaseStringUTFChars(locale, localeStr);
-}
-
-static jbyteArray
-android_media_MediaScanner_extractAlbumArt(
- JNIEnv *env, jobject thiz, jobject fileDescriptor)
-{
- ALOGV("extractAlbumArt");
- MediaScanner *mp = getNativeScanner_l(env, thiz);
- if (mp == NULL) {
- jniThrowException(env, kRunTimeException, "No scanner available");
- return NULL;
- }
-
- if (fileDescriptor == NULL) {
- jniThrowException(env, kIllegalArgumentException, NULL);
- return NULL;
- }
-
- int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
- MediaAlbumArt* mediaAlbumArt = mp->extractAlbumArt(fd);
- if (mediaAlbumArt == NULL) {
- return NULL;
- }
-
- jbyteArray array = env->NewByteArray(mediaAlbumArt->size());
- if (array != NULL) {
- const jbyte* data =
- reinterpret_cast<const jbyte*>(mediaAlbumArt->data());
- env->SetByteArrayRegion(array, 0, mediaAlbumArt->size(), data);
- }
-
- free(mediaAlbumArt);
- // if NewByteArray() returned NULL, an out-of-memory
- // exception will have been raised. I just want to
- // return null in that case.
- env->ExceptionClear();
- return array;
-}
-
-// This function gets a field ID, which in turn causes class initialization.
-// It is called from a static block in MediaScanner, which won't run until the
-// first time an instance of this class is used.
-static void
-android_media_MediaScanner_native_init(JNIEnv *env)
-{
- ALOGV("native_init");
- jclass clazz = env->FindClass(kClassMediaScanner);
- if (clazz == NULL) {
- return;
- }
-
- fields.context = env->GetFieldID(clazz, "mNativeContext", "J");
- if (fields.context == NULL) {
- return;
- }
-}
-
-static void
-android_media_MediaScanner_native_setup(JNIEnv *env, jobject thiz)
-{
- ALOGV("native_setup");
- MediaScanner *mp = new StagefrightMediaScanner;
-
- if (mp == NULL) {
- jniThrowException(env, kRunTimeException, "Out of memory");
- return;
- }
-
- env->SetLongField(thiz, fields.context, (jlong)mp);
-}
-
-static void
-android_media_MediaScanner_native_finalize(JNIEnv *env, jobject thiz)
-{
- ALOGV("native_finalize");
- MediaScanner *mp = getNativeScanner_l(env, thiz);
- if (mp == 0) {
- return;
- }
- delete mp;
- setNativeScanner_l(env, thiz, 0);
-}
-
-static const JNINativeMethod gMethods[] = {
- {
- "processDirectory",
- "(Ljava/lang/String;Landroid/media/MediaScannerClient;)V",
- (void *)android_media_MediaScanner_processDirectory
- },
-
- {
- "processFile",
- "(Ljava/lang/String;Ljava/lang/String;Landroid/media/MediaScannerClient;)Z",
- (void *)android_media_MediaScanner_processFile
- },
-
- {
- "setLocale",
- "(Ljava/lang/String;)V",
- (void *)android_media_MediaScanner_setLocale
- },
-
- {
- "extractAlbumArt",
- "(Ljava/io/FileDescriptor;)[B",
- (void *)android_media_MediaScanner_extractAlbumArt
- },
-
- {
- "native_init",
- "()V",
- (void *)android_media_MediaScanner_native_init
- },
-
- {
- "native_setup",
- "()V",
- (void *)android_media_MediaScanner_native_setup
- },
-
- {
- "native_finalize",
- "()V",
- (void *)android_media_MediaScanner_native_finalize
- },
-};
-
-// This function only registers the native methods, and is called from
-// JNI_OnLoad in android_media_MediaPlayer.cpp
-int register_android_media_MediaScanner(JNIEnv *env)
-{
- return AndroidRuntime::registerNativeMethods(env,
- kClassMediaScanner, gMethods, NELEM(gMethods));
-}
diff --git a/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java b/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java
index 8d39a93..680c879 100644
--- a/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java
+++ b/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java
@@ -130,6 +130,33 @@
}
}
+ @Override
+ public void onSetVolume(String routeId, int volume) {
+ MediaRoute2Info route = mRoutes.get(routeId);
+ if (route == null) {
+ return;
+ }
+ volume = Math.min(volume, Math.max(0, route.getVolumeMax()));
+ mRoutes.put(routeId, new MediaRoute2Info.Builder(route)
+ .setVolume(volume)
+ .build());
+ publishRoutes();
+ }
+
+ @Override
+ public void onUpdateVolume(String routeId, int delta) {
+ MediaRoute2Info route = mRoutes.get(routeId);
+ if (route == null) {
+ return;
+ }
+ int volume = route.getVolume() + delta;
+ volume = Math.min(volume, Math.max(0, route.getVolumeMax()));
+ mRoutes.put(routeId, new MediaRoute2Info.Builder(route)
+ .setVolume(volume)
+ .build());
+ publishRoutes();
+ }
+
void publishRoutes() {
MediaRoute2ProviderInfo info = new MediaRoute2ProviderInfo.Builder()
.addRoutes(mRoutes.values())
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
index da832ac..ca43d04 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
@@ -47,8 +47,10 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
@RunWith(AndroidJUnit4.class)
@SmallTest
@@ -190,17 +192,8 @@
MediaRouter2Manager.Callback mockCallback = mock(MediaRouter2Manager.Callback.class);
mManager.registerCallback(mExecutor, mockCallback);
- MediaRouter2.Callback mockRouterCallback = mock(MediaRouter2.Callback.class);
-
- mRouter2.setControlCategories(CONTROL_CATEGORIES_SPECIAL);
- mRouter2.registerCallback(mExecutor, mockRouterCallback);
- mRouter2.unregisterCallback(mockRouterCallback);
-
- verify(mockCallback, timeout(TIMEOUT_MS))
- .onRoutesChanged(argThat(routes -> routes.size() > 0));
-
Map<String, MediaRoute2Info> routes =
- createRouteMap(mManager.getAvailableRoutes(mPackageName));
+ waitAndGetRoutesWithManager(CONTROL_CATEGORIES_SPECIAL);
Assert.assertEquals(1, routes.size());
Assert.assertNotNull(routes.get(ROUTE_ID_SPECIAL_CATEGORY));
@@ -214,12 +207,10 @@
@Test
public void testGetRoutes() throws Exception {
MediaRouter2.Callback mockCallback = mock(MediaRouter2.Callback.class);
-
- mRouter2.setControlCategories(CONTROL_CATEGORIES_SPECIAL);
mRouter2.registerCallback(mExecutor, mockCallback);
- verify(mockCallback, timeout(TIMEOUT_MS).atLeastOnce())
- .onRoutesChanged(argThat(routes -> routes.size() > 0));
- Map<String, MediaRoute2Info> routes = createRouteMap(mRouter2.getRoutes());
+
+ Map<String, MediaRoute2Info> routes = waitAndGetRoutes(CONTROL_CATEGORIES_SPECIAL);
+
Assert.assertEquals(1, routes.size());
Assert.assertNotNull(routes.get(ROUTE_ID_SPECIAL_CATEGORY));
@@ -228,51 +219,40 @@
@Test
public void testOnRouteSelected() throws Exception {
- MediaRouter2.Callback mockRouterCallback = mock(MediaRouter2.Callback.class);
+ MediaRouter2.Callback routerCallback = new MediaRouter2.Callback();
MediaRouter2Manager.Callback managerCallback = mock(MediaRouter2Manager.Callback.class);
mManager.registerCallback(mExecutor, managerCallback);
- mRouter2.setControlCategories(CONTROL_CATEGORIES_ALL);
- mRouter2.registerCallback(mExecutor, mockRouterCallback);
+ mRouter2.registerCallback(mExecutor, routerCallback);
- verify(managerCallback, timeout(TIMEOUT_MS))
- .onRoutesChanged(argThat(routes -> routes.size() > 0));
-
- Map<String, MediaRoute2Info> routes =
- createRouteMap(mManager.getAvailableRoutes(mPackageName));
+ Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CONTROL_CATEGORIES_ALL);
MediaRoute2Info routeToSelect = routes.get(ROUTE_ID1);
- mManager.selectRoute(mPackageName, routeToSelect);
-
assertNotNull(routeToSelect);
+
+ mManager.selectRoute(mPackageName, routeToSelect);
verify(managerCallback, timeout(TIMEOUT_MS))
.onRouteAdded(argThat(route -> route.equals(routeToSelect)));
+ mRouter2.unregisterCallback(routerCallback);
mManager.unregisterCallback(managerCallback);
- mRouter2.unregisterCallback(mockRouterCallback);
}
/**
* Tests selecting and unselecting routes of a single provider.
*/
@Test
- public void testSingleProviderSelect() {
+ public void testSingleProviderSelect() throws Exception {
MediaRouter2Manager.Callback managerCallback = mock(MediaRouter2Manager.Callback.class);
MediaRouter2.Callback routerCallback = mock(MediaRouter2.Callback.class);
mManager.registerCallback(mExecutor, managerCallback);
- mRouter2.setControlCategories(CONTROL_CATEGORIES_ALL);
mRouter2.registerCallback(mExecutor, routerCallback);
- verify(managerCallback, timeout(TIMEOUT_MS))
- .onRoutesChanged(argThat(routes -> routes.size() > 0));
-
- Map<String, MediaRoute2Info> routes =
- createRouteMap(mManager.getAvailableRoutes(mPackageName));
+ Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CONTROL_CATEGORIES_ALL);
mManager.selectRoute(mPackageName, routes.get(ROUTE_ID1));
- verify(managerCallback, timeout(TIMEOUT_MS)
- )
+ verify(managerCallback, timeout(TIMEOUT_MS))
.onRouteChanged(argThat(routeInfo -> TextUtils.equals(ROUTE_ID1, routeInfo.getId())
&& TextUtils.equals(routeInfo.getClientPackageName(), mPackageName)));
@@ -291,14 +271,67 @@
}
@Test
- public void testVolumeHandling() {
+ public void testControlVolumeWithRouter() throws Exception {
MediaRouter2.Callback mockCallback = mock(MediaRouter2.Callback.class);
- mRouter2.setControlCategories(CONTROL_CATEGORIES_ALL);
+ Map<String, MediaRoute2Info> routes = waitAndGetRoutes(CONTROL_CATEGORIES_ALL);
mRouter2.registerCallback(mExecutor, mockCallback);
+
+ MediaRoute2Info volRoute = routes.get(ROUTE_ID_VARIABLE_VOLUME);
+ int originalVolume = volRoute.getVolume();
+ int deltaVolume = (originalVolume == volRoute.getVolumeMax() ? -1 : 1);
+ int targetVolume = originalVolume + deltaVolume;
+
+ mRouter2.requestSetVolume(volRoute, targetVolume);
verify(mockCallback, timeout(TIMEOUT_MS).atLeastOnce())
- .onRoutesChanged(argThat(routes -> routes.size() > 0));
- Map<String, MediaRoute2Info> routes = createRouteMap(mRouter2.getRoutes());
+ .onRouteChanged(argThat(route ->
+ route.getId().equals(volRoute.getId())
+ && route.getVolume() == targetVolume));
+
+ mRouter2.requestUpdateVolume(volRoute, -deltaVolume);
+ verify(mockCallback, timeout(TIMEOUT_MS).atLeastOnce())
+ .onRouteChanged(argThat(route ->
+ route.getId().equals(volRoute.getId())
+ && route.getVolume() == originalVolume));
+
+ mRouter2.unregisterCallback(mockCallback);
+ }
+
+ @Test
+ public void testControlVolumeWithManager() throws Exception {
+ MediaRouter2Manager.Callback managerCallback = mock(MediaRouter2Manager.Callback.class);
+ MediaRouter2.Callback mockCallback = mock(MediaRouter2.Callback.class);
+
+ mManager.registerCallback(mExecutor, managerCallback);
+ mRouter2.registerCallback(mExecutor, mockCallback);
+ Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CONTROL_CATEGORIES_ALL);
+
+ MediaRoute2Info volRoute = routes.get(ROUTE_ID_VARIABLE_VOLUME);
+ int originalVolume = volRoute.getVolume();
+ int deltaVolume = (originalVolume == volRoute.getVolumeMax() ? -1 : 1);
+ int targetVolume = originalVolume + deltaVolume;
+
+ mManager.requestSetVolume(volRoute, targetVolume);
+ verify(mockCallback, timeout(TIMEOUT_MS).atLeastOnce())
+ .onRouteChanged(argThat(route ->
+ route.getId().equals(volRoute.getId())
+ && route.getVolume() == targetVolume));
+
+ mManager.requestUpdateVolume(volRoute, -deltaVolume);
+ verify(mockCallback, timeout(TIMEOUT_MS).atLeastOnce())
+ .onRouteChanged(argThat(route ->
+ route.getId().equals(volRoute.getId())
+ && route.getVolume() == originalVolume));
+
+ mRouter2.unregisterCallback(mockCallback);
+ mManager.unregisterCallback(managerCallback);
+ }
+
+ @Test
+ public void testVolumeHandling() throws Exception {
+ MediaRouter2.Callback mockCallback = mock(MediaRouter2.Callback.class);
+ mRouter2.registerCallback(mExecutor, mockCallback);
+ Map<String, MediaRoute2Info> routes = waitAndGetRoutes(CONTROL_CATEGORIES_ALL);
MediaRoute2Info fixedVolumeRoute = routes.get(ROUTE_ID_FIXED_VOLUME);
MediaRoute2Info variableVolumeRoute = routes.get(ROUTE_ID_VARIABLE_VOLUME);
@@ -310,6 +343,48 @@
mRouter2.unregisterCallback(mockCallback);
}
+ Map<String, MediaRoute2Info> waitAndGetRoutes(List<String> controlCategories) throws Exception {
+ CountDownLatch latch = new CountDownLatch(1);
+ MediaRouter2.Callback callback = new MediaRouter2.Callback() {
+ @Override
+ public void onRoutesChanged(List<MediaRoute2Info> routes) {
+ if (routes.size() > 0) latch.countDown();
+ }
+ };
+ mRouter2.setControlCategories(controlCategories);
+ mRouter2.registerCallback(mExecutor, callback);
+ try {
+ latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ return createRouteMap(mRouter2.getRoutes());
+ } finally {
+ mRouter2.unregisterCallback(callback);
+ }
+ }
+
+ Map<String, MediaRoute2Info> waitAndGetRoutesWithManager(List<String> controlCategories)
+ throws Exception {
+ CountDownLatch latch = new CountDownLatch(1);
+
+ // Dummy callback is required to send control category info.
+ MediaRouter2.Callback routerCallback = new MediaRouter2.Callback();
+ MediaRouter2Manager.Callback managerCallback = new MediaRouter2Manager.Callback() {
+ @Override
+ public void onRoutesChanged(List<MediaRoute2Info> routes) {
+ if (routes.size() > 0) latch.countDown();
+ }
+ };
+ mManager.registerCallback(mExecutor, managerCallback);
+ mRouter2.setControlCategories(controlCategories);
+ mRouter2.registerCallback(mExecutor, routerCallback);
+ try {
+ latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ return createRouteMap(mManager.getAvailableRoutes(mPackageName));
+ } finally {
+ mRouter2.unregisterCallback(routerCallback);
+ mManager.unregisterCallback(managerCallback);
+ }
+ }
+
// Helper for getting routes easily
static Map<String, MediaRoute2Info> createRouteMap(List<MediaRoute2Info> routes) {
Map<String, MediaRoute2Info> routeMap = new HashMap<>();
diff --git a/packages/CarSystemUI/res/layout/car_top_navigation_bar_unprovisioned.xml b/packages/CarSystemUI/res/layout/car_top_navigation_bar_unprovisioned.xml
new file mode 100644
index 0000000..8247211
--- /dev/null
+++ b/packages/CarSystemUI/res/layout/car_top_navigation_bar_unprovisioned.xml
@@ -0,0 +1,147 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<com.android.systemui.statusbar.car.CarNavigationBarView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:systemui="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/car_top_bar"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@drawable/system_bar_background"
+ android:orientation="vertical">
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1">
+
+ <FrameLayout
+ android:id="@+id/left_hvac_container"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_alignParentStart="true"
+ >
+
+ <com.android.systemui.statusbar.car.CarNavigationButton
+ android:id="@+id/hvacleft"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@null"
+ systemui:broadcast="true"
+ systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end"
+ />
+
+ <com.android.systemui.statusbar.hvac.AnimatedTemperatureView
+ android:id="@+id/lefttext"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:paddingStart="@*android:dimen/car_padding_4"
+ android:paddingEnd="16dp"
+ android:gravity="center_vertical|start"
+ android:minEms="4"
+ android:textAppearance="@style/TextAppearance.CarStatus"
+ systemui:hvacAreaId="49"
+ systemui:hvacMaxText="@string/hvac_max_text"
+ systemui:hvacMaxValue="@dimen/hvac_max_value"
+ systemui:hvacMinText="@string/hvac_min_text"
+ systemui:hvacMinValue="@dimen/hvac_min_value"
+ systemui:hvacPivotOffset="60dp"
+ systemui:hvacPropertyId="358614275"
+ systemui:hvacTempFormat="%.0f\u00B0"
+ />
+ </FrameLayout>
+
+ <FrameLayout
+ android:id="@+id/clock_container"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_centerInParent="true">
+ <com.android.systemui.statusbar.car.CarNavigationButton
+ android:id="@+id/qs"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@null"/>
+ <com.android.systemui.statusbar.policy.Clock
+ android:id="@+id/clock"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:elevation="5dp"
+ android:singleLine="true"
+ android:textAppearance="@style/TextAppearance.StatusBar.Clock"/>
+ </FrameLayout>
+
+ <LinearLayout
+ android:id="@+id/system_icon_area"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_centerHorizontal="true"
+ android:layout_centerVertical="true"
+ android:layout_toEndOf="@+id/clock_container"
+ android:paddingStart="@*android:dimen/car_padding_1"
+ android:gravity="center_vertical"
+ android:orientation="horizontal"
+ >
+
+ <include
+ layout="@layout/system_icons"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:paddingStart="4dp"
+ android:gravity="center_vertical"
+ />
+ </LinearLayout>
+
+ <FrameLayout
+ android:id="@+id/right_hvac_container"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_alignParentEnd="true"
+ >
+
+ <com.android.systemui.statusbar.car.CarNavigationButton
+ android:id="@+id/hvacright"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@null"
+ systemui:broadcast="true"
+ systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end"
+ />
+
+ <com.android.systemui.statusbar.hvac.AnimatedTemperatureView
+ android:id="@+id/righttext"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:paddingStart="16dp"
+ android:paddingEnd="@*android:dimen/car_padding_4"
+ android:gravity="center_vertical|end"
+ android:minEms="4"
+ android:textAppearance="@style/TextAppearance.CarStatus"
+ systemui:hvacAreaId="68"
+ systemui:hvacMaxText="@string/hvac_max_text"
+ systemui:hvacMaxValue="@dimen/hvac_max_value"
+ systemui:hvacMinText="@string/hvac_min_text"
+ systemui:hvacMinValue="@dimen/hvac_min_value"
+ systemui:hvacPivotOffset="60dp"
+ systemui:hvacPropertyId="358614275"
+ systemui:hvacTempFormat="%.0f\u00B0"
+ />
+ </FrameLayout>
+ </RelativeLayout>
+
+</com.android.systemui.statusbar.car.CarNavigationBarView>
diff --git a/packages/CarSystemUI/res/layout/super_status_bar.xml b/packages/CarSystemUI/res/layout/super_status_bar.xml
index 08d48bf..37cd1d4 100644
--- a/packages/CarSystemUI/res/layout/super_status_bar.xml
+++ b/packages/CarSystemUI/res/layout/super_status_bar.xml
@@ -69,10 +69,10 @@
android:visibility="gone"
/>
- <include layout="@layout/car_top_navigation_bar"
+ <FrameLayout
+ android:id="@+id/car_top_navigation_bar_container"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- />
+ android:layout_height="wrap_content"/>
</LinearLayout>
<include layout="@layout/brightness_mirror"/>
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index a585727..9d47cdc 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -162,9 +162,11 @@
private BatteryMeterView mBatteryMeterView;
private Drawable mNotificationPanelBackground;
+ private ViewGroup mTopNavigationBarContainer;
private ViewGroup mNavigationBarWindow;
private ViewGroup mLeftNavigationBarWindow;
private ViewGroup mRightNavigationBarWindow;
+ private CarNavigationBarView mTopNavigationBarView;
private CarNavigationBarView mNavigationBarView;
private CarNavigationBarView mLeftNavigationBarView;
private CarNavigationBarView mRightNavigationBarView;
@@ -176,7 +178,7 @@
private CarFacetButtonController mCarFacetButtonController;
private ActivityManagerWrapper mActivityManagerWrapper;
private DeviceProvisionedController mDeviceProvisionedController;
- private boolean mDeviceIsProvisioned = true;
+ private boolean mDeviceIsSetUpForUser = true;
private HvacController mHvacController;
private DrivingStateHelper mDrivingStateHelper;
private PowerManagerHelper mPowerManagerHelper;
@@ -197,6 +199,9 @@
private boolean mNotificationListAtBottom;
// Was the notification list at the bottom when the user first touched the screen
private boolean mNotificationListAtBottomAtTimeOfTouch;
+ // To be attached to the top navigation bar (i.e. status bar) to pull down the notification
+ // panel.
+ private View.OnTouchListener mTopNavBarNotificationTouchListener;
// To be attached to the navigation bars such that they can close the notification panel if
// it's open.
private View.OnTouchListener mNavBarNotificationTouchListener;
@@ -364,7 +369,7 @@
// get the provisioned state before calling the parent class since it's that flow that
// builds the nav bar
mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
- mDeviceIsProvisioned = mDeviceProvisionedController.isDeviceProvisioned();
+ mDeviceIsSetUpForUser = mDeviceProvisionedController.isCurrentUserSetup();
// Keyboard related setup, before nav bars are created.
mHideNavBarForKeyboard = mContext.getResources().getBoolean(
@@ -376,6 +381,10 @@
mScreenLifecycle = Dependency.get(ScreenLifecycle.class);
mScreenLifecycle.addObserver(mScreenObserver);
+ // Need to initialize HVAC controller before calling super.start - before system bars are
+ // created.
+ mHvacController = new HvacController(mContext);
+
super.start();
mTaskStackListener = new TaskStackListenerImpl();
mActivityManagerWrapper = ActivityManagerWrapper.getInstance();
@@ -394,25 +403,18 @@
mHvacController.connectToCarService();
- CarSystemUIFactory factory = SystemUIFactory.getInstance();
- if (!mDeviceIsProvisioned) {
- mDeviceProvisionedController.addCallback(
- new DeviceProvisionedController.DeviceProvisionedListener() {
- @Override
- public void onDeviceProvisionedChanged() {
- mHandler.post(() -> {
- // on initial boot we are getting a call even though the value
- // is the same so we are confirming the reset is needed
- boolean deviceProvisioned =
- mDeviceProvisionedController.isDeviceProvisioned();
- if (mDeviceIsProvisioned != deviceProvisioned) {
- mDeviceIsProvisioned = deviceProvisioned;
- restartNavBars();
- }
- });
- }
- });
- }
+ mDeviceProvisionedController.addCallback(
+ new DeviceProvisionedController.DeviceProvisionedListener() {
+ @Override
+ public void onUserSetupChanged() {
+ mHandler.post(() -> restartNavBarsIfNecessary());
+ }
+
+ @Override
+ public void onUserSwitched() {
+ mHandler.post(() -> restartNavBarsIfNecessary());
+ }
+ });
// Register a listener for driving state changes.
mDrivingStateHelper = new DrivingStateHelper(mContext, this::onDrivingStateChanged);
@@ -424,6 +426,14 @@
mSwitchToGuestTimer = new SwitchToGuestTimer(mContext);
}
+ private void restartNavBarsIfNecessary() {
+ boolean currentUserSetup = mDeviceProvisionedController.isCurrentUserSetup();
+ if (mDeviceIsSetUpForUser != currentUserSetup) {
+ mDeviceIsSetUpForUser = currentUserSetup;
+ restartNavBars();
+ }
+ }
+
/**
* Remove all content from navbars and rebuild them. Used to allow for different nav bars
* before and after the device is provisioned. . Also for change of density and font size.
@@ -432,8 +442,8 @@
// remove and reattach all hvac components such that we don't keep a reference to unused
// ui elements
mHvacController.removeAllComponents();
- addTemperatureViewToController(mStatusBarWindow);
mCarFacetButtonController.removeAll();
+
if (mNavigationBarWindow != null) {
mNavigationBarWindow.removeAllViews();
mNavigationBarView = null;
@@ -527,7 +537,6 @@
@Override
protected void makeStatusBarView(@Nullable RegisterStatusBarResult result) {
super.makeStatusBarView(result);
- mHvacController = new HvacController(mContext);
CarSystemUIFactory factory = SystemUIFactory.getInstance();
mCarFacetButtonController = factory.getCarDependencyComponent()
@@ -552,7 +561,8 @@
* touch listeners needed for opening and closing the notification panel
*/
private void connectNotificationsUI() {
- // Attached to the status bar to detect pull down of the notification shade.
+ // Attached to the top navigation bar (i.e. status bar) to detect pull down of the
+ // notification shade.
GestureDetector openGestureDetector = new GestureDetector(mContext,
new OpenNotificationGestureListener() {
@Override
@@ -585,6 +595,18 @@
GestureDetector handleBarCloseNotificationGestureDetector = new GestureDetector(mContext,
new HandleBarCloseNotificationGestureListener());
+ mTopNavBarNotificationTouchListener = (v, event) -> {
+ if (!mDeviceIsSetUpForUser) {
+ return true;
+ }
+ boolean consumed = openGestureDetector.onTouchEvent(event);
+ if (consumed) {
+ return true;
+ }
+ maybeCompleteAnimation(event);
+ return true;
+ };
+
mNavBarNotificationTouchListener =
(v, event) -> {
boolean consumed = navBarCloseNotificationGestureDetector.onTouchEvent(event);
@@ -595,21 +617,6 @@
return true;
};
- // The following are the ui elements that the user would call the status bar.
- // This will set the status bar so it they can make call backs.
- CarNavigationBarView topBar = mStatusBarWindow.findViewById(R.id.car_top_bar);
- topBar.setStatusBar(this);
- topBar.setStatusBarWindowTouchListener((v1, event1) -> {
-
- boolean consumed = openGestureDetector.onTouchEvent(event1);
- if (consumed) {
- return true;
- }
- maybeCompleteAnimation(event1);
- return true;
- }
- );
-
mNotificationClickHandlerFactory = new NotificationClickHandlerFactory(mBarService);
mNotificationClickHandlerFactory.registerClickListener((launchResult, alertEntry) -> {
if (launchResult == ActivityManager.START_TASK_TO_FRONT
@@ -916,23 +923,30 @@
}
private void buildNavBarContent() {
+ // Always build top bar.
+ buildTopBar((mDeviceIsSetUpForUser) ? R.layout.car_top_navigation_bar :
+ R.layout.car_top_navigation_bar_unprovisioned);
+
if (mShowBottom) {
- buildBottomBar((mDeviceIsProvisioned) ? R.layout.car_navigation_bar :
+ buildBottomBar((mDeviceIsSetUpForUser) ? R.layout.car_navigation_bar :
R.layout.car_navigation_bar_unprovisioned);
}
if (mShowLeft) {
- buildLeft((mDeviceIsProvisioned) ? R.layout.car_left_navigation_bar :
+ buildLeft((mDeviceIsSetUpForUser) ? R.layout.car_left_navigation_bar :
R.layout.car_left_navigation_bar_unprovisioned);
}
if (mShowRight) {
- buildRight((mDeviceIsProvisioned) ? R.layout.car_right_navigation_bar :
+ buildRight((mDeviceIsSetUpForUser) ? R.layout.car_right_navigation_bar :
R.layout.car_right_navigation_bar_unprovisioned);
}
}
private void buildNavBarWindows() {
+ mTopNavigationBarContainer = mStatusBarWindow
+ .findViewById(R.id.car_top_navigation_bar_container);
+
if (mShowBottom) {
mNavigationBarWindow = (ViewGroup) View.inflate(mContext,
R.layout.navigation_bar_window, null);
@@ -965,15 +979,25 @@
}
boolean isKeyboardVisible = (vis & InputMethodService.IME_VISIBLE) != 0;
- if (!isKeyboardVisible) {
- attachBottomNavBarWindow();
- } else {
- detachBottomNavBarWindow();
- }
+ showBottomNavBarWindow(isKeyboardVisible);
}
private void attachNavBarWindows() {
- attachBottomNavBarWindow();
+ if (mShowBottom && !mBottomNavBarVisible) {
+ mBottomNavBarVisible = true;
+
+ WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+ LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
+ WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+ | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
+ PixelFormat.TRANSLUCENT);
+ lp.setTitle("CarNavigationBar");
+ lp.windowAnimations = 0;
+ mWindowManager.addView(mNavigationBarWindow, lp);
+ }
if (mShowLeft) {
int width = mContext.getResources().getDimensionPixelSize(
@@ -1011,47 +1035,32 @@
}
}
- /**
- * Attaches the bottom nav bar window. Can be extended to modify the specific behavior of
- * attaching the bottom nav bar.
- */
- protected void attachBottomNavBarWindow() {
+ private void showBottomNavBarWindow(boolean isKeyboardVisible) {
if (!mShowBottom) {
return;
}
- if (mBottomNavBarVisible) {
+ // If keyboard is visible and bottom nav bar not visible, this is the correct state, so do
+ // nothing. Same with if keyboard is not visible and bottom nav bar is visible.
+ if (isKeyboardVisible ^ mBottomNavBarVisible) {
return;
}
- mBottomNavBarVisible = true;
- WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
- LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
- WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
- WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
- | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
- | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
- PixelFormat.TRANSLUCENT);
- lp.setTitle("CarNavigationBar");
- lp.windowAnimations = 0;
- mWindowManager.addView(mNavigationBarWindow, lp);
+ mNavigationBarWindow.setVisibility(isKeyboardVisible ? View.GONE : View.VISIBLE);
+ mBottomNavBarVisible = !isKeyboardVisible;
}
- /**
- * Detaches the bottom nav bar window. Can be extended to modify the specific behavior of
- * detaching the bottom nav bar.
- */
- protected void detachBottomNavBarWindow() {
- if (!mShowBottom) {
- return;
+ private void buildTopBar(int layout) {
+ mTopNavigationBarContainer.removeAllViews();
+ View.inflate(mContext, layout, mTopNavigationBarContainer);
+ mTopNavigationBarView = (CarNavigationBarView) mTopNavigationBarContainer.getChildAt(0);
+ if (mTopNavigationBarView == null) {
+ Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_top_navigation_bar");
+ throw new RuntimeException("Unable to build top nav bar due to missing layout");
}
-
- if (!mBottomNavBarVisible) {
- return;
- }
- mBottomNavBarVisible = false;
- mWindowManager.removeView(mNavigationBarWindow);
+ mTopNavigationBarView.setStatusBar(this);
+ addTemperatureViewToController(mTopNavigationBarView);
+ mTopNavigationBarView.setStatusBarWindowTouchListener(mTopNavBarNotificationTouchListener);
}
private void buildBottomBar(int layout) {
@@ -1062,7 +1071,7 @@
mNavigationBarView = (CarNavigationBarView) mNavigationBarWindow.getChildAt(0);
if (mNavigationBarView == null) {
Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_navigation_bar");
- throw new RuntimeException("Unable to build botom nav bar due to missing layout");
+ throw new RuntimeException("Unable to build bottom nav bar due to missing layout");
}
mNavigationBarView.setStatusBar(this);
addTemperatureViewToController(mNavigationBarView);
@@ -1073,7 +1082,7 @@
View.inflate(mContext, layout, mLeftNavigationBarWindow);
mLeftNavigationBarView = (CarNavigationBarView) mLeftNavigationBarWindow.getChildAt(0);
if (mLeftNavigationBarView == null) {
- Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_navigation_bar");
+ Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_left_navigation_bar");
throw new RuntimeException("Unable to build left nav bar due to missing layout");
}
mLeftNavigationBarView.setStatusBar(this);
@@ -1086,7 +1095,7 @@
View.inflate(mContext, layout, mRightNavigationBarWindow);
mRightNavigationBarView = (CarNavigationBarView) mRightNavigationBarWindow.getChildAt(0);
if (mRightNavigationBarView == null) {
- Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_navigation_bar");
+ Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_right_navigation_bar");
throw new RuntimeException("Unable to build right nav bar due to missing layout");
}
mRightNavigationBarView.setStatusBar(this);
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/OngoingPrivacyChip.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/OngoingPrivacyChip.java
index ead1de2..88d641e 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/OngoingPrivacyChip.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/OngoingPrivacyChip.java
@@ -47,6 +47,7 @@
public class OngoingPrivacyChip extends LinearLayout implements View.OnClickListener {
private Context mContext;
+ private Handler mHandler;
private LinearLayout mIconsContainer;
private List<PrivacyItem> mPrivacyItems;
@@ -88,6 +89,7 @@
private void init(Context context) {
mContext = context;
+ mHandler = new Handler(Looper.getMainLooper());
mPrivacyItems = new ArrayList<>();
sAppOpsController = Dependency.get(AppOpsController.class);
mUserManager = mContext.getSystemService(UserManager.class);
@@ -131,8 +133,7 @@
@Override
public void onClick(View v) {
updatePrivacyList();
- Handler mUiHandler = new Handler(Looper.getMainLooper());
- mUiHandler.post(() -> {
+ mHandler.post(() -> {
mActivityStarter.postStartActivityDismissingKeyguard(
new Intent(Intent.ACTION_REVIEW_ONGOING_PERMISSION_USAGE), 0);
});
@@ -152,21 +153,17 @@
}
private void updatePrivacyList() {
- mPrivacyItems = mCurrentUserIds.stream()
+ List<PrivacyItem> privacyItems = mCurrentUserIds.stream()
.flatMap(item -> sAppOpsController.getActiveAppOpsForUser(item).stream())
.filter(Objects::nonNull)
.map(item -> toPrivacyItem(item))
.filter(Objects::nonNull)
.collect(Collectors.toList());
- mPrivacyDialogBuilder = new PrivacyDialogBuilder(mContext, mPrivacyItems);
-
- Handler refresh = new Handler(Looper.getMainLooper());
- refresh.post(new Runnable() {
- @Override
- public void run() {
- updateView();
- }
- });
+ if (!privacyItems.equals(mPrivacyItems)) {
+ mPrivacyItems = privacyItems;
+ mPrivacyDialogBuilder = new PrivacyDialogBuilder(mContext, mPrivacyItems);
+ mHandler.post(this::updateView);
+ }
}
private PrivacyItem toPrivacyItem(AppOpItem appOpItem) {
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyApplication.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyApplication.java
index 5ec7a77..820bea9 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyApplication.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyApplication.java
@@ -23,16 +23,20 @@
import android.graphics.drawable.Drawable;
import android.util.Log;
+import java.util.Objects;
+
/**
* Class to hold the data for the applications that are using the AppOps permissions.
*/
public class PrivacyApplication {
private static final String TAG = "PrivacyApplication";
+ private String mPackageName;
private Drawable mIcon;
private String mApplicationName;
public PrivacyApplication(String packageName, Context context) {
+ mPackageName = packageName;
try {
CarUserManagerHelper carUserManagerHelper = new CarUserManagerHelper(context);
ApplicationInfo app = context.getPackageManager()
@@ -59,4 +63,17 @@
public String getApplicationName() {
return mApplicationName;
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ PrivacyApplication that = (PrivacyApplication) o;
+ return mPackageName.equals(that.mPackageName);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mPackageName);
+ }
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyItem.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyItem.java
index fca1373..d3e123e 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyItem.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyItem.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.car.privacy;
+import java.util.Objects;
+
/**
* Class for holding the data of each privacy item displayed in {@link OngoingPrivacyDialog}
*/
@@ -43,4 +45,18 @@
public PrivacyType getPrivacyType() {
return mPrivacyType;
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ PrivacyItem that = (PrivacyItem) o;
+ return mPrivacyType == that.mPrivacyType
+ && mPrivacyApplication.equals(that.mPrivacyApplication);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mPrivacyType, mPrivacyApplication);
+ }
}
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
index 81c5bcd..642dc82 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
@@ -35,7 +35,6 @@
import android.net.http.SslError;
import android.os.Bundle;
import android.telephony.CarrierConfigManager;
-import android.telephony.Rlog;
import android.telephony.SubscriptionManager;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -477,11 +476,11 @@
}
private static void logd(String s) {
- Rlog.d(TAG, s);
+ Log.d(TAG, s);
}
private static void loge(String s) {
- Rlog.d(TAG, s);
+ Log.d(TAG, s);
}
}
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CustomConfigLoader.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CustomConfigLoader.java
index 02c61d7..46b1d5f 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CustomConfigLoader.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CustomConfigLoader.java
@@ -19,7 +19,6 @@
import android.content.Intent;
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
-import android.telephony.Rlog;
import android.text.TextUtils;
import android.util.Log;
@@ -27,7 +26,6 @@
import com.android.internal.util.ArrayUtils;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
/**
@@ -44,7 +42,7 @@
private static final String INTER_GROUP_DELIMITER = "\\s*:\\s*";
private static final String TAG = CustomConfigLoader.class.getSimpleName();
- private static final boolean VDBG = Rlog.isLoggable(TAG, Log.VERBOSE);
+ private static final boolean VDBG = Log.isLoggable(TAG, Log.VERBOSE);
/**
* loads and parses the carrier config, return a list of carrier action for the given signal
@@ -70,7 +68,7 @@
// return an empty list if no match found
List<Integer> actionList = new ArrayList<>();
if (carrierConfigManager == null) {
- Rlog.e(TAG, "load carrier config failure with carrier config manager uninitialized");
+ Log.e(TAG, "load carrier config failure with carrier config manager uninitialized");
return actionList;
}
PersistableBundle b = carrierConfigManager.getConfig();
@@ -101,8 +99,8 @@
.EXTRA_DEFAULT_NETWORK_AVAILABLE_KEY, false));
break;
default:
- Rlog.e(TAG, "load carrier config failure with un-configured key: " +
- intent.getAction());
+ Log.e(TAG, "load carrier config failure with un-configured key: "
+ + intent.getAction());
break;
}
if (!ArrayUtils.isEmpty(configs)) {
@@ -111,12 +109,12 @@
matchConfig(config, arg1, arg2, actionList);
if (!actionList.isEmpty()) {
// return the first match
- if (VDBG) Rlog.d(TAG, "found match action list: " + actionList.toString());
+ if (VDBG) Log.d(TAG, "found match action list: " + actionList.toString());
return actionList;
}
}
}
- Rlog.d(TAG, "no matching entry for signal: " + intent.getAction() + "arg1: " + arg1
+ Log.d(TAG, "no matching entry for signal: " + intent.getAction() + "arg1: " + arg1
+ "arg2: " + arg2);
}
return actionList;
@@ -166,7 +164,7 @@
try {
actionList.add(Integer.parseInt(idx));
} catch (NumberFormatException e) {
- Rlog.e(TAG, "NumberFormatException(string: " + idx + " config:" + config + "): "
+ Log.e(TAG, "NumberFormatException(string: " + idx + " config:" + config + "): "
+ e);
}
}
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index 1b27b52..48d34ae 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -16,6 +16,7 @@
package com.android.externalstorage;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.usage.StorageStatsManager;
import android.content.ContentResolver;
@@ -298,6 +299,53 @@
return projection != null ? projection : DEFAULT_ROOT_PROJECTION;
}
+ /**
+ * Check that the directory is the root of storage or blocked file from tree.
+ *
+ * @param docId the docId of the directory to be checked
+ * @return true, should be blocked from tree. Otherwise, false.
+ */
+ @Override
+ protected boolean shouldBlockFromTree(@NonNull String docId) {
+ try {
+ final File dir = getFileForDocId(docId, true /* visible */).getCanonicalFile();
+ if (!dir.isDirectory()) {
+ return false;
+ }
+
+ final String path = dir.getAbsolutePath();
+
+ // Block Download folder from tree
+ if (MediaStore.Downloads.isDownloadDir(path)) {
+ return true;
+ }
+
+ final ArrayMap<String, RootInfo> roots = new ArrayMap<>();
+
+ synchronized (mRootsLock) {
+ roots.putAll(mRoots);
+ }
+
+ // block root of storage
+ for (int i = 0; i < roots.size(); i++) {
+ RootInfo rootInfo = roots.valueAt(i);
+ // skip home root
+ if (TextUtils.equals(rootInfo.rootId, ROOT_ID_HOME)) {
+ continue;
+ }
+
+ // block the root of storage
+ if (TextUtils.equals(path, rootInfo.visiblePath.getAbsolutePath())) {
+ return true;
+ }
+ }
+ return false;
+ } catch (IOException e) {
+ throw new IllegalArgumentException(
+ "Failed to determine if " + docId + " should block from tree " + ": " + e);
+ }
+ }
+
@Override
protected String getDocIdForFile(File file) throws FileNotFoundException {
return getDocIdForFileMaybeCreate(file, false);
diff --git a/packages/PackageInstaller/res/values-television/themes.xml b/packages/PackageInstaller/res/values-television/themes.xml
new file mode 100644
index 0000000..5ae4957
--- /dev/null
+++ b/packages/PackageInstaller/res/values-television/themes.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<resources>
+
+ <style name="Theme.AlertDialogActivity.NoAnimation">
+ <item name="android:windowAnimationStyle">@null</item>
+ </style>
+
+ <style name="Theme.AlertDialogActivity"
+ parent="@android:style/Theme.DeviceDefault.Light.Dialog.Alert" />
+
+ <style name="Theme.AlertDialogActivity.NoActionBar"
+ parent="@android:style/Theme.DeviceDefault.Light.NoActionBar">
+ </style>
+
+</resources>
diff --git a/packages/PrintSpooler/TEST_MAPPING b/packages/PrintSpooler/TEST_MAPPING
new file mode 100644
index 0000000..4fa8822
--- /dev/null
+++ b/packages/PrintSpooler/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsPrintTestCases",
+ "options": [
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ }
+ ]
+ }
+ ]
+}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 5e2b7c8..17c621e 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -51,6 +51,7 @@
import com.android.internal.util.XmlUtils;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockPatternView;
+import com.android.internal.widget.LockscreenCredential;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -1992,8 +1993,11 @@
try {
LockPatternUtils lpu = new LockPatternUtils(mContext);
List<LockPatternView.Cell> cellPattern =
- LockPatternUtils.stringToPattern(lockPattern);
- lpu.saveLockPattern(cellPattern, null, UserHandle.USER_SYSTEM);
+ LockPatternUtils.byteArrayToPattern(lockPattern.getBytes());
+ lpu.setLockCredential(
+ LockscreenCredential.createPattern(cellPattern),
+ LockscreenCredential.createNone(),
+ UserHandle.USER_SYSTEM);
} catch (IllegalArgumentException e) {
// Don't want corrupted lock pattern to hang the reboot process
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
index 2ef0422..748f356 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
@@ -115,4 +115,15 @@
Log.e(TAG, "Failed to clean up screenshot of recents animation", e);
}
}
+
+ /**
+ * @see {{@link IRecentsAnimationController#setWillFinishToHome(boolean)}}.
+ */
+ public void setWillFinishToHome(boolean willFinishToHome) {
+ try {
+ mAnimationController.setWillFinishToHome(willFinishToHome);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to set overview reached state", e);
+ }
+ }
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
index d45603f..ebac3d9 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
@@ -33,10 +33,9 @@
import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternChecker;
import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockscreenCredential;
import com.android.systemui.R;
-import java.util.Arrays;
-
/**
* Base class for PIN and password unlock screens.
*/
@@ -132,19 +131,20 @@
protected void verifyPasswordAndUnlock() {
if (mDismissing) return; // already verified but haven't been dismissed; don't do it again.
- final byte[] entry = getPasswordText();
+ final LockscreenCredential password =
+ LockscreenCredential.createPassword(getPasswordText());
setPasswordEntryInputEnabled(false);
if (mPendingLockCheck != null) {
mPendingLockCheck.cancel(false);
}
final int userId = KeyguardUpdateMonitor.getCurrentUser();
- if (entry.length <= MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT) {
+ if (password.size() <= MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT) {
// to avoid accidental lockout, only count attempts that are long enough to be a
// real password. This may require some tweaking.
setPasswordEntryInputEnabled(true);
onPasswordChecked(userId, false /* matched */, 0, false /* not valid - too short */);
- Arrays.fill(entry, (byte) 0);
+ password.zeroize();
return;
}
@@ -152,9 +152,9 @@
LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL);
LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL_UNLOCKED);
}
- mPendingLockCheck = LockPatternChecker.checkPassword(
+ mPendingLockCheck = LockPatternChecker.checkCredential(
mLockPatternUtils,
- entry,
+ password,
userId,
new LockPatternChecker.OnCheckCallback() {
@@ -166,7 +166,7 @@
}
onPasswordChecked(userId, true /* matched */, 0 /* timeoutMs */,
true /* isValidPassword */);
- Arrays.fill(entry, (byte) 0);
+ password.zeroize();
}
@Override
@@ -181,7 +181,7 @@
onPasswordChecked(userId, false /* matched */, timeoutMs,
true /* isValidPassword */);
}
- Arrays.fill(entry, (byte) 0);
+ password.zeroize();
}
@Override
@@ -192,7 +192,7 @@
LatencyTracker.getInstance(mContext).onActionEnd(
ACTION_CHECK_CREDENTIAL_UNLOCKED);
}
- Arrays.fill(entry, (byte) 0);
+ password.zeroize();
}
});
}
@@ -223,7 +223,7 @@
}
protected abstract void resetPasswordText(boolean animate, boolean announce);
- protected abstract byte[] getPasswordText();
+ protected abstract CharSequence getPasswordText();
protected abstract void setPasswordEntryEnabled(boolean enabled);
protected abstract void setPasswordEntryInputEnabled(boolean enabled);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
index e3ac0f6..12c9fc9 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
@@ -243,8 +243,8 @@
}
@Override
- protected byte[] getPasswordText() {
- return charSequenceToByteArray(mPasswordEntry.getText());
+ protected CharSequence getPasswordText() {
+ return mPasswordEntry.getText();
}
@Override
@@ -379,18 +379,4 @@
return getContext().getString(
com.android.internal.R.string.keyguard_accessibility_password_unlock);
}
-
- /*
- * This method avoids creating a new string when getting a byte array from EditView#getText().
- */
- private static byte[] charSequenceToByteArray(CharSequence chars) {
- if (chars == null) {
- return null;
- }
- byte[] bytes = new byte[chars.length()];
- for (int i = 0; i < chars.length(); i++) {
- bytes[i] = (byte) chars.charAt(i);
- }
- return bytes;
- }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
index 297052f..9eb168a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
@@ -39,6 +39,7 @@
import com.android.internal.widget.LockPatternChecker;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockPatternView;
+import com.android.internal.widget.LockscreenCredential;
import com.android.settingslib.animation.AppearAnimationCreator;
import com.android.settingslib.animation.AppearAnimationUtils;
import com.android.settingslib.animation.DisappearAnimationUtils;
@@ -297,9 +298,9 @@
LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL);
LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL_UNLOCKED);
}
- mPendingLockCheck = LockPatternChecker.checkPattern(
+ mPendingLockCheck = LockPatternChecker.checkCredential(
mLockPatternUtils,
- pattern,
+ LockscreenCredential.createPattern(pattern),
userId,
new LockPatternChecker.OnCheckCallback() {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
index 274f739..8e9df55 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
@@ -167,8 +167,8 @@
}
@Override
- protected byte[] getPasswordText() {
- return charSequenceToByteArray(mPasswordEntry.getText());
+ protected CharSequence getPasswordText() {
+ return mPasswordEntry.getText();
}
@Override
@@ -266,18 +266,4 @@
return getContext().getString(
com.android.internal.R.string.keyguard_accessibility_pin_unlock);
}
-
- /*
- * This method avoids creating a new string when getting a byte array from EditView#getText().
- */
- private static byte[] charSequenceToByteArray(CharSequence chars) {
- if (chars == null) {
- return null;
- }
- byte[] bytes = new byte[chars.length()];
- for (int i = 0; i < chars.length(); i++) {
- bytes[i] = (byte) chars.charAt(i);
- }
- return bytes;
- }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
index af4e61b..5d35169 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
@@ -31,6 +31,7 @@
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
+import android.graphics.text.LineBreaker;
import android.net.Uri;
import android.os.Trace;
import android.provider.Settings;
@@ -152,6 +153,7 @@
mRowWithHeaderTextSize = mContext.getResources().getDimensionPixelSize(
R.dimen.header_row_font_size);
mTitle.setOnClickListener(this);
+ mTitle.setBreakStrategy(LineBreaker.BREAK_STRATEGY_BALANCED);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 3a7a7f7..ad20986 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -460,7 +460,7 @@
| WindowManager.LayoutParams.FLAG_SLIPPERY
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
PixelFormat.TRANSLUCENT);
- lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS
+ lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS
| WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
if (!DEBUG_SCREENSHOT_ROUNDED_CORNERS) {
diff --git a/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java b/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java
index a8591bb..10009f5 100644
--- a/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java
@@ -203,7 +203,7 @@
mWinParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
mWinParams.format = PixelFormat.TRANSLUCENT;
- mWinParams.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ mWinParams.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
mWinParams.setTitle(SizeCompatModeActivityController.class.getSimpleName()
+ context.getDisplayId());
}
diff --git a/packages/SystemUI/src/com/android/systemui/SysUIToast.java b/packages/SystemUI/src/com/android/systemui/SysUIToast.java
index 8bcf057..0f7f1be 100644
--- a/packages/SystemUI/src/com/android/systemui/SysUIToast.java
+++ b/packages/SystemUI/src/com/android/systemui/SysUIToast.java
@@ -31,7 +31,7 @@
public static Toast makeText(Context context, CharSequence text, @Duration int duration) {
Toast toast = Toast.makeText(context, text, duration);
toast.getWindowParams().privateFlags |=
- WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
return toast;
}
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistModule.java b/packages/SystemUI/src/com/android/systemui/assist/AssistModule.java
index 739eade..6f5a17d 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistModule.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistModule.java
@@ -21,6 +21,7 @@
import android.os.HandlerThread;
import android.os.SystemClock;
+import androidx.annotation.Nullable;
import androidx.slice.Clock;
import com.android.internal.app.AssistUtils;
@@ -68,6 +69,7 @@
}
@Provides
+ @Nullable
static AssistHandleViewController provideAssistHandleViewController(
NavigationBarController navigationBarController) {
return navigationBarController.getAssistHandlerViewController();
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index a9359d4..f1abdb3 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -580,7 +580,7 @@
WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
PixelFormat.TRANSLUCENT);
- lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
lp.setTitle("BiometricPrompt");
lp.token = windowToken;
return lp;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
index 8df072e..bebaa4b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
@@ -26,7 +26,7 @@
import android.widget.TextView;
import com.android.internal.widget.LockPatternChecker;
-import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockscreenCredential;
import com.android.systemui.R;
/**
@@ -96,13 +96,16 @@
}
private void checkPasswordAndUnlock() {
- final byte[] password = LockPatternUtils.charSequenceToByteArray(mPasswordField.getText());
- if (password == null || password.length == 0) {
- return;
- }
+ try (LockscreenCredential password = mCredentialType == Utils.CREDENTIAL_PIN
+ ? LockscreenCredential.createPinOrNone(mPasswordField.getText())
+ : LockscreenCredential.createPasswordOrNone(mPasswordField.getText())) {
+ if (password.isNone()) {
+ return;
+ }
- mPendingLockCheck = LockPatternChecker.checkPassword(mLockPatternUtils,
- password, mUserId, this::onCredentialChecked);
+ mPendingLockCheck = LockPatternChecker.checkCredential(mLockPatternUtils,
+ password, mUserId, this::onCredentialChecked);
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java
index 6c36f82..14414a4 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java
@@ -22,6 +22,7 @@
import com.android.internal.widget.LockPatternChecker;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockPatternView;
+import com.android.internal.widget.LockscreenCredential;
import com.android.systemui.R;
import java.util.List;
@@ -64,11 +65,13 @@
return;
}
- mPendingLockCheck = LockPatternChecker.checkPattern(
- mLockPatternUtils,
- pattern,
- mUserId,
- this::onPatternChecked);
+ try (LockscreenCredential credential = LockscreenCredential.createPattern(pattern)) {
+ mPendingLockCheck = LockPatternChecker.checkCredential(
+ mLockPatternUtils,
+ credential,
+ mUserId,
+ this::onPatternChecked);
+ }
}
private void onPatternChecked(boolean matched, int timeoutMs) {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
index ca4ec6d..b069ba3 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
@@ -75,7 +75,7 @@
handler, wakeLock, machine, dockManager, dozeLog),
createDozeUi(context, host, wakeLock, machine, handler, alarmManager, params,
dozeLog),
- new DozeScreenState(wrappedService, handler, params, wakeLock),
+ new DozeScreenState(wrappedService, handler, host, params, wakeLock),
createDozeScreenBrightness(context, wrappedService, sensorManager, host, params,
handler),
new DozeWallpaperState(context, getBiometricUnlockController(dozeService)),
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
index 07dd2cd..1a6bd60 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
@@ -63,9 +63,10 @@
void setDozeScreenBrightness(int value);
/**
- * Makes scrims black and changes animation durations.
+ * Fade out screen before switching off the display power mode.
+ * @param onDisplayOffCallback Executed when the display is black.
*/
- default void prepareForGentleWakeUp() {}
+ void prepareForGentleSleep(Runnable onDisplayOffCallback);
void onIgnoreTouchWhilePulsing(boolean ignore);
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
index 38ee2fe..95c42fc 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
@@ -16,6 +16,12 @@
package com.android.systemui.doze;
+import static com.android.systemui.doze.DozeMachine.State.DOZE;
+import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD;
+import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSED;
+import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSING;
+import static com.android.systemui.doze.DozeMachine.State.DOZE_PULSE_DONE;
+
import android.os.Handler;
import android.util.Log;
import android.view.Display;
@@ -48,21 +54,24 @@
private final Handler mHandler;
private final Runnable mApplyPendingScreenState = this::applyPendingScreenState;
private final DozeParameters mParameters;
+ private final DozeHost mDozeHost;
private int mPendingScreenState = Display.STATE_UNKNOWN;
private SettableWakeLock mWakeLock;
- public DozeScreenState(DozeMachine.Service service, Handler handler,
+ public DozeScreenState(DozeMachine.Service service, Handler handler, DozeHost host,
DozeParameters parameters, WakeLock wakeLock) {
mDozeService = service;
mHandler = handler;
mParameters = parameters;
+ mDozeHost = host;
mWakeLock = new SettableWakeLock(wakeLock, TAG);
}
@Override
public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) {
int screenState = newState.screenState(mParameters);
+ mDozeHost.prepareForGentleSleep(null);
if (newState == DozeMachine.State.FINISH) {
// Make sure not to apply the screen state after DozeService was destroyed.
@@ -79,12 +88,13 @@
return;
}
- boolean messagePending = mHandler.hasCallbacks(mApplyPendingScreenState);
- boolean pulseEnding = oldState == DozeMachine.State.DOZE_PULSE_DONE
- && newState == DozeMachine.State.DOZE_AOD;
- boolean turningOn = (oldState == DozeMachine.State.DOZE_AOD_PAUSED
- || oldState == DozeMachine.State.DOZE) && newState == DozeMachine.State.DOZE_AOD;
- boolean justInitialized = oldState == DozeMachine.State.INITIALIZED;
+ final boolean messagePending = mHandler.hasCallbacks(mApplyPendingScreenState);
+ final boolean pulseEnding = oldState == DOZE_PULSE_DONE && newState == DOZE_AOD;
+ final boolean turningOn = (oldState == DOZE_AOD_PAUSED
+ || oldState == DOZE) && newState == DOZE_AOD;
+ final boolean turningOff = (oldState == DOZE_AOD && newState == DOZE)
+ || (oldState == DOZE_AOD_PAUSING && newState == DOZE_AOD_PAUSED);
+ final boolean justInitialized = oldState == DozeMachine.State.INITIALIZED;
if (messagePending || justInitialized || pulseEnding || turningOn) {
// During initialization, we hide the navigation bar. That is however only applied after
// a traversal; setting the screen state here is immediate however, so it can happen
@@ -93,7 +103,7 @@
mPendingScreenState = screenState;
// Delay screen state transitions even longer while animations are running.
- boolean shouldDelayTransition = newState == DozeMachine.State.DOZE_AOD
+ boolean shouldDelayTransition = newState == DOZE_AOD
&& mParameters.shouldControlScreenOff() && !turningOn;
if (shouldDelayTransition) {
@@ -114,6 +124,8 @@
} else if (DEBUG) {
Log.d(TAG, "Pending display state change to " + screenState);
}
+ } else if (turningOff) {
+ mDozeHost.prepareForGentleSleep(() -> applyScreenState(screenState));
} else {
applyScreenState(screenState);
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
index 2c0ccd21..f155783 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
@@ -135,7 +135,6 @@
break;
case DOZE:
case DOZE_AOD_PAUSED:
- mHost.prepareForGentleWakeUp();
unscheduleTimeTick();
break;
case DOZE_REQUEST_PULSE:
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java
index 8224365..3f15966 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java
@@ -107,7 +107,7 @@
| LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
lp.setTitle("pip-dismiss-overlay");
- lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
lp.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
mWindowManager.addView(mDismissView, lp);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index ae83567..2e24403 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -28,10 +28,12 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
+import android.provider.Settings;
import android.service.quicksettings.Tile;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
+import android.widget.FrameLayout;
import android.widget.LinearLayout;
import com.android.internal.logging.MetricsLogger;
@@ -49,6 +51,8 @@
import com.android.systemui.qs.external.CustomTile;
import com.android.systemui.settings.BrightnessController;
import com.android.systemui.settings.ToggleSliderView;
+import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.statusbar.phone.NPVPluginManager;
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
import com.android.systemui.statusbar.policy.BrightnessMirrorController.BrightnessMirrorListener;
import com.android.systemui.tuner.TunerService;
@@ -98,6 +102,10 @@
private BrightnessMirrorController mBrightnessMirrorController;
private View mDivider;
+ private FrameLayout mPluginFrame;
+ private final PluginManager mPluginManager;
+ private NPVPluginManager mNPVPluginManager;
+
public QSPanel(Context context) {
this(context, null);
}
@@ -106,9 +114,13 @@
this(context, attrs, null);
}
+ public QSPanel(Context context, AttributeSet attrs, DumpController dumpController) {
+ this(context, attrs, dumpController, null);
+ }
+
@Inject
public QSPanel(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
- DumpController dumpController) {
+ DumpController dumpController, PluginManager pluginManager) {
super(context, attrs);
mContext = context;
@@ -136,6 +148,15 @@
mBrightnessController = new BrightnessController(getContext(),
findViewById(R.id.brightness_slider));
mDumpController = dumpController;
+ mPluginManager = pluginManager;
+ if (mPluginManager != null && Settings.System.getInt(
+ mContext.getContentResolver(), "npv_plugin_flag", 0) == 2) {
+ mPluginFrame = (FrameLayout) LayoutInflater.from(mContext).inflate(
+ R.layout.status_bar_expanded_plugin_frame, this, false);
+ addView(mPluginFrame);
+ mNPVPluginManager = new NPVPluginManager(mPluginFrame, mPluginManager);
+ }
+
}
protected void addDivider() {
@@ -377,6 +398,7 @@
if (mListening) {
refreshAllTiles();
}
+ if (mNPVPluginManager != null) mNPVPluginManager.setListening(listening);
}
public void setListening(boolean listening, boolean expanded) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java
index c1ce163..aa64449 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java
@@ -497,7 +497,7 @@
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
flags,
PixelFormat.TRANSLUCENT);
- lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
lp.setTitle("RecentsOnboarding");
lp.gravity = gravity;
return lp;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
index 0f277ca..2d1c087 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
@@ -128,7 +128,7 @@
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
lp.token = new Binder();
- lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
lp.setTitle("ScreenPinningConfirmation");
lp.gravity = Gravity.FILL;
return lp;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
index e09e9cd..5144a95 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
@@ -233,11 +233,13 @@
}
/** @return {@link NavigationBarFragment} on the default display. */
+ @Nullable
public NavigationBarFragment getDefaultNavigationBarFragment() {
return mNavigationBars.get(DEFAULT_DISPLAY);
}
/** @return {@link AssistHandleViewController} (only on the default display). */
+ @Nullable
public AssistHandleViewController getAssistHandlerViewController() {
NavigationBarFragment navBar = getDefaultNavigationBarFragment();
return navBar == null ? null : navBar.getAssistHandlerViewController();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DoubleTapHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DoubleTapHelper.java
index 0d62703..78ea5c0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DoubleTapHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DoubleTapHelper.java
@@ -100,6 +100,7 @@
event.getY() - mActivationY);
}
if (withinDoubleTapSlop) {
+ makeInactive();
if (!mDoubleTapListener.onDoubleTap()) {
return false;
}
@@ -134,6 +135,7 @@
if (mActivated) {
mActivated = false;
mActivationListener.onActiveChanged(false);
+ mView.removeCallbacks(mTapTimeoutRunnable);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
index ca7c227..442c089 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
@@ -276,7 +276,7 @@
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
PixelFormat.TRANSLUCENT);
mEdgePanelLp.privateFlags |=
- WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
mEdgePanelLp.setTitle(TAG + mDisplayId);
mEdgePanelLp.accessibilityTitle = mContext.getString(R.string.nav_bar_edge_panel);
mEdgePanelLp.windowAnimations = 0;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FloatingRotationButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FloatingRotationButton.java
index deb314b..a784984 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FloatingRotationButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FloatingRotationButton.java
@@ -82,7 +82,7 @@
final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(mDiameter, mDiameter,
mMargin, mMargin, WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, flags,
PixelFormat.TRANSLUCENT);
- lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
lp.setTitle("FloatingRotationButton");
switch (mWindowManager.getDefaultDisplay().getRotation()) {
case Surface.ROTATION_0:
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 177294b..9ab635c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -43,6 +43,7 @@
import android.os.PowerManager;
import android.os.SystemClock;
import android.provider.DeviceConfig;
+import android.provider.Settings;
import android.util.AttributeSet;
import android.util.Log;
import android.util.MathUtils;
@@ -541,7 +542,10 @@
mQsNavbarScrim = findViewById(R.id.qs_navbar_scrim);
mLastOrientation = getResources().getConfiguration().orientation;
mPluginFrame = findViewById(R.id.plugin_frame);
- mNPVPluginManager = new NPVPluginManager(mPluginFrame, mPluginManager);
+ if (Settings.System.getInt(
+ mContext.getContentResolver(), "npv_plugin_flag", 0) == 1) {
+ mNPVPluginManager = new NPVPluginManager(mPluginFrame, mPluginManager);
+ }
initBottomArea();
@@ -778,7 +782,7 @@
mPluginFrame.setLayoutParams(lp);
}
- mNPVPluginManager.replaceFrameLayout(mPluginFrame);
+ if (mNPVPluginManager != null) mNPVPluginManager.replaceFrameLayout(mPluginFrame);
}
private void initBottomArea() {
@@ -808,8 +812,10 @@
int oldMaxHeight = mQsMaxExpansionHeight;
if (mQs != null) {
mQsMinExpansionHeight = mKeyguardShowing ? 0 : mQs.getQsMinExpansionHeight();
- mNPVPluginManager.setYOffset(mQsMinExpansionHeight);
- mQsMinExpansionHeight += mNPVPluginManager.getHeight();
+ if (mNPVPluginManager != null) {
+ mNPVPluginManager.setYOffset(mQsMinExpansionHeight);
+ mQsMinExpansionHeight += mNPVPluginManager.getHeight();
+ }
mQsMaxExpansionHeight = mQs.getDesiredHeight();
mNotificationStackScroller.setMaxTopPadding(
mQsMaxExpansionHeight + mQsNotificationTopPadding);
@@ -1915,9 +1921,11 @@
mBarState != StatusBarState.KEYGUARD && (!mQsExpanded
|| mQsExpansionFromOverscroll));
updateEmptyShadeView();
- mNPVPluginManager.changeVisibility((mBarState != StatusBarState.KEYGUARD)
- ? View.VISIBLE
- : View.INVISIBLE);
+ if (mNPVPluginManager != null) {
+ mNPVPluginManager.changeVisibility((mBarState != StatusBarState.KEYGUARD)
+ ? View.VISIBLE
+ : View.INVISIBLE);
+ }
mQsNavbarScrim.setVisibility(mBarState == StatusBarState.SHADE && mQsExpanded
&& !mStackScrollerOverscrolling && mQsScrimEnabled
? View.VISIBLE
@@ -1975,7 +1983,9 @@
float qsExpansionFraction = getQsExpansionFraction();
mQs.setQsExpansion(qsExpansionFraction, getHeaderTranslation());
int heightDiff = mQs.getDesiredHeight() - mQs.getQsMinExpansionHeight();
- mNPVPluginManager.setExpansion(qsExpansionFraction, getHeaderTranslation(), heightDiff);
+ if (mNPVPluginManager != null) {
+ mNPVPluginManager.setExpansion(qsExpansionFraction, getHeaderTranslation(), heightDiff);
+ }
mNotificationStackScroller.setQsExpansionFraction(qsExpansionFraction);
}
@@ -2396,7 +2406,7 @@
appearAmount = mNotificationStackScroller.calculateAppearFractionBypass();
}
startHeight = -mQs.getQsMinExpansionHeight();
- startHeight -= mNPVPluginManager.getHeight();
+ if (mNPVPluginManager != null) startHeight -= mNPVPluginManager.getHeight();
}
float translation = MathUtils.lerp(startHeight, 0,
Math.min(1.0f, appearAmount))
@@ -2540,7 +2550,7 @@
mKeyguardStatusBar.setListening(listening);
if (mQs == null) return;
mQs.setListening(listening);
- mNPVPluginManager.setListening(listening);
+ if (mNPVPluginManager != null) mNPVPluginManager.setListening(listening);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 5ba19cc..e7d896c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -258,7 +258,7 @@
final ScrimState oldState = mState;
mState = state;
- Trace.traceCounter(Trace.TRACE_TAG_APP, "scrim_state", mState.getIndex());
+ Trace.traceCounter(Trace.TRACE_TAG_APP, "scrim_state", mState.ordinal());
if (mCallback != null) {
mCallback.onCancelled();
@@ -519,22 +519,6 @@
}
/**
- * Set front scrim to black, cancelling animations, in order to prepare to fade them
- * away once the display turns on.
- */
- public void prepareForGentleWakeUp() {
- if (mState == ScrimState.AOD && mDozeParameters.getAlwaysOn()) {
- mInFrontAlpha = 1f;
- mInFrontTint = Color.BLACK;
- mBehindTint = Color.BLACK;
- mAnimateChange = false;
- updateScrims();
- mAnimateChange = true;
- mAnimationDuration = ANIMATION_DURATION_LONG;
- }
- }
-
- /**
* If the lock screen sensor is active.
*/
public void setWakeLockScreenSensorActive(boolean active) {
@@ -595,6 +579,8 @@
setScrimAlpha(mScrimInFront, mInFrontAlpha);
setScrimAlpha(mScrimBehind, mBehindAlpha);
setScrimAlpha(mScrimForBubble, mBubbleAlpha);
+ // The animation could have all already finished, let's call onFinished just in case
+ onFinished();
dispatchScrimsVisible();
}
@@ -693,9 +679,9 @@
@Override
public void onAnimationEnd(Animator animation) {
+ scrim.setTag(TAG_KEY_ANIM, null);
onFinished(lastCallback);
- scrim.setTag(TAG_KEY_ANIM, null);
dispatchScrimsVisible();
if (!mDeferFinishedListener && mOnAnimationFinished != null) {
@@ -759,9 +745,9 @@
}
private void onFinished(Callback callback) {
- if (!hasReachedFinalState(mScrimBehind)
- || !hasReachedFinalState(mScrimInFront)
- || !hasReachedFinalState(mScrimForBubble)) {
+ if (isAnimating(mScrimBehind)
+ || isAnimating(mScrimInFront)
+ || isAnimating(mScrimForBubble)) {
if (callback != null && callback != mCallback) {
// Since we only notify the callback that we're finished once everything has
// finished, we need to make sure that any changing callbacks are also invoked
@@ -794,11 +780,6 @@
}
}
- private boolean hasReachedFinalState(ScrimView scrim) {
- return scrim.getViewAlpha() == getCurrentScrimAlpha(scrim)
- && scrim.getTint() == getCurrentScrimTint(scrim);
- }
-
private boolean isAnimating(View scrim) {
return scrim.getTag(TAG_KEY_ANIM) != null;
}
@@ -854,10 +835,7 @@
} else {
// update the alpha directly
updateScrimColor(scrim, alpha, getCurrentScrimTint(scrim));
- onFinished();
}
- } else {
- onFinished();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index 7463c7c..e0597159 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -30,12 +30,30 @@
/**
* Initial state.
*/
- UNINITIALIZED(-1),
+ UNINITIALIZED,
+
+ /**
+ * When turned off by sensors (prox, presence.)
+ */
+ OFF {
+ @Override
+ public void prepare(ScrimState previousState) {
+ mFrontTint = Color.BLACK;
+ mBehindTint = previousState.mBehindTint;
+ mBubbleTint = previousState.mBubbleTint;
+
+ mFrontAlpha = 1f;
+ mBehindAlpha = previousState.mBehindAlpha;
+ mBubbleAlpha = previousState.mBubbleAlpha;
+
+ mAnimationDuration = ScrimController.ANIMATION_DURATION_LONG;
+ }
+ },
/**
* On the lock screen.
*/
- KEYGUARD(0) {
+ KEYGUARD {
@Override
public void prepare(ScrimState previousState) {
mBlankScreen = false;
@@ -65,7 +83,7 @@
/**
* Showing password challenge on the keyguard.
*/
- BOUNCER(1) {
+ BOUNCER {
@Override
public void prepare(ScrimState previousState) {
mBehindAlpha = ScrimController.GRADIENT_SCRIM_ALPHA_BUSY;
@@ -77,7 +95,7 @@
/**
* Showing password challenge on top of a FLAG_SHOW_WHEN_LOCKED activity.
*/
- BOUNCER_SCRIMMED(2) {
+ BOUNCER_SCRIMMED {
@Override
public void prepare(ScrimState previousState) {
mBehindAlpha = 0;
@@ -89,7 +107,7 @@
/**
* Changing screen brightness from quick settings.
*/
- BRIGHTNESS_MIRROR(3) {
+ BRIGHTNESS_MIRROR {
@Override
public void prepare(ScrimState previousState) {
mBehindAlpha = 0;
@@ -101,7 +119,7 @@
/**
* Always on display or screen off.
*/
- AOD(4) {
+ AOD {
@Override
public void prepare(ScrimState previousState) {
final boolean alwaysOnEnabled = mDozeParameters.getAlwaysOn();
@@ -136,7 +154,7 @@
/**
* When phone wakes up because you received a notification.
*/
- PULSING(5) {
+ PULSING {
@Override
public void prepare(ScrimState previousState) {
mFrontAlpha = mAodFrontScrimAlpha;
@@ -164,7 +182,7 @@
/**
* Unlocked on top of an app (launcher or any other activity.)
*/
- UNLOCKED(6) {
+ UNLOCKED {
@Override
public void prepare(ScrimState previousState) {
// State that UI will sync to.
@@ -201,7 +219,7 @@
/**
* Unlocked with a bubble expanded.
*/
- BUBBLE_EXPANDED(7) {
+ BUBBLE_EXPANDED {
@Override
public void prepare(ScrimState previousState) {
mFrontTint = Color.TRANSPARENT;
@@ -237,17 +255,12 @@
DozeParameters mDozeParameters;
boolean mDisplayRequiresBlanking;
boolean mWallpaperSupportsAmbientMode;
- int mIndex;
boolean mHasBackdrop;
boolean mLaunchingAffordanceWithPreview;
boolean mWakeLockScreenSensorActive;
boolean mKeyguardFadingAway;
long mKeyguardFadingAwayDuration;
- ScrimState(int index) {
- mIndex = index;
- }
-
public void init(ScrimView scrimInFront, ScrimView scrimBehind, ScrimView scrimForBubble,
DozeParameters dozeParameters) {
mScrimInFront = scrimInFront;
@@ -262,10 +275,6 @@
public void prepare(ScrimState previousState) {
}
- public int getIndex() {
- return mIndex;
- }
-
public float getFrontAlpha() {
return mFrontAlpha;
}
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 9093687..d1fe46e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -4018,6 +4018,13 @@
} else if (isPulsing()) {
mScrimController.transitionTo(ScrimState.PULSING,
mDozeScrimController.getScrimCallback());
+ } else if (mDozeServiceHost.hasPendingScreenOffCallback()) {
+ mScrimController.transitionTo(ScrimState.OFF, new ScrimController.Callback() {
+ @Override
+ public void onFinished() {
+ mDozeServiceHost.executePendingScreenOffCallback();
+ }
+ });
} else if (mDozing && !unlocking) {
mScrimController.transitionTo(ScrimState.AOD);
} else if (mIsKeyguard && !unlocking) {
@@ -4044,6 +4051,7 @@
private boolean mAnimateWakeup;
private boolean mAnimateScreenOff;
private boolean mIgnoreTouchWhilePulsing;
+ private Runnable mPendingScreenOffCallback;
@VisibleForTesting
boolean mWakeLockScreenPerformsAuth = SystemProperties.getBoolean(
"persist.sysui.wake_performs_auth", true);
@@ -4266,8 +4274,33 @@
}
@Override
- public void prepareForGentleWakeUp() {
- mScrimController.prepareForGentleWakeUp();
+ public void prepareForGentleSleep(Runnable onDisplayOffCallback) {
+ if (onDisplayOffCallback != null) {
+ Log.w(TAG, "Overlapping onDisplayOffCallback. Ignoring previous one.");
+ }
+ mPendingScreenOffCallback = onDisplayOffCallback;
+ updateScrimController();
+ }
+
+ /**
+ * When the dozing host is waiting for scrims to fade out to change the display state.
+ */
+ boolean hasPendingScreenOffCallback() {
+ return mPendingScreenOffCallback != null;
+ }
+
+ /**
+ * Executes an nullifies the pending display state callback.
+ *
+ * @see #hasPendingScreenOffCallback()
+ * @see #prepareForGentleSleep(Runnable)
+ */
+ void executePendingScreenOffCallback() {
+ if (mPendingScreenOffCallback == null) {
+ return;
+ }
+ mPendingScreenOffCallback.run();
+ mPendingScreenOffCallback = null;
}
private void dispatchTap(View view, float x, float y) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
index ce929b7..44be6bc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
@@ -93,10 +93,10 @@
public static void setShowForAllUsers(Dialog dialog, boolean show) {
if (show) {
dialog.getWindow().getAttributes().privateFlags |=
- WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
} else {
dialog.getWindow().getAttributes().privateFlags &=
- ~WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ ~WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
}
}
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 43795dc..cca9479 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -23,12 +23,16 @@
import android.app.Notification;
import android.app.PendingIntent;
import android.app.RemoteInput;
+import android.content.ClipDescription;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.content.pm.ShortcutManager;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.net.Uri;
import android.os.Bundle;
+import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.text.Editable;
@@ -53,8 +57,13 @@
import android.widget.ProgressBar;
import android.widget.TextView;
+import androidx.core.view.inputmethod.EditorInfoCompat;
+import androidx.core.view.inputmethod.InputConnectionCompat;
+import androidx.core.view.inputmethod.InputContentInfoCompat;
+
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
+import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
@@ -65,6 +74,7 @@
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.phone.LightBarController;
+import java.util.HashMap;
import java.util.function.Consumer;
/**
@@ -88,6 +98,8 @@
private RemoteInputController mController;
private RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler;
+ private IStatusBarService mStatusBarManagerService;
+
private NotificationEntry mEntry;
private boolean mRemoved;
@@ -103,6 +115,8 @@
public RemoteInputView(Context context, AttributeSet attrs) {
super(context, attrs);
mRemoteInputQuickSettingsDisabler = Dependency.get(RemoteInputQuickSettingsDisabler.class);
+ mStatusBarManagerService = IStatusBarService.Stub.asInterface(
+ ServiceManager.getService(Context.STATUS_BAR_SERVICE));
}
@Override
@@ -128,7 +142,7 @@
if (isSoftImeEvent || isKeyboardEnterKey) {
if (mEditText.length() > 0) {
- sendRemoteInput();
+ sendRemoteInput(prepareRemoteInputFromText());
}
// Consume action to prevent IME from closing.
return true;
@@ -141,7 +155,7 @@
mEditText.mRemoteInputView = this;
}
- private void sendRemoteInput() {
+ protected Intent prepareRemoteInputFromText() {
Bundle results = new Bundle();
results.putString(mRemoteInput.getResultKey(), mEditText.getText().toString());
Intent fillInIntent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
@@ -153,6 +167,25 @@
RemoteInput.setResultsSource(fillInIntent, RemoteInput.SOURCE_CHOICE);
}
+ return fillInIntent;
+ }
+
+ protected Intent prepareRemoteInputFromData(String contentType, Uri data) {
+ HashMap<String, Uri> results = new HashMap<>();
+ results.put(contentType, data);
+ try {
+ mStatusBarManagerService.grantInlineReplyUriPermission(
+ mEntry.notification.getKey(), data);
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to grant URI permissions:" + e.getMessage(), e);
+ }
+ Intent fillInIntent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ RemoteInput.addDataResultToIntent(mRemoteInput, fillInIntent, results);
+
+ return fillInIntent;
+ }
+
+ private void sendRemoteInput(Intent intent) {
mEditText.setEnabled(false);
mSendButton.setVisibility(INVISIBLE);
mProgressBar.setVisibility(VISIBLE);
@@ -176,7 +209,7 @@
MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_SEND,
mEntry.notification.getPackageName());
try {
- mPendingIntent.send(mContext, 0, fillInIntent);
+ mPendingIntent.send(mContext, 0, intent);
} catch (PendingIntent.CanceledException e) {
Log.i(TAG, "Unable to send remote input result", e);
MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_FAIL,
@@ -195,7 +228,9 @@
LayoutInflater.from(context).inflate(R.layout.remote_input, root, false);
v.mController = controller;
v.mEntry = entry;
- v.mEditText.setTextOperationUser(computeTextOperationUser(entry.notification.getUser()));
+ UserHandle user = computeTextOperationUser(entry.notification.getUser());
+ v.mEditText.mUser = user;
+ v.mEditText.setTextOperationUser(user);
v.setTag(VIEW_TAG);
return v;
@@ -204,7 +239,7 @@
@Override
public void onClick(View v) {
if (v == mSendButton) {
- sendRemoteInput();
+ sendRemoteInput(prepareRemoteInputFromText());
}
}
@@ -518,6 +553,7 @@
private RemoteInputView mRemoteInputView;
boolean mShowImeOnInputConnection;
private LightBarController mLightBarController;
+ UserHandle mUser;
public RemoteEditText(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -617,11 +653,47 @@
@Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
+ String[] allowedDataTypes = mRemoteInputView.mRemoteInput.getAllowedDataTypes()
+ .toArray(new String[0]);
+ EditorInfoCompat.setContentMimeTypes(outAttrs, allowedDataTypes);
final InputConnection inputConnection = super.onCreateInputConnection(outAttrs);
- if (mShowImeOnInputConnection && inputConnection != null) {
+ final InputConnectionCompat.OnCommitContentListener callback =
+ new InputConnectionCompat.OnCommitContentListener() {
+ @Override
+ public boolean onCommitContent(
+ InputContentInfoCompat inputContentInfoCompat, int i,
+ Bundle bundle) {
+ Uri contentUri = inputContentInfoCompat.getContentUri();
+ ClipDescription description = inputContentInfoCompat.getDescription();
+ String mimeType = null;
+ if (description != null && description.getMimeTypeCount() > 0) {
+ mimeType = description.getMimeType(0);
+ }
+ if (mimeType != null) {
+ Intent dataIntent = mRemoteInputView.prepareRemoteInputFromData(
+ mimeType, contentUri);
+ mRemoteInputView.sendRemoteInput(dataIntent);
+ }
+ return true;
+ }
+ };
+
+ InputConnection ic = InputConnectionCompat.createWrapper(
+ inputConnection, outAttrs, callback);
+
+ Context userContext = null;
+ try {
+ userContext = mContext.createPackageContextAsUser(
+ mContext.getPackageName(), 0, mUser);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "Unable to create user context:" + e.getMessage(), e);
+ }
+
+ if (mShowImeOnInputConnection && ic != null) {
+ Context targetContext = userContext != null ? userContext : getContext();
final InputMethodManager imm =
- getContext().getSystemService(InputMethodManager.class);
+ targetContext.getSystemService(InputMethodManager.class);
if (imm != null) {
// onCreateInputConnection is called by InputMethodManager in the middle of
// setting up the connection to the IME; wait with requesting the IME until that
@@ -636,7 +708,7 @@
}
}
- return inputConnection;
+ return ic;
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
index 1ce0172..98ec4594 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
@@ -20,13 +20,11 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.app.Instrumentation;
import android.hardware.display.AmbientDisplayConfiguration;
import android.os.Handler;
import android.os.Looper;
@@ -34,7 +32,6 @@
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
-import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -45,23 +42,24 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
public class DozeDockHandlerTest extends SysuiTestCase {
- private DozeDockHandler mDockHandler;
+ @Mock
private DozeMachine mMachine;
- private DozeHostFake mHost;
+ @Mock
+ private DozeHost mHost;
private AmbientDisplayConfiguration mConfig;
- private Instrumentation mInstrumentation;
private DockManagerFake mDockManagerFake;
+ private DozeDockHandler mDockHandler;
@Before
public void setUp() throws Exception {
- mInstrumentation = InstrumentationRegistry.getInstrumentation();
- mMachine = mock(DozeMachine.class);
- mHost = spy(new DozeHostFake());
+ MockitoAnnotations.initMocks(this);
mConfig = DozeConfigurationUtil.createMockConfig();
doReturn(false).when(mConfig).alwaysOnEnabled(anyInt());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java
deleted file mode 100644
index abfa755..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * 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.systemui.doze;
-
-import android.annotation.NonNull;
-
-/**
- * A rudimentary fake for DozeHost.
- */
-class DozeHostFake implements DozeHost {
- Callback callback;
- boolean pulseExtended;
- boolean animateWakeup;
- boolean animateScreenOff;
- boolean dozing;
- float doubleTapX;
- float doubleTapY;
- float aodDimmingScrimOpacity;
-
- @Override
- public void addCallback(@NonNull Callback callback) {
- this.callback = callback;
- }
-
- @Override
- public void removeCallback(@NonNull Callback callback) {
- this.callback = null;
- }
-
- @Override
- public void startDozing() {
- dozing = true;
- }
-
- @Override
- public void pulseWhileDozing(@NonNull PulseCallback callback, int reason) {
- throw new RuntimeException("not implemented");
- }
-
- @Override
- public void stopDozing() {
- dozing = false;
- }
-
- @Override
- public void dozeTimeTick() {
- // Nothing to do in here. Real host would just update the UI.
- }
-
- @Override
- public boolean isPowerSaveActive() {
- return false;
- }
-
- @Override
- public boolean isPulsingBlocked() {
- return false;
- }
-
- @Override
- public boolean isProvisioned() {
- return false;
- }
-
- @Override
- public boolean isBlockingDoze() {
- return false;
- }
-
- @Override
- public void onIgnoreTouchWhilePulsing(boolean ignore) {
- }
-
- @Override
- public void extendPulse(int reason) {
- pulseExtended = true;
- }
-
- @Override
- public void stopPulsing() {}
-
- @Override
- public void setAnimateWakeup(boolean animateWakeup) {
- this.animateWakeup = animateWakeup;
- }
-
- @Override
- public void setAnimateScreenOff(boolean animateScreenOff) {
- this.animateScreenOff = animateScreenOff;
- }
-
- @Override
- public void onSlpiTap(float x, float y) {
- doubleTapX = y;
- doubleTapY = y;
- }
-
- @Override
- public void setDozeScreenBrightness(int value) {
- }
-
- @Override
- public void setAodDimmingScrim(float scrimOpacity) {
- aodDimmingScrimOpacity = scrimOpacity;
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
index aa62e9a..316b891 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
@@ -30,6 +30,11 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
import android.content.Intent;
import android.os.PowerManager;
@@ -45,6 +50,8 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
@SmallTest
@@ -55,22 +62,27 @@
static final int[] SENSOR_TO_OPACITY = new int[]{-1, 10, 0, 0, 0};
DozeServiceFake mServiceFake;
- DozeScreenBrightness mScreen;
FakeSensorManager.FakeGenericSensor mSensor;
FakeSensorManager mSensorManager;
- DozeHostFake mHostFake;
+ @Mock
+ DozeHost mDozeHost;
+ DozeScreenBrightness mScreen;
@Before
public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
Settings.System.putIntForUser(mContext.getContentResolver(),
Settings.System.SCREEN_BRIGHTNESS, DEFAULT_BRIGHTNESS,
UserHandle.USER_CURRENT);
+ doAnswer(invocation -> {
+ ((Runnable) invocation.getArgument(0)).run();
+ return null;
+ }).when(mDozeHost).prepareForGentleSleep(any());
mServiceFake = new DozeServiceFake();
- mHostFake = new DozeHostFake();
mSensorManager = new FakeSensorManager(mContext);
mSensor = mSensorManager.getFakeLightSensor();
mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager,
- mSensor.getSensor(), mHostFake, null /* handler */,
+ mSensor.getSensor(), mDozeHost, null /* handler */,
DEFAULT_BRIGHTNESS, SENSOR_TO_BRIGHTNESS, SENSOR_TO_OPACITY,
true /* debuggable */);
}
@@ -173,7 +185,7 @@
@Test
public void testNullSensor() throws Exception {
mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager,
- null /* sensor */, mHostFake, null /* handler */,
+ null /* sensor */, mDozeHost, null /* handler */,
DEFAULT_BRIGHTNESS, SENSOR_TO_BRIGHTNESS, SENSOR_TO_OPACITY,
true /* debuggable */);
@@ -203,26 +215,7 @@
mSensor.sendSensorEvent(0);
assertEquals(1, mServiceFake.screenBrightness);
- assertEquals(10/255f, mHostFake.aodDimmingScrimOpacity, 0.001f /* delta */);
- }
-
- @Test
- public void pausingAod_softBlanks() throws Exception {
- mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
- mScreen.transitionTo(INITIALIZED, DOZE_AOD);
-
- mSensor.sendSensorEvent(2);
-
- mScreen.transitionTo(DOZE_AOD, DOZE_AOD_PAUSING);
- mScreen.transitionTo(DOZE_AOD_PAUSING, DOZE_AOD_PAUSED);
-
- assertEquals(1f, mHostFake.aodDimmingScrimOpacity, 0.001f /* delta */);
-
- mSensor.sendSensorEvent(0);
- assertEquals(1f, mHostFake.aodDimmingScrimOpacity, 0.001f /* delta */);
-
- mScreen.transitionTo(DOZE_AOD_PAUSED, DOZE_AOD);
- assertEquals(1f, mHostFake.aodDimmingScrimOpacity, 0.001f /* delta */);
+ verify(mDozeHost).setAodDimmingScrim(eq(10f / 255f));
}
@Test
@@ -232,8 +225,9 @@
mScreen.transitionTo(DOZE_AOD, DOZE_AOD_PAUSING);
mScreen.transitionTo(DOZE_AOD_PAUSING, DOZE_AOD_PAUSED);
+ reset(mDozeHost);
mSensor.sendSensorEvent(1);
- assertEquals(1f, mHostFake.aodDimmingScrimOpacity, 0.001f /* delta */);
+ verify(mDozeHost).setAodDimmingScrim(eq(1f));
}
@Test
@@ -241,11 +235,12 @@
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
mScreen.transitionTo(INITIALIZED, DOZE_AOD);
mScreen.transitionTo(DOZE_AOD, DOZE);
- assertEquals(1f, mHostFake.aodDimmingScrimOpacity, 0.001f /* delta */);
+ verify(mDozeHost).setAodDimmingScrim(eq(1f));
+ reset(mDozeHost);
mScreen.transitionTo(DOZE, DOZE_AOD);
mSensor.sendSensorEvent(2);
- assertEquals(0f, mHostFake.aodDimmingScrimOpacity, 0.001f /* delta */);
+ verify(mDozeHost).setAodDimmingScrim(eq(0f));
}
@Test
@@ -260,11 +255,10 @@
mSensor.sendSensorEvent(0);
+ reset(mDozeHost);
mScreen.transitionTo(DOZE_AOD_PAUSED, DOZE_AOD);
-
mSensor.sendSensorEvent(2);
-
- assertEquals(0f, mHostFake.aodDimmingScrimOpacity, 0.001f /* delta */);
+ verify(mDozeHost).setAodDimmingScrim(eq(0f));
}
@Test
@@ -273,11 +267,11 @@
mScreen.transitionTo(INITIALIZED, DOZE_AOD);
mSensor.sendSensorEvent(2);
-
mScreen.transitionTo(DOZE_AOD, DOZE_AOD_PAUSING);
mScreen.transitionTo(DOZE_AOD_PAUSING, DOZE_AOD_PAUSED);
- mScreen.transitionTo(DOZE_AOD_PAUSED, DOZE_AOD);
- assertEquals(0f, mHostFake.aodDimmingScrimOpacity, 0.001f /* delta */);
+ reset(mDozeHost);
+ mScreen.transitionTo(DOZE_AOD_PAUSED, DOZE_AOD);
+ verify(mDozeHost).setAodDimmingScrim(eq(0f));
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java
index bfd448a..b92f173 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java
@@ -18,6 +18,8 @@
import static com.android.systemui.doze.DozeMachine.State.DOZE;
import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD;
+import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSED;
+import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSING;
import static com.android.systemui.doze.DozeMachine.State.DOZE_PULSING;
import static com.android.systemui.doze.DozeMachine.State.DOZE_REQUEST_PULSE;
import static com.android.systemui.doze.DozeMachine.State.FINISH;
@@ -30,6 +32,9 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.os.Looper;
@@ -46,6 +51,7 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -53,12 +59,14 @@
@SmallTest
public class DozeScreenStateTest extends SysuiTestCase {
- DozeServiceFake mServiceFake;
- DozeScreenState mScreen;
- FakeHandler mHandlerFake;
+ private DozeServiceFake mServiceFake;
+ private FakeHandler mHandlerFake;
@Mock
- DozeParameters mDozeParameters;
- WakeLockFake mWakeLock;
+ private DozeHost mDozeHost;
+ @Mock
+ private DozeParameters mDozeParameters;
+ private WakeLockFake mWakeLock;
+ private DozeScreenState mScreen;
@Before
public void setUp() throws Exception {
@@ -68,7 +76,8 @@
mServiceFake = new DozeServiceFake();
mHandlerFake = new FakeHandler(Looper.getMainLooper());
mWakeLock = new WakeLockFake();
- mScreen = new DozeScreenState(mServiceFake, mHandlerFake, mDozeParameters, mWakeLock);
+ mScreen = new DozeScreenState(mServiceFake, mHandlerFake, mDozeHost, mDozeParameters,
+ mWakeLock);
}
@Test
@@ -183,4 +192,20 @@
assertThat(mWakeLock.isHeld(), is(false));
}
+ @Test
+ public void test_animatesPausing() {
+ ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
+ doAnswer(invocation -> null).when(mDozeHost).prepareForGentleSleep(captor.capture());
+ mHandlerFake.setMode(QUEUEING);
+
+ mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+ mScreen.transitionTo(INITIALIZED, DOZE_AOD_PAUSING);
+ mScreen.transitionTo(DOZE_AOD_PAUSING, DOZE_AOD_PAUSED);
+
+ mHandlerFake.dispatchQueuedMessages();
+ verify(mDozeHost).prepareForGentleSleep(eq(captor.getValue()));
+ captor.getValue().run();
+ assertEquals(Display.STATE_OFF, mServiceFake.screenState);
+ }
+
}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
index e5ae6d5..777fec7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -20,6 +20,7 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
@@ -50,14 +51,22 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@RunWithLooper(setAsMainLooper = true)
public class DozeTriggersTest extends SysuiTestCase {
- private DozeTriggers mTriggers;
+
+ @Mock
private DozeMachine mMachine;
- private DozeHostFake mHost;
+ @Mock
+ private DozeHost mHost;
+ @Mock
+ private AlarmManager mAlarmManager;
+ private DozeTriggers mTriggers;
private FakeSensorManager mSensors;
private Sensor mTapSensor;
private DockManager mDockManagerFake;
@@ -65,9 +74,7 @@
@Before
public void setUp() throws Exception {
- mMachine = mock(DozeMachine.class);
- AlarmManager alarmManager = mock(AlarmManager.class);
- mHost = spy(new DozeHostFake());
+ MockitoAnnotations.initMocks(this);
AmbientDisplayConfiguration config = DozeConfigurationUtil.createMockConfig();
DozeParameters parameters = DozeConfigurationUtil.createMockParameters();
mSensors = spy(new FakeSensorManager(mContext));
@@ -78,7 +85,7 @@
new AsyncSensorManager(mSensors, null, new Handler());
mProximitySensor = new FakeProximitySensor(getContext(), asyncSensorManager);
- mTriggers = new DozeTriggers(mContext, mMachine, mHost, alarmManager, config, parameters,
+ mTriggers = new DozeTriggers(mContext, mMachine, mHost, mAlarmManager, config, parameters,
asyncSensorManager, Handler.createAsync(Looper.myLooper()), wakeLock, true,
mDockManagerFake, mProximitySensor, mock(DozeLog.class));
waitForSensorManager();
@@ -87,19 +94,21 @@
@Test
public void testOnNotification_stillWorksAfterOneFailedProxCheck() throws Exception {
when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE);
+ ArgumentCaptor<DozeHost.Callback> captor = ArgumentCaptor.forClass(DozeHost.Callback.class);
+ doAnswer(invocation -> null).when(mHost).addCallback(captor.capture());
mTriggers.transitionTo(DozeMachine.State.UNINITIALIZED, DozeMachine.State.INITIALIZED);
mTriggers.transitionTo(DozeMachine.State.INITIALIZED, DozeMachine.State.DOZE);
clearInvocations(mMachine);
mProximitySensor.setLastEvent(new ProximitySensor.ProximityEvent(true, 1));
- mHost.callback.onNotificationAlerted(null /* pulseSuppressedListener */);
+ captor.getValue().onNotificationAlerted(null /* pulseSuppressedListener */);
mProximitySensor.alertListeners();
verify(mMachine, never()).requestState(any());
verify(mMachine, never()).requestPulse(anyInt());
- mHost.callback.onNotificationAlerted(null /* pulseSuppressedListener */);
+ captor.getValue().onNotificationAlerted(null /* pulseSuppressedListener */);
waitForSensorManager();
mProximitySensor.setLastEvent(new ProximitySensor.ProximityEvent(false, 2));
mProximitySensor.alertListeners();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DoubleTapHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DoubleTapHelperTest.java
new file mode 100644
index 0000000..df1233a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DoubleTapHelperTest.java
@@ -0,0 +1,369 @@
+/*
+ * 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.phone;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.res.Resources;
+import android.os.SystemClock;
+import android.testing.AndroidTestingRunner;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class DoubleTapHelperTest extends SysuiTestCase {
+
+ private DoubleTapHelper mDoubleTapHelper;
+ private int mTouchSlop;
+ private int mDoubleTouchSlop;
+ @Mock private View mView;
+ @Mock private DoubleTapHelper.ActivationListener mActivationListener;
+ @Mock private DoubleTapHelper.DoubleTapListener mDoubleTapListener;
+ @Mock private DoubleTapHelper.SlideBackListener mSlideBackListener;
+ @Mock private DoubleTapHelper.DoubleTapLogListener mDoubleTapLogListener;
+ @Mock private Resources mResources;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
+ // The double tap slop has to be less than the regular slop, otherwise it has no effect.
+ mDoubleTouchSlop = mTouchSlop - 1;
+ when(mView.getContext()).thenReturn(mContext);
+ when(mView.getResources()).thenReturn(mResources);
+ when(mResources.getDimension(R.dimen.double_tap_slop))
+ .thenReturn((float) mDoubleTouchSlop);
+
+ mDoubleTapHelper = new DoubleTapHelper(mView,
+ mActivationListener,
+ mDoubleTapListener,
+ mSlideBackListener, mDoubleTapLogListener);
+ }
+
+ @Test
+ public void testDoubleTap_success() {
+ long downtimeA = SystemClock.uptimeMillis();
+ long downtimeB = downtimeA + 100;
+
+ MotionEvent evDownA = MotionEvent.obtain(downtimeA,
+ downtimeA,
+ MotionEvent.ACTION_DOWN,
+ 1,
+ 1,
+ 0);
+ MotionEvent evUpA = MotionEvent.obtain(downtimeA,
+ downtimeA + 1,
+ MotionEvent.ACTION_UP,
+ 1,
+ 1,
+ 0);
+ MotionEvent evDownB = MotionEvent.obtain(downtimeB,
+ downtimeB,
+ MotionEvent.ACTION_DOWN,
+ 1,
+ 1,
+ 0);
+ MotionEvent evUpB = MotionEvent.obtain(downtimeB,
+ downtimeB + 1,
+ MotionEvent.ACTION_UP,
+ 1,
+ 1,
+ 0);
+
+ mDoubleTapHelper.onTouchEvent(evDownA);
+ mDoubleTapHelper.onTouchEvent(evUpA);
+ verify(mActivationListener).onActiveChanged(true);
+ verify(mView).postDelayed(any(Runnable.class), anyLong());
+ verify(mDoubleTapLogListener, never()).onDoubleTapLog(anyBoolean(), anyFloat(), anyFloat());
+ verify(mDoubleTapListener, never()).onDoubleTap();
+
+ mDoubleTapHelper.onTouchEvent(evDownB);
+ mDoubleTapHelper.onTouchEvent(evUpB);
+ verify(mDoubleTapLogListener).onDoubleTapLog(true, 0, 0);
+ verify(mDoubleTapListener).onDoubleTap();
+
+ evDownA.recycle();
+ evUpA.recycle();
+ evDownB.recycle();
+ evUpB.recycle();
+ }
+
+ @Test
+ public void testSingleTap_timeout() {
+ long downtimeA = SystemClock.uptimeMillis();
+
+ MotionEvent evDownA = MotionEvent.obtain(downtimeA,
+ downtimeA,
+ MotionEvent.ACTION_DOWN,
+ 1,
+ 1,
+ 0);
+ MotionEvent evUpA = MotionEvent.obtain(downtimeA,
+ downtimeA + 1,
+ MotionEvent.ACTION_UP,
+ 1,
+ 1,
+ 0);
+
+ ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
+ mDoubleTapHelper.onTouchEvent(evDownA);
+ mDoubleTapHelper.onTouchEvent(evUpA);
+ verify(mActivationListener).onActiveChanged(true);
+ verify(mView).postDelayed(runnableCaptor.capture(), anyLong());
+ runnableCaptor.getValue().run();
+ verify(mActivationListener).onActiveChanged(true);
+
+ evDownA.recycle();
+ evUpA.recycle();
+ }
+
+ @Test
+ public void testSingleTap_slop() {
+ long downtimeA = SystemClock.uptimeMillis();
+
+ MotionEvent evDownA = MotionEvent.obtain(downtimeA,
+ downtimeA,
+ MotionEvent.ACTION_DOWN,
+ 1,
+ 1,
+ 0);
+ MotionEvent evUpA = MotionEvent.obtain(downtimeA,
+ downtimeA + 1,
+ MotionEvent.ACTION_UP,
+ 1 + mTouchSlop,
+ 1,
+ 0);
+
+ ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
+ mDoubleTapHelper.onTouchEvent(evDownA);
+ mDoubleTapHelper.onTouchEvent(evUpA);
+ verify(mActivationListener, never()).onActiveChanged(true);
+ verify(mDoubleTapListener, never()).onDoubleTap();
+
+ evDownA.recycle();
+ evUpA.recycle();
+ }
+
+ @Test
+ public void testDoubleTap_slop() {
+ long downtimeA = SystemClock.uptimeMillis();
+ long downtimeB = downtimeA + 100;
+
+ MotionEvent evDownA = MotionEvent.obtain(downtimeA,
+ downtimeA,
+ MotionEvent.ACTION_DOWN,
+ 1,
+ 1,
+ 0);
+ MotionEvent evUpA = MotionEvent.obtain(downtimeA,
+ downtimeA + 1,
+ MotionEvent.ACTION_UP,
+ 1,
+ 1,
+ 0);
+ MotionEvent evDownB = MotionEvent.obtain(downtimeB,
+ downtimeB,
+ MotionEvent.ACTION_DOWN,
+ 1,
+ 1,
+ 0);
+ MotionEvent evUpB = MotionEvent.obtain(downtimeB,
+ downtimeB + 1,
+ MotionEvent.ACTION_UP,
+ 1,
+ 1 + mDoubleTouchSlop,
+ 0);
+
+ mDoubleTapHelper.onTouchEvent(evDownA);
+ mDoubleTapHelper.onTouchEvent(evUpA);
+ verify(mActivationListener).onActiveChanged(true);
+ verify(mView).postDelayed(any(Runnable.class), anyLong());
+
+ mDoubleTapHelper.onTouchEvent(evDownB);
+ mDoubleTapHelper.onTouchEvent(evUpB);
+ verify(mDoubleTapLogListener).onDoubleTapLog(false, 0, mDoubleTouchSlop);
+ verify(mActivationListener).onActiveChanged(false);
+ verify(mDoubleTapListener, never()).onDoubleTap();
+
+ evDownA.recycle();
+ evUpA.recycle();
+ evDownB.recycle();
+ evUpB.recycle();
+ }
+
+ @Test
+ public void testSlideBack() {
+ long downtimeA = SystemClock.uptimeMillis();
+ long downtimeB = downtimeA + 100;
+
+ MotionEvent evDownA = MotionEvent.obtain(downtimeA,
+ downtimeA,
+ MotionEvent.ACTION_DOWN,
+ 1,
+ 1,
+ 0);
+ MotionEvent evUpA = MotionEvent.obtain(downtimeA,
+ downtimeA + 1,
+ MotionEvent.ACTION_UP,
+ 1,
+ 1,
+ 0);
+ MotionEvent evDownB = MotionEvent.obtain(downtimeB,
+ downtimeB,
+ MotionEvent.ACTION_DOWN,
+ 1,
+ 1,
+ 0);
+ MotionEvent evUpB = MotionEvent.obtain(downtimeB,
+ downtimeB + 1,
+ MotionEvent.ACTION_UP,
+ 1,
+ 1,
+ 0);
+
+ when(mSlideBackListener.onSlideBack()).thenReturn(true);
+
+ mDoubleTapHelper.onTouchEvent(evDownA);
+ mDoubleTapHelper.onTouchEvent(evUpA);
+ verify(mActivationListener, never()).onActiveChanged(true);
+ verify(mActivationListener, never()).onActiveChanged(false);
+ verify(mDoubleTapListener, never()).onDoubleTap();
+ mDoubleTapHelper.onTouchEvent(evDownB);
+ mDoubleTapHelper.onTouchEvent(evUpB);
+ verify(mActivationListener, never()).onActiveChanged(true);
+ verify(mActivationListener, never()).onActiveChanged(false);
+ verify(mDoubleTapListener, never()).onDoubleTap();
+
+ evDownA.recycle();
+ evUpA.recycle();
+ evDownB.recycle();
+ evUpB.recycle();
+ }
+
+
+ @Test
+ public void testMoreThanTwoTaps() {
+ long downtimeA = SystemClock.uptimeMillis();
+ long downtimeB = downtimeA + 100;
+ long downtimeC = downtimeB + 100;
+ long downtimeD = downtimeC + 100;
+
+ MotionEvent evDownA = MotionEvent.obtain(downtimeA,
+ downtimeA,
+ MotionEvent.ACTION_DOWN,
+ 1,
+ 1,
+ 0);
+ MotionEvent evUpA = MotionEvent.obtain(downtimeA,
+ downtimeA + 1,
+ MotionEvent.ACTION_UP,
+ 1,
+ 1,
+ 0);
+ MotionEvent evDownB = MotionEvent.obtain(downtimeB,
+ downtimeB,
+ MotionEvent.ACTION_DOWN,
+ 1,
+ 1,
+ 0);
+ MotionEvent evUpB = MotionEvent.obtain(downtimeB,
+ downtimeB + 1,
+ MotionEvent.ACTION_UP,
+ 1,
+ 1,
+ 0);
+ MotionEvent evDownC = MotionEvent.obtain(downtimeC,
+ downtimeC,
+ MotionEvent.ACTION_DOWN,
+ 1,
+ 1,
+ 0);
+ MotionEvent evUpC = MotionEvent.obtain(downtimeC,
+ downtimeC + 1,
+ MotionEvent.ACTION_UP,
+ 1,
+ 1,
+ 0);
+ MotionEvent evDownD = MotionEvent.obtain(downtimeD,
+ downtimeD,
+ MotionEvent.ACTION_DOWN,
+ 1,
+ 1,
+ 0);
+ MotionEvent evUpD = MotionEvent.obtain(downtimeD,
+ downtimeD + 1,
+ MotionEvent.ACTION_UP,
+ 1,
+ 1,
+ 0);
+
+ mDoubleTapHelper.onTouchEvent(evDownA);
+ mDoubleTapHelper.onTouchEvent(evUpA);
+ verify(mActivationListener).onActiveChanged(true);
+ verify(mView).postDelayed(any(Runnable.class), anyLong());
+ verify(mDoubleTapLogListener, never()).onDoubleTapLog(anyBoolean(), anyFloat(), anyFloat());
+ verify(mDoubleTapListener, never()).onDoubleTap();
+
+ mDoubleTapHelper.onTouchEvent(evDownB);
+ mDoubleTapHelper.onTouchEvent(evUpB);
+ verify(mDoubleTapLogListener).onDoubleTapLog(true, 0, 0);
+ verify(mDoubleTapListener).onDoubleTap();
+
+ reset(mView);
+ reset(mActivationListener);
+ reset(mDoubleTapLogListener);
+ reset(mDoubleTapListener);
+
+ mDoubleTapHelper.onTouchEvent(evDownC);
+ mDoubleTapHelper.onTouchEvent(evUpC);
+ verify(mActivationListener).onActiveChanged(true);
+ verify(mView).postDelayed(any(Runnable.class), anyLong());
+ verify(mDoubleTapLogListener, never()).onDoubleTapLog(anyBoolean(), anyFloat(), anyFloat());
+ verify(mDoubleTapListener, never()).onDoubleTap();
+
+ mDoubleTapHelper.onTouchEvent(evDownD);
+ mDoubleTapHelper.onTouchEvent(evUpD);
+ verify(mDoubleTapLogListener).onDoubleTapLog(true, 0, 0);
+ verify(mDoubleTapListener).onDoubleTap();
+
+ evDownA.recycle();
+ evUpA.recycle();
+ evDownB.recycle();
+ evUpB.recycle();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 214e26a..f3ff7ec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -133,6 +133,20 @@
}
@Test
+ public void transitionToOff() {
+ mScrimController.transitionTo(ScrimState.OFF);
+ mScrimController.finishAnimationsImmediately();
+
+ assertScrimAlpha(OPAQUE /* front */,
+ SEMI_TRANSPARENT /* back */,
+ TRANSPARENT /* bubble */);
+
+ assertScrimTint(true /* front */,
+ true /* behind */,
+ false /* bubble */);
+ }
+
+ @Test
public void transitionToAod_withRegularWallpaper() {
mScrimController.transitionTo(ScrimState.AOD);
mScrimController.finishAnimationsImmediately();
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index 5b826b1..b0e401b 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -2584,15 +2584,15 @@
// ACTION: Logged when trampoline activity finishes.
// TIME: Indicates total time taken by trampoline activity to finish in MS.
- PROVISIONING_TRAMPOLINE_ACTIVITY_TIME_MS = 523;
+ PROVISIONING_TRAMPOLINE_ACTIVITY_TIME_MS = 523 [deprecated=true];
// ACTION: Logged when encryption activity finishes.
// TIME: Indicates total time taken by post encryption activity to finish in MS.
- PROVISIONING_POST_ENCRYPTION_ACTIVITY_TIME_MS = 524;
+ PROVISIONING_POST_ENCRYPTION_ACTIVITY_TIME_MS = 524 [deprecated=true];
// ACTION: Logged when finalization activity finishes.
// TIME: Indicates time taken by finalization activity to finish in MS.
- PROVISIONING_FINALIZATION_ACTIVITY_TIME_MS = 525;
+ PROVISIONING_FINALIZATION_ACTIVITY_TIME_MS = 525 [deprecated=true];
// OPEN: Settings Support > Phone/Chat -> Disclaimer
DIALOG_SUPPORT_DISCLAIMER = 526;
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 91269c7..e909e7a 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -16,13 +16,6 @@
package com.android.server.accessibility;
-import static android.accessibilityservice.AccessibilityService.SHOW_MODE_AUTO;
-import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE;
-import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN;
-import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HIDDEN;
-import static android.accessibilityservice.AccessibilityService.SHOW_MODE_IGNORE_HARD_KEYBOARD;
-import static android.accessibilityservice.AccessibilityService.SHOW_MODE_MASK;
-
import static com.android.internal.util.FunctionalUtils.ignoreRemoteException;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
@@ -50,8 +43,6 @@
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.database.ContentObserver;
-import android.graphics.Point;
-import android.graphics.Rect;
import android.graphics.Region;
import android.hardware.display.DisplayManager;
import android.hardware.fingerprint.IFingerprintService;
@@ -119,7 +110,6 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
@@ -136,6 +126,7 @@
*/
public class AccessibilityManagerService extends IAccessibilityManager.Stub
implements AbstractAccessibilityServiceConnection.SystemSupport,
+ AccessibilityUserState.ServiceInfoChangeListener,
AccessibilityWindowManager.AccessibilityEventSender,
AccessibilitySecurityPolicy.AccessibilityUserManager {
@@ -178,12 +169,6 @@
private final SimpleStringSplitter mStringColonSplitter =
new SimpleStringSplitter(COMPONENT_NAME_SEPARATOR);
- private final Rect mTempRect = new Rect();
-
- private final Rect mTempRect1 = new Rect();
-
- private final Point mTempPoint = new Point();
-
private final PackageManager mPackageManager;
private final PowerManager mPowerManager;
@@ -226,7 +211,7 @@
private final RemoteCallbackList<IAccessibilityManagerClient> mGlobalClients =
new RemoteCallbackList<>();
- private final SparseArray<UserState> mUserStates = new SparseArray<>();
+ private final SparseArray<AccessibilityUserState> mUserStates = new SparseArray<>();
private final UiAutomationManager mUiAutomationManager = new UiAutomationManager(mLock);
@@ -237,7 +222,7 @@
private boolean mIsAccessibilityButtonShown;
- private UserState getCurrentUserStateLocked() {
+ private AccessibilityUserState getCurrentUserStateLocked() {
return getUserStateLocked(mCurrentUserId);
}
@@ -293,6 +278,11 @@
return mIsAccessibilityButtonShown;
}
+ @Override
+ public void onServiceInfoChangedLocked(AccessibilityUserState userState) {
+ scheduleNotifyClientsOfServicesStateChangeLocked(userState);
+ }
+
@Nullable
public FingerprintGestureDispatcher getFingerprintGestureDispatcher() {
return mFingerprintGestureDispatcher;
@@ -307,40 +297,40 @@
}
}
- private UserState getUserState(int userId) {
+ private AccessibilityUserState getUserState(int userId) {
synchronized (mLock) {
return getUserStateLocked(userId);
}
}
- private UserState getUserStateLocked(int userId) {
- UserState state = mUserStates.get(userId);
+ @NonNull
+ private AccessibilityUserState getUserStateLocked(int userId) {
+ AccessibilityUserState state = mUserStates.get(userId);
if (state == null) {
- state = new UserState(userId);
+ state = new AccessibilityUserState(userId, mContext, this);
mUserStates.put(userId, state);
}
return state;
}
boolean getBindInstantServiceAllowed(int userId) {
- final UserState userState = getUserState(userId);
- if (userState == null) return false;
- return userState.getBindInstantServiceAllowed();
+ synchronized (mLock) {
+ final AccessibilityUserState userState = getUserStateLocked(userId);
+ return userState.getBindInstantServiceAllowedLocked();
+ }
}
void setBindInstantServiceAllowed(int userId, boolean allowed) {
- UserState userState;
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.MANAGE_BIND_INSTANT_SERVICE,
+ "setBindInstantServiceAllowed");
synchronized (mLock) {
- userState = getUserState(userId);
- if (userState == null) {
- if (!allowed) {
- return;
- }
- userState = new UserState(userId);
- mUserStates.put(userId, userState);
+ final AccessibilityUserState userState = getUserStateLocked(userId);
+ if (allowed != userState.getBindInstantServiceAllowedLocked()) {
+ userState.setBindInstantServiceAllowedLocked(allowed);
+ onUserStateChangedLocked(userState);
}
}
- userState.setBindInstantServiceAllowed(allowed);
}
private void registerBroadcastReceivers() {
@@ -354,7 +344,7 @@
return;
}
// We will update when the automation service dies.
- UserState userState = getCurrentUserStateLocked();
+ AccessibilityUserState userState = getCurrentUserStateLocked();
// We have to reload the installed services since some services may
// have different attributes, resolve info (does not support equals),
// etc. Remove them then to force reload.
@@ -376,8 +366,8 @@
if (userId != mCurrentUserId) {
return;
}
- UserState userState = getUserStateLocked(userId);
- boolean reboundAService = userState.mBindingServices.removeIf(
+ AccessibilityUserState userState = getUserStateLocked(userId);
+ boolean reboundAService = userState.getBindingServicesLocked().removeIf(
component -> component != null
&& component.getPackageName().equals(packageName));
if (reboundAService) {
@@ -395,14 +385,14 @@
if (userId != mCurrentUserId) {
return;
}
- UserState userState = getUserStateLocked(userId);
+ AccessibilityUserState userState = getUserStateLocked(userId);
Iterator<ComponentName> it = userState.mEnabledServices.iterator();
while (it.hasNext()) {
ComponentName comp = it.next();
String compPkg = comp.getPackageName();
if (compPkg.equals(packageName)) {
it.remove();
- userState.mBindingServices.remove(comp);
+ userState.getBindingServicesLocked().remove(comp);
// Update the enabled services setting.
persistComponentNamesToSettingLocked(
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
@@ -430,7 +420,7 @@
if (userId != mCurrentUserId) {
return false;
}
- UserState userState = getUserStateLocked(userId);
+ AccessibilityUserState userState = getUserStateLocked(userId);
Iterator<ComponentName> it = userState.mEnabledServices.iterator();
while (it.hasNext()) {
ComponentName comp = it.next();
@@ -441,7 +431,7 @@
return true;
}
it.remove();
- userState.mBindingServices.remove(comp);
+ userState.getBindingServicesLocked().remove(comp);
persistComponentNamesToSettingLocked(
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
userState.mEnabledServices, userId);
@@ -478,7 +468,7 @@
} else if (Intent.ACTION_USER_PRESENT.equals(action)) {
// We will update when the automation service dies.
synchronized (mLock) {
- UserState userState = getCurrentUserStateLocked();
+ AccessibilityUserState userState = getCurrentUserStateLocked();
if (readConfigurationForUserStateLocked(userState)) {
onUserStateChangedLocked(userState);
}
@@ -509,7 +499,7 @@
// If the client is from a process that runs across users such as
// the system UI or the system we add it to the global state that
// is shared across users.
- UserState userState = getUserStateLocked(resolvedUserId);
+ AccessibilityUserState userState = getUserStateLocked(resolvedUserId);
Client client = new Client(callback, Binder.getCallingUid(), userState);
if (mSecurityPolicy.isCallerInteractingAcrossUsers(userId)) {
mGlobalClients.register(callback, client);
@@ -517,7 +507,7 @@
Slog.i(LOG_TAG, "Added global client for pid:" + Binder.getCallingPid());
}
return IntPair.of(
- userState.getClientState(),
+ getClientStateLocked(userState),
client.mLastSentRelevantEventTypes);
} else {
userState.mUserClients.register(callback, client);
@@ -529,7 +519,7 @@
+ " and userId:" + mCurrentUserId);
}
return IntPair.of(
- (resolvedUserId == mCurrentUserId) ? userState.getClientState() : 0,
+ (resolvedUserId == mCurrentUserId) ? getClientStateLocked(userState) : 0,
client.mLastSentRelevantEventTypes);
}
}
@@ -644,7 +634,7 @@
.resolveCallingUserIdEnforcingPermissionsLocked(userId);
// The automation service can suppress other services.
- final UserState userState = getUserStateLocked(resolvedUserId);
+ final AccessibilityUserState userState = getUserStateLocked(resolvedUserId);
if (mUiAutomationManager.suppressingAccessibilityServicesLocked()) {
return Collections.emptyList();
}
@@ -754,15 +744,15 @@
}
synchronized (mLock) {
// Set the temporary state.
- UserState userState = getCurrentUserStateLocked();
+ AccessibilityUserState userState = getCurrentUserStateLocked();
- userState.mIsTouchExplorationEnabled = touchExplorationEnabled;
- userState.mIsDisplayMagnificationEnabled = false;
- userState.mIsNavBarMagnificationEnabled = false;
- userState.mIsAutoclickEnabled = false;
+ userState.setTouchExplorationEnabledLocked(touchExplorationEnabled);
+ userState.setDisplayMagnificationEnabledLocked(false);
+ userState.setNavBarMagnificationEnabledLocked(false);
+ userState.setAutoclickEnabledLocked(false);
userState.mEnabledServices.clear();
userState.mEnabledServices.add(service);
- userState.mBindingServices.clear();
+ userState.getBindingServicesLocked().clear();
userState.mTouchExplorationGrantedServices.clear();
userState.mTouchExplorationGrantedServices.add(service);
@@ -943,7 +933,7 @@
}
// Disconnect from services for the old user.
- UserState oldUserState = getCurrentUserStateLocked();
+ AccessibilityUserState oldUserState = getCurrentUserStateLocked();
oldUserState.onSwitchToAnotherUserLocked();
// Disable the local managers for the old user.
@@ -960,7 +950,7 @@
// The user changed.
mCurrentUserId = userId;
- UserState userState = getCurrentUserStateLocked();
+ AccessibilityUserState userState = getCurrentUserStateLocked();
readConfigurationForUserStateLocked(userState);
// Even if reading did not yield change, we have to update
@@ -979,8 +969,8 @@
private void announceNewUserIfNeeded() {
synchronized (mLock) {
- UserState userState = getCurrentUserStateLocked();
- if (userState.isHandlingAccessibilityEvents()) {
+ AccessibilityUserState userState = getCurrentUserStateLocked();
+ if (userState.isHandlingAccessibilityEventsLocked()) {
UserManager userManager = (UserManager) mContext.getSystemService(
Context.USER_SERVICE);
String message = mContext.getString(R.string.user_switched,
@@ -997,7 +987,7 @@
synchronized (mLock) {
int parentUserId = mSecurityPolicy.resolveProfileParentLocked(userId);
if (parentUserId == mCurrentUserId) {
- UserState userState = getUserStateLocked(mCurrentUserId);
+ AccessibilityUserState userState = getUserStateLocked(mCurrentUserId);
onUserStateChangedLocked(userState);
}
}
@@ -1015,7 +1005,7 @@
readComponentNamesFromStringLocked(oldSetting, mTempComponentNameSet, false);
readComponentNamesFromStringLocked(newSetting, mTempComponentNameSet, true);
- UserState userState = getUserStateLocked(UserHandle.USER_SYSTEM);
+ AccessibilityUserState userState = getUserStateLocked(UserHandle.USER_SYSTEM);
userState.mEnabledServices.clear();
userState.mEnabledServices.addAll(mTempComponentNameSet);
persistComponentNamesToSettingLocked(
@@ -1025,6 +1015,10 @@
onUserStateChangedLocked(userState);
}
+ private int getClientStateLocked(AccessibilityUserState userState) {
+ return userState.getClientStateLocked(mUiAutomationManager.isUiAutomationRunningLocked());
+ }
+
private InteractionBridge getInteractionBridge() {
synchronized (mLock) {
if (mInteractionBridge == null) {
@@ -1044,7 +1038,7 @@
// gestures to avoid user frustration when different
// behavior is observed from different combinations of
// enabled accessibility services.
- UserState state = getCurrentUserStateLocked();
+ AccessibilityUserState state = getCurrentUserStateLocked();
for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
AccessibilityServiceConnection service = state.mBoundServices.get(i);
if (service.mRequestTouchExplorationMode && service.mIsDefault == isDefault) {
@@ -1056,7 +1050,7 @@
}
private void notifyClearAccessibilityCacheLocked() {
- UserState state = getCurrentUserStateLocked();
+ AccessibilityUserState state = getCurrentUserStateLocked();
for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
AccessibilityServiceConnection service = state.mBoundServices.get(i);
service.notifyClearAccessibilityNodeInfoCache();
@@ -1065,25 +1059,17 @@
private void notifyMagnificationChangedLocked(int displayId, @NonNull Region region,
float scale, float centerX, float centerY) {
- final UserState state = getCurrentUserStateLocked();
+ final AccessibilityUserState state = getCurrentUserStateLocked();
for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
final AccessibilityServiceConnection service = state.mBoundServices.get(i);
service.notifyMagnificationChangedLocked(displayId, region, scale, centerX, centerY);
}
}
- private void notifySoftKeyboardShowModeChangedLocked(int showMode) {
- final UserState state = getCurrentUserStateLocked();
- for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
- final AccessibilityServiceConnection service = state.mBoundServices.get(i);
- service.notifySoftKeyboardShowModeChangedLocked(showMode);
- }
- }
-
private void notifyAccessibilityButtonClickedLocked(int displayId) {
- final UserState state = getCurrentUserStateLocked();
+ final AccessibilityUserState state = getCurrentUserStateLocked();
- int potentialTargets = state.mIsNavBarMagnificationEnabled ? 1 : 0;
+ int potentialTargets = state.isNavBarMagnificationEnabledLocked() ? 1 : 0;
for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
final AccessibilityServiceConnection service = state.mBoundServices.get(i);
if (service.mRequestAccessibilityButton) {
@@ -1095,7 +1081,7 @@
return;
}
if (potentialTargets == 1) {
- if (state.mIsNavBarMagnificationEnabled) {
+ if (state.isNavBarMagnificationEnabledLocked()) {
mMainHandler.sendMessage(obtainMessage(
AccessibilityManagerService::sendAccessibilityButtonToInputFilter, this,
displayId));
@@ -1110,13 +1096,13 @@
}
}
} else {
- if (state.mServiceAssignedToAccessibilityButton == null
- && !state.mIsNavBarMagnificationAssignedToAccessibilityButton) {
+ if (state.getServiceAssignedToAccessibilityButtonLocked() == null
+ && !state.isNavBarMagnificationAssignedToAccessibilityButtonLocked()) {
mMainHandler.sendMessage(obtainMessage(
AccessibilityManagerService::showAccessibilityButtonTargetSelection, this,
displayId));
- } else if (state.mIsNavBarMagnificationEnabled
- && state.mIsNavBarMagnificationAssignedToAccessibilityButton) {
+ } else if (state.isNavBarMagnificationEnabledLocked()
+ && state.isNavBarMagnificationAssignedToAccessibilityButtonLocked()) {
mMainHandler.sendMessage(obtainMessage(
AccessibilityManagerService::sendAccessibilityButtonToInputFilter, this,
displayId));
@@ -1125,7 +1111,7 @@
for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
final AccessibilityServiceConnection service = state.mBoundServices.get(i);
if (service.mRequestAccessibilityButton && (service.mComponentName.equals(
- state.mServiceAssignedToAccessibilityButton))) {
+ state.getServiceAssignedToAccessibilityButtonLocked()))) {
service.notifyAccessibilityButtonClickedLocked(displayId);
return;
}
@@ -1154,7 +1140,7 @@
}
private void notifyAccessibilityButtonVisibilityChangedLocked(boolean available) {
- final UserState state = getCurrentUserStateLocked();
+ final AccessibilityUserState state = getCurrentUserStateLocked();
mIsAccessibilityButtonShown = available;
for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
final AccessibilityServiceConnection clientConnection = state.mBoundServices.get(i);
@@ -1165,7 +1151,7 @@
}
}
- private boolean readInstalledAccessibilityServiceLocked(UserState userState) {
+ private boolean readInstalledAccessibilityServiceLocked(AccessibilityUserState userState) {
mTempAccessibilityServiceInfoList.clear();
int flags = PackageManager.GET_SERVICES
@@ -1174,7 +1160,7 @@
| PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
- if (userState.getBindInstantServiceAllowed()) {
+ if (userState.getBindInstantServiceAllowedLocked()) {
flags |= PackageManager.MATCH_INSTANT;
}
@@ -1209,7 +1195,7 @@
return false;
}
- private boolean readInstalledAccessibilityShortcutLocked(UserState userState) {
+ private boolean readInstalledAccessibilityShortcutLocked(AccessibilityUserState userState) {
final List<AccessibilityShortcutInfo> shortcutInfos = AccessibilityManager
.getInstance(mContext).getInstalledAccessibilityShortcutListAsUser(
mContext, mCurrentUserId);
@@ -1221,7 +1207,7 @@
return false;
}
- private boolean readEnabledAccessibilityServicesLocked(UserState userState) {
+ private boolean readEnabledAccessibilityServicesLocked(AccessibilityUserState userState) {
mTempComponentNameSet.clear();
readComponentNamesFromSettingLocked(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
userState.mUserId, mTempComponentNameSet);
@@ -1236,7 +1222,7 @@
}
private boolean readTouchExplorationGrantedAccessibilityServicesLocked(
- UserState userState) {
+ AccessibilityUserState userState) {
mTempComponentNameSet.clear();
readComponentNamesFromSettingLocked(
Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
@@ -1261,7 +1247,7 @@
private void notifyAccessibilityServicesDelayedLocked(AccessibilityEvent event,
boolean isDefault) {
try {
- UserState state = getCurrentUserStateLocked();
+ AccessibilityUserState state = getCurrentUserStateLocked();
for (int i = 0, count = state.mBoundServices.size(); i < count; i++) {
AccessibilityServiceConnection service = state.mBoundServices.get(i);
@@ -1276,7 +1262,7 @@
}
}
- private void updateRelevantEventsLocked(UserState userState) {
+ private void updateRelevantEventsLocked(AccessibilityUserState userState) {
mMainHandler.post(() -> {
broadcastToClients(userState, ignoreRemoteException(client -> {
int relevantEventTypes;
@@ -1296,7 +1282,7 @@
});
}
- private int computeRelevantEventTypesLocked(UserState userState, Client client) {
+ private int computeRelevantEventTypesLocked(AccessibilityUserState userState, Client client) {
int relevantEventTypes = 0;
int serviceCount = userState.mBoundServices.size();
@@ -1342,20 +1328,11 @@
}
private void broadcastToClients(
- UserState userState, Consumer<Client> clientAction) {
+ AccessibilityUserState userState, Consumer<Client> clientAction) {
mGlobalClients.broadcastForEachCookie(clientAction);
userState.mUserClients.broadcastForEachCookie(clientAction);
}
- private void unbindAllServicesLocked(UserState userState) {
- List<AccessibilityServiceConnection> services = userState.mBoundServices;
- for (int count = services.size(); count > 0; count--) {
- // When the service is unbound, it disappears from the list, so there's no need to
- // keep track of the index
- services.get(0).unbindLocked();
- }
- }
-
/**
* Populates a set with the {@link ComponentName}s stored in a colon
* separated value setting for a given user.
@@ -1422,7 +1399,7 @@
}
}
- private void updateServicesLocked(UserState userState) {
+ private void updateServicesLocked(AccessibilityUserState userState) {
Map<ComponentName, AccessibilityServiceConnection> componentNameToServiceMap =
userState.mComponentNameToServiceMap;
boolean isUnlockingOrUnlocked = LocalServices.getService(UserManagerInternal.class)
@@ -1442,7 +1419,7 @@
}
// Wait for the binding if it is in process.
- if (userState.mBindingServices.contains(componentName)) {
+ if (userState.getBindingServicesLocked().contains(componentName)) {
continue;
}
if (userState.mEnabledServices.contains(componentName)
@@ -1478,15 +1455,15 @@
if (audioManager != null) {
audioManager.setAccessibilityServiceUids(mTempIntArray);
}
- updateAccessibilityEnabledSetting(userState);
+ updateAccessibilityEnabledSettingLocked(userState);
}
- private void scheduleUpdateClientsIfNeededLocked(UserState userState) {
- final int clientState = userState.getClientState();
- if (userState.mLastSentClientState != clientState
+ private void scheduleUpdateClientsIfNeededLocked(AccessibilityUserState userState) {
+ final int clientState = getClientStateLocked(userState);
+ if (userState.getLastSentClientStateLocked() != clientState
&& (mGlobalClients.getRegisteredCallbackCount() > 0
|| userState.mUserClients.getRegisteredCallbackCount() > 0)) {
- userState.mLastSentClientState = clientState;
+ userState.setLastSentClientStateLocked(clientState);
mMainHandler.sendMessage(obtainMessage(
AccessibilityManagerService::sendStateToAllClients,
this, clientState, userState.mUserId));
@@ -1508,7 +1485,8 @@
client -> client.setState(clientState)));
}
- private void scheduleNotifyClientsOfServicesStateChangeLocked(UserState userState) {
+ private void scheduleNotifyClientsOfServicesStateChangeLocked(
+ AccessibilityUserState userState) {
updateRecommendedUiTimeoutLocked(userState);
mMainHandler.sendMessage(obtainMessage(
AccessibilityManagerService::sendServicesStateChanged,
@@ -1527,44 +1505,45 @@
client -> client.notifyServicesStateChanged(uiTimeout)));
}
- private void scheduleUpdateInputFilter(UserState userState) {
+ private void scheduleUpdateInputFilter(AccessibilityUserState userState) {
mMainHandler.sendMessage(obtainMessage(
AccessibilityManagerService::updateInputFilter, this, userState));
}
- private void scheduleUpdateFingerprintGestureHandling(UserState userState) {
+ private void scheduleUpdateFingerprintGestureHandling(AccessibilityUserState userState) {
mMainHandler.sendMessage(obtainMessage(
AccessibilityManagerService::updateFingerprintGestureHandling,
this, userState));
}
- private void updateInputFilter(UserState userState) {
+ private void updateInputFilter(AccessibilityUserState userState) {
if (mUiAutomationManager.suppressingAccessibilityServicesLocked()) return;
boolean setInputFilter = false;
AccessibilityInputFilter inputFilter = null;
synchronized (mLock) {
int flags = 0;
- if (userState.mIsDisplayMagnificationEnabled) {
+ if (userState.isDisplayMagnificationEnabledLocked()) {
flags |= AccessibilityInputFilter.FLAG_FEATURE_SCREEN_MAGNIFIER;
}
- if (userState.mIsNavBarMagnificationEnabled) {
+ if (userState.isNavBarMagnificationEnabledLocked()) {
flags |= AccessibilityInputFilter.FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER;
}
if (userHasMagnificationServicesLocked(userState)) {
flags |= AccessibilityInputFilter.FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER;
}
// Touch exploration without accessibility makes no sense.
- if (userState.isHandlingAccessibilityEvents() && userState.mIsTouchExplorationEnabled) {
+ if (userState.isHandlingAccessibilityEventsLocked()
+ && userState.isTouchExplorationEnabledLocked()) {
flags |= AccessibilityInputFilter.FLAG_FEATURE_TOUCH_EXPLORATION;
}
- if (userState.mIsFilterKeyEventsEnabled) {
+ if (userState.isFilterKeyEventsEnabledLocked()) {
flags |= AccessibilityInputFilter.FLAG_FEATURE_FILTER_KEY_EVENTS;
}
- if (userState.mIsAutoclickEnabled) {
+ if (userState.isAutoclickEnabledLocked()) {
flags |= AccessibilityInputFilter.FLAG_FEATURE_AUTOCLICK;
}
- if (userState.mIsPerformGesturesEnabled) {
+ if (userState.isPerformGesturesEnabledLocked()) {
flags |= AccessibilityInputFilter.FLAG_FEATURE_INJECT_MOTION_EVENTS;
}
if (flags != 0) {
@@ -1597,8 +1576,8 @@
String label = service.getServiceInfo().getResolveInfo()
.loadLabel(mContext.getPackageManager()).toString();
- final UserState userState = getCurrentUserStateLocked();
- if (userState.mIsTouchExplorationEnabled) {
+ final AccessibilityUserState userState = getCurrentUserStateLocked();
+ if (userState.isTouchExplorationEnabledLocked()) {
return;
}
if (mEnableTouchExplorationDialog != null
@@ -1608,40 +1587,40 @@
mEnableTouchExplorationDialog = new AlertDialog.Builder(mContext)
.setIconAttribute(android.R.attr.alertDialogIcon)
.setPositiveButton(android.R.string.ok, new OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- // The user allowed the service to toggle touch exploration.
- userState.mTouchExplorationGrantedServices.add(service.mComponentName);
- persistComponentNamesToSettingLocked(
- Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
- userState.mTouchExplorationGrantedServices, userState.mUserId);
- // Enable touch exploration.
- userState.mIsTouchExplorationEnabled = true;
- final long identity = Binder.clearCallingIdentity();
- try {
- Settings.Secure.putIntForUser(mContext.getContentResolver(),
- Settings.Secure.TOUCH_EXPLORATION_ENABLED, 1,
- userState.mUserId);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- onUserStateChangedLocked(userState);
- }
- })
- .setNegativeButton(android.R.string.cancel, new OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dialog.dismiss();
- }
- })
- .setTitle(R.string.enable_explore_by_touch_warning_title)
- .setMessage(mContext.getString(
- R.string.enable_explore_by_touch_warning_message, label))
- .create();
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ // The user allowed the service to toggle touch exploration.
+ userState.mTouchExplorationGrantedServices.add(service.mComponentName);
+ persistComponentNamesToSettingLocked(
+ Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
+ userState.mTouchExplorationGrantedServices, userState.mUserId);
+ // Enable touch exploration.
+ userState.setTouchExplorationEnabledLocked(true);
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.TOUCH_EXPLORATION_ENABLED, 1,
+ userState.mUserId);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ onUserStateChangedLocked(userState);
+ }
+ })
+ .setNegativeButton(android.R.string.cancel, new OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+ }
+ })
+ .setTitle(R.string.enable_explore_by_touch_warning_title)
+ .setMessage(mContext.getString(
+ R.string.enable_explore_by_touch_warning_message, label))
+ .create();
mEnableTouchExplorationDialog.getWindow().setType(
WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
mEnableTouchExplorationDialog.getWindow().getAttributes().privateFlags
- |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
mEnableTouchExplorationDialog.setCanceledOnTouchOutside(true);
mEnableTouchExplorationDialog.show();
}
@@ -1652,7 +1631,7 @@
*
* @param userState the new user state
*/
- private void onUserStateChangedLocked(UserState userState) {
+ private void onUserStateChangedLocked(AccessibilityUserState userState) {
// TODO: Remove this hack
mInitialized = true;
updateLegacyCapabilitiesLocked(userState);
@@ -1670,7 +1649,7 @@
updateAccessibilityButtonTargetsLocked(userState);
}
- private void updateWindowsForAccessibilityCallbackLocked(UserState userState) {
+ private void updateWindowsForAccessibilityCallbackLocked(AccessibilityUserState userState) {
// We observe windows for accessibility only if there is at least
// one bound service that can retrieve window content that specified
// it is interested in accessing such windows. For services that are
@@ -1702,7 +1681,7 @@
}
}
- private void updateLegacyCapabilitiesLocked(UserState userState) {
+ private void updateLegacyCapabilitiesLocked(AccessibilityUserState userState) {
// Up to JB-MR1 we had a white list with services that can enable touch
// exploration. When a service is first started we show a dialog to the
// use to get a permission to white list the service.
@@ -1724,20 +1703,20 @@
}
}
- private void updatePerformGesturesLocked(UserState userState) {
+ private void updatePerformGesturesLocked(AccessibilityUserState userState) {
final int serviceCount = userState.mBoundServices.size();
for (int i = 0; i < serviceCount; i++) {
AccessibilityServiceConnection service = userState.mBoundServices.get(i);
if ((service.getCapabilities()
& AccessibilityServiceInfo.CAPABILITY_CAN_PERFORM_GESTURES) != 0) {
- userState.mIsPerformGesturesEnabled = true;
+ userState.setPerformGesturesEnabledLocked(true);
return;
}
}
- userState.mIsPerformGesturesEnabled = false;
+ userState.setPerformGesturesEnabledLocked(false);
}
- private void updateFilterKeyEventsLocked(UserState userState) {
+ private void updateFilterKeyEventsLocked(AccessibilityUserState userState) {
final int serviceCount = userState.mBoundServices.size();
for (int i = 0; i < serviceCount; i++) {
AccessibilityServiceConnection service = userState.mBoundServices.get(i);
@@ -1745,14 +1724,14 @@
&& (service.getCapabilities()
& AccessibilityServiceInfo
.CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS) != 0) {
- userState.mIsFilterKeyEventsEnabled = true;
+ userState.setFilterKeyEventsEnabledLocked(true);
return;
}
}
- userState.mIsFilterKeyEventsEnabled = false;
+ userState.setFilterKeyEventsEnabledLocked(false);
}
- private boolean readConfigurationForUserStateLocked(UserState userState) {
+ private boolean readConfigurationForUserStateLocked(AccessibilityUserState userState) {
boolean somethingChanged = readInstalledAccessibilityServiceLocked(userState);
somethingChanged |= readInstalledAccessibilityShortcutLocked(userState);
somethingChanged |= readEnabledAccessibilityServicesLocked(userState);
@@ -1767,10 +1746,10 @@
return somethingChanged;
}
- private void updateAccessibilityEnabledSetting(UserState userState) {
+ private void updateAccessibilityEnabledSettingLocked(AccessibilityUserState userState) {
final long identity = Binder.clearCallingIdentity();
final boolean isA11yEnabled = mUiAutomationManager.isUiAutomationRunningLocked()
- || userState.isHandlingAccessibilityEvents();
+ || userState.isHandlingAccessibilityEventsLocked();
try {
Settings.Secure.putIntForUser(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_ENABLED,
@@ -1781,18 +1760,18 @@
}
}
- private boolean readTouchExplorationEnabledSettingLocked(UserState userState) {
+ private boolean readTouchExplorationEnabledSettingLocked(AccessibilityUserState userState) {
final boolean touchExplorationEnabled = Settings.Secure.getIntForUser(
mContext.getContentResolver(),
Settings.Secure.TOUCH_EXPLORATION_ENABLED, 0, userState.mUserId) == 1;
- if (touchExplorationEnabled != userState.mIsTouchExplorationEnabled) {
- userState.mIsTouchExplorationEnabled = touchExplorationEnabled;
+ if (touchExplorationEnabled != userState.isTouchExplorationEnabledLocked()) {
+ userState.setTouchExplorationEnabledLocked(touchExplorationEnabled);
return true;
}
return false;
}
- private boolean readMagnificationEnabledSettingsLocked(UserState userState) {
+ private boolean readMagnificationEnabledSettingsLocked(AccessibilityUserState userState) {
final boolean displayMagnificationEnabled = Settings.Secure.getIntForUser(
mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED,
@@ -1801,40 +1780,40 @@
mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED,
0, userState.mUserId) == 1;
- if ((displayMagnificationEnabled != userState.mIsDisplayMagnificationEnabled)
- || (navBarMagnificationEnabled != userState.mIsNavBarMagnificationEnabled)) {
- userState.mIsDisplayMagnificationEnabled = displayMagnificationEnabled;
- userState.mIsNavBarMagnificationEnabled = navBarMagnificationEnabled;
+ if ((displayMagnificationEnabled != userState.isDisplayMagnificationEnabledLocked())
+ || (navBarMagnificationEnabled != userState.isNavBarMagnificationEnabledLocked())) {
+ userState.setDisplayMagnificationEnabledLocked(displayMagnificationEnabled);
+ userState.setNavBarMagnificationEnabledLocked(navBarMagnificationEnabled);
return true;
}
return false;
}
- private boolean readAutoclickEnabledSettingLocked(UserState userState) {
+ private boolean readAutoclickEnabledSettingLocked(AccessibilityUserState userState) {
final boolean autoclickEnabled = Settings.Secure.getIntForUser(
mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_AUTOCLICK_ENABLED,
0, userState.mUserId) == 1;
- if (autoclickEnabled != userState.mIsAutoclickEnabled) {
- userState.mIsAutoclickEnabled = autoclickEnabled;
+ if (autoclickEnabled != userState.isAutoclickEnabledLocked()) {
+ userState.setAutoclickEnabledLocked(autoclickEnabled);
return true;
}
return false;
}
- private boolean readHighTextContrastEnabledSettingLocked(UserState userState) {
+ private boolean readHighTextContrastEnabledSettingLocked(AccessibilityUserState userState) {
final boolean highTextContrastEnabled = Settings.Secure.getIntForUser(
mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, 0,
userState.mUserId) == 1;
- if (highTextContrastEnabled != userState.mIsTextHighContrastEnabled) {
- userState.mIsTextHighContrastEnabled = highTextContrastEnabled;
+ if (highTextContrastEnabled != userState.isTextHighContrastEnabledLocked()) {
+ userState.setTextHighContrastEnabledLocked(highTextContrastEnabled);
return true;
}
return false;
}
- private void updateTouchExplorationLocked(UserState userState) {
+ private void updateTouchExplorationLocked(AccessibilityUserState userState) {
boolean enabled = mUiAutomationManager.isTouchExplorationEnabledLocked();
final int serviceCount = userState.mBoundServices.size();
for (int i = 0; i < serviceCount; i++) {
@@ -1844,8 +1823,8 @@
break;
}
}
- if (enabled != userState.mIsTouchExplorationEnabled) {
- userState.mIsTouchExplorationEnabled = enabled;
+ if (enabled != userState.isTouchExplorationEnabledLocked()) {
+ userState.setTouchExplorationEnabledLocked(enabled);
final long identity = Binder.clearCallingIdentity();
try {
Settings.Secure.putIntForUser(mContext.getContentResolver(),
@@ -1857,60 +1836,61 @@
}
}
- private boolean readAccessibilityShortcutSettingLocked(UserState userState) {
+ private boolean readAccessibilityShortcutSettingLocked(AccessibilityUserState userState) {
String componentNameToEnableString = AccessibilityShortcutController
.getTargetServiceComponentNameString(mContext, userState.mUserId);
if ((componentNameToEnableString == null) || componentNameToEnableString.isEmpty()) {
- if (userState.mServiceToEnableWithShortcut == null) {
+ if (userState.getServiceToEnableWithShortcutLocked() == null) {
return false;
}
- userState.mServiceToEnableWithShortcut = null;
+ userState.setServiceToEnableWithShortcutLocked(null);
return true;
}
ComponentName componentNameToEnable =
ComponentName.unflattenFromString(componentNameToEnableString);
if ((componentNameToEnable != null)
- && componentNameToEnable.equals(userState.mServiceToEnableWithShortcut)) {
+ && componentNameToEnable.equals(userState.getServiceToEnableWithShortcutLocked())) {
return false;
}
- userState.mServiceToEnableWithShortcut = componentNameToEnable;
+ userState.setServiceToEnableWithShortcutLocked(componentNameToEnable);
scheduleNotifyClientsOfServicesStateChangeLocked(userState);
return true;
}
- private boolean readAccessibilityButtonSettingsLocked(UserState userState) {
+ private boolean readAccessibilityButtonSettingsLocked(AccessibilityUserState userState) {
String componentId = Settings.Secure.getStringForUser(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT, userState.mUserId);
if (TextUtils.isEmpty(componentId)) {
- if ((userState.mServiceAssignedToAccessibilityButton == null)
- && !userState.mIsNavBarMagnificationAssignedToAccessibilityButton) {
+ if ((userState.getServiceAssignedToAccessibilityButtonLocked() == null)
+ && !userState.isNavBarMagnificationAssignedToAccessibilityButtonLocked()) {
return false;
}
- userState.mServiceAssignedToAccessibilityButton = null;
- userState.mIsNavBarMagnificationAssignedToAccessibilityButton = false;
+ userState.setServiceAssignedToAccessibilityButtonLocked(null);
+ userState.setNavBarMagnificationAssignedToAccessibilityButtonLocked(false);
return true;
}
if (componentId.equals(MagnificationController.class.getName())) {
- if (userState.mIsNavBarMagnificationAssignedToAccessibilityButton) {
+ if (userState.isNavBarMagnificationAssignedToAccessibilityButtonLocked()) {
return false;
}
- userState.mServiceAssignedToAccessibilityButton = null;
- userState.mIsNavBarMagnificationAssignedToAccessibilityButton = true;
+ userState.setServiceAssignedToAccessibilityButtonLocked(null);
+ userState.setNavBarMagnificationAssignedToAccessibilityButtonLocked(true);
return true;
}
ComponentName componentName = ComponentName.unflattenFromString(componentId);
- if (Objects.equals(componentName, userState.mServiceAssignedToAccessibilityButton)) {
+ if (Objects.equals(componentName,
+ userState.getServiceAssignedToAccessibilityButtonLocked())) {
return false;
}
- userState.mServiceAssignedToAccessibilityButton = componentName;
- userState.mIsNavBarMagnificationAssignedToAccessibilityButton = false;
+ userState.setServiceAssignedToAccessibilityButtonLocked(componentName);
+ userState.setNavBarMagnificationAssignedToAccessibilityButtonLocked(false);
return true;
}
- private boolean readUserRecommendedUiTimeoutSettingsLocked(UserState userState) {
+ private boolean readUserRecommendedUiTimeoutSettingsLocked(AccessibilityUserState userState) {
final int nonInteractiveUiTimeout = Settings.Secure.getIntForUser(
mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_NON_INTERACTIVE_UI_TIMEOUT_MS, 0,
@@ -1919,10 +1899,10 @@
mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_INTERACTIVE_UI_TIMEOUT_MS, 0,
userState.mUserId);
- if (nonInteractiveUiTimeout != userState.mUserNonInteractiveUiTimeout
- || interactiveUiTimeout != userState.mUserInteractiveUiTimeout) {
- userState.mUserNonInteractiveUiTimeout = nonInteractiveUiTimeout;
- userState.mUserInteractiveUiTimeout = interactiveUiTimeout;
+ if (nonInteractiveUiTimeout != userState.getUserNonInteractiveUiTimeoutLocked()
+ || interactiveUiTimeout != userState.getUserInteractiveUiTimeoutLocked()) {
+ userState.setUserNonInteractiveUiTimeoutLocked(nonInteractiveUiTimeout);
+ userState.setUserInteractiveUiTimeoutLocked(interactiveUiTimeout);
scheduleNotifyClientsOfServicesStateChangeLocked(userState);
return true;
}
@@ -1936,22 +1916,22 @@
*
* @param userState
*/
- private void updateAccessibilityShortcutLocked(UserState userState) {
- if (userState.mServiceToEnableWithShortcut == null) {
+ private void updateAccessibilityShortcutLocked(AccessibilityUserState userState) {
+ if (userState.getServiceToEnableWithShortcutLocked() == null) {
return;
}
boolean shortcutServiceIsInstalled =
AccessibilityShortcutController.getFrameworkShortcutFeaturesMap()
- .containsKey(userState.mServiceToEnableWithShortcut);
+ .containsKey(userState.getServiceToEnableWithShortcutLocked());
for (int i = 0; !shortcutServiceIsInstalled && (i < userState.mInstalledServices.size());
i++) {
if (userState.mInstalledServices.get(i).getComponentName()
- .equals(userState.mServiceToEnableWithShortcut)) {
+ .equals(userState.getServiceToEnableWithShortcutLocked())) {
shortcutServiceIsInstalled = true;
}
}
if (!shortcutServiceIsInstalled) {
- userState.mServiceToEnableWithShortcut = null;
+ userState.setServiceToEnableWithShortcutLocked(null);
final long identity = Binder.clearCallingIdentity();
try {
Settings.Secure.putStringForUser(mContext.getContentResolver(),
@@ -1967,7 +1947,7 @@
}
private boolean canRequestAndRequestsTouchExplorationLocked(
- AccessibilityServiceConnection service, UserState userState) {
+ AccessibilityServiceConnection service, AccessibilityUserState userState) {
// Service not ready or cannot request the feature - well nothing to do.
if (!service.canReceiveEventsLocked() || !service.mRequestTouchExplorationMode) {
return false;
@@ -1997,7 +1977,7 @@
return false;
}
- private void updateMagnificationLocked(UserState userState) {
+ private void updateMagnificationLocked(AccessibilityUserState userState) {
if (userState.mUserId != mCurrentUserId) {
return;
}
@@ -2012,8 +1992,8 @@
// We would skip overlay display because it uses overlay window to simulate secondary
// displays in one display. It's not a real display and there's no input events for it.
final ArrayList<Display> displays = getValidDisplayList();
- if (userState.mIsDisplayMagnificationEnabled
- || userState.mIsNavBarMagnificationEnabled) {
+ if (userState.isDisplayMagnificationEnabledLocked()
+ || userState.isNavBarMagnificationEnabledLocked()) {
for (int i = 0; i < displays.size(); i++) {
final Display display = displays.get(i);
getMagnificationController().register(display.getDisplayId());
@@ -2037,7 +2017,7 @@
* Returns whether the specified user has any services that are capable of
* controlling magnification.
*/
- private boolean userHasMagnificationServicesLocked(UserState userState) {
+ private boolean userHasMagnificationServicesLocked(AccessibilityUserState userState) {
final List<AccessibilityServiceConnection> services = userState.mBoundServices;
for (int i = 0, count = services.size(); i < count; i++) {
final AccessibilityServiceConnection service = services.get(i);
@@ -2052,7 +2032,7 @@
* Returns whether the specified user has any services that are capable of
* controlling magnification and are actively listening for magnification updates.
*/
- private boolean userHasListeningMagnificationServicesLocked(UserState userState,
+ private boolean userHasListeningMagnificationServicesLocked(AccessibilityUserState userState,
int displayId) {
final List<AccessibilityServiceConnection> services = userState.mBoundServices;
for (int i = 0, count = services.size(); i < count; i++) {
@@ -2065,7 +2045,7 @@
return false;
}
- private void updateFingerprintGestureHandling(UserState userState) {
+ private void updateFingerprintGestureHandling(AccessibilityUserState userState) {
final List<AccessibilityServiceConnection> services;
synchronized (mLock) {
services = userState.mBoundServices;
@@ -2097,7 +2077,7 @@
}
}
- private void updateAccessibilityButtonTargetsLocked(UserState userState) {
+ private void updateAccessibilityButtonTargetsLocked(AccessibilityUserState userState) {
for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) {
final AccessibilityServiceConnection service = userState.mBoundServices.get(i);
if (service.mRequestAccessibilityButton) {
@@ -2107,9 +2087,9 @@
}
}
- private void updateRecommendedUiTimeoutLocked(UserState userState) {
- int newNonInteractiveUiTimeout = userState.mUserNonInteractiveUiTimeout;
- int newInteractiveUiTimeout = userState.mUserInteractiveUiTimeout;
+ private void updateRecommendedUiTimeoutLocked(AccessibilityUserState userState) {
+ int newNonInteractiveUiTimeout = userState.getUserNonInteractiveUiTimeoutLocked();
+ int newInteractiveUiTimeout = userState.getUserInteractiveUiTimeoutLocked();
// read from a11y services if user does not specify value
if (newNonInteractiveUiTimeout == 0 || newInteractiveUiTimeout == 0) {
int serviceNonInteractiveUiTimeout = 0;
@@ -2132,8 +2112,8 @@
newInteractiveUiTimeout = serviceInteractiveUiTimeout;
}
}
- userState.mNonInteractiveUiTimeout = newNonInteractiveUiTimeout;
- userState.mInteractiveUiTimeout = newInteractiveUiTimeout;
+ userState.setNonInteractiveUiTimeoutLocked(newNonInteractiveUiTimeout);
+ userState.setInteractiveUiTimeoutLocked(newInteractiveUiTimeout);
}
@GuardedBy("mLock")
@@ -2180,8 +2160,8 @@
final Map<ComponentName, ToggleableFrameworkFeatureInfo> frameworkFeatureMap =
AccessibilityShortcutController.getFrameworkShortcutFeaturesMap();
synchronized(mLock) {
- final UserState userState = getUserStateLocked(mCurrentUserId);
- final ComponentName serviceName = userState.mServiceToEnableWithShortcut;
+ final AccessibilityUserState userState = getUserStateLocked(mCurrentUserId);
+ final ComponentName serviceName = userState.getServiceToEnableWithShortcutLocked();
if (serviceName == null) {
return;
}
@@ -2218,11 +2198,11 @@
"getAccessibilityShortcutService requires the MANAGE_ACCESSIBILITY permission");
}
synchronized(mLock) {
- final UserState userState = getUserStateLocked(mCurrentUserId);
- if (userState.mServiceToEnableWithShortcut == null) {
+ final AccessibilityUserState userState = getUserStateLocked(mCurrentUserId);
+ if (userState.getServiceToEnableWithShortcutLocked() == null) {
return null;
}
- return userState.mServiceToEnableWithShortcut.flattenToString();
+ return userState.getServiceToEnableWithShortcutLocked().flattenToString();
}
}
@@ -2237,7 +2217,7 @@
userId);
setting.write(ComponentNameSet.add(setting.read(), componentName));
- UserState userState = getUserStateLocked(userId);
+ AccessibilityUserState userState = getUserStateLocked(userId);
if (userState.mEnabledServices.add(componentName)) {
onUserStateChangedLocked(userState);
}
@@ -2254,7 +2234,7 @@
userId);
setting.write(ComponentNameSet.remove(setting.read(), componentName));
- UserState userState = getUserStateLocked(userId);
+ AccessibilityUserState userState = getUserStateLocked(userId);
if (userState.mEnabledServices.remove(componentName)) {
onUserStateChangedLocked(userState);
}
@@ -2322,14 +2302,14 @@
@Override
public long getRecommendedTimeoutMillis() {
synchronized(mLock) {
- final UserState userState = getCurrentUserStateLocked();
+ final AccessibilityUserState userState = getCurrentUserStateLocked();
return getRecommendedTimeoutMillisLocked(userState);
}
}
- private long getRecommendedTimeoutMillisLocked(UserState userState) {
- return IntPair.of(userState.mInteractiveUiTimeout,
- userState.mNonInteractiveUiTimeout);
+ private long getRecommendedTimeoutMillisLocked(AccessibilityUserState userState) {
+ return IntPair.of(userState.getInteractiveUiTimeoutLocked(),
+ userState.getNonInteractiveUiTimeoutLocked());
}
@Override
@@ -2338,78 +2318,20 @@
synchronized (mLock) {
pw.println("ACCESSIBILITY MANAGER (dumpsys accessibility)");
pw.println();
+ pw.append("currentUserId=").append(String.valueOf(mCurrentUserId));
+ pw.println();
final int userCount = mUserStates.size();
for (int i = 0; i < userCount; i++) {
- UserState userState = mUserStates.valueAt(i);
- pw.append("User state[attributes:{id=" + userState.mUserId);
- pw.append(", currentUser=" + (userState.mUserId == mCurrentUserId));
- pw.append(", touchExplorationEnabled=" + userState.mIsTouchExplorationEnabled);
- pw.append(", displayMagnificationEnabled="
- + userState.mIsDisplayMagnificationEnabled);
- pw.append(", navBarMagnificationEnabled="
- + userState.mIsNavBarMagnificationEnabled);
- pw.append(", autoclickEnabled=" + userState.mIsAutoclickEnabled);
- pw.append(", nonInteractiveUiTimeout=" + userState.mNonInteractiveUiTimeout);
- pw.append(", interactiveUiTimeout=" + userState.mInteractiveUiTimeout);
- pw.append(", installedServiceCount=" + userState.mInstalledServices.size());
- if (mUiAutomationManager.isUiAutomationRunningLocked()) {
- pw.append(", ");
- mUiAutomationManager.dumpUiAutomationService(fd, pw, args);
- pw.println();
- }
- pw.append("}");
- pw.println();
- pw.append(" Bound services:{");
- final int serviceCount = userState.mBoundServices.size();
- for (int j = 0; j < serviceCount; j++) {
- if (j > 0) {
- pw.append(", ");
- pw.println();
- pw.append(" ");
- }
- AccessibilityServiceConnection service = userState.mBoundServices.get(j);
- service.dump(fd, pw, args);
- }
- pw.println("}");
- pw.append(" Enabled services:{");
- Iterator<ComponentName> it = userState.mEnabledServices.iterator();
- if (it.hasNext()) {
- ComponentName componentName = it.next();
- pw.append(componentName.toShortString());
- while (it.hasNext()) {
- componentName = it.next();
- pw.append(", ");
- pw.append(componentName.toShortString());
- }
- }
- pw.println("}");
- pw.append(" Binding services:{");
- it = userState.mBindingServices.iterator();
- if (it.hasNext()) {
- ComponentName componentName = it.next();
- pw.append(componentName.toShortString());
- while (it.hasNext()) {
- componentName = it.next();
- pw.append(", ");
- pw.append(componentName.toShortString());
- }
- }
- pw.println("}]");
+ mUserStates.valueAt(i).dump(fd, pw, args);
+ }
+ if (mUiAutomationManager.isUiAutomationRunningLocked()) {
+ mUiAutomationManager.dumpUiAutomationService(fd, pw, args);
pw.println();
}
mA11yWindowManager.dump(fd, pw, args);
}
}
- private void putSecureIntForUser(String key, int value, int userid) {
- final long identity = Binder.clearCallingIdentity();
- try {
- Settings.Secure.putIntForUser(mContext.getContentResolver(), key, value, userid);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
//TODO remove after refactoring KeyEventDispatcherTest
final class MainHandler extends Handler {
public static final int MSG_SEND_KEY_EVENT_TO_INPUT_FILTER = 8;
@@ -2446,7 +2368,7 @@
@Override
public void onClientChangeLocked(boolean serviceInfoChanged) {
- AccessibilityManagerService.UserState userState = getUserStateLocked(mCurrentUserId);
+ AccessibilityUserState userState = getUserStateLocked(mCurrentUserId);
onUserStateChangedLocked(userState);
if (serviceInfoChanged) {
scheduleNotifyClientsOfServicesStateChangeLocked(userState);
@@ -2474,7 +2396,7 @@
info.setCapabilities(AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT);
info.flags |= AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS;
info.flags |= AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
- final UserState userState;
+ final AccessibilityUserState userState;
synchronized (mLock) {
userState = getCurrentUserStateLocked();
}
@@ -2593,7 +2515,7 @@
if (mInputFilter != null) {
mInputFilter.onDisplayChanged();
}
- UserState userState = getCurrentUserStateLocked();
+ AccessibilityUserState userState = getCurrentUserStateLocked();
if (displayId != Display.DEFAULT_DISPLAY) {
final List<AccessibilityServiceConnection> services = userState.mBoundServices;
for (int i = 0; i < services.size(); i++) {
@@ -2618,7 +2540,7 @@
if (mInputFilter != null) {
mInputFilter.onDisplayChanged();
}
- UserState userState = getCurrentUserStateLocked();
+ AccessibilityUserState userState = getCurrentUserStateLocked();
if (displayId != Display.DEFAULT_DISPLAY) {
final List<AccessibilityServiceConnection> services = userState.mBoundServices;
for (int i = 0; i < services.size(); i++) {
@@ -2658,7 +2580,8 @@
final String[] mPackageNames;
int mLastSentRelevantEventTypes;
- private Client(IAccessibilityManagerClient callback, int clientUid, UserState userState) {
+ private Client(IAccessibilityManagerClient callback, int clientUid,
+ AccessibilityUserState userState) {
mCallback = callback;
mPackageNames = mPackageManager.getPackagesForUid(clientUid);
synchronized (mLock) {
@@ -2667,316 +2590,6 @@
}
}
- public class UserState {
- public final int mUserId;
-
- // Non-transient state.
-
- public final RemoteCallbackList<IAccessibilityManagerClient> mUserClients =
- new RemoteCallbackList<>();
-
- // Transient state.
-
- public final ArrayList<AccessibilityServiceConnection> mBoundServices = new ArrayList<>();
-
- public final Map<ComponentName, AccessibilityServiceConnection> mComponentNameToServiceMap =
- new HashMap<>();
-
- public final List<AccessibilityServiceInfo> mInstalledServices =
- new ArrayList<>();
-
- public final List<AccessibilityShortcutInfo> mInstalledShortcuts = new ArrayList<>();
-
- private final Set<ComponentName> mBindingServices = new HashSet<>();
-
- public final Set<ComponentName> mEnabledServices = new HashSet<>();
-
- public final Set<ComponentName> mTouchExplorationGrantedServices =
- new HashSet<>();
-
- public ComponentName mServiceChangingSoftKeyboardMode;
-
- public ComponentName mServiceToEnableWithShortcut;
-
- public int mLastSentClientState = -1;
- public int mNonInteractiveUiTimeout = 0;
- public int mInteractiveUiTimeout = 0;
-
- private int mSoftKeyboardShowMode = 0;
-
- public boolean mIsNavBarMagnificationAssignedToAccessibilityButton;
- public ComponentName mServiceAssignedToAccessibilityButton;
-
- public boolean mIsTouchExplorationEnabled;
- public boolean mIsTextHighContrastEnabled;
- public boolean mIsDisplayMagnificationEnabled;
- public boolean mIsNavBarMagnificationEnabled;
- public boolean mIsAutoclickEnabled;
- public boolean mIsPerformGesturesEnabled;
- public boolean mIsFilterKeyEventsEnabled;
- public int mUserNonInteractiveUiTimeout;
- public int mUserInteractiveUiTimeout;
-
- private boolean mBindInstantServiceAllowed;
-
- public UserState(int userId) {
- mUserId = userId;
- }
-
- public int getClientState() {
- int clientState = 0;
- final boolean a11yEnabled = (mUiAutomationManager.isUiAutomationRunningLocked()
- || isHandlingAccessibilityEvents());
- if (a11yEnabled) {
- clientState |= AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED;
- }
- // Touch exploration relies on enabled accessibility.
- if (a11yEnabled && mIsTouchExplorationEnabled) {
- clientState |= AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED;
- }
- if (mIsTextHighContrastEnabled) {
- clientState |= AccessibilityManager.STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED;
- }
- return clientState;
- }
-
- public boolean isHandlingAccessibilityEvents() {
- return !mBoundServices.isEmpty() || !mBindingServices.isEmpty();
- }
-
- public void onSwitchToAnotherUserLocked() {
- // Unbind all services.
- unbindAllServicesLocked(this);
-
- // Clear service management state.
- mBoundServices.clear();
- mBindingServices.clear();
-
- // Clear event management state.
- mLastSentClientState = -1;
-
- // clear UI timeout
- mNonInteractiveUiTimeout = 0;
- mInteractiveUiTimeout = 0;
-
- // Clear state persisted in settings.
- mEnabledServices.clear();
- mTouchExplorationGrantedServices.clear();
- mIsTouchExplorationEnabled = false;
- mIsDisplayMagnificationEnabled = false;
- mIsNavBarMagnificationEnabled = false;
- mServiceAssignedToAccessibilityButton = null;
- mIsNavBarMagnificationAssignedToAccessibilityButton = false;
- mIsAutoclickEnabled = false;
- mUserNonInteractiveUiTimeout = 0;
- mUserInteractiveUiTimeout = 0;
- }
-
- public void addServiceLocked(AccessibilityServiceConnection serviceConnection) {
- if (!mBoundServices.contains(serviceConnection)) {
- serviceConnection.onAdded();
- mBoundServices.add(serviceConnection);
- mComponentNameToServiceMap.put(serviceConnection.mComponentName, serviceConnection);
- scheduleNotifyClientsOfServicesStateChangeLocked(this);
- }
- }
-
- /**
- * Removes a service.
- * There are three states to a service here: off, bound, and binding.
- * This stops tracking the service as bound.
- *
- * @param serviceConnection The service.
- */
- public void removeServiceLocked(AccessibilityServiceConnection serviceConnection) {
- mBoundServices.remove(serviceConnection);
- serviceConnection.onRemoved();
- if ((mServiceChangingSoftKeyboardMode != null)
- && (mServiceChangingSoftKeyboardMode.equals(
- serviceConnection.getServiceInfo().getComponentName()))) {
- setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null);
- }
- // It may be possible to bind a service twice, which confuses the map. Rebuild the map
- // to make sure we can still reach a service
- mComponentNameToServiceMap.clear();
- for (int i = 0; i < mBoundServices.size(); i++) {
- AccessibilityServiceConnection boundClient = mBoundServices.get(i);
- mComponentNameToServiceMap.put(boundClient.mComponentName, boundClient);
- }
- scheduleNotifyClientsOfServicesStateChangeLocked(this);
- }
-
- /**
- * Make sure a services disconnected but still 'on' state is reflected in UserState
- * There are three states to a service here: off, bound, and binding.
- * This drops a service from a bound state, to the binding state.
- * The binding state describes the situation where a service is on, but not bound.
- *
- * @param serviceConnection The service.
- */
- public void serviceDisconnectedLocked(AccessibilityServiceConnection serviceConnection) {
- removeServiceLocked(serviceConnection);
- mBindingServices.add(serviceConnection.getComponentName());
- }
-
- public Set<ComponentName> getBindingServicesLocked() {
- return mBindingServices;
- }
-
- /**
- * Returns enabled service list.
- */
- public Set<ComponentName> getEnabledServicesLocked() {
- return mEnabledServices;
- }
-
- public int getSoftKeyboardShowMode() {
- return mSoftKeyboardShowMode;
- }
-
- /**
- * Set the soft keyboard mode. This mode is a bit odd, as it spans multiple settings.
- * The ACCESSIBILITY_SOFT_KEYBOARD_MODE setting can be checked by the rest of the system
- * to see if it should suppress showing the IME. The SHOW_IME_WITH_HARD_KEYBOARD setting
- * setting can be changed by the user, and prevents the system from suppressing the soft
- * keyboard when the hard keyboard is connected. The hard keyboard setting needs to defer
- * to the user's preference, if they have supplied one.
- *
- * @param newMode The new mode
- * @param requester The service requesting the change, so we can undo it when the
- * service stops. Set to null if something other than a service is forcing
- * the change.
- *
- * @return Whether or not the soft keyboard mode equals the new mode after the call
- */
- public boolean setSoftKeyboardModeLocked(int newMode, @Nullable ComponentName requester) {
- if ((newMode != SHOW_MODE_AUTO) && (newMode != SHOW_MODE_HIDDEN)
- && (newMode != SHOW_MODE_IGNORE_HARD_KEYBOARD))
- {
- Slog.w(LOG_TAG, "Invalid soft keyboard mode");
- return false;
- }
- if (mSoftKeyboardShowMode == newMode) return true;
-
- if (newMode == SHOW_MODE_IGNORE_HARD_KEYBOARD) {
- if (hasUserOverriddenHardKeyboardSettingLocked()) {
- // The user has specified a default for this setting
- return false;
- }
- // Save the original value. But don't do this if the value in settings is already
- // the new mode. That happens when we start up after a reboot, and we don't want
- // to overwrite the value we had from when we first started controlling the setting.
- if (getSoftKeyboardValueFromSettings() != SHOW_MODE_IGNORE_HARD_KEYBOARD) {
- setOriginalHardKeyboardValue(
- Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0) != 0);
- }
- putSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 1, mUserId);
- } else if (mSoftKeyboardShowMode == SHOW_MODE_IGNORE_HARD_KEYBOARD) {
- putSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD,
- getOriginalHardKeyboardValue() ? 1 : 0, mUserId);
- }
-
- saveSoftKeyboardValueToSettings(newMode);
- mSoftKeyboardShowMode = newMode;
- mServiceChangingSoftKeyboardMode = requester;
- notifySoftKeyboardShowModeChangedLocked(mSoftKeyboardShowMode);
- return true;
- }
-
- /**
- * If the settings are inconsistent with the internal state, make the internal state
- * match the settings.
- */
- public void reconcileSoftKeyboardModeWithSettingsLocked() {
- final ContentResolver cr = mContext.getContentResolver();
- final boolean showWithHardKeyboardSettings =
- Settings.Secure.getInt(cr, Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0) != 0;
- if (mSoftKeyboardShowMode == SHOW_MODE_IGNORE_HARD_KEYBOARD) {
- if (!showWithHardKeyboardSettings) {
- // The user has overridden the setting. Respect that and prevent further changes
- // to this behavior.
- setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null);
- setUserOverridesHardKeyboardSettingLocked();
- }
- }
-
- // If the setting and the internal state are out of sync, set both to default
- if (getSoftKeyboardValueFromSettings() != mSoftKeyboardShowMode)
- {
- Slog.e(LOG_TAG,
- "Show IME setting inconsistent with internal state. Overwriting");
- setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null);
- putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
- SHOW_MODE_AUTO, mUserId);
- }
- }
-
- private void setUserOverridesHardKeyboardSettingLocked() {
- final int softKeyboardSetting = Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0);
- putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
- softKeyboardSetting | SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN,
- mUserId);
- }
-
- private boolean hasUserOverriddenHardKeyboardSettingLocked() {
- final int softKeyboardSetting = Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0);
- return (softKeyboardSetting & SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN)
- != 0;
- }
-
- private void setOriginalHardKeyboardValue(boolean originalHardKeyboardValue) {
- final int oldSoftKeyboardSetting = Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0);
- final int newSoftKeyboardSetting = oldSoftKeyboardSetting
- & (~SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE)
- | ((originalHardKeyboardValue) ? SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE : 0);
- putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
- newSoftKeyboardSetting, mUserId);
- }
-
- private void saveSoftKeyboardValueToSettings(int softKeyboardShowMode) {
- final int oldSoftKeyboardSetting = Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0);
- final int newSoftKeyboardSetting = oldSoftKeyboardSetting & (~SHOW_MODE_MASK)
- | softKeyboardShowMode;
- putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
- newSoftKeyboardSetting, mUserId);
- }
-
- private int getSoftKeyboardValueFromSettings() {
- return Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
- SHOW_MODE_AUTO) & SHOW_MODE_MASK;
- }
-
- private boolean getOriginalHardKeyboardValue() {
- return (Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0)
- & SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE) != 0;
- }
-
- public boolean getBindInstantServiceAllowed() {
- synchronized (mLock) {
- return mBindInstantServiceAllowed;
- }
- }
-
- public void setBindInstantServiceAllowed(boolean allowed) {
- synchronized (mLock) {
- mContext.enforceCallingOrSelfPermission(
- Manifest.permission.MANAGE_BIND_INSTANT_SERVICE,
- "setBindInstantServiceAllowed");
- if (allowed) {
- mBindInstantServiceAllowed = allowed;
- onUserStateChangedLocked(this);
- }
- }
- }
- }
-
private final class AccessibilityContentObserver extends ContentObserver {
private final Uri mTouchExplorationEnabledUri = Settings.Secure.getUriFor(
@@ -3057,7 +2670,7 @@
synchronized (mLock) {
// Profiles share the accessibility state of the parent. Therefore,
// we are checking for changes only the parent settings.
- UserState userState = getCurrentUserStateLocked();
+ AccessibilityUserState userState = getCurrentUserStateLocked();
if (mTouchExplorationEnabledUri.equals(uri)) {
if (readTouchExplorationEnabledSettingLocked(userState)) {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index e7f3ccc..d154060 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -35,7 +35,6 @@
import android.util.Slog;
import android.view.Display;
-import com.android.server.accessibility.AccessibilityManagerService.UserState;
import com.android.server.wm.WindowManagerInternal;
import java.lang.ref.WeakReference;
@@ -52,13 +51,13 @@
class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnection {
private static final String LOG_TAG = "AccessibilityServiceConnection";
/*
- Holding a weak reference so there isn't a loop of references. UserState keeps lists of bound
- and binding services. These are freed on user changes, but just in case it somehow gets lost
- the weak reference will let the memory get GCed.
+ Holding a weak reference so there isn't a loop of references. AccessibilityUserState keeps
+ lists of bound and binding services. These are freed on user changes, but just in case it
+ somehow gets lost the weak reference will let the memory get GCed.
Having the reference be null when being called is a very bad sign, but we check the condition.
*/
- final WeakReference<UserState> mUserStateWeakReference;
+ final WeakReference<AccessibilityUserState> mUserStateWeakReference;
final Intent mIntent;
private final Handler mMainHandler;
@@ -66,7 +65,7 @@
private boolean mWasConnectedAndDied;
- public AccessibilityServiceConnection(UserState userState, Context context,
+ AccessibilityServiceConnection(AccessibilityUserState userState, Context context,
ComponentName componentName,
AccessibilityServiceInfo accessibilityServiceInfo, int id, Handler mainHandler,
Object lock, AccessibilitySecurityPolicy securityPolicy, SystemSupport systemSupport,
@@ -74,7 +73,7 @@
SystemActionPerformer systemActionPerfomer, AccessibilityWindowManager awm) {
super(context, componentName, accessibilityServiceInfo, id, mainHandler, lock,
securityPolicy, systemSupport, windowManagerInternal, systemActionPerfomer, awm);
- mUserStateWeakReference = new WeakReference<UserState>(userState);
+ mUserStateWeakReference = new WeakReference<AccessibilityUserState>(userState);
mIntent = new Intent().setComponent(mComponentName);
mMainHandler = mainHandler;
mIntent.putExtra(Intent.EXTRA_CLIENT_LABEL,
@@ -89,13 +88,13 @@
}
public void bindLocked() {
- UserState userState = mUserStateWeakReference.get();
+ AccessibilityUserState userState = mUserStateWeakReference.get();
if (userState == null) return;
final long identity = Binder.clearCallingIdentity();
try {
int flags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE
| Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS;
- if (userState.getBindInstantServiceAllowed()) {
+ if (userState.getBindInstantServiceAllowedLocked()) {
flags |= Context.BIND_ALLOW_INSTANT;
}
if (mService == null && mContext.bindServiceAsUser(
@@ -109,7 +108,7 @@
public void unbindLocked() {
mContext.unbindService(this);
- UserState userState = mUserStateWeakReference.get();
+ AccessibilityUserState userState = mUserStateWeakReference.get();
if (userState == null) return;
userState.removeServiceLocked(this);
mSystemSupport.getMagnificationController().resetAllIfNeeded(mId);
@@ -123,7 +122,7 @@
@Override
public void disableSelf() {
synchronized (mLock) {
- UserState userState = mUserStateWeakReference.get();
+ AccessibilityUserState userState = mUserStateWeakReference.get();
if (userState == null) return;
if (userState.getEnabledServicesLocked().remove(mComponentName)) {
final long identity = Binder.clearCallingIdentity();
@@ -156,7 +155,7 @@
}
}
mServiceInterface = IAccessibilityServiceClient.Stub.asInterface(service);
- UserState userState = mUserStateWeakReference.get();
+ AccessibilityUserState userState = mUserStateWeakReference.get();
if (userState == null) return;
userState.addServiceLocked(this);
mSystemSupport.onClientChangeLocked(false);
@@ -177,7 +176,7 @@
private void initializeService() {
IAccessibilityServiceClient serviceInterface = null;
synchronized (mLock) {
- UserState userState = mUserStateWeakReference.get();
+ AccessibilityUserState userState = mUserStateWeakReference.get();
if (userState == null) return;
Set<ComponentName> bindingServices = userState.getBindingServicesLocked();
if (bindingServices.contains(mComponentName) || mWasConnectedAndDied) {
@@ -240,7 +239,7 @@
if (!hasRightsToCurrentUserLocked()) {
return false;
}
- final UserState userState = mUserStateWeakReference.get();
+ final AccessibilityUserState userState = mUserStateWeakReference.get();
if (userState == null) return false;
return userState.setSoftKeyboardModeLocked(showMode, mComponentName);
}
@@ -248,8 +247,8 @@
@Override
public int getSoftKeyboardShowMode() {
- final UserState userState = mUserStateWeakReference.get();
- return (userState != null) ? userState.getSoftKeyboardShowMode() : 0;
+ final AccessibilityUserState userState = mUserStateWeakReference.get();
+ return (userState != null) ? userState.getSoftKeyboardShowModeLocked() : 0;
}
@Override
@@ -258,7 +257,7 @@
if (!hasRightsToCurrentUserLocked()) {
return false;
}
- UserState userState = mUserStateWeakReference.get();
+ AccessibilityUserState userState = mUserStateWeakReference.get();
return (userState != null) && isAccessibilityButtonAvailableLocked(userState);
}
}
@@ -273,7 +272,7 @@
return;
}
mWasConnectedAndDied = true;
- UserState userState = mUserStateWeakReference.get();
+ AccessibilityUserState userState = mUserStateWeakReference.get();
if (userState != null) {
userState.serviceDisconnectedLocked(this);
}
@@ -283,7 +282,7 @@
}
}
- public boolean isAccessibilityButtonAvailableLocked(UserState userState) {
+ public boolean isAccessibilityButtonAvailableLocked(AccessibilityUserState userState) {
// If the service does not request the accessibility button, it isn't available
if (!mRequestAccessibilityButton) {
return false;
@@ -295,8 +294,8 @@
}
// If magnification is on and assigned to the accessibility button, services cannot be
- if (userState.mIsNavBarMagnificationEnabled
- && userState.mIsNavBarMagnificationAssignedToAccessibilityButton) {
+ if (userState.isNavBarMagnificationEnabledLocked()
+ && userState.isNavBarMagnificationAssignedToAccessibilityButtonLocked()) {
return false;
}
@@ -314,13 +313,14 @@
return true;
} else {
// With more than one active service, we derive the target from the user's settings
- if (userState.mServiceAssignedToAccessibilityButton == null) {
+ if (userState.getServiceAssignedToAccessibilityButtonLocked() == null) {
// If the user has not made an assignment, we treat the button as available to
// all services until the user interacts with the button to make an assignment
return true;
} else {
// If an assignment was made, it defines availability
- return mComponentName.equals(userState.mServiceAssignedToAccessibilityButton);
+ return mComponentName.equals(
+ userState.getServiceAssignedToAccessibilityButtonLocked());
}
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
new file mode 100644
index 0000000..69f1e0e
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
@@ -0,0 +1,573 @@
+/*
+ * 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.accessibility;
+
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_AUTO;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HIDDEN;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_IGNORE_HARD_KEYBOARD;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_MASK;
+
+import android.accessibilityservice.AccessibilityService.SoftKeyboardShowMode;
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.AccessibilityShortcutInfo;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.Binder;
+import android.os.RemoteCallbackList;
+import android.provider.Settings;
+import android.util.Slog;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.IAccessibilityManagerClient;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Class that hold states and settings per user and share between
+ * {@link AccessibilityManagerService} and {@link AccessibilityServiceConnection}.
+ */
+class AccessibilityUserState {
+ private static final String LOG_TAG = AccessibilityUserState.class.getSimpleName();
+
+ final int mUserId;
+
+ // Non-transient state.
+
+ final RemoteCallbackList<IAccessibilityManagerClient> mUserClients = new RemoteCallbackList<>();
+
+ // Transient state.
+
+ final ArrayList<AccessibilityServiceConnection> mBoundServices = new ArrayList<>();
+
+ final Map<ComponentName, AccessibilityServiceConnection> mComponentNameToServiceMap =
+ new HashMap<>();
+
+ final List<AccessibilityServiceInfo> mInstalledServices = new ArrayList<>();
+
+ final List<AccessibilityShortcutInfo> mInstalledShortcuts = new ArrayList<>();
+
+ final Set<ComponentName> mBindingServices = new HashSet<>();
+
+ final Set<ComponentName> mEnabledServices = new HashSet<>();
+
+ final Set<ComponentName> mTouchExplorationGrantedServices = new HashSet<>();
+
+ private final ServiceInfoChangeListener mServiceInfoChangeListener;
+
+ private ComponentName mServiceAssignedToAccessibilityButton;
+
+ private ComponentName mServiceChangingSoftKeyboardMode;
+
+ private ComponentName mServiceToEnableWithShortcut;
+
+ private boolean mBindInstantServiceAllowed;
+ private boolean mIsAutoclickEnabled;
+ private boolean mIsDisplayMagnificationEnabled;
+ private boolean mIsFilterKeyEventsEnabled;
+ private boolean mIsNavBarMagnificationAssignedToAccessibilityButton;
+ private boolean mIsNavBarMagnificationEnabled;
+ private boolean mIsPerformGesturesEnabled;
+ private boolean mIsTextHighContrastEnabled;
+ private boolean mIsTouchExplorationEnabled;
+ private int mUserInteractiveUiTimeout;
+ private int mUserNonInteractiveUiTimeout;
+ private int mNonInteractiveUiTimeout = 0;
+ private int mInteractiveUiTimeout = 0;
+ private int mLastSentClientState = -1;
+
+ private Context mContext;
+
+ @SoftKeyboardShowMode
+ private int mSoftKeyboardShowMode = SHOW_MODE_AUTO;
+
+ interface ServiceInfoChangeListener {
+ void onServiceInfoChangedLocked(AccessibilityUserState userState);
+ }
+
+ AccessibilityUserState(int userId, @NonNull Context context,
+ @NonNull ServiceInfoChangeListener serviceInfoChangeListener) {
+ mUserId = userId;
+ mContext = context;
+ mServiceInfoChangeListener = serviceInfoChangeListener;
+ }
+
+ boolean isHandlingAccessibilityEventsLocked() {
+ return !mBoundServices.isEmpty() || !mBindingServices.isEmpty();
+ }
+
+ void onSwitchToAnotherUserLocked() {
+ // Unbind all services.
+ unbindAllServicesLocked();
+
+ // Clear service management state.
+ mBoundServices.clear();
+ mBindingServices.clear();
+
+ // Clear event management state.
+ mLastSentClientState = -1;
+
+ // clear UI timeout
+ mNonInteractiveUiTimeout = 0;
+ mInteractiveUiTimeout = 0;
+
+ // Clear state persisted in settings.
+ mEnabledServices.clear();
+ mTouchExplorationGrantedServices.clear();
+ mIsTouchExplorationEnabled = false;
+ mIsDisplayMagnificationEnabled = false;
+ mIsNavBarMagnificationEnabled = false;
+ mServiceAssignedToAccessibilityButton = null;
+ mIsNavBarMagnificationAssignedToAccessibilityButton = false;
+ mIsAutoclickEnabled = false;
+ mUserNonInteractiveUiTimeout = 0;
+ mUserInteractiveUiTimeout = 0;
+ }
+
+ void addServiceLocked(AccessibilityServiceConnection serviceConnection) {
+ if (!mBoundServices.contains(serviceConnection)) {
+ serviceConnection.onAdded();
+ mBoundServices.add(serviceConnection);
+ mComponentNameToServiceMap.put(serviceConnection.getComponentName(), serviceConnection);
+ mServiceInfoChangeListener.onServiceInfoChangedLocked(this);
+ }
+ }
+
+ /**
+ * Removes a service.
+ * There are three states to a service here: off, bound, and binding.
+ * This stops tracking the service as bound.
+ *
+ * @param serviceConnection The service.
+ */
+ void removeServiceLocked(AccessibilityServiceConnection serviceConnection) {
+ mBoundServices.remove(serviceConnection);
+ serviceConnection.onRemoved();
+ if ((mServiceChangingSoftKeyboardMode != null)
+ && (mServiceChangingSoftKeyboardMode.equals(
+ serviceConnection.getServiceInfo().getComponentName()))) {
+ setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null);
+ }
+ // It may be possible to bind a service twice, which confuses the map. Rebuild the map
+ // to make sure we can still reach a service
+ mComponentNameToServiceMap.clear();
+ for (int i = 0; i < mBoundServices.size(); i++) {
+ AccessibilityServiceConnection boundClient = mBoundServices.get(i);
+ mComponentNameToServiceMap.put(boundClient.getComponentName(), boundClient);
+ }
+ mServiceInfoChangeListener.onServiceInfoChangedLocked(this);
+ }
+
+ /**
+ * Make sure a services disconnected but still 'on' state is reflected in AccessibilityUserState
+ * There are three states to a service here: off, bound, and binding.
+ * This drops a service from a bound state, to the binding state.
+ * The binding state describes the situation where a service is on, but not bound.
+ *
+ * @param serviceConnection The service.
+ */
+ void serviceDisconnectedLocked(AccessibilityServiceConnection serviceConnection) {
+ removeServiceLocked(serviceConnection);
+ mBindingServices.add(serviceConnection.getComponentName());
+ }
+
+ /**
+ * Set the soft keyboard mode. This mode is a bit odd, as it spans multiple settings.
+ * The ACCESSIBILITY_SOFT_KEYBOARD_MODE setting can be checked by the rest of the system
+ * to see if it should suppress showing the IME. The SHOW_IME_WITH_HARD_KEYBOARD setting
+ * setting can be changed by the user, and prevents the system from suppressing the soft
+ * keyboard when the hard keyboard is connected. The hard keyboard setting needs to defer
+ * to the user's preference, if they have supplied one.
+ *
+ * @param newMode The new mode
+ * @param requester The service requesting the change, so we can undo it when the
+ * service stops. Set to null if something other than a service is forcing
+ * the change.
+ *
+ * @return Whether or not the soft keyboard mode equals the new mode after the call
+ */
+ boolean setSoftKeyboardModeLocked(@SoftKeyboardShowMode int newMode,
+ @Nullable ComponentName requester) {
+ if ((newMode != SHOW_MODE_AUTO)
+ && (newMode != SHOW_MODE_HIDDEN)
+ && (newMode != SHOW_MODE_IGNORE_HARD_KEYBOARD)) {
+ Slog.w(LOG_TAG, "Invalid soft keyboard mode");
+ return false;
+ }
+ if (mSoftKeyboardShowMode == newMode) {
+ return true;
+ }
+
+ if (newMode == SHOW_MODE_IGNORE_HARD_KEYBOARD) {
+ if (hasUserOverriddenHardKeyboardSetting()) {
+ // The user has specified a default for this setting
+ return false;
+ }
+ // Save the original value. But don't do this if the value in settings is already
+ // the new mode. That happens when we start up after a reboot, and we don't want
+ // to overwrite the value we had from when we first started controlling the setting.
+ if (getSoftKeyboardValueFromSettings() != SHOW_MODE_IGNORE_HARD_KEYBOARD) {
+ setOriginalHardKeyboardValue(getSecureIntForUser(
+ Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0, mUserId) != 0);
+ }
+ putSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 1, mUserId);
+ } else if (mSoftKeyboardShowMode == SHOW_MODE_IGNORE_HARD_KEYBOARD) {
+ putSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD,
+ getOriginalHardKeyboardValue() ? 1 : 0, mUserId);
+ }
+
+ saveSoftKeyboardValueToSettings(newMode);
+ mSoftKeyboardShowMode = newMode;
+ mServiceChangingSoftKeyboardMode = requester;
+ for (int i = mBoundServices.size() - 1; i >= 0; i--) {
+ final AccessibilityServiceConnection service = mBoundServices.get(i);
+ service.notifySoftKeyboardShowModeChangedLocked(mSoftKeyboardShowMode);
+ }
+ return true;
+ }
+
+ @SoftKeyboardShowMode
+ int getSoftKeyboardShowModeLocked() {
+ return mSoftKeyboardShowMode;
+ }
+
+ /**
+ * If the settings are inconsistent with the internal state, make the internal state
+ * match the settings.
+ */
+ void reconcileSoftKeyboardModeWithSettingsLocked() {
+ final boolean showWithHardKeyboardSettings =
+ getSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0, mUserId) != 0;
+ if (mSoftKeyboardShowMode == SHOW_MODE_IGNORE_HARD_KEYBOARD) {
+ if (!showWithHardKeyboardSettings) {
+ // The user has overridden the setting. Respect that and prevent further changes
+ // to this behavior.
+ setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null);
+ setUserOverridesHardKeyboardSetting();
+ }
+ }
+
+ // If the setting and the internal state are out of sync, set both to default
+ if (getSoftKeyboardValueFromSettings() != mSoftKeyboardShowMode) {
+ Slog.e(LOG_TAG, "Show IME setting inconsistent with internal state. Overwriting");
+ setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null);
+ putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+ SHOW_MODE_AUTO, mUserId);
+ }
+ }
+
+ boolean getBindInstantServiceAllowedLocked() {
+ return mBindInstantServiceAllowed;
+ }
+
+ /* Need to have a permission check on callee */
+ void setBindInstantServiceAllowedLocked(boolean allowed) {
+ mBindInstantServiceAllowed = allowed;
+ }
+
+ Set<ComponentName> getBindingServicesLocked() {
+ return mBindingServices;
+ }
+
+ /**
+ * Returns enabled service list.
+ */
+ Set<ComponentName> getEnabledServicesLocked() {
+ return mEnabledServices;
+ }
+
+ List<AccessibilityServiceConnection> getBoundServicesLocked() {
+ return mBoundServices;
+ }
+
+ int getClientStateLocked(boolean isUiAutomationRunning) {
+ int clientState = 0;
+ final boolean a11yEnabled = isUiAutomationRunning
+ || isHandlingAccessibilityEventsLocked();
+ if (a11yEnabled) {
+ clientState |= AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED;
+ }
+ // Touch exploration relies on enabled accessibility.
+ if (a11yEnabled && mIsTouchExplorationEnabled) {
+ clientState |= AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED;
+ }
+ if (mIsTextHighContrastEnabled) {
+ clientState |= AccessibilityManager.STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED;
+ }
+ return clientState;
+ }
+
+ private void setUserOverridesHardKeyboardSetting() {
+ final int softKeyboardSetting = getSecureIntForUser(
+ Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, SHOW_MODE_AUTO, mUserId);
+ putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+ softKeyboardSetting | SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN,
+ mUserId);
+ }
+
+ private boolean hasUserOverriddenHardKeyboardSetting() {
+ final int softKeyboardSetting = getSecureIntForUser(
+ Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, SHOW_MODE_AUTO, mUserId);
+ return (softKeyboardSetting & SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN)
+ != 0;
+ }
+
+ private void setOriginalHardKeyboardValue(boolean originalHardKeyboardValue) {
+ final int oldSoftKeyboardSetting = getSecureIntForUser(
+ Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, SHOW_MODE_AUTO, mUserId);
+ final int newSoftKeyboardSetting = oldSoftKeyboardSetting
+ & (~SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE)
+ | ((originalHardKeyboardValue) ? SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE : 0);
+ putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+ newSoftKeyboardSetting, mUserId);
+ }
+
+ private void saveSoftKeyboardValueToSettings(int softKeyboardShowMode) {
+ final int oldSoftKeyboardSetting = getSecureIntForUser(
+ Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, SHOW_MODE_AUTO, mUserId);
+ final int newSoftKeyboardSetting = oldSoftKeyboardSetting & (~SHOW_MODE_MASK)
+ | softKeyboardShowMode;
+ putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+ newSoftKeyboardSetting, mUserId);
+ }
+
+ private int getSoftKeyboardValueFromSettings() {
+ return getSecureIntForUser(
+ Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, SHOW_MODE_AUTO, mUserId)
+ & SHOW_MODE_MASK;
+ }
+
+ private boolean getOriginalHardKeyboardValue() {
+ return (getSecureIntForUser(
+ Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, SHOW_MODE_AUTO, mUserId)
+ & SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE) != 0;
+ }
+
+ private void unbindAllServicesLocked() {
+ final List<AccessibilityServiceConnection> services = mBoundServices;
+ for (int count = services.size(); count > 0; count--) {
+ // When the service is unbound, it disappears from the list, so there's no need to
+ // keep track of the index
+ services.get(0).unbindLocked();
+ }
+ }
+
+ private int getSecureIntForUser(String key, int def, int userId) {
+ return Settings.Secure.getIntForUser(mContext.getContentResolver(), key, def, userId);
+ }
+
+ private void putSecureIntForUser(String key, int value, int userId) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ Settings.Secure.putIntForUser(mContext.getContentResolver(), key, value, userId);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.append("User state[");
+ pw.println();
+ pw.append(" attributes:{id=").append(String.valueOf(mUserId));
+ pw.append(", touchExplorationEnabled=").append(String.valueOf(mIsTouchExplorationEnabled));
+ pw.append(", displayMagnificationEnabled=").append(String.valueOf(
+ mIsDisplayMagnificationEnabled));
+ pw.append(", navBarMagnificationEnabled=").append(String.valueOf(
+ mIsNavBarMagnificationEnabled));
+ pw.append(", autoclickEnabled=").append(String.valueOf(mIsAutoclickEnabled));
+ pw.append(", nonInteractiveUiTimeout=").append(String.valueOf(mNonInteractiveUiTimeout));
+ pw.append(", interactiveUiTimeout=").append(String.valueOf(mInteractiveUiTimeout));
+ pw.append(", installedServiceCount=").append(String.valueOf(mInstalledServices.size()));
+ pw.append("}");
+ pw.println();
+ pw.append(" Bound services:{");
+ final int serviceCount = mBoundServices.size();
+ for (int j = 0; j < serviceCount; j++) {
+ if (j > 0) {
+ pw.append(", ");
+ pw.println();
+ pw.append(" ");
+ }
+ AccessibilityServiceConnection service = mBoundServices.get(j);
+ service.dump(fd, pw, args);
+ }
+ pw.println("}");
+ pw.append(" Enabled services:{");
+ Iterator<ComponentName> it = mEnabledServices.iterator();
+ if (it.hasNext()) {
+ ComponentName componentName = it.next();
+ pw.append(componentName.toShortString());
+ while (it.hasNext()) {
+ componentName = it.next();
+ pw.append(", ");
+ pw.append(componentName.toShortString());
+ }
+ }
+ pw.println("}");
+ pw.append(" Binding services:{");
+ it = mBindingServices.iterator();
+ if (it.hasNext()) {
+ ComponentName componentName = it.next();
+ pw.append(componentName.toShortString());
+ while (it.hasNext()) {
+ componentName = it.next();
+ pw.append(", ");
+ pw.append(componentName.toShortString());
+ }
+ }
+ pw.println("}]");
+ }
+
+ public boolean isAutoclickEnabledLocked() {
+ return mIsAutoclickEnabled;
+ }
+
+ public void setAutoclickEnabledLocked(boolean enabled) {
+ mIsAutoclickEnabled = enabled;
+ }
+
+ public boolean isDisplayMagnificationEnabledLocked() {
+ return mIsDisplayMagnificationEnabled;
+ }
+
+ public void setDisplayMagnificationEnabledLocked(boolean enabled) {
+ mIsDisplayMagnificationEnabled = enabled;
+ }
+
+ public boolean isFilterKeyEventsEnabledLocked() {
+ return mIsFilterKeyEventsEnabled;
+ }
+
+ public void setFilterKeyEventsEnabledLocked(boolean enabled) {
+ mIsFilterKeyEventsEnabled = enabled;
+ }
+
+ public int getInteractiveUiTimeoutLocked() {
+ return mInteractiveUiTimeout;
+ }
+
+ public void setInteractiveUiTimeoutLocked(int timeout) {
+ mInteractiveUiTimeout = timeout;
+ }
+
+ public int getLastSentClientStateLocked() {
+ return mLastSentClientState;
+ }
+
+ public void setLastSentClientStateLocked(int state) {
+ mLastSentClientState = state;
+ }
+
+ public boolean isNavBarMagnificationAssignedToAccessibilityButtonLocked() {
+ return mIsNavBarMagnificationAssignedToAccessibilityButton;
+ }
+
+ public void setNavBarMagnificationAssignedToAccessibilityButtonLocked(boolean assigned) {
+ mIsNavBarMagnificationAssignedToAccessibilityButton = assigned;
+ }
+
+ public boolean isNavBarMagnificationEnabledLocked() {
+ return mIsNavBarMagnificationEnabled;
+ }
+
+ public void setNavBarMagnificationEnabledLocked(boolean enabled) {
+ mIsNavBarMagnificationEnabled = enabled;
+ }
+
+ public int getNonInteractiveUiTimeoutLocked() {
+ return mNonInteractiveUiTimeout;
+ }
+
+ public void setNonInteractiveUiTimeoutLocked(int timeout) {
+ mNonInteractiveUiTimeout = timeout;
+ }
+
+ public boolean isPerformGesturesEnabledLocked() {
+ return mIsPerformGesturesEnabled;
+ }
+
+ public void setPerformGesturesEnabledLocked(boolean enabled) {
+ mIsPerformGesturesEnabled = enabled;
+ }
+
+ public ComponentName getServiceAssignedToAccessibilityButtonLocked() {
+ return mServiceAssignedToAccessibilityButton;
+ }
+
+ public void setServiceAssignedToAccessibilityButtonLocked(ComponentName componentName) {
+ mServiceAssignedToAccessibilityButton = componentName;
+ }
+
+ public ComponentName getServiceChangingSoftKeyboardModeLocked() {
+ return mServiceChangingSoftKeyboardMode;
+ }
+
+ public void setServiceChangingSoftKeyboardModeLocked(
+ ComponentName serviceChangingSoftKeyboardMode) {
+ mServiceChangingSoftKeyboardMode = serviceChangingSoftKeyboardMode;
+ }
+
+ public ComponentName getServiceToEnableWithShortcutLocked() {
+ return mServiceToEnableWithShortcut;
+ }
+
+ public void setServiceToEnableWithShortcutLocked(ComponentName componentName) {
+ mServiceToEnableWithShortcut = componentName;
+ }
+
+ public boolean isTextHighContrastEnabledLocked() {
+ return mIsTextHighContrastEnabled;
+ }
+
+ public void setTextHighContrastEnabledLocked(boolean enabled) {
+ mIsTextHighContrastEnabled = enabled;
+ }
+
+ public boolean isTouchExplorationEnabledLocked() {
+ return mIsTouchExplorationEnabled;
+ }
+
+ public void setTouchExplorationEnabledLocked(boolean enabled) {
+ mIsTouchExplorationEnabled = enabled;
+ }
+
+ public int getUserInteractiveUiTimeoutLocked() {
+ return mUserInteractiveUiTimeout;
+ }
+
+ public void setUserInteractiveUiTimeoutLocked(int timeout) {
+ mUserInteractiveUiTimeout = timeout;
+ }
+
+ public int getUserNonInteractiveUiTimeoutLocked() {
+ return mUserNonInteractiveUiTimeout;
+ }
+
+ public void setUserNonInteractiveUiTimeoutLocked(int timeout) {
+ mUserNonInteractiveUiTimeout = timeout;
+ }
+}
diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
index b35300c..7d129ea 100644
--- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
@@ -286,7 +286,7 @@
window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);
- window.addPrivateFlags(WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS);
+ window.addPrivateFlags(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS);
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
window.setGravity(Gravity.BOTTOM | Gravity.CENTER);
window.setCloseOnTouchOutside(true);
diff --git a/services/backup/backuplib/java/com/android/server/backup/TransportManager.java b/services/backup/backuplib/java/com/android/server/backup/TransportManager.java
index de48f4b..30ce4cf 100644
--- a/services/backup/backuplib/java/com/android/server/backup/TransportManager.java
+++ b/services/backup/backuplib/java/com/android/server/backup/TransportManager.java
@@ -93,8 +93,7 @@
mTransportWhitelist = Preconditions.checkNotNull(whitelist);
mCurrentTransportName = selectedTransport;
mTransportStats = new TransportStats();
- mTransportClientManager = TransportClientManager.createEncryptingClientManager(mUserId,
- context, mTransportStats);
+ mTransportClientManager = new TransportClientManager(mUserId, context, mTransportStats);
}
@VisibleForTesting
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 1d666ad..dc24cff 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -59,6 +59,8 @@
public static final int PACKAGE_CONFIGURATOR = 9;
public static final int PACKAGE_INCIDENT_REPORT_APPROVER = 10;
public static final int PACKAGE_APP_PREDICTOR = 11;
+ public static final int PACKAGE_TELEPHONY = 12;
+ public static final int PACKAGE_WIFI = 13;
@IntDef(value = {
PACKAGE_SYSTEM,
PACKAGE_SETUP_WIZARD,
@@ -72,6 +74,8 @@
PACKAGE_CONFIGURATOR,
PACKAGE_INCIDENT_REPORT_APPROVER,
PACKAGE_APP_PREDICTOR,
+ PACKAGE_TELEPHONY,
+ PACKAGE_WIFI,
})
@Retention(RetentionPolicy.SOURCE)
public @interface KnownPackage {}
@@ -546,10 +550,11 @@
*/
public abstract boolean isResolveActivityComponent(@NonNull ComponentInfo component);
+
/**
- * Returns the package name for a known package.
+ * Returns a list of package names for a known package
*/
- public abstract @Nullable String getKnownPackageName(
+ public abstract @NonNull String[] getKnownPackageNames(
@KnownPackage int knownPackage, int userId);
/**
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index e0f60b4..0bb72cb 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1149,7 +1149,6 @@
private static NetworkCapabilities createDefaultNetworkCapabilitiesForUid(int uid) {
final NetworkCapabilities netCap = new NetworkCapabilities();
netCap.addCapability(NET_CAPABILITY_INTERNET);
- netCap.addCapability(NET_CAPABILITY_NOT_RESTRICTED);
netCap.removeCapability(NET_CAPABILITY_NOT_VPN);
netCap.setSingleUid(uid);
return netCap;
@@ -1159,7 +1158,6 @@
int transportType, NetworkRequest.Type type) {
final NetworkCapabilities netCap = new NetworkCapabilities();
netCap.addCapability(NET_CAPABILITY_INTERNET);
- netCap.addCapability(NET_CAPABILITY_NOT_RESTRICTED);
if (transportType > -1) {
netCap.addTransportType(transportType);
}
diff --git a/services/core/java/com/android/server/GnssManagerService.java b/services/core/java/com/android/server/GnssManagerService.java
index b6e619e..44a8234 100644
--- a/services/core/java/com/android/server/GnssManagerService.java
+++ b/services/core/java/com/android/server/GnssManagerService.java
@@ -17,6 +17,7 @@
package com.android.server;
import android.Manifest;
+import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.content.Context;
@@ -298,7 +299,8 @@
* @param packageName name of requesting package
* @return true if callback is successfully added, false otherwise
*/
- public boolean addGnssBatchingCallback(IBatchedLocationCallback callback, String packageName) {
+ public boolean addGnssBatchingCallback(IBatchedLocationCallback callback, String packageName,
+ @NonNull String listenerIdentity) {
mContext.enforceCallingPermission(
android.Manifest.permission.LOCATION_HARDWARE,
"Location Hardware permission not granted to access hardware batching");
@@ -316,7 +318,8 @@
}
CallerIdentity callerIdentity =
- new CallerIdentity(Binder.getCallingUid(), Binder.getCallingPid(), packageName);
+ new CallerIdentity(Binder.getCallingUid(), Binder.getCallingPid(), packageName,
+ listenerIdentity);
synchronized (mGnssBatchingLock) {
mGnssBatchingCallback = callback;
mGnssBatchingDeathCallback =
@@ -494,7 +497,7 @@
private <TListener extends IInterface> boolean addGnssDataListenerLocked(
TListener listener,
String packageName,
- String listenerName,
+ @NonNull String listenerIdentifier,
RemoteListenerHelper<TListener> gnssDataProvider,
ArrayMap<IBinder,
LinkedListener<TListener>> gnssDataListeners,
@@ -513,10 +516,11 @@
}
CallerIdentity callerIdentity =
- new CallerIdentity(Binder.getCallingUid(), Binder.getCallingPid(), packageName);
+ new CallerIdentity(Binder.getCallingUid(), Binder.getCallingPid(), packageName,
+ listenerIdentifier);
LinkedListener<TListener> linkedListener =
new LocationManagerServiceUtils.LinkedListener<>(
- listener, listenerName, callerIdentity, binderDeathCallback);
+ listener, listenerIdentifier, callerIdentity, binderDeathCallback);
IBinder binder = listener.asBinder();
if (!linkedListener.linkToListenerDeathNotificationLocked(binder)) {
return false;
@@ -606,7 +610,7 @@
return addGnssDataListenerLocked(
listener,
packageName,
- "GnssStatusListener",
+ "Gnss status",
mGnssStatusProvider,
mGnssStatusListeners,
this::unregisterGnssStatusCallback);
@@ -632,12 +636,13 @@
* @return true if listener is successfully added, false otherwise
*/
public boolean addGnssMeasurementsListener(
- IGnssMeasurementsListener listener, String packageName) {
+ IGnssMeasurementsListener listener, String packageName,
+ @NonNull String listenerIdentifier) {
synchronized (mGnssMeasurementsListeners) {
return addGnssDataListenerLocked(
listener,
packageName,
- "GnssMeasurementsListener",
+ listenerIdentifier,
mGnssMeasurementsProvider,
mGnssMeasurementsListeners,
this::removeGnssMeasurementsListener);
@@ -689,12 +694,13 @@
* @return true if listener is successfully added, false otherwise
*/
public boolean addGnssNavigationMessageListener(
- IGnssNavigationMessageListener listener, String packageName) {
+ IGnssNavigationMessageListener listener, String packageName,
+ @NonNull String listenerIdentifier) {
synchronized (mGnssNavigationMessageListeners) {
return addGnssDataListenerLocked(
listener,
packageName,
- "GnssNavigationMessageListener",
+ listenerIdentifier,
mGnssNavigationMessageProvider,
mGnssNavigationMessageListeners,
this::removeGnssNavigationMessageListener);
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 09f62ff..aa22feb 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -1223,8 +1223,10 @@
PowerManager.WakeLock mWakeLock;
private Receiver(ILocationListener listener, PendingIntent intent, int pid, int uid,
- String packageName, WorkSource workSource, boolean hideFromAppOps) {
- super(new CallerIdentity(uid, pid, packageName), "LocationListener");
+ String packageName, WorkSource workSource, boolean hideFromAppOps,
+ @NonNull String listenerIdentifier) {
+ super(new CallerIdentity(uid, pid, packageName, listenerIdentifier),
+ "LocationListener");
mListener = listener;
mPendingIntent = intent;
if (listener != null) {
@@ -1532,9 +1534,12 @@
}
@Override
- public boolean addGnssBatchingCallback(IBatchedLocationCallback callback, String packageName) {
+ public boolean addGnssBatchingCallback(IBatchedLocationCallback callback, String packageName,
+ String listenerIdentifier) {
+ Preconditions.checkNotNull(listenerIdentifier);
+
return mGnssManagerService == null ? false : mGnssManagerService.addGnssBatchingCallback(
- callback, packageName);
+ callback, packageName, listenerIdentifier);
}
@Override
@@ -1696,11 +1701,12 @@
}
}
- private boolean reportLocationAccessNoThrow(
- int pid, int uid, String packageName, int allowedResolutionLevel) {
+ private boolean reportLocationAccessNoThrow(int pid, int uid, String packageName,
+ int allowedResolutionLevel, @Nullable String message) {
int op = resolutionLevelToOp(allowedResolutionLevel);
if (op >= 0) {
- if (mAppOps.noteOpNoThrow(op, uid, packageName) != AppOpsManager.MODE_ALLOWED) {
+ if (mAppOps.noteOpNoThrow(op, uid, packageName, message)
+ != AppOpsManager.MODE_ALLOWED) {
return false;
}
}
@@ -2133,12 +2139,13 @@
@GuardedBy("mLock")
private Receiver getReceiverLocked(ILocationListener listener, int pid, int uid,
- String packageName, WorkSource workSource, boolean hideFromAppOps) {
+ String packageName, WorkSource workSource, boolean hideFromAppOps,
+ @NonNull String listenerIdentifier) {
IBinder binder = listener.asBinder();
Receiver receiver = mReceivers.get(binder);
if (receiver == null) {
receiver = new Receiver(listener, null, pid, uid, packageName, workSource,
- hideFromAppOps);
+ hideFromAppOps, listenerIdentifier);
if (!receiver.linkToListenerDeathNotificationLocked(
receiver.getListener().asBinder())) {
return null;
@@ -2150,11 +2157,11 @@
@GuardedBy("mLock")
private Receiver getReceiverLocked(PendingIntent intent, int pid, int uid, String packageName,
- WorkSource workSource, boolean hideFromAppOps) {
+ WorkSource workSource, boolean hideFromAppOps, @NonNull String listenerIdentifier) {
Receiver receiver = mReceivers.get(intent);
if (receiver == null) {
receiver = new Receiver(null, intent, pid, uid, packageName, workSource,
- hideFromAppOps);
+ hideFromAppOps, listenerIdentifier);
mReceivers.put(intent, receiver);
}
return receiver;
@@ -2216,7 +2223,9 @@
@Override
public void requestLocationUpdates(LocationRequest request, ILocationListener listener,
- PendingIntent intent, String packageName) {
+ PendingIntent intent, String packageName, String listenerIdentifier) {
+ Preconditions.checkNotNull(listenerIdentifier);
+
synchronized (mLock) {
if (request == null) request = DEFAULT_LOCATION_REQUEST;
checkPackageName(packageName);
@@ -2271,10 +2280,10 @@
Receiver receiver;
if (intent != null) {
receiver = getReceiverLocked(intent, pid, uid, packageName, workSource,
- hideFromAppOps);
+ hideFromAppOps, listenerIdentifier);
} else {
receiver = getReceiverLocked(listener, pid, uid, packageName, workSource,
- hideFromAppOps);
+ hideFromAppOps, listenerIdentifier);
}
requestLocationUpdatesLocked(sanitizedRequest, receiver, uid, packageName);
} finally {
@@ -2343,9 +2352,9 @@
synchronized (mLock) {
Receiver receiver;
if (intent != null) {
- receiver = getReceiverLocked(intent, pid, uid, packageName, null, false);
+ receiver = getReceiverLocked(intent, pid, uid, packageName, null, false, "");
} else {
- receiver = getReceiverLocked(listener, pid, uid, packageName, null, false);
+ receiver = getReceiverLocked(listener, pid, uid, packageName, null, false, "");
}
long identity = Binder.clearCallingIdentity();
@@ -2464,8 +2473,8 @@
}
// Don't report location access if there is no last location to deliver.
if (lastLocation != null) {
- if (!reportLocationAccessNoThrow(
- pid, uid, packageName, allowedResolutionLevel)) {
+ if (!reportLocationAccessNoThrow(pid, uid, packageName, allowedResolutionLevel,
+ null)) {
if (D) {
Log.d(TAG, "not returning last loc for no op app: " + packageName);
}
@@ -2519,7 +2528,9 @@
@Override
public void requestGeofence(LocationRequest request, Geofence geofence, PendingIntent intent,
- String packageName) {
+ String packageName, String listenerIdentifier) {
+ Preconditions.checkNotNull(listenerIdentifier);
+
if (request == null) request = DEFAULT_LOCATION_REQUEST;
int allowedResolutionLevel = getCallerAllowedResolutionLevel();
checkResolutionLevelIsSufficientForGeofenceUse(allowedResolutionLevel);
@@ -2564,9 +2575,8 @@
mActivityManager.getPackageImportance(packageName));
}
- mGeofenceManager.addFence(sanitizedRequest, geofence, intent,
- allowedResolutionLevel,
- uid, packageName);
+ mGeofenceManager.addFence(sanitizedRequest, geofence, intent, allowedResolutionLevel,
+ uid, packageName, listenerIdentifier);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -2613,10 +2623,13 @@
}
@Override
- public boolean addGnssMeasurementsListener(
- IGnssMeasurementsListener listener, String packageName) {
+ public boolean addGnssMeasurementsListener(IGnssMeasurementsListener listener,
+ String packageName, String listenerIdentifier) {
+ Preconditions.checkNotNull(listenerIdentifier);
+
return mGnssManagerService == null ? false
- : mGnssManagerService.addGnssMeasurementsListener(listener, packageName);
+ : mGnssManagerService.addGnssMeasurementsListener(listener, packageName,
+ listenerIdentifier);
}
@Override
@@ -2643,10 +2656,13 @@
}
@Override
- public boolean addGnssNavigationMessageListener(
- IGnssNavigationMessageListener listener, String packageName) {
+ public boolean addGnssNavigationMessageListener(IGnssNavigationMessageListener listener,
+ String packageName, String listenerIdentifier) {
+ Preconditions.checkNotNull(listenerIdentifier);
+
return mGnssManagerService == null ? false
- : mGnssManagerService.addGnssNavigationMessageListener(listener, packageName);
+ : mGnssManagerService.addGnssNavigationMessageListener(listener, packageName,
+ listenerIdentifier);
}
@Override
@@ -2719,12 +2735,21 @@
return true;
}
}
-
return false;
}
}
@Override
+ public List<String> getProviderPackages(String providerName) {
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_DEVICE_CONFIG,
+ Manifest.permission.READ_DEVICE_CONFIG + " permission required");
+ synchronized (mLock) {
+ LocationProvider provider = getLocationProviderLocked(providerName);
+ return provider == null ? Collections.emptyList() : provider.getPackagesLocked();
+ }
+ }
+
+ @Override
public void setExtraLocationControllerPackage(String packageName) {
mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE,
Manifest.permission.LOCATION_HARDWARE + " permission required");
@@ -2943,7 +2968,8 @@
receiver.mCallerIdentity.mPid,
receiver.mCallerIdentity.mUid,
receiver.mCallerIdentity.mPackageName,
- receiver.mAllowedResolutionLevel)) {
+ receiver.mAllowedResolutionLevel,
+ "Location sent to " + receiver.mCallerIdentity.mListenerIdentifier)) {
if (D) {
Log.d(TAG, "skipping loc update for no op app: "
+ receiver.mCallerIdentity.mPackageName);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 8c672a1..55cd933 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -900,7 +900,7 @@
// The other observer methods are unused
@Override
- public void onIntentStarted(Intent intent) {
+ public void onIntentStarted(Intent intent, long timestampNs) {
}
@Override
@@ -912,7 +912,11 @@
}
@Override
- public void onActivityLaunchFinished(byte[] finalActivity) {
+ public void onActivityLaunchFinished(byte[] finalActivity, long timestampNs) {
+ }
+
+ @Override
+ public void onReportFullyDrawn(byte[] finalActivity, long timestampNs) {
}
};
@@ -8658,7 +8662,7 @@
lp.format = v.getBackground().getOpacity();
lp.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
- lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
((WindowManager)mContext.getSystemService(
Context.WINDOW_SERVICE)).addView(v, lp);
}
diff --git a/services/core/java/com/android/server/am/AppErrorDialog.java b/services/core/java/com/android/server/am/AppErrorDialog.java
index a80a5b5..852c9b65 100644
--- a/services/core/java/com/android/server/am/AppErrorDialog.java
+++ b/services/core/java/com/android/server/am/AppErrorDialog.java
@@ -92,7 +92,7 @@
WindowManager.LayoutParams attrs = getWindow().getAttributes();
attrs.setTitle("Application Error: " + mProc.info.processName);
attrs.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR
- | WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ | WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
getWindow().setAttributes(attrs);
if (mProc.isPersistent()) {
getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
diff --git a/services/core/java/com/android/server/am/AppNotRespondingDialog.java b/services/core/java/com/android/server/am/AppNotRespondingDialog.java
index cb76e2f..65d7e01 100644
--- a/services/core/java/com/android/server/am/AppNotRespondingDialog.java
+++ b/services/core/java/com/android/server/am/AppNotRespondingDialog.java
@@ -94,7 +94,7 @@
WindowManager.LayoutParams attrs = getWindow().getAttributes();
attrs.setTitle("Application Not Responding: " + mProc.info.processName);
attrs.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR |
- WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
getWindow().setAttributes(attrs);
}
diff --git a/services/core/java/com/android/server/am/UserSwitchingDialog.java b/services/core/java/com/android/server/am/UserSwitchingDialog.java
index 98f5557..bbf5772 100644
--- a/services/core/java/com/android/server/am/UserSwitchingDialog.java
+++ b/services/core/java/com/android/server/am/UserSwitchingDialog.java
@@ -78,7 +78,7 @@
WindowManager.LayoutParams attrs = getWindow().getAttributes();
attrs.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR |
- WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
getWindow().setAttributes(attrs);
}
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 6c4cc2d..2ac6eb0 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -68,6 +68,7 @@
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -1398,6 +1399,12 @@
}
private void updatePermissionRevokedCompat(int uid, int switchCode, int mode) {
+ PackageManagerInternal packageManagerInternal = LocalServices.getService(
+ PackageManagerInternal.class);
+ if (packageManagerInternal.getUidTargetSdkVersion(uid) >= Build.VERSION_CODES.M) {
+ return;
+ }
+
PackageManager packageManager = mContext.getPackageManager();
String[] packageNames = packageManager.getPackagesForUid(uid);
if (ArrayUtils.isEmpty(packageNames)) {
diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java
index 96ba8ef..648e07a 100644
--- a/services/core/java/com/android/server/hdmi/Constants.java
+++ b/services/core/java/com/android/server/hdmi/Constants.java
@@ -394,14 +394,10 @@
static final String PROPERTY_PREFERRED_ADDRESS_PLAYBACK = "persist.sys.hdmi.addr.playback";
static final String PROPERTY_PREFERRED_ADDRESS_TV = "persist.sys.hdmi.addr.tv";
-
// TODO(OEM): Set this to false to keep the playback device in sleep upon hotplug event.
// True by default.
static final String PROPERTY_WAKE_ON_HOTPLUG = "ro.hdmi.wake_on_hotplug";
- // 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 save the ARC port id on system audio device.
* <p>When ARC is initiated, this port will be used to turn on ARC.
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
index 61d4d4b..211d028 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
@@ -1238,8 +1238,8 @@
}
// Wake up if the current device if ready to route.
mService.wakeUp();
- if (getLocalActivePort() == portId) {
- HdmiLogger.debug("Not switching to the same port " + portId);
+ if ((getLocalActivePort() == portId) && (portId != Constants.CEC_SWITCH_ARC)) {
+ HdmiLogger.debug("Not switching to the same port " + portId + " except for arc");
return;
}
// Switch to HOME if the current active port is not HOME yet
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index 413e7a0..0944324 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -24,6 +24,7 @@
import android.os.PowerManager.WakeLock;
import android.os.SystemProperties;
import android.provider.Settings.Global;
+import android.sysprop.HdmiProperties;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
@@ -47,7 +48,7 @@
SystemProperties.getBoolean(Constants.PROPERTY_WAKE_ON_HOTPLUG, true);
private static final boolean SET_MENU_LANGUAGE =
- SystemProperties.getBoolean(Constants.PROPERTY_SET_MENU_LANGUAGE, false);
+ HdmiProperties.set_menu_language().orElse(false);
// Used to keep the device awake while it is the active source. For devices that
// cannot wake up via CEC commands, this address the inconvenience of having to
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index f1af73a..362955d 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -199,8 +199,7 @@
int deviceId, int sourceMask, int sw);
private static native boolean nativeHasKeys(long ptr,
int deviceId, int sourceMask, int[] keyCodes, boolean[] keyExists);
- private static native void nativeRegisterInputChannel(long ptr, InputChannel inputChannel,
- int displayId);
+ private static native void nativeRegisterInputChannel(long ptr, InputChannel inputChannel);
private static native void nativeRegisterInputMonitor(long ptr, InputChannel inputChannel,
int displayId, boolean isGestureMonitor);
private static native void nativeUnregisterInputChannel(long ptr, InputChannel inputChannel);
@@ -525,7 +524,6 @@
throw new IllegalArgumentException("displayId must >= 0.");
}
-
final long ident = Binder.clearCallingIdentity();
try {
InputChannel[] inputChannels = InputChannel.openInputChannelPair(inputChannelName);
@@ -533,7 +531,7 @@
inputChannels[0].setToken(host.asBinder());
nativeRegisterInputMonitor(mPtr, inputChannels[0], displayId,
true /*isGestureMonitor*/);
- return new InputMonitor(inputChannelName, inputChannels[1], host);
+ return new InputMonitor(inputChannels[1], host);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -551,7 +549,7 @@
}
inputChannel.setToken(new Binder());
- nativeRegisterInputChannel(mPtr, inputChannel, Display.INVALID_DISPLAY);
+ nativeRegisterInputChannel(mPtr, inputChannel);
}
/**
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index b7fcd3f..471fa72 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -3570,10 +3570,14 @@
return;
}
if (!setVisible) {
- // Client hides the IME directly.
- if (mCurClient != null && mCurClient.client != null) {
- executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO(
- MSG_APPLY_IME_VISIBILITY, setVisible ? 1 : 0, mCurClient));
+ if (mCurClient != null) {
+ // IMMS only knows of focused window, not the actual IME target.
+ // e.g. it isn't aware of any window that has both
+ // NOT_FOCUSABLE, ALT_FOCUSABLE_IM flags set and can the IME target.
+ // Send it to window manager to hide IME from IME target window.
+ // TODO(b/139861270): send to mCurClient.client once IMMS is aware of
+ // actual IME target.
+ mWindowManagerInternal.hideIme(mCurClient.selfReportedDisplayId);
}
} else {
// Send to window manager to show IME after IME layout finishes.
@@ -4208,7 +4212,7 @@
// with other IME windows based on type vs. grouping based on whichever token happens
// to get selected by the system later on.
attrs.token = mSwitchingDialogToken;
- attrs.privateFlags |= LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ attrs.privateFlags |= LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
attrs.setTitle("Select input method");
w.setAttributes(attrs);
updateSystemUiLocked(mImeWindowVis, mBackDisposition);
diff --git a/services/core/java/com/android/server/integrity/engine/RuleEvaluator.java b/services/core/java/com/android/server/integrity/engine/RuleEvaluator.java
index a4bf2b0..6416505 100644
--- a/services/core/java/com/android/server/integrity/engine/RuleEvaluator.java
+++ b/services/core/java/com/android/server/integrity/engine/RuleEvaluator.java
@@ -37,6 +37,9 @@
/**
* Match the list of rules against an app install metadata.
*
+ * <p>Rules must be in disjunctive normal form (DNF). A rule should contain AND'ed formulas
+ * only. All rules are OR'ed together by default.
+ *
* @param rules The list of rules to evaluate.
* @param appInstallMetadata Metadata of the app to be installed, and to evaluate the rules
* against.
@@ -45,7 +48,7 @@
*/
static Rule evaluateRules(List<Rule> rules, AppInstallMetadata appInstallMetadata) {
for (Rule rule : rules) {
- if (isMatch(rule, appInstallMetadata)) {
+ if (isConjunctionOfFormulas(rule.getFormula()) && isMatch(rule, appInstallMetadata)) {
return rule;
}
}
@@ -99,4 +102,25 @@
return false;
}
+
+ private static boolean isConjunctionOfFormulas(Formula formula) {
+ if (formula == null) {
+ return false;
+ }
+ if (isAtomicFormula(formula)) {
+ return true;
+ }
+ OpenFormula openFormula = (OpenFormula) formula;
+ return openFormula.getConnector() == OpenFormula.Connector.AND
+ && openFormula.getFormulas().stream().allMatch(RuleEvaluator::isAtomicFormula);
+ }
+
+ private static boolean isAtomicFormula(Formula formula) {
+ if (formula instanceof AtomicFormula) {
+ return true;
+ }
+ OpenFormula openFormula = (OpenFormula) formula;
+ return openFormula.getConnector() == OpenFormula.Connector.NOT
+ && openFormula.getFormulas().get(0) instanceof AtomicFormula;
+ }
}
diff --git a/services/core/java/com/android/server/location/CallerIdentity.java b/services/core/java/com/android/server/location/CallerIdentity.java
index da31d0b..61e5d1f 100644
--- a/services/core/java/com/android/server/location/CallerIdentity.java
+++ b/services/core/java/com/android/server/location/CallerIdentity.java
@@ -16,6 +16,8 @@
package com.android.server.location;
+import android.annotation.NonNull;
+
/**
* Represents the calling process's uid, pid, and package name.
*/
@@ -23,10 +25,13 @@
public final int mUid;
public final int mPid;
public final String mPackageName;
+ public final @NonNull String mListenerIdentifier;
- public CallerIdentity(int uid, int pid, String packageName) {
+ public CallerIdentity(int uid, int pid, String packageName,
+ @NonNull String listenerIdentifier) {
mUid = uid;
mPid = pid;
mPackageName = packageName;
+ mListenerIdentifier = listenerIdentifier;
}
}
diff --git a/services/core/java/com/android/server/location/GeofenceManager.java b/services/core/java/com/android/server/location/GeofenceManager.java
index a192206..895c4b3 100644
--- a/services/core/java/com/android/server/location/GeofenceManager.java
+++ b/services/core/java/com/android/server/location/GeofenceManager.java
@@ -16,6 +16,7 @@
package com.android.server.location;
+import android.annotation.NonNull;
import android.app.AppOpsManager;
import android.app.PendingIntent;
import android.content.ContentResolver;
@@ -151,14 +152,16 @@
}
public void addFence(LocationRequest request, Geofence geofence, PendingIntent intent,
- int allowedResolutionLevel, int uid, String packageName) {
+ int allowedResolutionLevel, int uid, String packageName,
+ @NonNull String listenerIdentifier) {
if (D) {
Slog.d(TAG, "addFence: request=" + request + ", geofence=" + geofence
+ ", intent=" + intent + ", uid=" + uid + ", packageName=" + packageName);
}
GeofenceState state = new GeofenceState(geofence,
- request.getExpireAt(), allowedResolutionLevel, uid, packageName, intent);
+ request.getExpireAt(), allowedResolutionLevel, uid, packageName, listenerIdentifier,
+ intent);
synchronized (mLock) {
// first make sure it doesn't already exist
for (int i = mFences.size() - 1; i >= 0; i--) {
@@ -301,7 +304,8 @@
int op = LocationManagerService.resolutionLevelToOp(state.mAllowedResolutionLevel);
if (op >= 0) {
if (mAppOps.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION, state.mUid,
- state.mPackageName) != AppOpsManager.MODE_ALLOWED) {
+ state.mPackageName, state.mListenerIdentifier)
+ != AppOpsManager.MODE_ALLOWED) {
if (D) {
Slog.d(TAG, "skipping geofence processing for no op app: "
+ state.mPackageName);
diff --git a/services/core/java/com/android/server/location/GeofenceState.java b/services/core/java/com/android/server/location/GeofenceState.java
index 3ebe20a..fe0719d 100644
--- a/services/core/java/com/android/server/location/GeofenceState.java
+++ b/services/core/java/com/android/server/location/GeofenceState.java
@@ -17,6 +17,7 @@
package com.android.server.location;
+import android.annotation.NonNull;
import android.app.PendingIntent;
import android.location.Geofence;
import android.location.Location;
@@ -38,13 +39,14 @@
public final int mAllowedResolutionLevel;
public final int mUid;
public final String mPackageName;
+ public final @NonNull String mListenerIdentifier;
public final PendingIntent mIntent;
int mState; // current state
double mDistanceToCenter; // current distance to center of fence
- public GeofenceState(Geofence fence, long expireAt,
- int allowedResolutionLevel, int uid, String packageName, PendingIntent intent) {
+ public GeofenceState(Geofence fence, long expireAt, int allowedResolutionLevel, int uid,
+ String packageName, @NonNull String listenerIdentifier, PendingIntent intent) {
mState = STATE_UNKNOWN;
mDistanceToCenter = Double.MAX_VALUE;
@@ -53,6 +55,7 @@
mAllowedResolutionLevel = allowedResolutionLevel;
mUid = uid;
mPackageName = packageName;
+ mListenerIdentifier = listenerIdentifier;
mIntent = intent;
mLocation = new Location("");
diff --git a/services/core/java/com/android/server/location/RemoteListenerHelper.java b/services/core/java/com/android/server/location/RemoteListenerHelper.java
index aa8a25a..0929d93 100644
--- a/services/core/java/com/android/server/location/RemoteListenerHelper.java
+++ b/services/core/java/com/android/server/location/RemoteListenerHelper.java
@@ -182,7 +182,9 @@
}
return mAppOps.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION, callerIdentity.mUid,
- callerIdentity.mPackageName) == AppOpsManager.MODE_ALLOWED;
+ callerIdentity.mPackageName,
+ "Location sent to " + callerIdentity.mListenerIdentifier)
+ == AppOpsManager.MODE_ALLOWED;
}
protected void logPermissionDisabledEventNotReported(String tag, String packageName,
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index bad484f..63ba138 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -118,6 +118,7 @@
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockPatternUtils.CredentialType;
import com.android.internal.widget.LockSettingsInternal;
+import com.android.internal.widget.LockscreenCredential;
import com.android.internal.widget.VerifyCredentialResponse;
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
@@ -2058,7 +2059,8 @@
@UserIdInt int userHandle) {
synchronized (this) {
mUserPasswordMetrics.put(userHandle,
- PasswordMetrics.computeForCredential(credentialType, password));
+ PasswordMetrics.computeForCredential(
+ LockscreenCredential.createRaw(credentialType, password)));
}
}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
index a5d59e3..0a8e5bd 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
@@ -16,16 +16,12 @@
package com.android.server.locksettings;
-import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
-import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
-
-import static com.android.internal.widget.LockPatternUtils.stringToPattern;
-
import android.app.ActivityManager;
import android.os.ShellCommand;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockPatternUtils.RequestThrottledException;
+import com.android.internal.widget.LockscreenCredential;
import java.io.PrintWriter;
@@ -189,31 +185,49 @@
mLockPatternUtils.isSyntheticPasswordEnabled()));
}
+ private LockscreenCredential getOldCredential() {
+ if (mLockPatternUtils.isLockPasswordEnabled(mCurrentUserId)) {
+ final int quality = mLockPatternUtils.getKeyguardStoredPasswordQuality(mCurrentUserId);
+ if (LockPatternUtils.isQualityAlphabeticPassword(quality)) {
+ return LockscreenCredential.createPassword(mOld);
+ } else {
+ return LockscreenCredential.createPin(mOld);
+ }
+ } else if (mLockPatternUtils.isLockPatternEnabled(mCurrentUserId)) {
+ return LockscreenCredential.createPattern(LockPatternUtils.byteArrayToPattern(
+ mOld.getBytes()));
+ } else {
+ return LockscreenCredential.createNone();
+ }
+ }
+
private void runSetPattern() {
- byte[] oldBytes = mOld != null ? mOld.getBytes() : null;
- mLockPatternUtils.saveLockPattern(stringToPattern(mNew), oldBytes, mCurrentUserId);
+ mLockPatternUtils.setLockCredential(
+ LockscreenCredential.createPattern(LockPatternUtils.byteArrayToPattern(
+ mNew.getBytes())),
+ getOldCredential(),
+ mCurrentUserId);
getOutPrintWriter().println("Pattern set to '" + mNew + "'");
}
private void runSetPassword() {
- byte[] newBytes = mNew != null ? mNew.getBytes() : null;
- byte[] oldBytes = mOld != null ? mOld.getBytes() : null;
- mLockPatternUtils.saveLockPassword(newBytes, oldBytes, PASSWORD_QUALITY_ALPHABETIC,
+ mLockPatternUtils.setLockCredential(LockscreenCredential.createPassword(mNew),
+ getOldCredential(),
mCurrentUserId);
getOutPrintWriter().println("Password set to '" + mNew + "'");
}
private void runSetPin() {
- byte[] newBytes = mNew != null ? mNew.getBytes() : null;
- byte[] oldBytes = mOld != null ? mOld.getBytes() : null;
- mLockPatternUtils.saveLockPassword(newBytes, oldBytes, PASSWORD_QUALITY_NUMERIC,
+ mLockPatternUtils.setLockCredential(LockscreenCredential.createPin(mNew),
+ getOldCredential(),
mCurrentUserId);
getOutPrintWriter().println("Pin set to '" + mNew + "'");
}
private void runClear() {
- byte[] oldBytes = mOld != null ? mOld.getBytes() : null;
- mLockPatternUtils.clearLock(oldBytes, mCurrentUserId);
+ mLockPatternUtils.setLockCredential(LockscreenCredential.createNone(),
+ getOldCredential(),
+ mCurrentUserId);
getOutPrintWriter().println("Lock credential cleared");
}
@@ -238,13 +252,8 @@
}
try {
- final boolean result;
- if (havePassword) {
- byte[] passwordBytes = mOld != null ? mOld.getBytes() : null;
- result = mLockPatternUtils.checkPassword(passwordBytes, mCurrentUserId);
- } else {
- result = mLockPatternUtils.checkPattern(stringToPattern(mOld), mCurrentUserId);
- }
+ final boolean result = mLockPatternUtils.checkCredential(getOldCredential(),
+ mCurrentUserId, null);
if (!result) {
if (!mLockPatternUtils.isManagedProfileWithUnifiedChallenge(mCurrentUserId)) {
mLockPatternUtils.reportFailedPasswordAttempt(mCurrentUserId);
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
index 5594614..f4cad63 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
@@ -95,8 +95,6 @@
@VisibleForTesting
public static class CredentialHash {
- /** Deprecated private static final int VERSION_LEGACY = 0; */
- private static final int VERSION_GATEKEEPER = 1;
private CredentialHash(byte[] hash, @CredentialType int type) {
if (type != LockPatternUtils.CREDENTIAL_TYPE_NONE) {
@@ -126,42 +124,6 @@
byte[] hash;
@CredentialType int type;
-
- public byte[] toBytes() {
- try {
- ByteArrayOutputStream os = new ByteArrayOutputStream();
- DataOutputStream dos = new DataOutputStream(os);
- dos.write(VERSION_GATEKEEPER);
- dos.write(type);
- if (hash != null && hash.length > 0) {
- dos.writeInt(hash.length);
- dos.write(hash);
- } else {
- dos.writeInt(0);
- }
- dos.close();
- return os.toByteArray();
- } catch (IOException e) {
- throw new IllegalStateException("Fail to serialze credential hash", e);
- }
- }
-
- public static CredentialHash fromBytes(byte[] bytes) {
- try {
- DataInputStream is = new DataInputStream(new ByteArrayInputStream(bytes));
- /* int version = */ is.read();
- int type = is.read();
- int hashSize = is.readInt();
- byte[] hash = null;
- if (hashSize > 0) {
- hash = new byte[hashSize];
- is.readFully(hash);
- }
- return new CredentialHash(hash, type);
- } catch (IOException e) {
- throw new IllegalStateException("Fail to deserialze credential hash", e);
- }
- }
}
public LockSettingsStorage(Context context) {
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
index e753a7b..9eac252 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
@@ -93,7 +93,7 @@
public void unselectRoute(String packageName, String routeId) {
if (mConnectionReady) {
- mActiveConnection.unselectRotue(packageName, routeId);
+ mActiveConnection.unselectRoute(packageName, routeId);
updateBinding();
}
}
@@ -105,6 +105,20 @@
}
}
+ public void requestSetVolume(MediaRoute2Info route, int volume) {
+ if (mConnectionReady) {
+ mActiveConnection.requestSetVolume(route.getId(), volume);
+ updateBinding();
+ }
+ }
+
+ public void requestUpdateVolume(MediaRoute2Info route, int delta) {
+ if (mConnectionReady) {
+ mActiveConnection.requestUpdateVolume(route.getId(), delta);
+ updateBinding();
+ }
+ }
+
@Nullable
public MediaRoute2ProviderInfo getProviderInfo() {
return mProviderInfo;
@@ -324,7 +338,7 @@
}
}
- public void unselectRotue(String packageName, String routeId) {
+ public void unselectRoute(String packageName, String routeId) {
try {
mProvider.unselectRoute(packageName, routeId);
} catch (RemoteException ex) {
@@ -340,6 +354,22 @@
}
}
+ public void requestSetVolume(String routeId, int volume) {
+ try {
+ mProvider.requestSetVolume(routeId, volume);
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "Failed to deliver request to request set volume.", ex);
+ }
+ }
+
+ public void requestUpdateVolume(String routeId, int delta) {
+ try {
+ mProvider.requestUpdateVolume(routeId, delta);
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "Failed to deliver request to request update volume.", ex);
+ }
+ }
+
@Override
public void binderDied() {
mHandler.post(() -> onConnectionDied(Connection.this));
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 668f2be..74d59ac 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -198,6 +198,34 @@
}
}
+ public void requestSetVolume2(IMediaRouter2Client client, MediaRoute2Info route, int volume) {
+ Objects.requireNonNull(client, "client must not be null");
+ Objects.requireNonNull(route, "route must not be null");
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ requestSetVolumeLocked(client, route, volume);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ public void requestUpdateVolume2(IMediaRouter2Client client, MediaRoute2Info route, int delta) {
+ Objects.requireNonNull(client, "client must not be null");
+ Objects.requireNonNull(route, "route must not be null");
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ requestUpdateVolumeLocked(client, route, delta);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
public void selectClientRoute2(@NonNull IMediaRouter2Manager manager,
String packageName, @Nullable MediaRoute2Info route) {
final long token = Binder.clearCallingIdentity();
@@ -210,6 +238,37 @@
}
}
+ public void requestSetVolume2Manager(IMediaRouter2Manager manager,
+ MediaRoute2Info route, int volume) {
+ Objects.requireNonNull(manager, "manager must not be null");
+ Objects.requireNonNull(route, "route must not be null");
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ requestSetVolumeLocked(manager, route, volume);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ public void requestUpdateVolume2Manager(IMediaRouter2Manager manager,
+ MediaRoute2Info route, int delta) {
+ Objects.requireNonNull(manager, "manager must not be null");
+ Objects.requireNonNull(route, "route must not be null");
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ requestUpdateVolumeLocked(manager, route, delta);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+
public void registerClient(@NonNull IMediaRouterClient client, @NonNull String packageName) {
Objects.requireNonNull(client, "client must not be null");
@@ -362,6 +421,30 @@
}
}
+ private void requestSetVolumeLocked(IMediaRouter2Client client, MediaRoute2Info route,
+ int volume) {
+ final IBinder binder = client.asBinder();
+ ClientRecord clientRecord = mAllClientRecords.get(binder);
+
+ if (clientRecord != null) {
+ clientRecord.mUserRecord.mHandler.sendMessage(
+ obtainMessage(UserHandler::requestSetVolume,
+ clientRecord.mUserRecord.mHandler, route, volume));
+ }
+ }
+
+ private void requestUpdateVolumeLocked(IMediaRouter2Client client, MediaRoute2Info route,
+ int delta) {
+ final IBinder binder = client.asBinder();
+ ClientRecord clientRecord = mAllClientRecords.get(binder);
+
+ if (clientRecord != null) {
+ clientRecord.mUserRecord.mHandler.sendMessage(
+ obtainMessage(UserHandler::requestUpdateVolume,
+ clientRecord.mUserRecord.mHandler, route, delta));
+ }
+ }
+
private void registerManagerLocked(IMediaRouter2Manager manager,
int uid, int pid, String packageName, int userId, boolean trusted) {
final IBinder binder = manager.asBinder();
@@ -424,6 +507,31 @@
}
}
+ private void requestSetVolumeLocked(IMediaRouter2Manager manager, MediaRoute2Info route,
+ int volume) {
+ final IBinder binder = manager.asBinder();
+ ManagerRecord managerRecord = mAllManagerRecords.get(binder);
+
+ if (managerRecord != null) {
+ managerRecord.mUserRecord.mHandler.sendMessage(
+ obtainMessage(UserHandler::requestSetVolume,
+ managerRecord.mUserRecord.mHandler, route, volume));
+ }
+ }
+
+ private void requestUpdateVolumeLocked(IMediaRouter2Manager manager, MediaRoute2Info route,
+ int delta) {
+ final IBinder binder = manager.asBinder();
+ ManagerRecord managerRecord = mAllManagerRecords.get(binder);
+
+ if (managerRecord != null) {
+ managerRecord.mUserRecord.mHandler.sendMessage(
+ obtainMessage(UserHandler::requestUpdateVolume,
+ managerRecord.mUserRecord.mHandler, route, delta));
+ }
+ }
+
+
private void initializeUserLocked(UserRecord userRecord) {
if (DEBUG) {
Slog.d(TAG, userRecord + ": Initialized");
@@ -679,6 +787,20 @@
}
}
+ private void requestSetVolume(MediaRoute2Info route, int volume) {
+ final MediaRoute2ProviderProxy provider = findProvider(route.getProviderId());
+ if (provider != null) {
+ provider.requestSetVolume(route, volume);
+ }
+ }
+
+ private void requestUpdateVolume(MediaRoute2Info route, int delta) {
+ final MediaRoute2ProviderProxy provider = findProvider(route.getProviderId());
+ if (provider != null) {
+ provider.requestUpdateVolume(route, delta);
+ }
+ }
+
private void scheduleUpdateProviderInfos() {
if (!mProviderInfosUpdateScheduled) {
mProviderInfosUpdateScheduled = true;
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index 796a25d..afd92f6 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -499,6 +499,32 @@
mService2.setControlCategories2(client, categories);
}
+ // Binder call
+ @Override
+ public void requestSetVolume2(IMediaRouter2Client client, MediaRoute2Info route, int volume) {
+ mService2.requestSetVolume2(client, route, volume);
+ }
+
+ // Binder call
+ @Override
+ public void requestUpdateVolume2(IMediaRouter2Client client, MediaRoute2Info route, int delta) {
+ mService2.requestUpdateVolume2(client, route, delta);
+ }
+
+ // Binder call
+ @Override
+ public void requestSetVolume2Manager(IMediaRouter2Manager manager,
+ MediaRoute2Info route, int volume) {
+ mService2.requestSetVolume2Manager(manager, route, volume);
+ }
+
+ // Binder call
+ @Override
+ public void requestUpdateVolume2Manager(IMediaRouter2Manager manager,
+ MediaRoute2Info route, int delta) {
+ mService2.requestUpdateVolume2Manager(manager, route, delta);
+ }
+
void restoreBluetoothA2dp() {
try {
boolean a2dpOn;
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 976a0c6..812ce32 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -217,7 +217,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.notification.SystemNotificationChannels;
-import com.android.internal.os.SomeArgs;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.ConcurrentUtils;
@@ -385,6 +384,7 @@
private static final int MSG_SUBSCRIPTION_OVERRIDE = 16;
private static final int MSG_METERED_RESTRICTED_PACKAGES_CHANGED = 17;
private static final int MSG_SET_NETWORK_TEMPLATE_ENABLED = 18;
+ private static final int MSG_SUBSCRIPTION_PLANS_CHANGED = 19;
private static final int UID_MSG_STATE_CHANGED = 100;
private static final int UID_MSG_GONE = 101;
@@ -1509,6 +1509,11 @@
latch.await(5, TimeUnit.SECONDS);
}
+ @VisibleForTesting
+ Handler getHandlerForTesting() {
+ return mHandler;
+ }
+
/**
* Update mobile policies with data cycle information from {@link CarrierConfigManager}
* if necessary.
@@ -3064,6 +3069,34 @@
mContext.enforceCallingOrSelfPermission(MANAGE_SUBSCRIPTION_PLANS, TAG);
}
+ private void enforceSubscriptionPlanValidity(SubscriptionPlan[] plans) {
+ // nothing to check if no plans
+ if (plans.length == 0) {
+ return;
+ }
+
+ long applicableNetworkTypes = 0;
+ boolean allNetworks = false;
+ for (SubscriptionPlan plan : plans) {
+ if (plan.getNetworkTypes() == null) {
+ allNetworks = true;
+ } else {
+ if ((applicableNetworkTypes & plan.getNetworkTypesBitMask()) != 0) {
+ throw new IllegalArgumentException(
+ "Multiple subscription plans defined for a single network type.");
+ } else {
+ applicableNetworkTypes |= plan.getNetworkTypesBitMask();
+ }
+ }
+ }
+
+ // ensure at least one plan applies for every network type
+ if (!allNetworks) {
+ throw new IllegalArgumentException(
+ "No generic subscription plan that applies to all network types.");
+ }
+ }
+
@Override
public SubscriptionPlan[] getSubscriptionPlans(int subId, String callingPackage) {
enforceSubscriptionPlanAccess(subId, Binder.getCallingUid(), callingPackage);
@@ -3228,6 +3261,7 @@
@Override
public void setSubscriptionPlans(int subId, SubscriptionPlan[] plans, String callingPackage) {
enforceSubscriptionPlanAccess(subId, Binder.getCallingUid(), callingPackage);
+ enforceSubscriptionPlanValidity(plans);
for (SubscriptionPlan plan : plans) {
Preconditions.checkNotNull(plan);
@@ -3256,6 +3290,8 @@
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId);
mContext.sendBroadcast(intent, android.Manifest.permission.MANAGE_SUBSCRIPTION_PLANS);
+ mHandler.sendMessage(
+ mHandler.obtainMessage(MSG_SUBSCRIPTION_PLANS_CHANGED, subId, 0, plans));
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -3282,7 +3318,7 @@
@Override
public void setSubscriptionOverride(int subId, int overrideMask, int overrideValue,
- long networkTypeMask, long timeoutMillis, String callingPackage) {
+ long timeoutMillis, String callingPackage) {
enforceSubscriptionPlanAccess(subId, Binder.getCallingUid(), callingPackage);
// We can only override when carrier told us about plans
@@ -3300,16 +3336,11 @@
final boolean overrideEnabled = Settings.Global.getInt(mContext.getContentResolver(),
NETPOLICY_OVERRIDE_ENABLED, 1) != 0;
if (overrideEnabled || overrideValue == 0) {
- SomeArgs args = SomeArgs.obtain();
- args.arg1 = subId;
- args.arg2 = overrideMask;
- args.arg3 = overrideValue;
- args.arg4 = networkTypeMask;
- mHandler.sendMessage(mHandler.obtainMessage(MSG_SUBSCRIPTION_OVERRIDE, args));
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_SUBSCRIPTION_OVERRIDE,
+ overrideMask, overrideValue, subId));
if (timeoutMillis > 0) {
- args.arg3 = 0;
- mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SUBSCRIPTION_OVERRIDE, args),
- timeoutMillis);
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SUBSCRIPTION_OVERRIDE,
+ overrideMask, 0, subId), timeoutMillis);
}
}
}
@@ -4445,11 +4476,20 @@
}
private void dispatchSubscriptionOverride(INetworkPolicyListener listener, int subId,
- int overrideMask, int overrideValue, long networkTypeMask) {
+ int overrideMask, int overrideValue) {
if (listener != null) {
try {
- listener.onSubscriptionOverride(subId, overrideMask, overrideValue,
- networkTypeMask);
+ listener.onSubscriptionOverride(subId, overrideMask, overrideValue);
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+
+ private void dispatchSubscriptionPlansChanged(INetworkPolicyListener listener, int subId,
+ SubscriptionPlan[] plans) {
+ if (listener != null) {
+ try {
+ listener.onSubscriptionPlansChanged(subId, plans);
} catch (RemoteException ignored) {
}
}
@@ -4550,16 +4590,13 @@
return true;
}
case MSG_SUBSCRIPTION_OVERRIDE: {
- final SomeArgs args = (SomeArgs) msg.obj;
- final int subId = (int) args.arg1;
- final int overrideMask = (int) args.arg2;
- final int overrideValue = (int) args.arg3;
- final long networkTypeMask = (long) args.arg4;
+ final int overrideMask = msg.arg1;
+ final int overrideValue = msg.arg2;
+ final int subId = (int) msg.obj;
final int length = mListeners.beginBroadcast();
for (int i = 0; i < length; i++) {
final INetworkPolicyListener listener = mListeners.getBroadcastItem(i);
- dispatchSubscriptionOverride(listener, subId, overrideMask, overrideValue,
- networkTypeMask);
+ dispatchSubscriptionOverride(listener, subId, overrideMask, overrideValue);
}
mListeners.finishBroadcast();
return true;
@@ -4576,6 +4613,17 @@
setNetworkTemplateEnabledInner(template, enabled);
return true;
}
+ case MSG_SUBSCRIPTION_PLANS_CHANGED: {
+ final SubscriptionPlan[] plans = (SubscriptionPlan[]) msg.obj;
+ final int subId = msg.arg1;
+ final int length = mListeners.beginBroadcast();
+ for (int i = 0; i < length; i++) {
+ final INetworkPolicyListener listener = mListeners.getBroadcastItem(i);
+ dispatchSubscriptionPlansChanged(listener, subId, plans);
+ }
+ mListeners.finishBroadcast();
+ return true;
+ }
default: {
return false;
}
diff --git a/services/core/java/com/android/server/notification/NotificationDelegate.java b/services/core/java/com/android/server/notification/NotificationDelegate.java
index 61be1f5..6f0ad33 100644
--- a/services/core/java/com/android/server/notification/NotificationDelegate.java
+++ b/services/core/java/com/android/server/notification/NotificationDelegate.java
@@ -17,6 +17,7 @@
package com.android.server.notification;
import android.app.Notification;
+import android.net.Uri;
import android.service.notification.NotificationStats;
import com.android.internal.statusbar.NotificationVisibility;
@@ -49,6 +50,12 @@
void onNotificationBubbleChanged(String key, boolean isBubble);
/**
+ * Grant permission to read the specified URI to the package associated with the
+ * NotificationRecord associated with the given key.
+ */
+ void grantInlineReplyUriPermission(String key, Uri uri, int callingUid);
+
+ /**
* Notifies that smart replies and actions have been added to the UI.
*/
void onNotificationSmartSuggestionsAdded(String key, int smartReplyCount, int smartActionCount,
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 4457e9c..ca979f8 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -21,6 +21,7 @@
import static android.app.Notification.FLAG_AUTOGROUP_SUMMARY;
import static android.app.Notification.FLAG_BUBBLE;
import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
+import static android.app.Notification.FLAG_INSISTENT;
import static android.app.Notification.FLAG_NO_CLEAR;
import static android.app.Notification.FLAG_ONGOING_EVENT;
import static android.app.Notification.FLAG_ONLY_ALERT_ONCE;
@@ -55,6 +56,7 @@
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
import static android.os.UserHandle.USER_NULL;
@@ -1154,6 +1156,56 @@
}
}
}
+
+ @Override
+ /**
+ * Grant permission to read the specified URI to the package specified in the
+ * NotificationRecord associated with the given key. The callingUid represents the UID of
+ * SystemUI from which this method is being called.
+ *
+ * For this to work, SystemUI must have permission to read the URI when running under the
+ * user associated with the NotificationRecord, and this grant will fail when trying
+ * to grant URI permissions across users.
+ */
+ public void grantInlineReplyUriPermission(String key, Uri uri, int callingUid) {
+ synchronized (mNotificationLock) {
+ NotificationRecord r = mNotificationsByKey.get(key);
+ if (r != null) {
+ IBinder owner = r.permissionOwner;
+ if (owner == null) {
+ r.permissionOwner = mUgmInternal.newUriPermissionOwner("NOTIF:" + key);
+ owner = r.permissionOwner;
+ }
+ int uid = callingUid;
+ int userId = r.sbn.getUserId();
+ if (userId == UserHandle.USER_ALL) {
+ userId = USER_SYSTEM;
+ }
+ if (UserHandle.getUserId(uid) != userId) {
+ try {
+ final String[] pkgs = mPackageManager.getPackagesForUid(callingUid);
+ if (pkgs == null) {
+ Log.e(TAG, "Cannot grant uri permission to unknown UID: "
+ + callingUid);
+ }
+ final String pkg = pkgs[0]; // Get the SystemUI package
+ // Find the UID for SystemUI for the correct user
+ uid = mPackageManager.getPackageUid(pkg, 0, userId);
+ } catch (RemoteException re) {
+ Log.e(TAG, "Cannot talk to package manager", re);
+ }
+ }
+ grantUriPermission(owner, uri, uid, r.sbn.getPackageName(), userId);
+ } else {
+ Log.w(TAG, "No record found for notification key:" + key);
+
+ // TODO: figure out cancel story. I think it's: sysui needs to tell us
+ // whenever noitifications held by a lifetimextender go away
+ // IBinder owner = mUgmInternal.newUriPermissionOwner("InlineReply:" + key);
+ // pass in userId and package as well as key (key for logging purposes)
+ }
+ }
+ }
};
@VisibleForTesting
@@ -1183,7 +1235,7 @@
}
@GuardedBy("mNotificationLock")
- private void clearSoundLocked() {
+ void clearSoundLocked() {
mSoundNotificationKey = null;
long identity = Binder.clearCallingIdentity();
try {
@@ -1198,7 +1250,7 @@
}
@GuardedBy("mNotificationLock")
- private void clearVibrateLocked() {
+ void clearVibrateLocked() {
mVibrateNotificationKey = null;
long identity = Binder.clearCallingIdentity();
try {
@@ -4495,13 +4547,13 @@
if (record != null && record.getAudioAttributes() != null) {
if ((mListenerHints & HINT_HOST_DISABLE_NOTIFICATION_EFFECTS) != 0) {
if (record.getAudioAttributes().getUsage()
- != AudioAttributes.USAGE_VOICE_COMMUNICATION) {
+ != AudioAttributes.USAGE_NOTIFICATION_RINGTONE) {
return "listenerNoti";
}
}
if ((mListenerHints & HINT_HOST_DISABLE_CALL_EFFECTS) != 0) {
if (record.getAudioAttributes().getUsage()
- == AudioAttributes.USAGE_VOICE_COMMUNICATION) {
+ == AudioAttributes.USAGE_NOTIFICATION_RINGTONE) {
return "listenerCall";
}
}
@@ -6060,7 +6112,6 @@
mIsAutomotive
? record.getImportance() > NotificationManager.IMPORTANCE_DEFAULT
: record.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT;
-
// Remember if this notification already owns the notification channels.
boolean wasBeep = key != null && key.equals(mSoundNotificationKey);
boolean wasBuzz = key != null && key.equals(mVibrateNotificationKey);
@@ -6076,7 +6127,6 @@
}
if (aboveThreshold && isNotificationForCurrentUser(record)) {
-
if (mSystemReady && mAudioManager != null) {
Uri soundUri = record.getSound();
hasValidSound = soundUri != null && !Uri.EMPTY.equals(soundUri);
@@ -6091,7 +6141,6 @@
vibration = mFallbackVibrationPattern;
}
hasValidVibrate = vibration != null;
-
boolean hasAudibleAlert = hasValidSound || hasValidVibrate;
if (hasAudibleAlert && !shouldMuteNotificationLocked(record)) {
if (!sentAccessibilityEvent) {
@@ -6248,11 +6297,29 @@
return true;
}
+ // A looping ringtone, such as an incoming call is playing
+ if (isLoopingRingtoneNotification(mNotificationsByKey.get(mSoundNotificationKey))
+ || isLoopingRingtoneNotification(
+ mNotificationsByKey.get(mVibrateNotificationKey))) {
+ return true;
+ }
+
+ return false;
+ }
+
+ @GuardedBy("mNotificationLock")
+ private boolean isLoopingRingtoneNotification(final NotificationRecord playingRecord) {
+ if (playingRecord != null) {
+ if (playingRecord.getAudioAttributes().getUsage() == USAGE_NOTIFICATION_RINGTONE
+ && (playingRecord.getNotification().flags & FLAG_INSISTENT) != 0) {
+ return true;
+ }
+ }
return false;
}
private boolean playSound(final NotificationRecord record, Uri soundUri) {
- boolean looping = (record.getNotification().flags & Notification.FLAG_INSISTENT) != 0;
+ boolean looping = (record.getNotification().flags & FLAG_INSISTENT) != 0;
// play notifications if there is no user of exclusive audio focus
// and the stream volume is not 0 (non-zero volume implies not silenced by SILENT or
// VIBRATE ringer mode)
@@ -6304,7 +6371,6 @@
try {
Thread.sleep(waitMs);
} catch (InterruptedException e) { }
-
// Notifications might be canceled before it actually vibrates due to waitMs,
// so need to check the notification still valide for vibrate.
synchronized (mNotificationLock) {
@@ -7012,7 +7078,6 @@
private void grantUriPermission(IBinder owner, Uri uri, int sourceUid, String targetPkg,
int targetUserId) {
if (uri == null || !ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) return;
-
final long ident = Binder.clearCallingIdentity();
try {
mUgm.grantUriPermissionFromOwner(owner, sourceUid, targetPkg,
diff --git a/services/core/java/com/android/server/os/TEST_MAPPING b/services/core/java/com/android/server/os/TEST_MAPPING
new file mode 100644
index 0000000..502f1e8
--- /dev/null
+++ b/services/core/java/com/android/server/os/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsUsbTests"
+ }
+ ]
+}
diff --git a/services/core/java/com/android/server/pm/ComponentResolver.java b/services/core/java/com/android/server/pm/ComponentResolver.java
index 976cdfb..b1eb7e7 100644
--- a/services/core/java/com/android/server/pm/ComponentResolver.java
+++ b/services/core/java/com/android/server/pm/ComponentResolver.java
@@ -50,6 +50,7 @@
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ArrayUtils;
import com.android.server.IntentResolver;
import java.io.PrintWriter;
@@ -386,8 +387,11 @@
addProvidersLocked(pkg, chatty);
addServicesLocked(pkg, chatty);
}
- final String setupWizardPackage = sPackageManagerInternal.getKnownPackageName(
- PACKAGE_SETUP_WIZARD, UserHandle.USER_SYSTEM);
+ // expect single setupwizard package
+ final String setupWizardPackage = ArrayUtils.firstOrNull(
+ sPackageManagerInternal.getKnownPackageNames(
+ PACKAGE_SETUP_WIZARD, UserHandle.USER_SYSTEM));
+
for (int i = newIntents.size() - 1; i >= 0; --i) {
final PackageParser.ActivityIntentInfo intentInfo = newIntents.get(i);
final PackageParser.Package disabledPkg = sPackageManagerInternal
@@ -421,8 +425,11 @@
final List<ActivityIntentInfo> protectedFilters = mProtectedFilters;
mProtectedFilters = null;
- final String setupWizardPackage = sPackageManagerInternal.getKnownPackageName(
- PACKAGE_SETUP_WIZARD, UserHandle.USER_SYSTEM);
+ // expect single setupwizard package
+ final String setupWizardPackage = ArrayUtils.firstOrNull(
+ sPackageManagerInternal.getKnownPackageNames(
+ PACKAGE_SETUP_WIZARD, UserHandle.USER_SYSTEM));
+
if (DEBUG_FILTERS && setupWizardPackage == null) {
Slog.i(TAG, "No setup wizard;"
+ " All protected intents capped to priority 0");
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 67d0f62..982ecbb 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -974,124 +974,9 @@
@Override public final boolean hasFeature(String feature) {
return PackageManagerService.this.hasSystemFeature(feature, 0);
}
-
- final List<PackageParser.Package> getStaticOverlayPackages(
- Collection<PackageParser.Package> allPackages, String targetPackageName) {
- if ("android".equals(targetPackageName)) {
- // Static RROs targeting to "android", ie framework-res.apk, are already applied by
- // native AssetManager.
- return null;
- }
-
- List<PackageParser.Package> overlayPackages = null;
- for (PackageParser.Package p : allPackages) {
- if (targetPackageName.equals(p.mOverlayTarget) && p.mOverlayIsStatic) {
- if (overlayPackages == null) {
- overlayPackages = new ArrayList<>();
- }
- overlayPackages.add(p);
- }
- }
- if (overlayPackages != null) {
- Comparator<PackageParser.Package> cmp =
- Comparator.comparingInt(p -> p.mOverlayPriority);
- overlayPackages.sort(cmp);
- }
- return overlayPackages;
- }
-
- final String[] getStaticOverlayPaths(List<PackageParser.Package> overlayPackages,
- String targetPath) {
- if (overlayPackages == null || overlayPackages.isEmpty()) {
- return null;
- }
- List<String> overlayPathList = null;
- for (PackageParser.Package overlayPackage : overlayPackages) {
- if (targetPath == null) {
- if (overlayPathList == null) {
- overlayPathList = new ArrayList<>();
- }
- overlayPathList.add(overlayPackage.baseCodePath);
- continue;
- }
-
- try {
- // Creates idmaps for system to parse correctly the Android manifest of the
- // target package.
- //
- // OverlayManagerService will update each of them with a correct gid from its
- // target package app id.
- mInstaller.idmap(targetPath, overlayPackage.baseCodePath,
- UserHandle.getSharedAppGid(
- UserHandle.getUserGid(UserHandle.USER_SYSTEM)));
- if (overlayPathList == null) {
- overlayPathList = new ArrayList<>();
- }
- overlayPathList.add(overlayPackage.baseCodePath);
- } catch (InstallerException e) {
- Slog.e(TAG, "Failed to generate idmap for " + targetPath + " and " +
- overlayPackage.baseCodePath);
- }
- }
- return overlayPathList == null ? null : overlayPathList.toArray(new String[0]);
- }
-
- String[] getStaticOverlayPaths(String targetPackageName, String targetPath) {
- List<PackageParser.Package> overlayPackages;
- synchronized (mInstallLock) {
- synchronized (mLock) {
- overlayPackages = getStaticOverlayPackages(
- mPackages.values(), targetPackageName);
- }
- // It is safe to keep overlayPackages without holding mPackages because static overlay
- // packages can't be uninstalled or disabled.
- return getStaticOverlayPaths(overlayPackages, targetPath);
- }
- }
-
- @Override public final String[] getOverlayApks(String targetPackageName) {
- return getStaticOverlayPaths(targetPackageName, null);
- }
-
- @Override public final String[] getOverlayPaths(String targetPackageName,
- String targetPath) {
- return getStaticOverlayPaths(targetPackageName, targetPath);
- }
- }
-
- class ParallelPackageParserCallback extends PackageParserCallback {
- List<PackageParser.Package> mOverlayPackages = null;
-
- void findStaticOverlayPackages() {
- synchronized (mLock) {
- for (PackageParser.Package p : mPackages.values()) {
- if (p.mOverlayIsStatic) {
- if (mOverlayPackages == null) {
- mOverlayPackages = new ArrayList<>();
- }
- mOverlayPackages.add(p);
- }
- }
- }
- }
-
- @Override
- synchronized String[] getStaticOverlayPaths(String targetPackageName, String targetPath) {
- // We can trust mOverlayPackages without holding mPackages because package uninstall
- // can't happen while running parallel parsing.
- // And we can call mInstaller inside getStaticOverlayPaths without holding mInstallLock
- // because mInstallLock is held before running parallel parsing.
- // Moreover holding mPackages or mInstallLock on each parsing thread causes dead-lock.
- return mOverlayPackages == null ? null :
- getStaticOverlayPaths(
- getStaticOverlayPackages(mOverlayPackages, targetPackageName),
- targetPath);
- }
}
final PackageParser.Callback mPackageParserCallback = new PackageParserCallback();
- final ParallelPackageParserCallback mParallelPackageParserCallback =
- new ParallelPackageParserCallback();
// Currently known shared libraries.
final ArrayMap<String, LongSparseArray<SharedLibraryInfo>> mSharedLibraries = new ArrayMap<>();
@@ -1603,6 +1488,8 @@
final @Nullable String mConfiguratorPackage;
final @Nullable String mAppPredictionServicePackage;
final @Nullable String mIncidentReportApproverPackage;
+ final @Nullable String[] mTelephonyPackages;
+ final @Nullable String mWifiPackage;
final @NonNull String mServicesSystemSharedLibraryPackageName;
final @NonNull String mSharedSystemSharedLibraryPackageName;
@@ -2806,8 +2693,6 @@
systemScanFlags | partition.scanFlag, 0);
}
- mParallelPackageParserCallback.findStaticOverlayPackages();
-
scanDirTracedLI(frameworkDir, systemParseFlags,
systemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED, 0);
if (!mPackages.containsKey("android")) {
@@ -3059,6 +2944,8 @@
mContext.getString(R.string.config_deviceConfiguratorPackageName);
mAppPredictionServicePackage = getAppPredictionServicePackageName();
mIncidentReportApproverPackage = getIncidentReportApproverPackageName();
+ mTelephonyPackages = getTelephonyPackageNames();
+ mWifiPackage = mContext.getString(R.string.config_wifiPackage);
// Now that we know all of the shared libraries, update all clients to have
// the correct library paths.
@@ -8467,7 +8354,7 @@
}
try (ParallelPackageParser parallelPackageParser = new ParallelPackageParser(
mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir,
- mParallelPackageParserCallback)) {
+ mPackageParserCallback)) {
// Submit files for parsing in parallel
int fileCount = 0;
for (File file : files) {
@@ -10683,6 +10570,50 @@
return changedAbiCodePath;
}
+ /**
+ * Sets the enabled state of components configured through {@link SystemConfig}.
+ * This modifies the {@link PackageSetting} object.
+ **/
+ static void configurePackageComponents(PackageParser.Package pkg) {
+ final ArrayMap<String, Boolean> componentsEnabledStates = SystemConfig.getInstance()
+ .getComponentsEnabledStates(pkg.packageName);
+ if (componentsEnabledStates == null) {
+ return;
+ }
+
+ for (int i = pkg.activities.size() - 1; i >= 0; i--) {
+ final PackageParser.Activity component = pkg.activities.get(i);
+ final Boolean enabled = componentsEnabledStates.get(component.className);
+ if (enabled != null) {
+ component.info.enabled = enabled;
+ }
+ }
+
+ for (int i = pkg.receivers.size() - 1; i >= 0; i--) {
+ final PackageParser.Activity component = pkg.receivers.get(i);
+ final Boolean enabled = componentsEnabledStates.get(component.className);
+ if (enabled != null) {
+ component.info.enabled = enabled;
+ }
+ }
+
+ for (int i = pkg.providers.size() - 1; i >= 0; i--) {
+ final PackageParser.Provider component = pkg.providers.get(i);
+ final Boolean enabled = componentsEnabledStates.get(component.className);
+ if (enabled != null) {
+ component.info.enabled = enabled;
+ }
+ }
+
+ for (int i = pkg.services.size() - 1; i >= 0; i--) {
+ final PackageParser.Service component = pkg.services.get(i);
+ final Boolean enabled = componentsEnabledStates.get(component.className);
+ if (enabled != null) {
+ component.info.enabled = enabled;
+ }
+ }
+ }
+
/**
* Just scans the package without any side effects.
@@ -10850,6 +10781,10 @@
pkg.applicationInfo.initForUser(UserHandle.USER_SYSTEM);
}
+ if (pkg.isSystem()) {
+ configurePackageComponents(pkg);
+ }
+
final String cpuAbiOverride = deriveAbiOverride(pkg.cpuAbiOverride, pkgSetting);
if ((scanFlags & SCAN_NEW_INSTALL) == 0) {
@@ -16065,10 +16000,6 @@
* will be used to scan and reconcile the package.
*/
private static class PrepareResult {
- public final int installReason;
- public final String volumeUuid;
- public final String installerPackageName;
- public final UserHandle user;
public final boolean replace;
public final int scanFlags;
public final int parseFlags;
@@ -16077,24 +16008,16 @@
public final PackageParser.Package packageToScan;
public final boolean clearCodeCache;
public final boolean system;
- /* The original package name if it was changed during an update, otherwise {@code null}. */
- @Nullable
- public final String renamedPackage;
public final PackageFreezer freezer;
public final PackageSetting originalPs;
public final PackageSetting disabledPs;
public final PackageSetting[] childPackageSettings;
- private PrepareResult(int installReason, String volumeUuid,
- String installerPackageName, UserHandle user, boolean replace, int scanFlags,
+ private PrepareResult(boolean replace, int scanFlags,
int parseFlags, PackageParser.Package existingPackage,
PackageParser.Package packageToScan, boolean clearCodeCache, boolean system,
- String renamedPackage, PackageFreezer freezer, PackageSetting originalPs,
+ PackageFreezer freezer, PackageSetting originalPs,
PackageSetting disabledPs, PackageSetting[] childPackageSettings) {
- this.installReason = installReason;
- this.volumeUuid = volumeUuid;
- this.installerPackageName = installerPackageName;
- this.user = user;
this.replace = replace;
this.scanFlags = scanFlags;
this.parseFlags = parseFlags;
@@ -16102,7 +16025,6 @@
this.packageToScan = packageToScan;
this.clearCodeCache = clearCodeCache;
this.system = system;
- this.renamedPackage = renamedPackage;
this.freezer = freezer;
this.originalPs = originalPs;
this.disabledPs = disabledPs;
@@ -16142,8 +16064,6 @@
private PrepareResult preparePackageLI(InstallArgs args, PackageInstalledInfo res)
throws PrepareFailure {
final int installFlags = args.installFlags;
- final String installerPackageName = args.installerPackageName;
- final String volumeUuid = args.volumeUuid;
final File tmpPackageFile = new File(args.getCodePath());
final boolean onExternal = args.volumeUuid != null;
final boolean instantApp = ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0);
@@ -16568,14 +16488,12 @@
final PackageParser.Package existingPackage;
String renamedPackage = null;
boolean sysPkg = false;
- String targetVolumeUuid = volumeUuid;
int targetScanFlags = scanFlags;
int targetParseFlags = parseFlags;
final PackageSetting ps;
final PackageSetting disabledPs;
final PackageSetting[] childPackages;
if (replace) {
- targetVolumeUuid = null;
if (pkg.applicationInfo.isStaticSharedLibrary()) {
// Static libs have a synthetic package name containing the version
// and cannot be updated as an update would get a new package name,
@@ -16827,9 +16745,8 @@
// we're passing the freezer back to be closed in a later phase of install
shouldCloseFreezerBeforeReturn = false;
- return new PrepareResult(args.installReason, targetVolumeUuid, installerPackageName,
- args.user, replace, targetScanFlags, targetParseFlags, existingPackage, pkg,
- replace /* clearCodeCache */, sysPkg, renamedPackage, freezer,
+ return new PrepareResult(replace, targetScanFlags, targetParseFlags,
+ existingPackage, pkg, replace /* clearCodeCache */, sysPkg, freezer,
ps, disabledPs, childPackages);
} finally {
if (shouldCloseFreezerBeforeReturn) {
@@ -19798,6 +19715,16 @@
}
@Override
+ public String[] getTelephonyPackageNames() {
+ String names = mContext.getString(R.string.config_telephonyPackages);
+ String[] telephonyPackageNames = null;
+ if (!TextUtils.isEmpty(names)) {
+ telephonyPackageNames = names.trim().split(",");
+ }
+ return telephonyPackageNames;
+ }
+
+ @Override
public void setApplicationEnabledSetting(String appPackageName,
int newState, int flags, int userId, String callingPackage) {
if (!mUserManager.exists(userId)) return;
@@ -22902,34 +22829,38 @@
}
@Override
- public String getKnownPackageName(int knownPackage, int userId) {
+ public @NonNull String[] getKnownPackageNames(int knownPackage, int userId) {
switch(knownPackage) {
case PackageManagerInternal.PACKAGE_BROWSER:
- return mPermissionManager.getDefaultBrowser(userId);
+ return new String[]{mPermissionManager.getDefaultBrowser(userId)};
case PackageManagerInternal.PACKAGE_INSTALLER:
- return mRequiredInstallerPackage;
+ return new String[]{mRequiredInstallerPackage};
case PackageManagerInternal.PACKAGE_SETUP_WIZARD:
- return mSetupWizardPackage;
+ return new String[]{mSetupWizardPackage};
case PackageManagerInternal.PACKAGE_SYSTEM:
- return "android";
+ return new String[]{"android"};
case PackageManagerInternal.PACKAGE_VERIFIER:
- return mRequiredVerifierPackage;
+ return new String[]{mRequiredVerifierPackage};
case PackageManagerInternal.PACKAGE_SYSTEM_TEXT_CLASSIFIER:
- return mSystemTextClassifierPackage;
+ return new String[]{mSystemTextClassifierPackage};
case PackageManagerInternal.PACKAGE_PERMISSION_CONTROLLER:
- return mRequiredPermissionControllerPackage;
+ return new String[]{mRequiredPermissionControllerPackage};
case PackageManagerInternal.PACKAGE_WELLBEING:
- return mWellbeingPackage;
+ return new String[]{mWellbeingPackage};
case PackageManagerInternal.PACKAGE_DOCUMENTER:
- return mDocumenterPackage;
+ return new String[]{mDocumenterPackage};
case PackageManagerInternal.PACKAGE_CONFIGURATOR:
- return mConfiguratorPackage;
+ return new String[]{mConfiguratorPackage};
case PackageManagerInternal.PACKAGE_INCIDENT_REPORT_APPROVER:
- return mIncidentReportApproverPackage;
+ return new String[]{mIncidentReportApproverPackage};
case PackageManagerInternal.PACKAGE_APP_PREDICTOR:
- return mAppPredictionServicePackage;
+ return new String[]{mAppPredictionServicePackage};
+ case PackageManagerInternal.PACKAGE_TELEPHONY:
+ return mTelephonyPackages;
+ case PackageManagerInternal.PACKAGE_WIFI:
+ return new String[]{mWifiPackage};
}
- return null;
+ return ArrayUtils.emptyArray(String.class);
}
@Override
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 1c1c947..52a7c6e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -468,7 +468,7 @@
* @param pckg
*/
private int displayPackageFilePath(String pckg, int userId) throws RemoteException {
- PackageInfo info = mInterface.getPackageInfo(pckg, 0, userId);
+ PackageInfo info = mInterface.getPackageInfo(pckg, PackageManager.MATCH_APEX, userId);
if (info != null && info.applicationInfo != null) {
final PrintWriter pw = getOutPrintWriter();
pw.print("package:");
diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java
index 6d22faa..037912a 100644
--- a/services/core/java/com/android/server/pm/permission/BasePermission.java
+++ b/services/core/java/com/android/server/pm/permission/BasePermission.java
@@ -276,6 +276,12 @@
public boolean isAppPredictor() {
return (protectionLevel & PermissionInfo.PROTECTION_FLAG_APP_PREDICTOR) != 0;
}
+ public boolean isTelephony() {
+ return (protectionLevel & PermissionInfo.PROTECTION_FLAG_TELEPHONY) != 0;
+ }
+ public boolean isWifi() {
+ return (protectionLevel & PermissionInfo.PROTECTION_FLAG_WIFI) != 0;
+ }
public void transfer(@NonNull String origPackageName, @NonNull String newPackageName) {
if (!origPackageName.equals(sourcePackageName)) {
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 793cdd2..f247037 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -437,17 +437,20 @@
// Installer
grantSystemFixedPermissionsToSystemPackage(
- getKnownPackage(PackageManagerInternal.PACKAGE_INSTALLER, userId),
+ ArrayUtils.firstOrNull(getKnownPackages(
+ PackageManagerInternal.PACKAGE_INSTALLER, userId)),
userId, STORAGE_PERMISSIONS);
// Verifier
- final String verifier = getKnownPackage(PackageManagerInternal.PACKAGE_VERIFIER, userId);
+ final String verifier = ArrayUtils.firstOrNull(getKnownPackages(
+ PackageManagerInternal.PACKAGE_VERIFIER, userId));
grantSystemFixedPermissionsToSystemPackage(verifier, userId, STORAGE_PERMISSIONS);
grantPermissionsToSystemPackage(verifier, userId, PHONE_PERMISSIONS, SMS_PERMISSIONS);
// SetupWizard
grantPermissionsToSystemPackage(
- getKnownPackage(PackageManagerInternal.PACKAGE_SETUP_WIZARD, userId), userId,
+ ArrayUtils.firstOrNull(getKnownPackages(
+ PackageManagerInternal.PACKAGE_SETUP_WIZARD, userId)), userId,
PHONE_PERMISSIONS, CONTACTS_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS,
CAMERA_PERMISSIONS);
@@ -596,7 +599,8 @@
userId, CONTACTS_PERMISSIONS, CALENDAR_PERMISSIONS);
// Browser
- String browserPackage = getKnownPackage(PackageManagerInternal.PACKAGE_BROWSER, userId);
+ String browserPackage = ArrayUtils.firstOrNull(getKnownPackages(
+ PackageManagerInternal.PACKAGE_BROWSER, userId));
if (browserPackage == null) {
browserPackage = getDefaultSystemHandlerActivityPackageForCategory(
Intent.CATEGORY_APP_BROWSER, userId);
@@ -761,8 +765,8 @@
}
}
- private String getKnownPackage(int knownPkgId, int userId) {
- return mServiceInternal.getKnownPackageName(knownPkgId, userId);
+ private @NonNull String[] getKnownPackages(int knownPkgId, int userId) {
+ return mServiceInternal.getKnownPackageNames(knownPkgId, userId);
}
private void grantDefaultPermissionsToDefaultSystemDialerApp(
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 89908f0..3f45b0b 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -3078,8 +3078,9 @@
}
}
}
- final String systemPackageName = mPackageManagerInt.getKnownPackageName(
- PackageManagerInternal.PACKAGE_SYSTEM, UserHandle.USER_SYSTEM);
+ // expect single system package
+ String systemPackageName = ArrayUtils.firstOrNull(mPackageManagerInt.getKnownPackageNames(
+ PackageManagerInternal.PACKAGE_SYSTEM, UserHandle.USER_SYSTEM));
final PackageParser.Package systemPackage =
mPackageManagerInt.getPackage(systemPackageName);
@@ -3195,18 +3196,19 @@
// need a separate flag anymore. Hence we need to check which
// permissions are needed by the permission controller
if (!allowed && bp.isInstaller()
- && (pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
- PackageManagerInternal.PACKAGE_INSTALLER, UserHandle.USER_SYSTEM))
- || pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
+ && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
+ PackageManagerInternal.PACKAGE_INSTALLER, UserHandle.USER_SYSTEM),
+ pkg.packageName) || ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
PackageManagerInternal.PACKAGE_PERMISSION_CONTROLLER,
- UserHandle.USER_SYSTEM)))) {
+ UserHandle.USER_SYSTEM), pkg.packageName)) {
// If this permission is to be granted to the system installer and
// this app is an installer, then it gets the permission.
allowed = true;
}
if (!allowed && bp.isVerifier()
- && pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
- PackageManagerInternal.PACKAGE_VERIFIER, UserHandle.USER_SYSTEM))) {
+ && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
+ PackageManagerInternal.PACKAGE_VERIFIER, UserHandle.USER_SYSTEM),
+ pkg.packageName)) {
// If this permission is to be granted to the system verifier and
// this app is a verifier, then it gets the permission.
allowed = true;
@@ -3222,53 +3224,71 @@
allowed = origPermissions.hasInstallPermission(perm);
}
if (!allowed && bp.isSetup()
- && pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
- PackageManagerInternal.PACKAGE_SETUP_WIZARD, UserHandle.USER_SYSTEM))) {
+ && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
+ PackageManagerInternal.PACKAGE_SETUP_WIZARD, UserHandle.USER_SYSTEM),
+ pkg.packageName)) {
// If this permission is to be granted to the system setup wizard and
// this app is a setup wizard, then it gets the permission.
allowed = true;
}
if (!allowed && bp.isSystemTextClassifier()
- && pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
+ && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
PackageManagerInternal.PACKAGE_SYSTEM_TEXT_CLASSIFIER,
- UserHandle.USER_SYSTEM))) {
+ UserHandle.USER_SYSTEM), pkg.packageName)) {
// Special permissions for the system default text classifier.
allowed = true;
}
if (!allowed && bp.isConfigurator()
- && pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
- PackageManagerInternal.PACKAGE_CONFIGURATOR,
- UserHandle.USER_SYSTEM))) {
+ && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
+ PackageManagerInternal.PACKAGE_CONFIGURATOR,
+ UserHandle.USER_SYSTEM), pkg.packageName)) {
// Special permissions for the device configurator.
allowed = true;
}
if (!allowed && bp.isWellbeing()
- && pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
- PackageManagerInternal.PACKAGE_WELLBEING, UserHandle.USER_SYSTEM))) {
+ && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
+ PackageManagerInternal.PACKAGE_WELLBEING, UserHandle.USER_SYSTEM),
+ pkg.packageName)) {
// Special permission granted only to the OEM specified wellbeing app
allowed = true;
}
if (!allowed && bp.isDocumenter()
- && pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
- PackageManagerInternal.PACKAGE_DOCUMENTER, UserHandle.USER_SYSTEM))) {
+ && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
+ PackageManagerInternal.PACKAGE_DOCUMENTER, UserHandle.USER_SYSTEM),
+ pkg.packageName)) {
// If this permission is to be granted to the documenter and
// this app is the documenter, then it gets the permission.
allowed = true;
}
if (!allowed && bp.isIncidentReportApprover()
- && pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
+ && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
PackageManagerInternal.PACKAGE_INCIDENT_REPORT_APPROVER,
- UserHandle.USER_SYSTEM))) {
+ UserHandle.USER_SYSTEM), pkg.packageName)) {
// If this permission is to be granted to the incident report approver and
// this app is the incident report approver, then it gets the permission.
allowed = true;
}
if (!allowed && bp.isAppPredictor()
- && pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
- PackageManagerInternal.PACKAGE_APP_PREDICTOR, UserHandle.USER_SYSTEM))) {
+ && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
+ PackageManagerInternal.PACKAGE_APP_PREDICTOR, UserHandle.USER_SYSTEM),
+ pkg.packageName)) {
// Special permissions for the system app predictor.
allowed = true;
}
+ if (!allowed && bp.isTelephony()
+ && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
+ PackageManagerInternal.PACKAGE_TELEPHONY, UserHandle.USER_SYSTEM),
+ pkg.packageName)) {
+ // Special permissions for the system telephony apps.
+ allowed = true;
+ }
+ if (!allowed && bp.isWifi()
+ && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
+ PackageManagerInternal.PACKAGE_WIFI, UserHandle.USER_SYSTEM),
+ pkg.packageName)) {
+ // Special permissions for the system wifi.
+ allowed = true;
+ }
}
return allowed;
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index c851cc6..2593c38 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -47,8 +47,8 @@
import static android.view.WindowManager.LayoutParams.LAST_SYSTEM_WINDOW;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR;
+import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
@@ -2189,10 +2189,10 @@
default:
// These are the windows that by default are shown only to the user that created
// them. If this needs to be overridden, set
- // {@link WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS} in
+ // {@link WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS} in
// {@link WindowManager.LayoutParams}. Note that permission
// {@link android.Manifest.permission.INTERNAL_SYSTEM_WINDOW} is required as well.
- if ((attrs.privateFlags & PRIVATE_FLAG_SHOW_FOR_ALL_USERS) == 0) {
+ if ((attrs.privateFlags & SYSTEM_FLAG_SHOW_FOR_ALL_USERS) == 0) {
return true;
}
break;
@@ -2446,7 +2446,7 @@
com.android.internal.R.styleable.Window_windowAnimationStyle, 0);
params.privateFlags |=
WindowManager.LayoutParams.PRIVATE_FLAG_FAKE_HARDWARE_ACCELERATED;
- params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ params.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
if (!compatInfo.supportsScreen()) {
params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 3439d38..65bb2342 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -29,6 +29,7 @@
import android.hardware.biometrics.IBiometricServiceReceiverInternal;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener;
+import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
@@ -1334,6 +1335,18 @@
}
@Override
+ public void grantInlineReplyUriPermission(String key, Uri uri) {
+ enforceStatusBarService();
+ int callingUid = Binder.getCallingUid();
+ long identity = Binder.clearCallingIdentity();
+ try {
+ mNotificationDelegate.grantInlineReplyUriPermission(key, uri, callingUid);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
(new StatusBarShellCommand(this, mContext)).exec(
diff --git a/services/core/java/com/android/server/wm/ActivityDisplay.java b/services/core/java/com/android/server/wm/ActivityDisplay.java
index 9d9a37c..673366f 100644
--- a/services/core/java/com/android/server/wm/ActivityDisplay.java
+++ b/services/core/java/com/android/server/wm/ActivityDisplay.java
@@ -1430,9 +1430,8 @@
continue;
}
- final ArrayList<ActivityRecord> activities = task.mActivities;
- for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = activities.get(activityNdx);
+ for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = task.getChildAt(activityNdx);
if (r.isActivityTypeHome()
&& ((userId == UserHandle.USER_ALL) || (r.mUserId == userId))) {
return r;
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java b/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java
index eff0f75..c6b17e2 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java
@@ -39,8 +39,13 @@
* If an activity is successfully started, the launch sequence's state will transition into
* {@code STARTED} via {@link #onActivityLaunched}. This is a transient state.
*
- * It must then transition to either {@code CANCELLED} with {@link #onActivityLaunchCancelled}
- * or into {@code FINISHED} with {@link #onActivityLaunchFinished}. These are terminal states.
+ * It must then transition to either {@code CANCELLED} with {@link #onActivityLaunchCancelled},
+ * which is a terminal state or into {@code FINISHED} with {@link #onActivityLaunchFinished}.
+ *
+ * The {@code FINISHED} with {@link #onActivityLaunchFinished} then may transition to
+ * {@code FULLY_DRAWN} with {@link #onReportFullyDrawn}, which is a terminal state.
+ * Note this transition may not happen if the reportFullyDrawn event is not receivied,
+ * in which case {@code FINISHED} is terminal.
*
* Note that the {@code ActivityRecordProto} provided as a parameter to some state transitions isn't
* necessarily the same within a single launch sequence: it is only the top-most activity at the
@@ -51,15 +56,15 @@
* until a subsequent transition into {@code INTENT_STARTED} initiates a new launch sequence.
*
* <pre>
- * ┌⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┐ ┌⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┐ ╔══════════════════════════╗
- * ╴╴▶ ⋮ INTENT_STARTED ⋮ ──▶ ⋮ ACTIVITY_LAUNCHED ⋮ ──▶ ║ ACTIVITY_LAUNCH_FINISHED ║
- * └⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┘ └⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┘ ╚══════════════════════════╝
- * : :
- * : :
- * ▼ ▼
- * ╔════════════════╗ ╔═══════════════════════════╗
- * ║ INTENT_FAILED ║ ║ ACTIVITY_LAUNCH_CANCELLED ║
- * ╚════════════════╝ ╚═══════════════════════════╝
+ * ┌⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┐ ┌⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┐ ┌--------------------------┐
+ * ╴╴▶ INTENT_STARTED ──▶ ACTIVITY_LAUNCHED ──▶ ACTIVITY_LAUNCH_FINISHED
+ * └⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┘ └⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┘ └--------------------------┘
+ * : : :
+ * : : :
+ * ▼ ▼ ▼
+ * ╔════════════════╗ ╔═══════════════════════════╗ ╔═══════════════════════════╗
+ * ║ INTENT_FAILED ║ ║ ACTIVITY_LAUNCH_CANCELLED ║ ║ REPORT_FULLY_DRAWN ║
+ * ╚════════════════╝ ╚═══════════════════════════╝ ╚═══════════════════════════╝
* </pre>
*/
public interface ActivityMetricsLaunchObserver {
@@ -111,7 +116,7 @@
* Multiple calls to this method cannot occur without first terminating the current
* launch sequence.
*/
- public void onIntentStarted(@NonNull Intent intent);
+ public void onIntentStarted(@NonNull Intent intent, long timestampNanos);
/**
* Notifies the observer that the current launch sequence has failed to launch an activity.
@@ -177,6 +182,9 @@
* drawn for the first time: the top-most activity at the time is what's reported here.
*
* @param finalActivity the top-most activity whose windows were first to fully draw
+ * @param timestampNanos the timestamp of ActivityLaunchFinished event in nanoseconds.
+ * To compute the TotalTime duration, deduct the timestamp {@link #onIntentStarted}
+ * from {@code timestampNanos}.
*
* Multiple calls to this method cannot occur without first terminating the current
* launch sequence.
@@ -186,5 +194,22 @@
* and only the latest activity that was top-most during first-frame drawn
* is reported here.
*/
- public void onActivityLaunchFinished(@NonNull @ActivityRecordProto byte[] finalActivity);
+ public void onActivityLaunchFinished(@NonNull @ActivityRecordProto byte[] finalActivity,
+ long timestampNanos);
+
+ /**
+ * Notifies the observer that the application self-reported itself as being fully drawn.
+ *
+ * @param activity the activity that triggers the ReportFullyDrawn event.
+ * @param timestampNanos the timestamp of ReportFullyDrawn event in nanoseconds.
+ * To compute the duration, deduct the deduct the timestamp {@link #onIntentStarted}
+ * from {@code timestampNanos}.
+ *
+ * @apiNote The behavior of ReportFullyDrawn mostly depends on the app.
+ * It is used as an accurate estimate of meanfully app startup time.
+ * This event may be missing for many apps.
+ */
+ public void onReportFullyDrawn(@NonNull @ActivityRecordProto byte[] activity,
+ long timestampNanos);
+
}
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index a0a2967..e6c6b12e 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -92,6 +92,7 @@
import com.android.internal.os.BackgroundThread;
import com.android.internal.os.SomeArgs;
import com.android.server.LocalServices;
+import java.util.concurrent.TimeUnit;
/**
* Listens to activity launches, transitions, visibility changes and window drawn callbacks to
@@ -132,8 +133,8 @@
// set to INVALID_START_TIME in reset.
// set to valid value in notifyActivityLaunching
- private long mCurrentTransitionStartTime = INVALID_START_TIME;
- private long mLastTransitionStartTime = INVALID_START_TIME;
+ private long mCurrentTransitionStartTimeNs = INVALID_START_TIME;
+ private long mLastTransitionStartTimeNs = INVALID_START_TIME;
private int mCurrentTransitionDeviceUptime;
private int mCurrentTransitionDelayMs;
@@ -326,12 +327,12 @@
intent));
}
- if (mCurrentTransitionStartTime == INVALID_START_TIME) {
+ if (mCurrentTransitionStartTimeNs == INVALID_START_TIME) {
- mCurrentTransitionStartTime = SystemClock.uptimeMillis();
- mLastTransitionStartTime = mCurrentTransitionStartTime;
+ mCurrentTransitionStartTimeNs = SystemClock.elapsedRealtimeNanos();
+ mLastTransitionStartTimeNs = mCurrentTransitionStartTimeNs;
- launchObserverNotifyIntentStarted(intent);
+ launchObserverNotifyIntentStarted(intent, mCurrentTransitionStartTimeNs);
}
}
@@ -382,14 +383,15 @@
? launchedActivity.getWindowingMode()
: WINDOWING_MODE_UNDEFINED;
final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.get(windowingMode);
- if (mCurrentTransitionStartTime == INVALID_START_TIME) {
+ if (mCurrentTransitionStartTimeNs == INVALID_START_TIME) {
// No transition is active ignore this launch.
return;
}
if (launchedActivity != null && launchedActivity.mDrawn) {
// Launched activity is already visible. We cannot measure windows drawn delay.
- reset(true /* abort */, info, "launched activity already visible");
+ reset(true /* abort */, info, "launched activity already visible",
+ 0L /* timestampNs */);
return;
}
@@ -407,7 +409,8 @@
if ((!isLoggableResultCode(resultCode) || launchedActivity == null || !processSwitch
|| windowingMode == WINDOWING_MODE_UNDEFINED) && !otherWindowModesLaunching) {
// Failed to launch or it was not a process switch, so we don't care about the timing.
- reset(true /* abort */, info, "failed to launch or not a process switch");
+ reset(true /* abort */, info, "failed to launch or not a process switch",
+ 0L /* timestampNs */);
return;
} else if (otherWindowModesLaunching) {
// Don't log this windowing mode but continue with the other windowing modes.
@@ -441,19 +444,20 @@
* Notifies the tracker that all windows of the app have been drawn.
*/
WindowingModeTransitionInfoSnapshot notifyWindowsDrawn(@WindowingMode int windowingMode,
- long timestamp) {
+ long timestampNs) {
if (DEBUG_METRICS) Slog.i(TAG, "notifyWindowsDrawn windowingMode=" + windowingMode);
final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.get(windowingMode);
if (info == null || info.loggedWindowsDrawn) {
return null;
}
- info.windowsDrawnDelayMs = calculateDelay(timestamp);
+ info.windowsDrawnDelayMs = calculateDelay(timestampNs);
info.loggedWindowsDrawn = true;
final WindowingModeTransitionInfoSnapshot infoSnapshot =
new WindowingModeTransitionInfoSnapshot(info);
if (allWindowsDrawn() && mLoggedTransitionStarting) {
- reset(false /* abort */, info, "notifyWindowsDrawn - all windows drawn");
+ reset(false /* abort */, info, "notifyWindowsDrawn - all windows drawn",
+ timestampNs /* timestampNs */);
}
return infoSnapshot;
}
@@ -476,7 +480,7 @@
* @param windowingModeToReason A map from windowing mode to a reason integer, which must be on
* of ActivityTaskManagerInternal.APP_TRANSITION_* reasons.
*/
- void notifyTransitionStarting(SparseIntArray windowingModeToReason, long timestamp) {
+ void notifyTransitionStarting(SparseIntArray windowingModeToReason, long timestampNs) {
if (!isAnyTransitionActive() || mLoggedTransitionStarting) {
// Ignore calls to this made after a reset and prior to notifyActivityLaunching.
@@ -484,7 +488,7 @@
return;
}
if (DEBUG_METRICS) Slog.i(TAG, "notifyTransitionStarting");
- mCurrentTransitionDelayMs = calculateDelay(timestamp);
+ mCurrentTransitionDelayMs = calculateDelay(timestampNs);
mLoggedTransitionStarting = true;
WindowingModeTransitionInfo foundInfo = null;
@@ -501,7 +505,8 @@
if (allWindowsDrawn()) {
// abort metrics collection if we cannot find a matching transition.
final boolean abortMetrics = foundInfo == null;
- reset(abortMetrics, foundInfo, "notifyTransitionStarting - all windows drawn");
+ reset(abortMetrics, foundInfo, "notifyTransitionStarting - all windows drawn",
+ timestampNs /* timestampNs */);
}
}
@@ -527,8 +532,8 @@
}
private boolean hasVisibleNonFinishingActivity(TaskRecord t) {
- for (int i = t.mActivities.size() - 1; i >= 0; --i) {
- final ActivityRecord r = t.mActivities.get(i);
+ for (int i = t.getChildCount() - 1; i >= 0; --i) {
+ final ActivityRecord r = t.getChildAt(i);
if (r.visible && !r.finishing) {
return true;
}
@@ -567,7 +572,8 @@
logAppTransitionCancel(info);
mWindowingModeTransitionInfo.remove(r.getWindowingMode());
if (mWindowingModeTransitionInfo.size() == 0) {
- reset(true /* abort */, info, "notifyVisibilityChanged to invisible");
+ reset(true /* abort */, info, "notifyVisibilityChanged to invisible",
+ 0L /* timestampNs */);
}
}
}
@@ -598,12 +604,16 @@
}
private boolean isAnyTransitionActive() {
- return mCurrentTransitionStartTime != INVALID_START_TIME
+ return mCurrentTransitionStartTimeNs != INVALID_START_TIME
&& mWindowingModeTransitionInfo.size() > 0;
}
- private void reset(boolean abort, WindowingModeTransitionInfo info, String cause) {
- if (DEBUG_METRICS) Slog.i(TAG, "reset abort=" + abort + ",cause=" + cause);
+ private void reset(boolean abort, WindowingModeTransitionInfo info, String cause,
+ long timestampNs) {
+ if (DEBUG_METRICS) {
+ Slog.i(TAG,
+ "reset abort=" + abort + ",cause=" + cause + ",timestamp=" + timestampNs);
+ }
if (!abort && isAnyTransitionActive()) {
logAppTransitionMultiEvents();
}
@@ -615,13 +625,13 @@
if (abort) {
launchObserverNotifyActivityLaunchCancelled(info);
} else {
- launchObserverNotifyActivityLaunchFinished(info);
+ launchObserverNotifyActivityLaunchFinished(info, timestampNs);
}
} else {
launchObserverNotifyIntentFailed();
}
- mCurrentTransitionStartTime = INVALID_START_TIME;
+ mCurrentTransitionStartTimeNs = INVALID_START_TIME;
mCurrentTransitionDelayMs = INVALID_DELAY;
mLoggedTransitionStarting = false;
mWindowingModeTransitionInfo.clear();
@@ -629,12 +639,14 @@
private int calculateCurrentDelay() {
// Shouldn't take more than 25 days to launch an app, so int is fine here.
- return (int) (SystemClock.uptimeMillis() - mCurrentTransitionStartTime);
+ return (int) TimeUnit.NANOSECONDS
+ .toMillis(SystemClock.elapsedRealtimeNanos() - mCurrentTransitionStartTimeNs);
}
- private int calculateDelay(long timestamp) {
+ private int calculateDelay(long timestampNs) {
// Shouldn't take more than 25 days to launch an app, so int is fine here.
- return (int) (timestamp - mCurrentTransitionStartTime);
+ return (int) TimeUnit.NANOSECONDS.toMillis(timestampNs -
+ mCurrentTransitionStartTimeNs);
}
private void logAppTransitionCancel(WindowingModeTransitionInfo info) {
@@ -679,7 +691,7 @@
// Take a snapshot of the transition info before sending it to the handler for logging.
// This will avoid any races with other operations that modify the ActivityRecord.
final WindowingModeTransitionInfoSnapshot infoSnapshot =
- new WindowingModeTransitionInfoSnapshot(info);
+ new WindowingModeTransitionInfoSnapshot(info);
final int currentTransitionDeviceUptime = mCurrentTransitionDeviceUptime;
final int currentTransitionDelayMs = mCurrentTransitionDelayMs;
BackgroundThread.getHandler().post(() -> logAppTransition(
@@ -811,7 +823,9 @@
final LogMaker builder = new LogMaker(APP_TRANSITION_REPORTED_DRAWN);
builder.setPackageName(r.packageName);
builder.addTaggedData(FIELD_CLASS_NAME, r.info.name);
- long startupTimeMs = SystemClock.uptimeMillis() - mLastTransitionStartTime;
+ long currentTimestampNs = SystemClock.elapsedRealtimeNanos();
+ long startupTimeMs =
+ TimeUnit.NANOSECONDS.toMillis(currentTimestampNs - mLastTransitionStartTimeNs);
builder.addTaggedData(APP_TRANSITION_REPORTED_DRAWN_MS, startupTimeMs);
builder.setType(restoredFromBundle
? TYPE_TRANSITION_REPORTED_DRAWN_WITH_BUNDLE
@@ -837,6 +851,10 @@
final WindowingModeTransitionInfoSnapshot infoSnapshot =
new WindowingModeTransitionInfoSnapshot(info, r, (int) startupTimeMs);
BackgroundThread.getHandler().post(() -> logAppFullyDrawn(infoSnapshot));
+
+ // Notify reportFullyDrawn event.
+ launchObserverNotifyReportFullyDrawn(r, currentTimestampNs);
+
return infoSnapshot;
}
@@ -1006,12 +1024,12 @@
}
/** Notify the {@link ActivityMetricsLaunchObserver} that a new launch sequence has begun. */
- private void launchObserverNotifyIntentStarted(Intent intent) {
+ private void launchObserverNotifyIntentStarted(Intent intent, long timestampNs) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
"MetricsLogger:launchObserverNotifyIntentStarted");
// Beginning a launch is timing sensitive and so should be observed as soon as possible.
- mLaunchObserver.onIntentStarted(intent);
+ mLaunchObserver.onIntentStarted(intent, timestampNs);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
@@ -1049,6 +1067,16 @@
}
/**
+ * Notifies the {@link ActivityMetricsLaunchObserver} the reportFullDrawn event.
+ */
+ private void launchObserverNotifyReportFullyDrawn(ActivityRecord r, long timestampNs) {
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ "MetricsLogger:launchObserverNotifyReportFullyDrawn");
+ mLaunchObserver.onReportFullyDrawn(convertActivityRecordToProto(r), timestampNs);
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ }
+
+ /**
* Notify the {@link ActivityMetricsLaunchObserver} that the current launch sequence is
* cancelled.
*/
@@ -1068,12 +1096,14 @@
* Notify the {@link ActivityMetricsLaunchObserver} that the current launch sequence's activity
* has fully finished (successfully).
*/
- private void launchObserverNotifyActivityLaunchFinished(WindowingModeTransitionInfo info) {
+ private void launchObserverNotifyActivityLaunchFinished(WindowingModeTransitionInfo info,
+ long timestampNs) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
"MetricsLogger:launchObserverNotifyActivityLaunchFinished");
- mLaunchObserver.onActivityLaunchFinished(
- convertActivityRecordToProto(info.launchedActivity));
+ mLaunchObserver
+ .onActivityLaunchFinished(convertActivityRecordToProto(info.launchedActivity),
+ timestampNs);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 4f52d9d..76a551f 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -144,10 +144,10 @@
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
+import static com.android.server.wm.ActivityTaskManagerService.getInputDispatchingTimeoutLocked;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_STARTING_WINDOW;
-import static com.android.server.wm.ActivityTaskManagerService.getInputDispatchingTimeoutLocked;
import static com.android.server.wm.TaskPersister.DEBUG;
import static com.android.server.wm.TaskPersister.IMAGE_EXTENSION;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -318,6 +318,7 @@
ArrayList<ResultInfo> results; // pending ActivityResult objs we have received
HashSet<WeakReference<PendingIntentRecord>> pendingResults; // all pending intents for this act
ArrayList<ReferrerIntent> newIntents; // any pending new intents for single-top mode
+ Intent mLastNewIntent; // the last new intent we delivered to client
ActivityOptions pendingOptions; // most recently given options
ActivityOptions returningOptions; // options that are coming back via convertToTranslucent
AppTimeTracker appTimeTracker; // set if we are tracking the time in this app/task/activity
@@ -1253,7 +1254,7 @@
boolean setOccludesParent(boolean occludesParent) {
final boolean changed = super.setOccludesParent(occludesParent);
- if (changed) {
+ if (changed && task != null) {
if (!occludesParent) {
getActivityStack().convertActivityToTranslucent(this);
}
@@ -1577,12 +1578,12 @@
task.mTaskId, shortComponentName, reason);
final ArrayList<ActivityRecord> activities = task.mActivities;
final int index = activities.indexOf(this);
- if (index < (activities.size() - 1)) {
+ if (index < (task.getChildCount() - 1)) {
if ((intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) {
// If the caller asked that this activity (and all above it)
// be cleared when the task is reset, don't lose that information,
// but propagate it up to the next activity.
- final ActivityRecord next = activities.get(index + 1);
+ final ActivityRecord next = task.getChildAt(index + 1);
next.intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
}
}
@@ -2788,12 +2789,12 @@
if (positionInTask == -1) {
throw new IllegalStateException("Activity not found in its task");
}
- if (positionInTask == task.mActivities.size() - 1) {
+ if (positionInTask == task.getChildCount() - 1) {
// It's the topmost activity in the task - should become resumed now
return true;
}
// Check if activity above is finishing now and this one becomes the topmost in task.
- final ActivityRecord activityAbove = task.mActivities.get(positionInTask + 1);
+ final ActivityRecord activityAbove = task.getChildAt(positionInTask + 1);
if (activityAbove.finishing && results == null) {
// We will only allow making active if activity above wasn't launched for result.
// Otherwise it will cause this activity to resume before getting result.
@@ -2844,11 +2845,14 @@
}
idle = false;
results = null;
+ if (newIntents != null && newIntents.size() > 0) {
+ mLastNewIntent = newIntents.get(newIntents.size() - 1);
+ }
newIntents = null;
stopped = false;
if (isActivityTypeHome()) {
- mStackSupervisor.updateHomeProcess(task.mActivities.get(0).app);
+ mStackSupervisor.updateHomeProcess(task.getChildAt(0).app);
}
if (nowVisible) {
@@ -3102,7 +3106,7 @@
void reportFullyDrawnLocked(boolean restoredFromBundle) {
final WindowingModeTransitionInfoSnapshot info = mStackSupervisor
- .getActivityMetricsLogger().logAppTransitionReportedDrawn(this, restoredFromBundle);
+ .getActivityMetricsLogger().logAppTransitionReportedDrawn(this, restoredFromBundle);
if (info != null) {
mStackSupervisor.reportActivityLaunchedLocked(false /* timeout */, this,
info.windowsFullyDrawnDelayMs, info.getLaunchState());
@@ -3110,13 +3114,13 @@
}
/** Called when the windows associated app window container are drawn. */
- void onWindowsDrawn(boolean drawn, long timestamp) {
+ void onWindowsDrawn(boolean drawn, long timestampNs) {
mDrawn = drawn;
if (!drawn) {
return;
}
final WindowingModeTransitionInfoSnapshot info = mStackSupervisor
- .getActivityMetricsLogger().notifyWindowsDrawn(getWindowingMode(), timestamp);
+ .getActivityMetricsLogger().notifyWindowsDrawn(getWindowingMode(), timestampNs);
final int windowsDrawnDelayMs = info != null ? info.windowsDrawnDelayMs : INVALID_DELAY;
final @LaunchState int launchState = info != null ? info.getLaunchState() : -1;
mStackSupervisor.reportActivityLaunchedLocked(false /* timeout */, this,
@@ -3542,7 +3546,7 @@
super.resolveOverrideConfiguration(newParentConfiguration);
// If the activity has override bounds, the relative configuration (e.g. screen size,
// layout) needs to be resolved according to the bounds.
- if (!matchParentBounds()) {
+ if (task != null && !matchParentBounds()) {
task.computeConfigResourceOverrides(getResolvedOverrideConfiguration(),
newParentConfiguration);
}
@@ -4198,12 +4202,19 @@
return true;
}
- // Restrict task snapshot starting window to launcher start, or there is no intent at all
- // (eg. task being brought to front). If the intent is something else, likely the app is
- // going to show some specific page or view, instead of what's left last time.
+ // Restrict task snapshot starting window to launcher start, or is same as the last
+ // delivered intent, or there is no intent at all (eg. task being brought to front). If
+ // the intent is something else, likely the app is going to show some specific page or
+ // view, instead of what's left last time.
for (int i = newIntents.size() - 1; i >= 0; i--) {
final Intent intent = newIntents.get(i);
- if (intent != null && !ActivityRecord.isMainIntent(intent)) {
+ if (intent == null || ActivityRecord.isMainIntent(intent)) {
+ continue;
+ }
+
+ final boolean sameIntent = mLastNewIntent != null ? mLastNewIntent.filterEquals(intent)
+ : this.intent.filterEquals(intent);
+ if (!sameIntent || intent.getExtras() != null) {
return false;
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 5959254..41c1e4e 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -33,7 +33,7 @@
import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
-import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
import static android.view.Display.INVALID_DISPLAY;
@@ -488,7 +488,7 @@
int numActivities() {
int count = 0;
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
- count += mTaskHistory.get(taskNdx).mActivities.size();
+ count += mTaskHistory.get(taskNdx).getChildCount();
}
return count;
}
@@ -1077,9 +1077,8 @@
ActivityRecord topRunningNonOverlayTaskActivity() {
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
final TaskRecord task = mTaskHistory.get(taskNdx);
- final ArrayList<ActivityRecord> activities = task.mActivities;
- for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = activities.get(activityNdx);
+ for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = task.getChildAt(activityNdx);
if (!r.finishing && !r.mTaskOverlay) {
return r;
}
@@ -1091,9 +1090,8 @@
ActivityRecord topRunningNonDelayedActivityLocked(ActivityRecord notTop) {
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
final TaskRecord task = mTaskHistory.get(taskNdx);
- final ArrayList<ActivityRecord> activities = task.mActivities;
- for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- ActivityRecord r = activities.get(activityNdx);
+ for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = task.getChildAt(activityNdx);
if (!r.finishing && !r.delayedResume && r != notTop && r.okToShowLocked()) {
return r;
}
@@ -1117,9 +1115,8 @@
if (task.mTaskId == taskId) {
continue;
}
- ArrayList<ActivityRecord> activities = task.mActivities;
- for (int i = activities.size() - 1; i >= 0; --i) {
- final ActivityRecord r = activities.get(i);
+ for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = task.getChildAt(activityNdx);
// Note: the taskId check depends on real taskId fields being non-zero
if (!r.finishing && (token != r.appToken) && r.okToShowLocked()) {
return r;
@@ -1431,10 +1428,8 @@
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
final TaskRecord task = mTaskHistory.get(taskNdx);
- final ArrayList<ActivityRecord> activities = task.mActivities;
-
- for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- ActivityRecord r = activities.get(activityNdx);
+ for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = task.getChildAt(activityNdx);
if (!r.okToShowLocked()) {
continue;
}
@@ -1500,9 +1495,10 @@
void awakeFromSleepingLocked() {
// Ensure activities are no longer sleeping.
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
- final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
- for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- activities.get(activityNdx).setSleeping(false);
+ final TaskRecord task = mTaskHistory.get(taskNdx);
+ for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = task.getChildAt(activityNdx);
+ r.setSleeping(false);
}
}
if (mPausingActivity != null) {
@@ -1516,9 +1512,9 @@
final int userId = UserHandle.getUserId(aInfo.uid);
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
- final List<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
- for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord ar = activities.get(activityNdx);
+ final TaskRecord task = mTaskHistory.get(taskNdx);
+ for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord ar = task.getChildAt(activityNdx);
if ((userId == ar.mUserId) && packageName.equals(ar.packageName)) {
ar.updateApplicationInfo(aInfo);
@@ -1594,9 +1590,9 @@
// Make sure any paused or stopped but visible activities are now sleeping.
// This ensures that the activity's onStop() is called.
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
- final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
- for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = activities.get(activityNdx);
+ final TaskRecord task = mTaskHistory.get(taskNdx);
+ for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = task.getChildAt(activityNdx);
if (r.isState(STARTED, STOPPING, STOPPED, PAUSED, PAUSING)) {
r.setSleeping(true);
}
@@ -1880,9 +1876,8 @@
}
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
final TaskRecord task = mTaskHistory.get(taskNdx);
- final ArrayList<ActivityRecord> activities = task.mActivities;
- for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = activities.get(activityNdx);
+ for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = task.getChildAt(activityNdx);
if (r.finishing) {
// We don't factor in finishing activities when determining translucency since
@@ -2113,9 +2108,8 @@
&& top != null && !top.mLaunchTaskBehind;
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
final TaskRecord task = mTaskHistory.get(taskNdx);
- final ArrayList<ActivityRecord> activities = task.mActivities;
- for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = activities.get(activityNdx);
+ for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = task.getChildAt(activityNdx);
final boolean isTop = r == top;
if (aboveTop && !isTop) {
continue;
@@ -2363,9 +2357,8 @@
void clearOtherAppTimeTrackers(AppTimeTracker except) {
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
final TaskRecord task = mTaskHistory.get(taskNdx);
- final ArrayList<ActivityRecord> activities = task.mActivities;
- for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = activities.get(activityNdx);
+ for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = task.getChildAt(activityNdx);
if ( r.appTimeTracker != except) {
r.appTimeTracker = null;
}
@@ -2423,9 +2416,9 @@
}
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
- final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
- for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = activities.get(activityNdx);
+ final TaskRecord task = mTaskHistory.get(taskNdx);
+ for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = task.getChildAt(activityNdx);
if (aboveTop) {
if (r == topActivity) {
aboveTop = false;
@@ -3210,14 +3203,13 @@
// We only do this for activities that are not the root of the task (since if we finish
// the root, we may no longer have the task!).
- final ArrayList<ActivityRecord> activities = task.mActivities;
- final int numActivities = activities.size();
+ final int numActivities = task.getChildCount();
int lastActivityNdx = task.findRootIndex(true /* effectiveRoot */);
if (lastActivityNdx == -1) {
lastActivityNdx = 0;
}
for (int i = numActivities - 1; i > lastActivityNdx; --i) {
- ActivityRecord target = activities.get(i);
+ ActivityRecord target = task.getChildAt(i);
// TODO: Why is this needed? Looks like we're breaking the loop before we reach the root
if (target.isRootOfTask()) break;
@@ -3257,7 +3249,7 @@
final TaskRecord targetTask;
final ActivityRecord bottom =
!mTaskHistory.isEmpty() && !mTaskHistory.get(0).mActivities.isEmpty() ?
- mTaskHistory.get(0).mActivities.get(0) : null;
+ mTaskHistory.get(0).getChildAt(0) : null;
if (bottom != null && target.taskAffinity.equals(bottom.getTaskRecord().affinity)) {
// If the activity currently at the bottom has the
// same task affinity as the one we are moving,
@@ -3279,7 +3271,7 @@
boolean noOptions = canMoveOptions;
final int start = replyChainEnd < 0 ? i : replyChainEnd;
for (int srcPos = start; srcPos >= i; --srcPos) {
- final ActivityRecord p = activities.get(srcPos);
+ final ActivityRecord p = task.getChildAt(srcPos);
if (p.finishing) {
continue;
}
@@ -3312,7 +3304,7 @@
// In this case, we want to finish this activity
// and everything above it, so be sneaky and pretend
// like these are all in the reply chain.
- end = activities.size() - 1;
+ end = task.getChildCount() - 1;
} else if (replyChainEnd < 0) {
end = i;
} else {
@@ -3320,7 +3312,7 @@
}
boolean noOptions = canMoveOptions;
for (int srcPos = i; srcPos <= end; srcPos++) {
- ActivityRecord p = activities.get(srcPos);
+ ActivityRecord p = task.getChildAt(srcPos);
if (p.finishing) {
continue;
}
@@ -3389,8 +3381,7 @@
int replyChainEnd = -1;
final String taskAffinity = task.affinity;
- final ArrayList<ActivityRecord> activities = affinityTask.mActivities;
- final int numActivities = activities.size();
+ final int numActivities = task.getChildCount();
// Do not operate on or below the effective root Activity.
int lastActivityNdx = affinityTask.findRootIndex(true /* effectiveRoot */);
@@ -3398,7 +3389,7 @@
lastActivityNdx = 0;
}
for (int i = numActivities - 1; i > lastActivityNdx; --i) {
- ActivityRecord target = activities.get(i);
+ ActivityRecord target = task.getChildAt(i);
// TODO: Why is this needed? Looks like we're breaking the loop before we reach the root
if (target.isRootOfTask()) break;
@@ -3435,7 +3426,7 @@
if (DEBUG_TASKS) Slog.v(TAG_TASKS,
"Finishing task at index " + start + " to " + i);
for (int srcPos = start; srcPos >= i; --srcPos) {
- final ActivityRecord p = activities.get(srcPos);
+ final ActivityRecord p = task.getChildAt(srcPos);
if (p.finishing) {
continue;
}
@@ -3443,7 +3434,7 @@
}
} else {
if (taskInsertionPoint < 0) {
- taskInsertionPoint = task.mActivities.size();
+ taskInsertionPoint = task.getChildCount();
}
@@ -3452,7 +3443,7 @@
"Reparenting from task=" + affinityTask + ":" + start + "-" + i
+ " to task=" + task + ":" + taskInsertionPoint);
for (int srcPos = start; srcPos >= i; --srcPos) {
- final ActivityRecord p = activities.get(srcPos);
+ final ActivityRecord p = task.getChildAt(srcPos);
p.reparent(task, taskInsertionPoint, "resetAffinityTaskIfNeededLocked");
if (DEBUG_ADD_REMOVE) Slog.i(TAG_ADD_REMOVE,
@@ -3589,9 +3580,9 @@
/** Finish all activities that were started for result from the specified activity. */
final void finishSubActivityLocked(ActivityRecord self, String resultWho, int requestCode) {
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
- ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
- for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- ActivityRecord r = activities.get(activityNdx);
+ final TaskRecord task = mTaskHistory.get(taskNdx);
+ for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = task.getChildAt(activityNdx);
if (r.resultTo == self && r.requestCode == requestCode) {
if ((r.resultWho == null && resultWho == null) ||
(r.resultWho != null && r.resultWho.equals(resultWho))) {
@@ -3637,11 +3628,11 @@
if (taskNdx < 0) {
break;
}
- activityNdx = mTaskHistory.get(taskNdx).mActivities.size() - 1;
+ activityNdx = mTaskHistory.get(taskNdx).getChildCount() - 1;
} while (activityNdx < 0);
}
if (activityNdx >= 0) {
- r = mTaskHistory.get(taskNdx).mActivities.get(activityNdx);
+ r = mTaskHistory.get(taskNdx).getChildAt(activityNdx);
if (r.isState(STARTED, RESUMED, PAUSING, PAUSED)) {
if (!r.isActivityTypeHome() || mService.mHomeProcess != r.app) {
Slog.w(TAG, " Force finishing activity "
@@ -3659,8 +3650,8 @@
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
TaskRecord tr = mTaskHistory.get(taskNdx);
if (tr.voiceSession != null && tr.voiceSession.asBinder() == sessionBinder) {
- for (int activityNdx = tr.mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
- ActivityRecord r = tr.mActivities.get(activityNdx);
+ for (int activityNdx = tr.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+ ActivityRecord r = tr.getChildAt(activityNdx);
if (!r.finishing) {
r.finishIfPossible("finish-voice", false /* oomAdj */);
didOne = true;
@@ -3668,8 +3659,8 @@
}
} else {
// Check if any of the activities are using voice
- for (int activityNdx = tr.mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
- ActivityRecord r = tr.mActivities.get(activityNdx);
+ for (int activityNdx = tr.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+ ActivityRecord r = tr.getChildAt(activityNdx);
if (r.voiceSession != null && r.voiceSession.asBinder() == sessionBinder) {
// Inform of cancellation
r.clearVoiceSessionLocked();
@@ -3695,9 +3686,9 @@
void finishAllActivitiesImmediately() {
boolean noActivitiesInStack = true;
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
- final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
- for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = activities.get(activityNdx);
+ final TaskRecord task = mTaskHistory.get(taskNdx);
+ for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = task.getChildAt(activityNdx);
noActivitiesInStack = false;
Slog.d(TAG, "finishAllActivitiesImmediatelyLocked: finishing " + r);
r.destroyIfPossible("finishAllActivitiesImmediatelyLocked");
@@ -3765,12 +3756,12 @@
return false;
}
int finishTo = start - 1;
- ActivityRecord parent = finishTo < 0 ? null : activities.get(finishTo);
+ ActivityRecord parent = finishTo < 0 ? null : task.getChildAt(finishTo);
boolean foundParentInTask = false;
final ComponentName dest = destIntent.getComponent();
if (start > 0 && dest != null) {
for (int i = finishTo; i >= 0; i--) {
- ActivityRecord r = activities.get(i);
+ ActivityRecord r = task.getChildAt(i);
if (r.info.packageName.equals(dest.getPackageName()) &&
r.info.name.equals(dest.getClassName())) {
finishTo = i;
@@ -3931,9 +3922,9 @@
boolean lastIsOpaque = false;
boolean activityRemoved = false;
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
- final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
- for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = activities.get(activityNdx);
+ final TaskRecord task = mTaskHistory.get(taskNdx);
+ for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = task.getChildAt(activityNdx);
if (r.finishing) {
continue;
}
@@ -3978,15 +3969,14 @@
}
if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Looking for activities to release in " + task);
int curNum = 0;
- final ArrayList<ActivityRecord> activities = task.mActivities;
- for (int actNdx = 0; actNdx < activities.size(); actNdx++) {
- final ActivityRecord activity = activities.get(actNdx);
+ for (int actNdx = 0; actNdx < task.getChildCount(); actNdx++) {
+ final ActivityRecord activity = task.getChildAt(actNdx);
if (activity.app == app && activity.isDestroyable()) {
if (DEBUG_RELEASE) Slog.v(TAG_RELEASE, "Destroying " + activity
+ " in state " + activity.getState() + " resumed=" + mResumedActivity
+ " pausing=" + mPausingActivity + " for reason " + reason);
activity.destroyImmediately(true /* removeFromApp */, reason);
- if (activities.get(actNdx) != activity) {
+ if (task.getChildAt(actNdx) != activity) {
// Was removed from list, back up so we don't miss the next one.
actNdx--;
}
@@ -4170,8 +4160,8 @@
if (timeTracker != null) {
// The caller wants a time tracker associated with this task.
- for (int i = tr.mActivities.size() - 1; i >= 0; i--) {
- tr.mActivities.get(i).appTimeTracker = timeTracker;
+ for (int i = tr.getChildCount() - 1; i >= 0; i--) {
+ tr.getChildAt(i).appTimeTracker = timeTracker;
}
}
@@ -4352,7 +4342,7 @@
return;
}
- Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "stack.resize_" + mStackId);
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "stack.resize_" + mStackId);
mService.deferWindowLayout();
try {
// Update override configurations of all tasks in the stack.
@@ -4378,7 +4368,7 @@
}
} finally {
mService.continueWindowLayout();
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
}
@@ -4425,9 +4415,9 @@
boolean willActivityBeVisibleLocked(IBinder token) {
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
- final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
- for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = activities.get(activityNdx);
+ final TaskRecord task = mTaskHistory.get(taskNdx);
+ for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = task.getChildAt(activityNdx);
if (r.appToken == token) {
return true;
}
@@ -4447,9 +4437,9 @@
void closeSystemDialogsLocked() {
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
- final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
- for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = activities.get(activityNdx);
+ final TaskRecord task = mTaskHistory.get(taskNdx);
+ for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = task.getChildAt(activityNdx);
if ((r.info.flags&ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS) != 0) {
r.finishIfPossible("close-sys", true /* oomAdj */);
}
@@ -4556,10 +4546,10 @@
final int top = mTaskHistory.size() - 1;
if (DEBUG_SWITCH) Slog.d(TAG_SWITCH, "Performing unhandledBack(): top activity at " + top);
if (top >= 0) {
- final ArrayList<ActivityRecord> activities = mTaskHistory.get(top).mActivities;
- int activityTop = activities.size() - 1;
+ final TaskRecord task = mTaskHistory.get(top);
+ int activityTop = task.getChildCount() - 1;
if (activityTop >= 0) {
- activities.get(activityTop).finishIfPossible("unhandled-back", true /* oomAdj */);
+ task.getChildAt(activityTop).finishIfPossible("unhandled-back", true /* oomAdj */);
}
}
}
@@ -4585,9 +4575,9 @@
void handleAppCrash(WindowProcessController app) {
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
- final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
- for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = activities.get(activityNdx);
+ final TaskRecord task = mTaskHistory.get(taskNdx);
+ for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = task.getChildAt(activityNdx);
if (r.app == app) {
Slog.w(TAG, " Force finishing activity "
+ r.intent.getComponent().flattenToShortString());
@@ -4705,9 +4695,9 @@
// All activities that came from the package must be
// restarted as if there was a config change.
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
- final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
- for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord a = activities.get(activityNdx);
+ final TaskRecord task = mTaskHistory.get(taskNdx);
+ for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord a = task.getChildAt(activityNdx);
if (a.info.packageName.equals(packageName)) {
a.forceNewConfig = true;
if (starting != null && a == starting && a.visible) {
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index f1284d81..7753f57 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -44,7 +44,7 @@
import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
import static android.os.Process.INVALID_UID;
import static android.os.Process.SYSTEM_UID;
-import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.Display.TYPE_VIRTUAL;
@@ -690,7 +690,7 @@
ResolveInfo resolveIntent(Intent intent, String resolvedType, int userId, int flags,
int filterCallingUid) {
try {
- Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "resolveIntent");
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "resolveIntent");
int modifiedFlags = flags
| PackageManager.MATCH_DEFAULT_ONLY | ActivityManagerService.STOCK_PM_FLAGS;
if (intent.isWebIntent()
@@ -711,7 +711,7 @@
Binder.restoreCallingIdentity(token);
}
} finally {
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
}
@@ -824,7 +824,7 @@
System.identityHashCode(r), task.mTaskId, r.shortComponentName);
if (r.isActivityTypeHome()) {
// Home process is the root process of the task.
- updateHomeProcess(task.mActivities.get(0).app);
+ updateHomeProcess(task.getChildAt(0).app);
}
mService.getPackageManagerInternalLocked().notifyPackageUse(
r.intent.getComponent().getPackageName(), NOTIFY_PACKAGE_USE_ACTIVITY);
@@ -1629,7 +1629,7 @@
mPendingTempOtherTaskInsetBounds = copyOrNull(tempOtherTaskInsetBounds);
}
- Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeDockedStack");
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "resizeDockedStack");
mService.deferWindowLayout();
try {
// Don't allow re-entry while resizing. E.g. due to docked stack detaching.
@@ -1695,7 +1695,7 @@
} finally {
mAllowDockedStackResize = true;
mService.continueWindowLayout();
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
}
@@ -1717,7 +1717,7 @@
return;
}
- Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizePinnedStack");
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "resizePinnedStack");
mService.deferWindowLayout();
try {
Rect insetBounds = null;
@@ -1739,7 +1739,7 @@
!DEFER_RESUME);
} finally {
mService.continueWindowLayout();
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
}
@@ -1899,9 +1899,9 @@
task.createTask(onTop, true /* showForAllUsers */);
if (DEBUG_RECENTS) Slog.v(TAG_RECENTS,
"Added restored task=" + task + " to stack=" + stack);
- final ArrayList<ActivityRecord> activities = task.mActivities;
- for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
- activities.get(activityNdx).setTask(task);
+ for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = task.getChildAt(activityNdx);
+ r.setTask(task);
}
return true;
}
@@ -2501,8 +2501,8 @@
return;
}
- for (int i = task.mActivities.size() - 1; i >= 0; i--) {
- final ActivityRecord r = task.mActivities.get(i);
+ for (int i = task.getChildCount() - 1; i >= 0; i--) {
+ final ActivityRecord r = task.getChildAt(i);
if (r.attachedToProcess()) {
mMultiWindowModeChangedActivities.add(r);
}
@@ -2524,8 +2524,8 @@
}
void scheduleUpdatePictureInPictureModeIfNeeded(TaskRecord task, Rect targetStackBounds) {
- for (int i = task.mActivities.size() - 1; i >= 0; i--) {
- final ActivityRecord r = task.mActivities.get(i);
+ for (int i = task.getChildCount() - 1; i >= 0; i--) {
+ final ActivityRecord r = task.getChildAt(i);
if (r.attachedToProcess()) {
mPipModeChangedActivities.add(r);
// If we are scheduling pip change, then remove this activity from multi-window
@@ -2543,8 +2543,8 @@
void updatePictureInPictureMode(TaskRecord task, Rect targetStackBounds, boolean forceUpdate) {
mHandler.removeMessages(REPORT_PIP_MODE_CHANGED_MSG);
- for (int i = task.mActivities.size() - 1; i >= 0; i--) {
- final ActivityRecord r = task.mActivities.get(i);
+ for (int i = task.getChildCount() - 1; i >= 0; i--) {
+ final ActivityRecord r = task.getChildAt(i);
if (r.attachedToProcess()) {
r.updatePictureInPictureMode(targetStackBounds, forceUpdate);
}
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 9397893..1a80006 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -770,13 +770,13 @@
boolean restrictedBgActivity = false;
if (!abort) {
try {
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER,
"shouldAbortBackgroundActivityStart");
restrictedBgActivity = shouldAbortBackgroundActivityStart(callingUid,
callingPid, callingPackage, realCallingUid, realCallingPid, callerApp,
originatingPendingIntent, allowBackgroundActivityStart, intent);
} finally {
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
}
}
@@ -1401,41 +1401,12 @@
final ActivityStack startedActivityStack;
try {
mService.deferWindowLayout();
- result = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor,
+ Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "startActivityInner");
+ result = startActivityInner(r, sourceRecord, voiceSession, voiceInteractor,
startFlags, doResume, options, inTask, outActivity, restrictedBgActivity);
} finally {
- final ActivityStack currentStack = r.getActivityStack();
- startedActivityStack = currentStack != null ? currentStack : mTargetStack;
-
- if (ActivityManager.isStartResultSuccessful(result)) {
- if (startedActivityStack != null) {
- // If there is no state change (e.g. a resumed activity is reparented to
- // top of another display) to trigger a visibility/configuration checking,
- // we have to update the configuration for changing to different display.
- final ActivityRecord currentTop =
- startedActivityStack.topRunningActivityLocked();
- if (currentTop != null && currentTop.shouldUpdateConfigForDisplayChanged()) {
- mRootActivityContainer.ensureVisibilityAndConfig(
- currentTop, currentTop.getDisplayId(),
- true /* markFrozenIfConfigChanged */, false /* deferResume */);
- }
- }
- } else {
- // If we are not able to proceed, disassociate the activity from the task.
- // Leaving an activity in an incomplete state can lead to issues, such as
- // performing operations without a window container.
- final ActivityStack stack = mStartActivity.getActivityStack();
- if (stack != null) {
- mStartActivity.finishIfPossible("startActivity", true /* oomAdj */);
- }
-
- // Stack should also be detached from display and be removed if it's empty.
- if (startedActivityStack != null && startedActivityStack.isAttached()
- && startedActivityStack.numActivities() == 0
- && !startedActivityStack.isActivityTypeHome()) {
- startedActivityStack.remove();
- }
- }
+ Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+ startedActivityStack = handleStartResult(r, result);
mService.continueWindowLayout();
}
@@ -1445,6 +1416,49 @@
}
/**
+ * If the start result is success, ensure that the configuration of the started activity matches
+ * the current display. Otherwise clean up unassociated containers to avoid leakage.
+ *
+ * @return the stack where the successful started activity resides.
+ */
+ private @Nullable ActivityStack handleStartResult(@NonNull ActivityRecord started, int result) {
+ final ActivityStack currentStack = started.getActivityStack();
+ ActivityStack startedActivityStack = currentStack != null ? currentStack : mTargetStack;
+
+ if (ActivityManager.isStartResultSuccessful(result)) {
+ if (startedActivityStack != null) {
+ // If there is no state change (e.g. a resumed activity is reparented to top of
+ // another display) to trigger a visibility/configuration checking, we have to
+ // update the configuration for changing to different display.
+ final ActivityRecord currentTop = startedActivityStack.topRunningActivityLocked();
+ if (currentTop != null && currentTop.shouldUpdateConfigForDisplayChanged()) {
+ mRootActivityContainer.ensureVisibilityAndConfig(
+ currentTop, currentTop.getDisplayId(),
+ true /* markFrozenIfConfigChanged */, false /* deferResume */);
+ }
+ }
+ return startedActivityStack;
+ }
+
+ // If we are not able to proceed, disassociate the activity from the task. Leaving an
+ // activity in an incomplete state can lead to issues, such as performing operations
+ // without a window container.
+ final ActivityStack stack = mStartActivity.getActivityStack();
+ if (stack != null) {
+ mStartActivity.finishIfPossible("startActivity", true /* oomAdj */);
+ }
+
+ // Stack should also be detached from display and be removed if it's empty.
+ if (startedActivityStack != null && startedActivityStack.isAttached()
+ && startedActivityStack.numActivities() == 0
+ && !startedActivityStack.isActivityTypeHome()) {
+ startedActivityStack.remove();
+ startedActivityStack = null;
+ }
+ return startedActivityStack;
+ }
+
+ /**
* Return true if background activity is really aborted.
*
* TODO(b/131748165): Refactor the logic so we don't need to call this method everywhere.
@@ -1469,7 +1483,7 @@
}
// Note: This method should only be called from {@link startActivity}.
- private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
+ private int startActivityInner(final ActivityRecord r, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
ActivityRecord[] outActivity, boolean restrictedBgActivity) {
@@ -1592,7 +1606,7 @@
// accordingly.
if (mTargetStack.isFocusable()
&& !mRootActivityContainer.isTopDisplayFocusedStack(mTargetStack)) {
- mTargetStack.moveToFront("startActivityUnchecked");
+ mTargetStack.moveToFront("startActivityInner");
}
mRootActivityContainer.resumeFocusedStacksTopActivities(
mTargetStack, mStartActivity, mOptions);
@@ -1873,7 +1887,7 @@
mTargetStack = computeStackFocus(mSourceRecord, false /* newTask */,
mLaunchFlags, mOptions);
mTargetStack.addTask(targetTask,
- !mLaunchTaskBehind /* toTop */, "startActivityUnchecked");
+ !mLaunchTaskBehind /* toTop */, "complyActivityFlags");
}
}
} else if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) == 0 && !mAddingToTask
@@ -2430,7 +2444,7 @@
if (mStartActivity.getTaskRecord() == null || mStartActivity.getTaskRecord() == parent) {
parent.addActivityToTop(mStartActivity);
} else {
- mStartActivity.reparent(parent, parent.mActivities.size() /* top */, reason);
+ mStartActivity.reparent(parent, parent.getChildCount() /* top */, reason);
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index ab35652..0488a3b 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -159,10 +159,10 @@
*
* @param reasons A map from windowing mode to a reason integer why the transition was started,
* which must be one of the APP_TRANSITION_* values.
- * @param timestamp The time at which the app transition started in
- * {@link SystemClock#uptimeMillis()} timebase.
+ * @param timestampNs The time at which the app transition started in
+ * {@link SystemClock#elapsedRealtimeNs()} ()} timebase.
*/
- public abstract void notifyAppTransitionStarting(SparseIntArray reasons, long timestamp);
+ public abstract void notifyAppTransitionStarting(SparseIntArray reasons, long timestampNs);
/**
* Callback for window manager to let activity manager know that the app transition was
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 9b9dc88..20113a6 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -52,7 +52,7 @@
import static android.os.FactoryTest.FACTORY_TEST_OFF;
import static android.os.Process.FIRST_APPLICATION_UID;
import static android.os.Process.SYSTEM_UID;
-import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES;
import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RTL;
@@ -1606,6 +1606,7 @@
}
final long origId = Binder.clearCallingIdentity();
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "finishActivity");
try {
boolean res;
final boolean finishWithRootActivity =
@@ -1633,6 +1634,7 @@
}
return res;
} finally {
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
Binder.restoreCallingIdentity(origId);
}
}
@@ -1667,6 +1669,7 @@
try {
WindowProcessController proc = null;
synchronized (mGlobalLock) {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityIdle");
ActivityStack stack = ActivityRecord.getStackLocked(token);
if (stack == null) {
return;
@@ -1681,6 +1684,7 @@
}
}
} finally {
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
Binder.restoreCallingIdentity(origId);
}
}
@@ -1707,10 +1711,12 @@
public final void activityPaused(IBinder token) {
final long origId = Binder.clearCallingIdentity();
synchronized (mGlobalLock) {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityPaused");
ActivityStack stack = ActivityRecord.getStackLocked(token);
if (stack != null) {
stack.activityPausedLocked(token, false);
}
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
Binder.restoreCallingIdentity(origId);
}
@@ -1731,6 +1737,7 @@
int restartingUid = 0;
final ActivityRecord r;
synchronized (mGlobalLock) {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityStopped");
r = ActivityRecord.isInStackLocked(token);
if (r != null) {
if (r.attachedToProcess()
@@ -1742,6 +1749,7 @@
}
r.activityStoppedLocked(icicle, persistentState, description);
}
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
if (restartingName != null) {
@@ -1763,12 +1771,14 @@
if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "ACTIVITY DESTROYED: " + token);
synchronized (mGlobalLock) {
final long origId = Binder.clearCallingIdentity();
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityDestroyed");
try {
final ActivityRecord activity = ActivityRecord.forTokenLocked(token);
if (activity != null) {
activity.destroyed("activityDestroyed");
}
} finally {
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
Binder.restoreCallingIdentity(origId);
}
}
@@ -1981,7 +1991,7 @@
final TaskRecord task = r.getTaskRecord();
int index = task.mActivities.lastIndexOf(r);
if (index > 0) {
- ActivityRecord under = task.mActivities.get(index - 1);
+ ActivityRecord under = task.getChildAt(index - 1);
under.returningOptions = safeOptions != null ? safeOptions.getOptions(r) : null;
}
return r.setOccludesParent(false);
@@ -5506,8 +5516,8 @@
void startProcessAsync(ActivityRecord activity, boolean knownToBeDead, boolean isTop,
String hostingType) {
try {
- if (Trace.isTagEnabled(TRACE_TAG_ACTIVITY_MANAGER)) {
- Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "dispatchingStartProcess:"
+ if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "dispatchingStartProcess:"
+ activity.processName);
}
// Post message to start process to avoid possible deadlock of calling into AMS with the
@@ -5517,7 +5527,7 @@
isTop, hostingType, activity.intent.getComponent());
mH.sendMessage(m);
} finally {
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
}
@@ -5670,11 +5680,11 @@
private void updateResumedAppTrace(@Nullable ActivityRecord resumed) {
if (mTracedResumedActivity != null) {
- Trace.asyncTraceEnd(TRACE_TAG_ACTIVITY_MANAGER,
+ Trace.asyncTraceEnd(TRACE_TAG_WINDOW_MANAGER,
constructResumedTraceName(mTracedResumedActivity.packageName), 0);
}
if (resumed != null) {
- Trace.asyncTraceBegin(TRACE_TAG_ACTIVITY_MANAGER,
+ Trace.asyncTraceBegin(TRACE_TAG_WINDOW_MANAGER,
constructResumedTraceName(resumed.packageName), 0);
}
mTracedResumedActivity = resumed;
@@ -6002,10 +6012,10 @@
@Override
public void notifyAppTransitionStarting(SparseIntArray reasons,
- long timestamp) {
+ long timestampNs) {
synchronized (mGlobalLock) {
mStackSupervisor.getActivityMetricsLogger().notifyTransitionStarting(
- reasons, timestamp);
+ reasons, timestampNs);
}
}
@@ -6773,7 +6783,14 @@
@Override
public boolean attachApplication(WindowProcessController wpc) throws RemoteException {
synchronized (mGlobalLockWithoutBoost) {
- return mRootActivityContainer.attachApplication(wpc);
+ if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "attachApplication:" + wpc.mName);
+ }
+ try {
+ return mRootActivityContainer.attachApplication(wpc);
+ } finally {
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 894dfd4..0c07e15 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -209,7 +209,7 @@
mDisplayContent.computeImeTarget(true /* updateImeTarget */);
mService.mAtmInternal.notifyAppTransitionStarting(mTempTransitionReasons.clone(),
- SystemClock.uptimeMillis());
+ SystemClock.elapsedRealtimeNanos());
if (transit == TRANSIT_SHOW_SINGLE_TASK_DISPLAY) {
mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index a261341..e56fdd2 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -512,7 +512,7 @@
if (DEBUG_VISIBILITY) Slog.v(TAG, "VIS " + this + ": interesting="
+ numInteresting + " visible=" + numVisible);
if (nowDrawn != reportedDrawn) {
- onWindowsDrawn(nowDrawn, SystemClock.uptimeMillis());
+ onWindowsDrawn(nowDrawn, SystemClock.elapsedRealtimeNanos());
reportedDrawn = nowDrawn;
}
if (nowVisible != reportedVisible) {
@@ -1420,7 +1420,7 @@
mReparenting = true;
getParent().removeChild(this);
- task.addChild(this, position);
+ task.addChild((ActivityRecord) this, position);
mReparenting = false;
@@ -2283,7 +2283,7 @@
}
}
- private final Runnable mAddStartingWindow = new Runnable() {
+ private class AddStartingWindow implements Runnable {
@Override
public void run() {
@@ -2343,7 +2343,9 @@
AppWindowToken.this);
}
}
- };
+ }
+
+ private final AddStartingWindow mAddStartingWindow = new AddStartingWindow();
private int getStartingWindowType(boolean newTask, boolean taskSwitch, boolean processRunning,
boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents,
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index 8afbbdf..300ee1d 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -626,6 +626,10 @@
return mFullConfiguration.windowConfiguration.isAlwaysOnTop();
}
+ boolean hasChild() {
+ return getChildCount() > 0;
+ }
+
abstract protected int getChildCount();
abstract protected E getChildAt(int index);
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 150ae79..60e9819 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -196,6 +196,11 @@
// Nav bar is never forced opaque.
private static final int NAV_BAR_FORCE_TRANSPARENT = 2;
+ /** Don't apply window animation (see {@link #selectAnimation}). */
+ static final int ANIMATION_NONE = -1;
+ /** Use the transit animation in style resource (see {@link #selectAnimation}). */
+ static final int ANIMATION_STYLEABLE = 0;
+
/**
* These are the system UI flags that, when changing, can cause the layout
* of the screen to change.
@@ -1036,9 +1041,9 @@
}
/**
- * Control the animation to run when a window's state changes. Return a
- * non-0 number to force the animation to a specific resource ID, or 0
- * to use the default animation.
+ * Control the animation to run when a window's state changes. Return a positive number to
+ * force the animation to a specific resource ID, {@link #ANIMATION_STYLEABLE} to use the
+ * style resource defining the animation, or {@link #ANIMATION_NONE} for no animation.
*
* @param win The window that is changing.
* @param transit What is happening to the window:
@@ -1047,9 +1052,9 @@
* {@link com.android.server.policy.WindowManagerPolicy#TRANSIT_SHOW}, or
* {@link com.android.server.policy.WindowManagerPolicy#TRANSIT_HIDE}.
*
- * @return Resource ID of the actual animation to use, or 0 for none.
+ * @return Resource ID of the actual animation to use, or {@link #ANIMATION_NONE} for none.
*/
- public int selectAnimationLw(WindowState win, int transit) {
+ int selectAnimation(WindowState win, int transit) {
if (DEBUG_ANIM) Slog.i(TAG, "selectAnimation in " + win
+ ": transit=" + transit);
if (win == mStatusBar) {
@@ -1057,7 +1062,7 @@
final boolean expanded = win.getAttrs().height == MATCH_PARENT
&& win.getAttrs().width == MATCH_PARENT;
if (isKeyguard || expanded) {
- return -1;
+ return ANIMATION_NONE;
}
if (transit == TRANSIT_EXIT
|| transit == TRANSIT_HIDE) {
@@ -1068,7 +1073,7 @@
}
} else if (win == mNavigationBar) {
if (win.getAttrs().windowAnimations != 0) {
- return 0;
+ return ANIMATION_STYLEABLE;
}
// This can be on either the bottom or the right or the left.
if (mNavigationBarPosition == NAV_BAR_BOTTOM) {
@@ -1101,7 +1106,7 @@
}
}
} else if (win.getAttrs().type == TYPE_DOCK_DIVIDER) {
- return selectDockedDividerAnimationLw(win, transit);
+ return selectDockedDividerAnimation(win, transit);
}
if (transit == TRANSIT_PREVIEW_DONE) {
@@ -1115,13 +1120,13 @@
// is shown. We don't want an animation on the dream, because
// we need it shown immediately with the keyguard animating away
// to reveal it.
- return -1;
+ return ANIMATION_NONE;
}
- return 0;
+ return ANIMATION_STYLEABLE;
}
- private int selectDockedDividerAnimationLw(WindowState win, int transit) {
+ private int selectDockedDividerAnimation(WindowState win, int transit) {
int insets = mDisplayContent.getDockedDividerController().getContentInsets();
// If the divider is behind the navigation bar, don't animate.
@@ -1140,14 +1145,14 @@
|| frame.bottom + insets >= win.getDisplayFrameLw().bottom);
final boolean offscreen = offscreenLandscape || offscreenPortrait;
if (behindNavBar || offscreen) {
- return 0;
+ return ANIMATION_STYLEABLE;
}
if (transit == TRANSIT_ENTER || transit == TRANSIT_SHOW) {
return R.anim.fade_in;
} else if (transit == TRANSIT_EXIT) {
return R.anim.fade_out;
} else {
- return 0;
+ return ANIMATION_STYLEABLE;
}
}
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index 7e085f6..bc95481 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -25,7 +25,7 @@
*/
class ImeInsetsSourceProvider extends InsetsSourceProvider {
- private WindowState mCurImeTarget;
+ private WindowState mImeTargetFromIme;
private Runnable mShowImeRunner;
private boolean mIsImeLayoutDrawn;
@@ -40,8 +40,8 @@
void onPostLayout() {
super.onPostLayout();
- if (mCurImeTarget != null
- && mCurImeTarget == mDisplayContent.mInputMethodTarget
+ if (mImeTargetFromIme != null
+ && isImeTargetFromDisplayContentAndImeSame()
&& mWin != null
&& mWin.isDrawnLw()
&& !mWin.mGivenInsetsPending) {
@@ -64,18 +64,33 @@
/**
* Called from {@link WindowManagerInternal#showImePostLayout} when {@link InputMethodService}
* requests to show IME on {@param imeTarget}.
- * @param imeTarget imeTarget on which IME is displayed.
+ * @param imeTarget imeTarget on which IME request is coming from.
*/
void scheduleShowImePostLayout(WindowState imeTarget) {
- mCurImeTarget = imeTarget;
+ mImeTargetFromIme = imeTarget;
mShowImeRunner = () -> {
// Target should still be the same.
- if (mCurImeTarget == mDisplayContent.mInputMethodTarget) {
+ if (isImeTargetFromDisplayContentAndImeSame()) {
mDisplayContent.mInputMethodTarget.showInsets(
WindowInsets.Type.ime(), true /* fromIme */);
}
- mCurImeTarget = null;
+ mImeTargetFromIme = null;
};
}
+ private boolean isImeTargetFromDisplayContentAndImeSame() {
+ // IMMS#mLastImeTargetWindow always considers focused window as
+ // IME target, however DisplayContent#computeImeTarget() can compute
+ // a different IME target.
+ // Refer to WindowManagerService#applyImeVisibility(token, false).
+ // If IMMS's imeTarget is child of DisplayContent's imeTarget and child window
+ // is above the parent, we will consider it as the same target for now.
+ // TODO(b/139861270): Remove the child & sublayer check once IMMS is aware of
+ // actual IME target.
+ return mImeTargetFromIme == mDisplayContent.mInputMethodTarget
+ || (mDisplayContent.mInputMethodTarget.getParentWindow() == mImeTargetFromIme
+ && mDisplayContent.mInputMethodTarget.mSubLayer
+ > mImeTargetFromIme.mSubLayer);
+ }
+
}
diff --git a/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
index d774dc3..82ac6b8 100644
--- a/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
+++ b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
@@ -189,7 +189,7 @@
| WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
PixelFormat.TRANSLUCENT);
- lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
lp.setTitle("ImmersiveModeConfirmation");
lp.windowAnimations = com.android.internal.R.style.Animation_ImmersiveModeConfirmation;
lp.token = getWindowToken();
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index eb9b3e9..9973e11 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -266,7 +266,8 @@
final IWindow focusedWindow = mFocusedWindow.get();
if (focusedWindow != null) {
- if (focusedWindow.asBinder() == newFocusedWindow.asBinder()) {
+ if (newFocusedWindow != null
+ && newFocusedWindow.asBinder() == focusedWindow.asBinder()) {
Slog.w(TAG, "notifyFocusChanged called with unchanged mFocusedWindow="
+ focusedWindow);
return false;
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index dc9a598..584c6e1 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -112,7 +112,7 @@
}
}
- private final Runnable mUpdateInputWindows = new Runnable() {
+ private class UpdateInputWindows implements Runnable {
@Override
public void run() {
synchronized (mService.mGlobalLock) {
@@ -148,7 +148,9 @@
mUpdateInputForAllWindowsConsumer.updateInputWindows(inDrag);
}
}
- };
+ }
+
+ private final UpdateInputWindows mUpdateInputWindows = new UpdateInputWindows();
public InputMonitor(WindowManagerService service, int displayId) {
mService = service;
diff --git a/services/core/java/com/android/server/wm/InsetsControlTarget.java b/services/core/java/com/android/server/wm/InsetsControlTarget.java
index b7184a5..22ba82a 100644
--- a/services/core/java/com/android/server/wm/InsetsControlTarget.java
+++ b/services/core/java/com/android/server/wm/InsetsControlTarget.java
@@ -33,4 +33,13 @@
*/
default void showInsets(@InsetType int types, boolean fromIme) {
}
+
+ /**
+ * Instructs the control target to hide inset sources.
+ *
+ * @param types to specify which types of insets source window should be hidden.
+ * @param fromIme {@code true} if IME hide request originated from {@link InputMethodService}.
+ */
+ default void hideInsets(@InsetType int types, boolean fromIme) {
+ }
}
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 2b5eb3a..52cc422 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -16,7 +16,7 @@
package com.android.server.wm;
-import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION;
@@ -180,7 +180,7 @@
if (!mKeyguardShowing) {
return;
}
- Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "keyguardGoingAway");
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "keyguardGoingAway");
mService.deferWindowLayout();
try {
setKeyguardGoingAway(true);
@@ -202,11 +202,8 @@
true /* taskSwitch */);
mWindowManager.executeAppTransition();
} finally {
- Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "keyguardGoingAway: surfaceLayout");
mService.continueWindowLayout();
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
-
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
}
diff --git a/services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java b/services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java
index 93e2d8d..362ed3c 100644
--- a/services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java
+++ b/services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java
@@ -70,9 +70,12 @@
}
@Override
- public void onIntentStarted(Intent intent) {
+ public void onIntentStarted(Intent intent, long timestampNs) {
mHandler.sendMessage(PooledLambda.obtainMessage(
- LaunchObserverRegistryImpl::handleOnIntentStarted, this, intent));
+ LaunchObserverRegistryImpl::handleOnIntentStarted,
+ this,
+ intent,
+ timestampNs));
}
@Override
@@ -99,9 +102,22 @@
@Override
public void onActivityLaunchFinished(
- @ActivityRecordProto byte[] activity) {
+ @ActivityRecordProto byte[] activity,
+ long timestampNs) {
mHandler.sendMessage(PooledLambda.obtainMessage(
- LaunchObserverRegistryImpl::handleOnActivityLaunchFinished, this, activity));
+ LaunchObserverRegistryImpl::handleOnActivityLaunchFinished,
+ this,
+ activity,
+ timestampNs));
+ }
+
+ @Override
+ public void onReportFullyDrawn(@ActivityRecordProto byte[] activity, long timestampNs) {
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ LaunchObserverRegistryImpl::handleOnReportFullyDrawn,
+ this,
+ activity,
+ timestampNs));
}
// Use PooledLambda.obtainMessage to invoke below methods. Every method reference must be
@@ -116,11 +132,11 @@
mList.remove(observer);
}
- private void handleOnIntentStarted(Intent intent) {
+ private void handleOnIntentStarted(Intent intent, long timestampNs) {
// Traverse start-to-end to meet the registerLaunchObserver multi-cast order guarantee.
for (int i = 0; i < mList.size(); i++) {
ActivityMetricsLaunchObserver o = mList.get(i);
- o.onIntentStarted(intent);
+ o.onIntentStarted(intent, timestampNs);
}
}
@@ -152,11 +168,20 @@
}
private void handleOnActivityLaunchFinished(
- @ActivityRecordProto byte[] activity) {
+ @ActivityRecordProto byte[] activity, long timestampNs) {
// Traverse start-to-end to meet the registerLaunchObserver multi-cast order guarantee.
for (int i = 0; i < mList.size(); i++) {
ActivityMetricsLaunchObserver o = mList.get(i);
- o.onActivityLaunchFinished(activity);
+ o.onActivityLaunchFinished(activity, timestampNs);
+ }
+ }
+
+ private void handleOnReportFullyDrawn(
+ @ActivityRecordProto byte[] activity, long timestampNs) {
+ // Traverse start-to-end to meet the registerLaunchObserver multi-cast order guarantee.
+ for (int i = 0; i < mList.size(); i++) {
+ ActivityMetricsLaunchObserver o = mList.get(i);
+ o.onReportFullyDrawn(activity, timestampNs);
}
}
}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index 062cdc5..12579e6 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -22,7 +22,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION;
-import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.WindowManager.TRANSIT_NONE;
import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
@@ -158,7 +158,7 @@
void startRecentsActivity(IRecentsAnimationRunner recentsAnimationRunner) {
ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "startRecentsActivity(): intent=%s", mTargetIntent);
- Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "RecentsAnimation#startRecentsActivity");
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "RecentsAnimation#startRecentsActivity");
// TODO(multi-display) currently only support recents animation in default display.
final DisplayContent dc =
@@ -263,7 +263,7 @@
throw e;
} finally {
mService.continueWindowLayout();
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
}
@@ -297,7 +297,7 @@
}
mWindowManager.inSurfaceTransaction(() -> {
- Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER,
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
"RecentsAnimation#onAnimationFinished_inSurfaceTransaction");
mService.deferWindowLayout();
try {
@@ -394,7 +394,7 @@
throw e;
} finally {
mService.continueWindowLayout();
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
});
}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index bd27905..d606e5d 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -98,8 +98,10 @@
private final ArrayList<WallpaperAnimationAdapter> mPendingWallpaperAnimations =
new ArrayList<>();
private final int mDisplayId;
- private final Runnable mFailsafeRunnable = () ->
- cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "failSafeRunnable");
+ private boolean mWillFinishToHome = false;
+ private final Runnable mFailsafeRunnable = () -> cancelAnimation(
+ mWillFinishToHome ? REORDER_MOVE_TO_TOP : REORDER_MOVE_TO_ORIGINAL_POSITION,
+ "failSafeRunnable");
// The recents component app token that is shown behind the visibile tasks
private AppWindowToken mTargetAppToken;
@@ -326,6 +328,13 @@
}
}
}
+
+ @Override
+ public void setWillFinishToHome(boolean willFinishToHome) {
+ synchronized (mService.getWindowManagerLock()) {
+ mWillFinishToHome = willFinishToHome;
+ }
+ }
};
/**
@@ -494,7 +503,8 @@
}
final SparseIntArray reasons = new SparseIntArray();
reasons.put(WINDOWING_MODE_FULLSCREEN, APP_TRANSITION_RECENTS_ANIM);
- mService.mAtmInternal.notifyAppTransitionStarting(reasons, SystemClock.uptimeMillis());
+ mService.mAtmInternal
+ .notifyAppTransitionStarting(reasons, SystemClock.elapsedRealtimeNanos());
}
private RemoteAnimationTarget[] createAppAnimations() {
diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java
index d78d517..60833c3 100644
--- a/services/core/java/com/android/server/wm/RootActivityContainer.java
+++ b/services/core/java/com/android/server/wm/RootActivityContainer.java
@@ -983,7 +983,7 @@
stack.resize(task.getRequestedOverrideBounds(), null /* tempTaskBounds */,
null /* tempTaskInsetBounds */, !PRESERVE_WINDOWS, !DEFER_RESUME);
- if (task.mActivities.size() == 1) {
+ if (task.getChildCount() == 1) {
// Defer resume until below, and do not schedule PiP changes until we animate below
task.reparent(stack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT, !ANIMATE, DEFER_RESUME,
false /* schedulePictureInPictureModeChange */, reason);
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index a181c18..f0717ca 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -58,7 +58,7 @@
import java.io.PrintWriter;
import java.util.function.Consumer;
-class Task extends WindowContainer<AppWindowToken> implements ConfigurationContainerListener{
+class Task extends WindowContainer<ActivityRecord> implements ConfigurationContainerListener{
static final String TAG = TAG_WITH_CLASS_NAME ? "Task" : TAG_WM;
// TODO: Track parent marks like this in WindowContainer.
@@ -121,8 +121,11 @@
// TODO: Remove after unification.
@Override
public void onConfigurationChanged(Configuration newParentConfig) {
- // Only forward configuration changes in cases where children won't get it from TaskRecord.
- onConfigurationChanged(newParentConfig, mTaskRecord == null /*forwardToChildren*/);
+ // Forward configuration changes in cases
+ // - children won't get it from TaskRecord
+ // - it's a pinned task
+ onConfigurationChanged(newParentConfig,
+ (mTaskRecord == null) || inPinnedWindowingMode() /*forwardToChildren*/);
}
Task(int taskId, TaskStack stack, int userId, WindowManagerService service, int resizeMode,
@@ -170,14 +173,14 @@
}
@Override
- void addChild(AppWindowToken wtoken, int position) {
+ void addChild(ActivityRecord child, int position) {
position = getAdjustedAddPosition(position);
- super.addChild(wtoken, position);
+ super.addChild(child, position);
mDeferRemoval = false;
}
@Override
- void positionChildAt(int position, AppWindowToken child, boolean includingParents) {
+ void positionChildAt(int position, ActivityRecord child, boolean includingParents) {
position = getAdjustedAddPosition(position);
super.positionChildAt(position, child, includingParents);
mDeferRemoval = false;
@@ -279,13 +282,13 @@
}
@Override
- void removeChild(AppWindowToken token) {
- if (!mChildren.contains(token)) {
+ void removeChild(ActivityRecord child) {
+ if (!mChildren.contains(child)) {
Slog.e(TAG, "removeChild: token=" + this + " not found.");
return;
}
- super.removeChild(token);
+ super.removeChild(child);
if (mChildren.isEmpty()) {
EventLog.writeEvent(WM_TASK_REMOVED, mTaskId, "removeAppToken: last token");
@@ -674,18 +677,18 @@
return null;
}
- void positionChildAtTop(AppWindowToken aToken) {
- positionChildAt(aToken, POSITION_TOP);
+ void positionChildAtTop(ActivityRecord child) {
+ positionChildAt(child, POSITION_TOP);
}
- void positionChildAt(AppWindowToken aToken, int position) {
- if (aToken == null) {
+ void positionChildAt(ActivityRecord child, int position) {
+ if (child == null) {
Slog.w(TAG_WM,
"Attempted to position of non-existing app");
return;
}
- positionChildAt(position, aToken, false /* includeParents */);
+ positionChildAt(position, child, false /* includeParents */);
}
void forceWindowsScaleable(boolean force) {
diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java
index 75333c7..299b32c 100644
--- a/services/core/java/com/android/server/wm/TaskRecord.java
+++ b/services/core/java/com/android/server/wm/TaskRecord.java
@@ -48,7 +48,7 @@
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.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
import static android.view.Display.DEFAULT_DISPLAY;
@@ -447,7 +447,7 @@
}
void cleanUpResourcesForDestroy() {
- if (!mActivities.isEmpty()) {
+ if (hasChild()) {
return;
}
@@ -553,7 +553,7 @@
// This method assumes that the task is already placed in the right stack.
// we do not mess with that decision and we only do the resize!
- Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeTask_" + mTaskId);
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "resizeTask_" + mTaskId);
boolean updatedConfig = false;
mTmpConfig.setTo(getResolvedOverrideConfiguration());
@@ -587,7 +587,7 @@
saveLaunchingStateIfNeeded();
- Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
return kept;
} finally {
mAtmService.continueWindowLayout();
@@ -1095,7 +1095,7 @@
// There are no non-finishing activities in the task.
return null;
}
- return mActivities.get(rootActivityIndex);
+ return getChildAt(rootActivityIndex);
}
ActivityRecord getTopActivity() {
@@ -1103,8 +1103,8 @@
}
ActivityRecord getTopActivity(boolean includeOverlays) {
- for (int i = mActivities.size() - 1; i >= 0; --i) {
- final ActivityRecord r = mActivities.get(i);
+ for (int i = getChildCount() - 1; i >= 0; --i) {
+ final ActivityRecord r = getChildAt(i);
if (r.finishing || (!includeOverlays && r.mTaskOverlay)) {
continue;
}
@@ -1115,8 +1115,8 @@
ActivityRecord topRunningActivityLocked() {
if (mStack != null) {
- for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
- ActivityRecord r = mActivities.get(activityNdx);
+ for (int activityNdx = getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+ ActivityRecord r = getChildAt(activityNdx);
if (!r.finishing && r.okToShowLocked()) {
return r;
}
@@ -1126,8 +1126,8 @@
}
boolean isVisible() {
- for (int i = mActivities.size() - 1; i >= 0; --i) {
- final ActivityRecord r = mActivities.get(i);
+ for (int i = getChildCount() - 1; i >= 0; --i) {
+ final ActivityRecord r = getChildAt(i);
if (r.visible) {
return true;
}
@@ -1139,8 +1139,8 @@
* Return true if any activities in this task belongs to input uid.
*/
boolean containsAppUid(int uid) {
- for (int i = mActivities.size() - 1; i >= 0; --i) {
- final ActivityRecord r = mActivities.get(i);
+ for (int i = getChildCount() - 1; i >= 0; --i) {
+ final ActivityRecord r = getChildAt(i);
if (r.getUid() == uid) {
return true;
}
@@ -1150,8 +1150,8 @@
void getAllRunningVisibleActivitiesLocked(ArrayList<ActivityRecord> outActivities) {
if (mStack != null) {
- for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
- ActivityRecord r = mActivities.get(activityNdx);
+ for (int activityNdx = getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+ ActivityRecord r = getChildAt(activityNdx);
if (!r.finishing && r.okToShowLocked() && r.visibleIgnoringKeyguard) {
outActivities.add(r);
}
@@ -1161,8 +1161,8 @@
ActivityRecord topRunningActivityWithStartingWindowLocked() {
if (mStack != null) {
- for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
- ActivityRecord r = mActivities.get(activityNdx);
+ for (int activityNdx = getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+ ActivityRecord r = getChildAt(activityNdx);
if (r.mStartingWindowState != STARTING_WINDOW_SHOWN
|| r.finishing || !r.okToShowLocked()) {
continue;
@@ -1179,8 +1179,8 @@
*/
void getNumRunningActivities(TaskActivitiesReport reportOut) {
reportOut.reset();
- for (int i = mActivities.size() - 1; i >= 0; --i) {
- final ActivityRecord r = mActivities.get(i);
+ for (int i = getChildCount() - 1; i >= 0; --i) {
+ final ActivityRecord r = getChildAt(i);
if (r.finishing) {
continue;
}
@@ -1227,17 +1227,17 @@
}
void addActivityToTop(ActivityRecord r) {
- addActivityAtIndex(mActivities.size(), r);
+ addActivityAtIndex(getChildCount(), r);
}
@Override
/*@WindowConfiguration.ActivityType*/
public int getActivityType() {
final int applicationType = super.getActivityType();
- if (applicationType != ACTIVITY_TYPE_UNDEFINED || mActivities.isEmpty()) {
+ if (applicationType != ACTIVITY_TYPE_UNDEFINED || !hasChild()) {
return applicationType;
}
- return mActivities.get(0).getActivityType();
+ return getChildAt(0).getActivityType();
}
/**
@@ -1259,7 +1259,7 @@
numFullscreen++;
}
// Only set this based on the first activity
- if (mActivities.isEmpty()) {
+ if (!hasChild()) {
if (r.getActivityType() == ACTIVITY_TYPE_UNDEFINED) {
// Normally non-standard activity type for the activity record will be set when the
// object is created, however we delay setting the standard application type until
@@ -1279,10 +1279,10 @@
r.setActivityType(getActivityType());
}
- final int size = mActivities.size();
+ final int size = getChildCount();
if (index == size && size > 0) {
- final ActivityRecord top = mActivities.get(size - 1);
+ final ActivityRecord top = getChildAt(size - 1);
if (top.mTaskOverlay) {
// Place below the task overlay activity since the overlay activity should always
// be on top.
@@ -1341,7 +1341,7 @@
mAtmService.getTaskChangeNotificationController().notifyTaskStackChanged();
}
- if (mActivities.isEmpty()) {
+ if (!hasChild()) {
return !mReuseTask;
}
updateEffectiveIntent();
@@ -1355,8 +1355,8 @@
*/
boolean onlyHasTaskOverlayActivities(boolean excludeFinishing) {
int count = 0;
- for (int i = mActivities.size() - 1; i >= 0; i--) {
- final ActivityRecord r = mActivities.get(i);
+ for (int i = getChildCount() - 1; i >= 0; i--) {
+ final ActivityRecord r = getChildAt(i);
if (excludeFinishing && r.finishing) {
continue;
}
@@ -1372,7 +1372,7 @@
// We will automatically remove the task either if it has explicitly asked for
// this, or it is empty and has never contained an activity that got shown to
// the user.
- return autoRemoveRecents || (mActivities.isEmpty() && !hasBeenVisible);
+ return autoRemoveRecents || (!hasChild() && !hasBeenVisible);
}
/**
@@ -1380,9 +1380,9 @@
* task starting at a specified index.
*/
final void performClearTaskAtIndexLocked(int activityNdx, String reason) {
- int numActivities = mActivities.size();
+ int numActivities = getChildCount();
for ( ; activityNdx < numActivities; ++activityNdx) {
- final ActivityRecord r = mActivities.get(activityNdx);
+ final ActivityRecord r = getChildAt(activityNdx);
if (r.finishing) {
continue;
}
@@ -1429,9 +1429,9 @@
* or null if none was found.
*/
final ActivityRecord performClearTaskLocked(ActivityRecord newR, int launchFlags) {
- int numActivities = mActivities.size();
+ int numActivities = getChildCount();
for (int activityNdx = numActivities - 1; activityNdx >= 0; --activityNdx) {
- ActivityRecord r = mActivities.get(activityNdx);
+ ActivityRecord r = getChildAt(activityNdx);
if (r.finishing) {
continue;
}
@@ -1440,7 +1440,7 @@
final ActivityRecord ret = r;
for (++activityNdx; activityNdx < numActivities; ++activityNdx) {
- r = mActivities.get(activityNdx);
+ r = getChildAt(activityNdx);
if (r.finishing) {
continue;
}
@@ -1591,8 +1591,8 @@
*/
final ActivityRecord findActivityInHistoryLocked(ActivityRecord r) {
final ComponentName realActivity = r.mActivityComponent;
- for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
- ActivityRecord candidate = mActivities.get(activityNdx);
+ for (int activityNdx = getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+ ActivityRecord candidate = getChildAt(activityNdx);
if (candidate.finishing) {
continue;
}
@@ -1609,12 +1609,12 @@
// Traverse upwards looking for any break between main task activities and
// utility activities.
int activityNdx;
- final int numActivities = mActivities.size();
+ final int numActivities = getChildCount();
final boolean relinquish = numActivities != 0 &&
- (mActivities.get(0).info.flags & FLAG_RELINQUISH_TASK_IDENTITY) != 0;
+ (getChildAt(0).info.flags & FLAG_RELINQUISH_TASK_IDENTITY) != 0;
for (activityNdx = Math.min(numActivities, 1); activityNdx < numActivities;
++activityNdx) {
- final ActivityRecord r = mActivities.get(activityNdx);
+ final ActivityRecord r = getChildAt(activityNdx);
if (relinquish && (r.info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0) {
// This will be the top activity for determining taskDescription. Pre-inc to
// overcome initial decrement below.
@@ -1642,7 +1642,7 @@
boolean navigationBarContrastWhenTransparent = false;
boolean topActivity = true;
for (--activityNdx; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = mActivities.get(activityNdx);
+ final ActivityRecord r = getChildAt(activityNdx);
if (r.mTaskOverlay) {
continue;
}
@@ -1697,9 +1697,9 @@
*/
int findRootIndex(boolean effectiveRoot) {
int effectiveNdx = -1;
- final int topActivityNdx = mActivities.size() - 1;
+ final int topActivityNdx = getChildCount() - 1;
for (int activityNdx = 0; activityNdx <= topActivityNdx; ++activityNdx) {
- final ActivityRecord r = mActivities.get(activityNdx);
+ final ActivityRecord r = getChildAt(activityNdx);
if (r.finishing) {
continue;
}
@@ -1720,7 +1720,7 @@
// But we still want to update the intent, so let's use the bottom activity.
effectiveRootIndex = 0;
}
- final ActivityRecord r = mActivities.get(effectiveRootIndex);
+ final ActivityRecord r = getChildAt(effectiveRootIndex);
setIntent(r);
// Update the task description when the activities change
@@ -2289,8 +2289,8 @@
}
void addStartingWindowsForVisibleActivities(boolean taskSwitch) {
- for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
- final ActivityRecord r = mActivities.get(activityNdx);
+ for (int activityNdx = getChildCount() - 1; activityNdx >= 0; --activityNdx) {
+ final ActivityRecord r = getChildAt(activityNdx);
if (r.visible) {
r.showStartingWindow(null /* prev */, false /* newTask */, taskSwitch);
}
@@ -2462,7 +2462,7 @@
sb.append(" StackId=");
sb.append(getStackId());
sb.append(" sz=");
- sb.append(mActivities.size());
+ sb.append(getChildCount());
sb.append('}');
return sb.toString();
}
@@ -2495,8 +2495,8 @@
final long token = proto.start(fieldId);
super.writeToProto(proto, CONFIGURATION_CONTAINER, logLevel);
proto.write(ID, mTaskId);
- for (int i = mActivities.size() - 1; i >= 0; i--) {
- ActivityRecord activity = mActivities.get(i);
+ for (int i = getChildCount() - 1; i >= 0; i--) {
+ ActivityRecord activity = getChildAt(i);
activity.writeToProto(proto, ACTIVITIES);
}
proto.write(STACK_ID, mStack.mStackId);
@@ -2607,10 +2607,9 @@
out.endTag(null, TAG_INTENT);
}
- final ArrayList<ActivityRecord> activities = mActivities;
- final int numActivities = activities.size();
+ final int numActivities = getChildCount();
for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) {
- final ActivityRecord r = activities.get(activityNdx);
+ final ActivityRecord r = getChildAt(activityNdx);
if (r.info.persistableMode == ActivityInfo.PERSIST_ROOT_ONLY || !r.isPersistable() ||
((r.intent.getFlags() & FLAG_ACTIVITY_NEW_DOCUMENT
| FLAG_ACTIVITY_RETAIN_IN_RECENTS) == FLAG_ACTIVITY_NEW_DOCUMENT) &&
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index f4b7672..0cb4826 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -514,6 +514,13 @@
public abstract void showImePostLayout(IBinder imeTargetWindowToken);
/**
+ * Hide IME using imeTargetWindow when requested.
+ *
+ * @param displayId on which IME is shown
+ */
+ public abstract void hideIme(int displayId);
+
+ /**
* Tell window manager about a package that should not be running with high refresh rate
* setting until removeNonHighRefreshRatePackage is called for the same package.
*
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 0287270..c485280 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -236,6 +236,7 @@
import android.view.SurfaceSession;
import android.view.View;
import android.view.WindowContentFrameStats;
+import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.view.WindowManager.RemoveContentMode;
@@ -7312,6 +7313,16 @@
}
@Override
+ public void hideIme(int displayId) {
+ synchronized (mGlobalLock) {
+ final DisplayContent dc = mRoot.getDisplayContent(displayId);
+ if (dc != null && dc.mInputMethodTarget != null) {
+ dc.mInputMethodTarget.hideInsets(WindowInsets.Type.ime(), true /* fromIme */);
+ }
+ }
+ }
+
+ @Override
public boolean isUidAllowedOnDisplay(int displayId, int uid) {
if (displayId == Display.DEFAULT_DISPLAY) {
return true;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 40bb059..7ff9b70 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -3377,6 +3377,15 @@
}
}
+ @Override
+ public void hideInsets(@InsetType int types, boolean fromIme) {
+ try {
+ mClient.hideInsets(types, fromIme);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to deliver showInsets", e);
+ }
+ }
+
Rect getBackdropFrame(Rect frame) {
// When the task is docked, we send fullscreen sized backDropFrame as soon as resizing
// start even if we haven't received the relayout window, so that the client requests
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 1328273..eac372f 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1348,13 +1348,16 @@
// frozen, there is no reason to animate and it can cause strange
// artifacts when we unfreeze the display if some different animation
// is running.
- Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "WSA#applyAnimationLocked");
if (mWin.mToken.okToAnimate()) {
- int anim = mWin.getDisplayContent().getDisplayPolicy().selectAnimationLw(mWin, transit);
+ int anim = mWin.getDisplayContent().getDisplayPolicy().selectAnimation(mWin, transit);
int attr = -1;
Animation a = null;
- if (anim != 0) {
- a = anim != -1 ? AnimationUtils.loadAnimation(mContext, anim) : null;
+ if (anim != DisplayPolicy.ANIMATION_STYLEABLE) {
+ if (anim != DisplayPolicy.ANIMATION_NONE) {
+ Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "WSA#loadAnimation");
+ a = AnimationUtils.loadAnimation(mContext, anim);
+ Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+ }
} else {
switch (transit) {
case WindowManagerPolicy.TRANSIT_ENTER:
@@ -1384,7 +1387,9 @@
+ " isEntrance=" + isEntrance + " Callers " + Debug.getCallers(3));
if (a != null) {
if (DEBUG_ANIM) logWithStack(TAG, "Loaded animation " + a + " for " + this);
+ Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "WSA#startAnimation");
mWin.startAnimation(a);
+ Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
mAnimationIsEntrance = isEntrance;
}
} else {
@@ -1395,7 +1400,6 @@
mWin.getDisplayContent().adjustForImeIfNeeded();
}
- Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
return mWin.isAnimating();
}
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 56f6d4b..0cfdebc 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -23,7 +23,6 @@
import static com.android.server.wm.WindowManagerService.LAYOUT_REPEAT_THRESHOLD;
import android.os.Debug;
-import android.os.Trace;
import android.util.Slog;
import java.io.PrintWriter;
@@ -52,15 +51,19 @@
/** The number of layout requests when deferring. */
private int mDeferredRequests;
- private final Runnable mPerformSurfacePlacement;
-
- public WindowSurfacePlacer(WindowManagerService service) {
- mService = service;
- mPerformSurfacePlacement = () -> {
+ private class Traverser implements Runnable {
+ @Override
+ public void run() {
synchronized (mService.mGlobalLock) {
performSurfacePlacement();
}
- };
+ }
+ }
+
+ private final Traverser mPerformSurfacePlacement = new Traverser();
+
+ WindowSurfacePlacer(WindowManagerService service) {
+ mService = service;
}
/**
@@ -152,7 +155,6 @@
return;
}
- Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "wmLayout");
mInLayout = true;
boolean recoveringMemory = false;
@@ -198,8 +200,6 @@
mInLayout = false;
Slog.wtf(TAG, "Unhandled exception while laying out windows", e);
}
-
- Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
}
void debugLayoutRepeats(final String msg, int pendingLayoutChanges) {
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index fb2fdab..dd2629d 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -201,8 +201,7 @@
void setDisplayViewports(JNIEnv* env, jobjectArray viewportObjArray);
- status_t registerInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel,
- int32_t displayId);
+ status_t registerInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel);
status_t registerInputMonitor(JNIEnv* env, const sp<InputChannel>& inputChannel,
int32_t displayId, bool isGestureMonitor);
status_t unregisterInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel);
@@ -435,10 +434,9 @@
}
status_t NativeInputManager::registerInputChannel(JNIEnv* /* env */,
- const sp<InputChannel>& inputChannel, int32_t displayId) {
+ const sp<InputChannel>& inputChannel) {
ATRACE_CALL();
- return mInputManager->getDispatcher()->registerInputChannel(
- inputChannel, displayId);
+ return mInputManager->getDispatcher()->registerInputChannel(inputChannel);
}
status_t NativeInputManager::registerInputMonitor(JNIEnv* /* env */,
@@ -1405,7 +1403,7 @@
}
static void nativeRegisterInputChannel(JNIEnv* env, jclass /* clazz */,
- jlong ptr, jobject inputChannelObj, jint displayId) {
+ jlong ptr, jobject inputChannelObj) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
@@ -1415,7 +1413,7 @@
return;
}
- status_t status = im->registerInputChannel(env, inputChannel, displayId);
+ status_t status = im->registerInputChannel(env, inputChannel);
if (status) {
std::string message;
@@ -1757,7 +1755,7 @@
{ "nativeHasKeys", "(JII[I[Z)Z",
(void*) nativeHasKeys },
{ "nativeRegisterInputChannel",
- "(JLandroid/view/InputChannel;I)V",
+ "(JLandroid/view/InputChannel;)V",
(void*) nativeRegisterInputChannel },
{ "nativeRegisterInputMonitor",
"(JLandroid/view/InputChannel;IZ)V",
diff --git a/services/core/jni/com_android_server_security_VerityUtils.cpp b/services/core/jni/com_android_server_security_VerityUtils.cpp
index 9ceb770..906b568 100644
--- a/services/core/jni/com_android_server_security_VerityUtils.cpp
+++ b/services/core/jni/com_android_server_security_VerityUtils.cpp
@@ -17,6 +17,8 @@
#define LOG_TAG "VerityUtils"
#include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedPrimitiveArray.h>
+#include <nativehelper/ScopedUtfChars.h>
#include "jni.h"
#include <utils/Log.h>
@@ -72,11 +74,17 @@
namespace {
int enableFsverity(JNIEnv* env, jobject /* clazz */, jstring filePath, jbyteArray signature) {
- const char* path = env->GetStringUTFChars(filePath, nullptr);
- ::android::base::unique_fd rfd(open(path, O_RDONLY | O_CLOEXEC));
- env->ReleaseStringUTFChars(filePath, path);
+ ScopedUtfChars path(env, filePath);
+ if (path.c_str() == nullptr) {
+ return EINVAL;
+ }
+ ::android::base::unique_fd rfd(open(path.c_str(), O_RDONLY | O_CLOEXEC));
if (rfd.get() < 0) {
- return errno;
+ return errno;
+ }
+ ScopedByteArrayRO signature_bytes(env, signature);
+ if (signature_bytes.get() == nullptr) {
+ return EINVAL;
}
fsverity_enable_arg arg = {};
@@ -85,11 +93,11 @@
arg.block_size = 4096;
arg.salt_size = 0;
arg.salt_ptr = reinterpret_cast<uintptr_t>(nullptr);
- arg.sig_size = env->GetArrayLength(signature);
- arg.sig_ptr = reinterpret_cast<uintptr_t>(signature);
+ arg.sig_size = signature_bytes.size();
+ arg.sig_ptr = reinterpret_cast<uintptr_t>(signature_bytes.get());
if (ioctl(rfd.get(), FS_IOC_ENABLE_VERITY, &arg) < 0) {
- return errno;
+ return errno;
}
return 0;
}
@@ -101,14 +109,16 @@
fsverity_digest *data = reinterpret_cast<fsverity_digest *>(&bytes);
data->digest_size = kSha256Bytes; // the only input/output parameter
- const char* path = env->GetStringUTFChars(filePath, nullptr);
- ::android::base::unique_fd rfd(open(path, O_RDONLY | O_CLOEXEC));
- env->ReleaseStringUTFChars(filePath, path);
+ ScopedUtfChars path(env, filePath);
+ if (path.c_str() == nullptr) {
+ return EINVAL;
+ }
+ ::android::base::unique_fd rfd(open(path.c_str(), O_RDONLY | O_CLOEXEC));
if (rfd.get() < 0) {
- return errno;
+ return errno;
}
if (ioctl(rfd.get(), FS_IOC_MEASURE_VERITY, data) < 0) {
- return errno;
+ return errno;
}
return 0;
}
diff --git a/services/devicepolicy/Android.bp b/services/devicepolicy/Android.bp
index 47790ce..91c05a8 100644
--- a/services/devicepolicy/Android.bp
+++ b/services/devicepolicy/Android.bp
@@ -5,4 +5,13 @@
libs: [
"services.core",
],
+
+ plugins: [
+ "compat-changeid-annotation-processor",
+ ],
}
+
+platform_compat_config {
+ name: "services-devicepolicy-platform-compat-config",
+ src: ":services.devicepolicy",
+}
\ No newline at end of file
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 9569ac8..0ae205a 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -137,6 +137,8 @@
import android.app.backup.IBackupManager;
import android.app.trust.TrustManager;
import android.app.usage.UsageStatsManagerInternal;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -235,6 +237,7 @@
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.compat.IPlatformCompat;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.notification.SystemNotificationChannels;
@@ -251,6 +254,7 @@
import com.android.internal.util.XmlUtils;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockSettingsInternal;
+import com.android.internal.widget.LockscreenCredential;
import com.android.server.LocalServices;
import com.android.server.LockGuard;
import com.android.server.SystemServerInitThreadPool;
@@ -494,6 +498,22 @@
private static final String LOG_TAG_PROFILE_OWNER = "profile-owner";
private static final String LOG_TAG_DEVICE_OWNER = "device-owner";
+ /**
+ * For admin apps targeting R+, throw when the app sets password requirement
+ * that is not taken into account at given quality. For example when quality is set
+ * to {@link DevicePolicyManager#PASSWORD_QUALITY_UNSPECIFIED}, it doesn't make sense to
+ * require certain password length. If the intent is to require a password of certain length
+ * having at least NUMERIC quality, the admin should first call
+ * {@link #setPasswordQuality(ComponentName, int, boolean)} and only then call
+ * {@link #setPasswordMinimumLength(ComponentName, int, boolean)}.
+ *
+ * <p>Conversely when an admin app targeting R+ lowers password quality, those
+ * requirements that stop making sense are reset to default values.
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
+ private static final long ADMIN_APP_PASSWORD_COMPLEXITY = 123562444L;
+
final Context mContext;
final Injector mInjector;
final IPackageManager mIPackageManager;
@@ -506,6 +526,7 @@
private final LockSettingsInternal mLockSettingsInternal;
private final DeviceAdminServiceController mDeviceAdminServiceController;
private final OverlayPackagesProvider mOverlayPackagesProvider;
+ private final IPlatformCompat mIPlatformCompat;
private final DevicePolicyCacheImpl mPolicyCache = new DevicePolicyCacheImpl();
private final DeviceStateCacheImpl mStateCache = new DeviceStateCacheImpl();
@@ -1984,6 +2005,11 @@
return LocalServices.getService(LockSettingsInternal.class);
}
+ IPlatformCompat getIPlatformCompat() {
+ return IPlatformCompat.Stub.asInterface(
+ ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
+ }
+
boolean hasUserSetupCompleted(DevicePolicyData userData) {
return userData.mUserSetupComplete;
}
@@ -2222,6 +2248,7 @@
mUsageStatsManagerInternal = Preconditions.checkNotNull(
injector.getUsageStatsManagerInternal());
mIPackageManager = Preconditions.checkNotNull(injector.getIPackageManager());
+ mIPlatformCompat = Preconditions.checkNotNull(injector.getIPlatformCompat());
mIPermissionManager = Preconditions.checkNotNull(injector.getIPermissionManager());
mTelephonyManager = Preconditions.checkNotNull(injector.getTelephonyManager());
@@ -4156,12 +4183,22 @@
.write();
}
+ private boolean passwordQualityInvocationOrderCheckEnabled(String packageName, int userId) {
+ try {
+ return mIPlatformCompat.isChangeEnabledByPackageName(ADMIN_APP_PASSWORD_COMPLEXITY,
+ packageName);
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "Failed to get a response from PLATFORM_COMPAT_SERVICE", e);
+ }
+ return getTargetSdk(packageName, userId) > Build.VERSION_CODES.Q;
+ }
+
/**
* For admins targeting R+ reset various password constraints to default values when quality is
* set to a value that makes those constraints that have no effect.
*/
private void resetInactivePasswordRequirementsIfRPlus(int userId, ActiveAdmin admin) {
- if (getTargetSdk(admin.info.getPackageName(), userId) > Build.VERSION_CODES.Q) {
+ if (passwordQualityInvocationOrderCheckEnabled(admin.info.getPackageName(), userId)) {
final PasswordMetrics metrics = admin.minimumPasswordMetrics;
if (metrics.quality < PASSWORD_QUALITY_NUMERIC) {
metrics.length = ActiveAdmin.DEF_MINIMUM_PASSWORD_LENGTH;
@@ -4326,7 +4363,8 @@
private void ensureMinimumQuality(
int userId, ActiveAdmin admin, int minimumQuality, String operation) {
if (admin.minimumPasswordMetrics.quality < minimumQuality
- && getTargetSdk(admin.info.getPackageName(), userId) > Build.VERSION_CODES.Q) {
+ && passwordQualityInvocationOrderCheckEnabled(admin.info.getPackageName(),
+ userId)) {
throw new IllegalStateException(String.format(
"password quality should be at least %d for %s", minimumQuality, operation));
}
@@ -5222,28 +5260,20 @@
// back in to the service.
final long ident = mInjector.binderClearCallingIdentity();
final boolean result;
+ final LockscreenCredential newCredential =
+ LockscreenCredential.createPasswordOrNone(password);
try {
if (token == null) {
// This is the legacy reset password for DPM. Here we want to be able to override
// the old device password without necessarily knowing it.
- if (!TextUtils.isEmpty(password)) {
- mLockPatternUtils.saveLockPassword(password.getBytes(), null, quality,
- userHandle, /*allowUntrustedChange */true);
- } else {
- mLockPatternUtils.clearLock(null, userHandle,
- /*allowUntrustedChange */ true);
- }
+ mLockPatternUtils.setLockCredential(
+ newCredential,
+ LockscreenCredential.createNone(),
+ userHandle, /*allowUntrustedChange */true);
result = true;
} else {
- if (!TextUtils.isEmpty(password)) {
- result = mLockPatternUtils.setLockCredentialWithToken(password.getBytes(),
- LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
- quality, tokenHandle, token, userHandle);
- } else {
- result = mLockPatternUtils.setLockCredentialWithToken(null,
- LockPatternUtils.CREDENTIAL_TYPE_NONE,
- quality, tokenHandle, token, userHandle);
- }
+ result = mLockPatternUtils.setLockCredentialWithToken(newCredential, tokenHandle,
+ token, userHandle);
}
boolean requireEntry = (flags & DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY) != 0;
if (requireEntry) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportUtils.java b/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportUtils.java
index 0838fbc..7cfbcc8 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportUtils.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportUtils.java
@@ -51,7 +51,7 @@
static final long REMOTE_BUGREPORT_TIMEOUT_MILLIS = 10 * DateUtils.MINUTE_IN_MILLIS;
static final String CTL_STOP = "ctl.stop";
- static final String REMOTE_BUGREPORT_SERVICE = "bugreportremote";
+ static final String REMOTE_BUGREPORT_SERVICE = "bugreportd";
static final String BUGREPORT_MIMETYPE = "application/vnd.android.bugreport";
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 4cf98d3..88859a7 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -921,7 +921,6 @@
false);
boolean disableCameraService = SystemProperties.getBoolean("config.disable_cameraservice",
false);
- boolean disableSlices = SystemProperties.getBoolean("config.disable_slices", false);
boolean enableLeftyService = SystemProperties.getBoolean("config.enable_lefty", false);
boolean isEmulator = SystemProperties.get("ro.kernel.qemu").equals("1");
@@ -1856,7 +1855,7 @@
t.traceEnd();
}
- if (!disableSlices) {
+ if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_SLICES_DISABLED)) {
t.traceBegin("StartSliceManagerService");
mSystemServiceManager.startService(SLICE_MANAGER_SERVICE_CLASS);
t.traceEnd();
diff --git a/services/print/java/com/android/server/print/TEST_MAPPING b/services/print/java/com/android/server/print/TEST_MAPPING
new file mode 100644
index 0000000..4fa8822
--- /dev/null
+++ b/services/print/java/com/android/server/print/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsPrintTestCases",
+ "options": [
+ {
+ "include-annotation": "android.platform.test.annotations.Presubmit"
+ }
+ ]
+ }
+ ]
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
index edf82ee..597d337 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
@@ -41,12 +41,14 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.testing.DexmakerShareClassLoaderRule;
import android.view.Display;
import com.android.server.wm.WindowManagerInternal;
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -65,9 +67,14 @@
"com.android.server.accessibility", "AccessibilityServiceConnectionTest");
static final int SERVICE_ID = 42;
+ // Mock package-private AccessibilityUserState class
+ @Rule
+ public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
+ new DexmakerShareClassLoaderRule();
+
AccessibilityServiceConnection mConnection;
- @Mock AccessibilityManagerService.UserState mMockUserState;
+ @Mock AccessibilityUserState mMockUserState;
@Mock Context mMockContext;
@Mock AccessibilityServiceInfo mMockServiceInfo;
@Mock ResolveInfo mMockResolveInfo;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
new file mode 100644
index 0000000..9180054
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
@@ -0,0 +1,297 @@
+/*
+ * 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.accessibility;
+
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_AUTO;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HIDDEN;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_IGNORE_HARD_KEYBOARD;
+import static android.view.accessibility.AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED;
+import static android.view.accessibility.AccessibilityManager.STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED;
+import static android.view.accessibility.AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.provider.Settings;
+import android.test.mock.MockContentResolver;
+import android.testing.DexmakerShareClassLoaderRule;
+
+import com.android.internal.util.test.FakeSettingsProvider;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/** Tests for AccessibilityUserState */
+public class AccessibilityUserStateTest {
+
+ private static final ComponentName COMPONENT_NAME =
+ new ComponentName("com.android.server.accessibility", "AccessibilityUserStateTest");
+
+ // Values of setting key SHOW_IME_WITH_HARD_KEYBOARD
+ private static final int STATE_HIDE_IME = 0;
+ private static final int STATE_SHOW_IME = 1;
+
+ private static final int USER_ID = 42;
+
+ // Mock package-private class AccessibilityServiceConnection
+ @Rule public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
+ new DexmakerShareClassLoaderRule();
+
+ @Mock private AccessibilityServiceInfo mMockServiceInfo;
+
+ @Mock private AccessibilityServiceConnection mMockConnection;
+
+ @Mock private AccessibilityUserState.ServiceInfoChangeListener mMockListener;
+
+ @Mock private Context mContext;
+
+ private MockContentResolver mMockResolver;
+
+ private AccessibilityUserState mUserState;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ FakeSettingsProvider.clearSettingsProvider();
+ mMockResolver = new MockContentResolver();
+ mMockResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+ when(mContext.getContentResolver()).thenReturn(mMockResolver);
+ when(mMockServiceInfo.getComponentName()).thenReturn(COMPONENT_NAME);
+ when(mMockConnection.getServiceInfo()).thenReturn(mMockServiceInfo);
+
+ mUserState = new AccessibilityUserState(USER_ID, mContext, mMockListener);
+ }
+
+ @After
+ public void tearDown() {
+ FakeSettingsProvider.clearSettingsProvider();
+ }
+
+ @Test
+ public void onSwitchToAnotherUser_userStateClearedNonDefaultValues() {
+ mUserState.getBoundServicesLocked().add(mMockConnection);
+ mUserState.getBindingServicesLocked().add(COMPONENT_NAME);
+ mUserState.setLastSentClientStateLocked(
+ STATE_FLAG_ACCESSIBILITY_ENABLED
+ | STATE_FLAG_TOUCH_EXPLORATION_ENABLED
+ | STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED);
+ mUserState.setNonInteractiveUiTimeoutLocked(30);
+ mUserState.setInteractiveUiTimeoutLocked(30);
+ mUserState.mEnabledServices.add(COMPONENT_NAME);
+ mUserState.mTouchExplorationGrantedServices.add(COMPONENT_NAME);
+ mUserState.setTouchExplorationEnabledLocked(true);
+ mUserState.setDisplayMagnificationEnabledLocked(true);
+ mUserState.setNavBarMagnificationEnabledLocked(true);
+ mUserState.setServiceAssignedToAccessibilityButtonLocked(COMPONENT_NAME);
+ mUserState.setNavBarMagnificationAssignedToAccessibilityButtonLocked(true);
+ mUserState.setAutoclickEnabledLocked(true);
+ mUserState.setUserNonInteractiveUiTimeoutLocked(30);
+ mUserState.setUserInteractiveUiTimeoutLocked(30);
+
+ mUserState.onSwitchToAnotherUserLocked();
+
+ verify(mMockConnection).unbindLocked();
+ assertTrue(mUserState.getBoundServicesLocked().isEmpty());
+ assertTrue(mUserState.getBindingServicesLocked().isEmpty());
+ assertEquals(-1, mUserState.getLastSentClientStateLocked());
+ assertEquals(0, mUserState.getNonInteractiveUiTimeoutLocked());
+ assertEquals(0, mUserState.getInteractiveUiTimeoutLocked());
+ assertTrue(mUserState.mEnabledServices.isEmpty());
+ assertTrue(mUserState.mTouchExplorationGrantedServices.isEmpty());
+ assertFalse(mUserState.isTouchExplorationEnabledLocked());
+ assertFalse(mUserState.isDisplayMagnificationEnabledLocked());
+ assertFalse(mUserState.isNavBarMagnificationEnabledLocked());
+ assertNull(mUserState.getServiceAssignedToAccessibilityButtonLocked());
+ assertFalse(mUserState.isNavBarMagnificationAssignedToAccessibilityButtonLocked());
+ assertFalse(mUserState.isAutoclickEnabledLocked());
+ assertEquals(0, mUserState.getUserNonInteractiveUiTimeoutLocked());
+ assertEquals(0, mUserState.getUserInteractiveUiTimeoutLocked());
+ }
+
+ @Test
+ public void addService_connectionAlreadyAdded_notAddAgain() {
+ mUserState.getBoundServicesLocked().add(mMockConnection);
+
+ mUserState.addServiceLocked(mMockConnection);
+
+ verify(mMockConnection, never()).onAdded();
+ }
+
+ @Test
+ public void addService_connectionNotYetAddedToBoundService_addAndNotifyServices() {
+ when(mMockConnection.getComponentName()).thenReturn(COMPONENT_NAME);
+
+ mUserState.addServiceLocked(mMockConnection);
+
+ verify(mMockConnection).onAdded();
+ assertTrue(mUserState.getBoundServicesLocked().contains(mMockConnection));
+ assertEquals(mMockConnection, mUserState.mComponentNameToServiceMap.get(COMPONENT_NAME));
+ verify(mMockListener).onServiceInfoChangedLocked(eq(mUserState));
+ }
+
+ @Test
+ public void reconcileSoftKeyboardMode_whenStateNotMatchSettings_setBothDefault() {
+ // When soft kb show mode is hidden in settings and is auto in state.
+ putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+ SHOW_MODE_HIDDEN, USER_ID);
+
+ mUserState.reconcileSoftKeyboardModeWithSettingsLocked();
+
+ assertEquals(SHOW_MODE_AUTO, mUserState.getSoftKeyboardShowModeLocked());
+ assertEquals(SHOW_MODE_AUTO, getSecureIntForUser(
+ Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, USER_ID));
+ assertNull(mUserState.getServiceChangingSoftKeyboardModeLocked());
+ }
+
+ @Test
+ public void
+ reconcileSoftKeyboardMode_stateIgnoreHardKb_settingsShowImeHardKb_setAutoOverride() {
+ // When show mode is ignore hard kb without original hard kb value
+ // and show ime with hard kb is hide
+ putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+ SHOW_MODE_IGNORE_HARD_KEYBOARD, USER_ID);
+ mUserState.setSoftKeyboardModeLocked(SHOW_MODE_IGNORE_HARD_KEYBOARD, COMPONENT_NAME);
+ putSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD,
+ STATE_HIDE_IME, USER_ID);
+
+ mUserState.reconcileSoftKeyboardModeWithSettingsLocked();
+
+ assertEquals(SHOW_MODE_AUTO | SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN,
+ getSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, USER_ID));
+ assertNull(mUserState.getServiceChangingSoftKeyboardModeLocked());
+ }
+
+ @Test
+ public void removeService_serviceChangingSoftKeyboardMode_removeAndSetSoftKbModeAuto() {
+ mUserState.setServiceChangingSoftKeyboardModeLocked(COMPONENT_NAME);
+ mUserState.mComponentNameToServiceMap.put(COMPONENT_NAME, mMockConnection);
+ mUserState.setSoftKeyboardModeLocked(SHOW_MODE_HIDDEN, COMPONENT_NAME);
+
+ mUserState.removeServiceLocked(mMockConnection);
+
+ assertFalse(mUserState.getBoundServicesLocked().contains(mMockConnection));
+ verify(mMockConnection).onRemoved();
+ assertEquals(SHOW_MODE_AUTO, mUserState.getSoftKeyboardShowModeLocked());
+ assertNull(mUserState.mComponentNameToServiceMap.get(COMPONENT_NAME));
+ verify(mMockListener).onServiceInfoChangedLocked(eq(mUserState));
+ }
+
+ @Test
+ public void serviceDisconnected_removeServiceAndAddToBinding() {
+ when(mMockConnection.getComponentName()).thenReturn(COMPONENT_NAME);
+ mUserState.addServiceLocked(mMockConnection);
+
+ mUserState.serviceDisconnectedLocked(mMockConnection);
+
+ assertFalse(mUserState.getBoundServicesLocked().contains(mMockConnection));
+ assertTrue(mUserState.getBindingServicesLocked().contains(COMPONENT_NAME));
+ }
+
+ @Test
+ public void setSoftKeyboardMode_withInvalidShowMode_shouldKeepDefaultAuto() {
+ final int invalidShowMode = SHOW_MODE_HIDDEN | SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE;
+
+ assertFalse(mUserState.setSoftKeyboardModeLocked(invalidShowMode, null));
+
+ assertEquals(SHOW_MODE_AUTO, mUserState.getSoftKeyboardShowModeLocked());
+ }
+
+ @Test
+ public void setSoftKeyboardMode_newModeSameWithCurrentState_returnTrue() {
+ when(mMockConnection.getComponentName()).thenReturn(COMPONENT_NAME);
+ mUserState.addServiceLocked(mMockConnection);
+
+ assertTrue(mUserState.setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null));
+ }
+
+ @Test
+ public void setSoftKeyboardMode_withIgnoreHardKb_whenHardKbOverridden_returnFalseAdNoChange() {
+ putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+ SHOW_MODE_AUTO | SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN, USER_ID);
+
+ assertFalse(mUserState.setSoftKeyboardModeLocked(SHOW_MODE_IGNORE_HARD_KEYBOARD, null));
+
+ assertEquals(SHOW_MODE_AUTO, mUserState.getSoftKeyboardShowModeLocked());
+ }
+
+ @Test
+ public void
+ setSoftKeyboardMode_withIgnoreHardKb_whenShowImeWithHardKb_setOriginalHardKbValue() {
+ putSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, STATE_SHOW_IME, USER_ID);
+
+ assertTrue(mUserState.setSoftKeyboardModeLocked(SHOW_MODE_IGNORE_HARD_KEYBOARD, null));
+
+ assertEquals(SHOW_MODE_IGNORE_HARD_KEYBOARD | SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE,
+ getSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, USER_ID));
+ }
+
+ @Test
+ public void setSoftKeyboardMode_whenCurrentIgnoreHardKb_shouldSetShowImeWithHardKbValue() {
+ mUserState.setSoftKeyboardModeLocked(SHOW_MODE_IGNORE_HARD_KEYBOARD, COMPONENT_NAME);
+ putSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, STATE_HIDE_IME, USER_ID);
+ putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+ SHOW_MODE_IGNORE_HARD_KEYBOARD | SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE, USER_ID);
+
+ assertTrue(mUserState.setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null));
+
+ assertEquals(STATE_SHOW_IME, getSecureIntForUser(
+ Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, USER_ID));
+ }
+
+ @Test
+ public void setSoftKeyboardMode_withRequester_shouldUpdateInternalStateAndSettingsAsIs() {
+ assertTrue(mUserState.setSoftKeyboardModeLocked(SHOW_MODE_HIDDEN, COMPONENT_NAME));
+
+ assertEquals(SHOW_MODE_HIDDEN, mUserState.getSoftKeyboardShowModeLocked());
+ assertEquals(SHOW_MODE_HIDDEN, getSecureIntForUser(
+ Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, USER_ID));
+ assertEquals(COMPONENT_NAME, mUserState.getServiceChangingSoftKeyboardModeLocked());
+ }
+
+ @Test
+ public void setSoftKeyboardMode_shouldNotifyBoundService() {
+ mUserState.addServiceLocked(mMockConnection);
+
+ assertTrue(mUserState.setSoftKeyboardModeLocked(SHOW_MODE_HIDDEN, COMPONENT_NAME));
+
+ verify(mMockConnection).notifySoftKeyboardShowModeChangedLocked(eq(SHOW_MODE_HIDDEN));
+ }
+
+ private int getSecureIntForUser(String key, int userId) {
+ return Settings.Secure.getIntForUser(mMockResolver, key, -1, userId);
+ }
+
+ private void putSecureIntForUser(String key, int value, int userId) {
+ Settings.Secure.putIntForUser(mMockResolver, key, value, userId);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
index deb6f71..8da927d 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
@@ -56,7 +56,6 @@
MessageCapturingHandler mMessageCapturingHandler;
- @Mock AccessibilityManagerService.UserState mMockUserState;
@Mock Context mMockContext;
@Mock AccessibilityServiceInfo mMockServiceInfo;
@Mock ResolveInfo mMockResolveInfo;
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 9a1fd9c..c3ef832 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -93,7 +93,7 @@
import androidx.test.filters.SmallTest;
import com.android.internal.R;
-import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockscreenCredential;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.devicepolicy.DevicePolicyManagerService.RestrictionsListener;
@@ -4266,9 +4266,9 @@
assertTrue(dpm.isResetPasswordTokenActive(admin1));
// test reset password with token
- when(getServices().lockPatternUtils.setLockCredentialWithToken(eq(password.getBytes()),
- eq(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD),
- eq(DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC), eq(handle), eq(token),
+ when(getServices().lockPatternUtils.setLockCredentialWithToken(
+ eq(LockscreenCredential.createPassword(password)),
+ eq(handle), eq(token),
eq(UserHandle.USER_SYSTEM)))
.thenReturn(true);
assertTrue(dpm.resetPasswordWithToken(admin1, password, token, 0));
diff --git a/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java b/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java
index 1fcd0ef..baf1ed0 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java
@@ -127,4 +127,57 @@
assertEquals(rule1, matchedRule);
}
+
+ @Test
+ public void testMatchRules_validForm() {
+ OpenFormula openFormula = new OpenFormula(OpenFormula.Connector.AND, Arrays.asList(
+ new AtomicFormula(AtomicFormula.Key.PACKAGE_NAME, AtomicFormula.Operator.EQ,
+ PACKAGE_NAME_1),
+ new AtomicFormula(AtomicFormula.Key.APP_CERTIFICATE,
+ AtomicFormula.Operator.EQ,
+ APP_CERTIFICATE)));
+ Rule rule = new Rule(
+ openFormula, Rule.Effect.DENY);
+
+ Rule matchedRule = RuleEvaluator.evaluateRules(Collections.singletonList(rule),
+ APP_INSTALL_METADATA);
+
+ assertEquals(rule, matchedRule);
+ }
+
+ @Test
+ public void testMatchRules_ruleNotInDNF() {
+ OpenFormula openFormula = new OpenFormula(OpenFormula.Connector.OR, Arrays.asList(
+ new AtomicFormula(AtomicFormula.Key.PACKAGE_NAME, AtomicFormula.Operator.EQ,
+ PACKAGE_NAME_1),
+ new AtomicFormula(AtomicFormula.Key.APP_CERTIFICATE,
+ AtomicFormula.Operator.EQ,
+ APP_CERTIFICATE)));
+ Rule rule = new Rule(
+ openFormula, Rule.Effect.DENY);
+
+ Rule matchedRule = RuleEvaluator.evaluateRules(Collections.singletonList(rule),
+ APP_INSTALL_METADATA);
+
+ assertEquals(Rule.EMPTY, matchedRule);
+ }
+
+ @Test
+ public void testMatchRules_openFormulaWithNot() {
+ OpenFormula openSubFormula = new OpenFormula(OpenFormula.Connector.AND, Arrays.asList(
+ new AtomicFormula(AtomicFormula.Key.PACKAGE_NAME, AtomicFormula.Operator.EQ,
+ PACKAGE_NAME_2),
+ new AtomicFormula(AtomicFormula.Key.APP_CERTIFICATE,
+ AtomicFormula.Operator.EQ,
+ APP_CERTIFICATE)));
+ OpenFormula openFormula = new OpenFormula(OpenFormula.Connector.NOT,
+ Collections.singletonList(openSubFormula));
+ Rule rule = new Rule(
+ openFormula, Rule.Effect.DENY);
+
+ Rule matchedRule = RuleEvaluator.evaluateRules(Collections.singletonList(rule),
+ APP_INSTALL_METADATA);
+
+ assertEquals(Rule.EMPTY, matchedRule);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
index f9ac022..537287d 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
@@ -35,7 +35,9 @@
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.hardware.authsecret.V1_0.IAuthSecret;
+import android.hardware.face.Face;
import android.hardware.face.FaceManager;
+import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintManager;
import android.os.FileUtils;
import android.os.IProgressListener;
@@ -249,11 +251,32 @@
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)).thenReturn(true);
when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
when(mFingerprintManager.hasEnrolledFingerprints(userId)).thenReturn(true);
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ Fingerprint fp = (Fingerprint) invocation.getArguments()[0];
+ FingerprintManager.RemovalCallback callback =
+ (FingerprintManager.RemovalCallback) invocation.getArguments()[2];
+ callback.onRemovalSucceeded(fp, 0);
+ return null;
+ }
+ }).when(mFingerprintManager).remove(any(), eq(userId), any());
+
// Hardware must be detected and templates must be enrolled
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(true);
when(mFaceManager.isHardwareDetected()).thenReturn(true);
when(mFaceManager.hasEnrolledTemplates(userId)).thenReturn(true);
+ doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ Face face = (Face) invocation.getArguments()[0];
+ FaceManager.RemovalCallback callback =
+ (FaceManager.RemovalCallback) invocation.getArguments()[2];
+ callback.onRemovalSucceeded(face, 0);
+ return null;
+ }
+ }).when(mFaceManager).remove(any(), eq(userId), any());
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java
index c00d33b..b60111e 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java
@@ -19,10 +19,9 @@
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
-import static com.android.internal.widget.LockPatternUtils.stringToPattern;
-
import static junit.framework.Assert.assertEquals;
+import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.never;
@@ -48,6 +47,8 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockPatternView;
+import com.android.internal.widget.LockscreenCredential;
import org.junit.Before;
import org.junit.Test;
@@ -55,6 +56,8 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.List;
+
/**
* Test class for {@link LockSettingsShellCommand}.
*
@@ -87,24 +90,30 @@
public void testWrongPassword() throws Exception {
when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(false);
when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(true);
- when(mLockPatternUtils.checkPassword("1234".getBytes(), mUserId)).thenReturn(false);
+ when(mLockPatternUtils.checkCredential(
+ LockscreenCredential.createPassword("1234"), mUserId, null)).thenReturn(false);
assertEquals(-1, mCommand.exec(mBinder, in, out, err,
new String[] { "set-pin", "--old", "1234" },
mShellCallback, mResultReceiver));
- verify(mLockPatternUtils, never()).saveLockPassword(any(byte[].class), any(byte[].class),
- anyInt(), anyInt());
+ verify(mLockPatternUtils, never()).setLockCredential(any(), any(),
+ anyInt(), anyBoolean());
}
@Test
public void testChangePin() throws Exception {
when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(false);
when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(true);
- when(mLockPatternUtils.checkPassword("1234".getBytes(), mUserId)).thenReturn(true);
+ when(mLockPatternUtils.getKeyguardStoredPasswordQuality(mUserId)).thenReturn(
+ PASSWORD_QUALITY_NUMERIC);
+ when(mLockPatternUtils.checkCredential(
+ LockscreenCredential.createPin("1234"), mUserId, null)).thenReturn(true);
assertEquals(0, mCommand.exec(new Binder(), in, out, err,
new String[] { "set-pin", "--old", "1234", "4321" },
mShellCallback, mResultReceiver));
- verify(mLockPatternUtils).saveLockPassword("4321".getBytes(), "1234".getBytes(),
- PASSWORD_QUALITY_NUMERIC, mUserId);
+ verify(mLockPatternUtils).setLockCredential(
+ LockscreenCredential.createPin("4321"),
+ LockscreenCredential.createPin("1234"),
+ mUserId);
}
@Test
@@ -121,12 +130,17 @@
public void testChangePassword() throws Exception {
when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(false);
when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(true);
- when(mLockPatternUtils.checkPassword("1234".getBytes(), mUserId)).thenReturn(true);
+ when(mLockPatternUtils.getKeyguardStoredPasswordQuality(mUserId)).thenReturn(
+ PASSWORD_QUALITY_ALPHABETIC);
+ when(mLockPatternUtils.checkCredential(
+ LockscreenCredential.createPassword("1234"), mUserId, null)).thenReturn(true);
assertEquals(0, mCommand.exec(new Binder(), in, out, err,
new String[] { "set-password", "--old", "1234", "4321" },
mShellCallback, mResultReceiver));
- verify(mLockPatternUtils).saveLockPassword("4321".getBytes(), "1234".getBytes(),
- PASSWORD_QUALITY_ALPHABETIC, mUserId);
+ verify(mLockPatternUtils).setLockCredential(
+ LockscreenCredential.createPassword("4321"),
+ LockscreenCredential.createPassword("1234"),
+ mUserId);
}
@Test
@@ -143,11 +157,15 @@
public void testChangePattern() throws Exception {
when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(true);
when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(false);
- when(mLockPatternUtils.checkPattern(stringToPattern("1234"), mUserId)).thenReturn(true);
+ when(mLockPatternUtils.checkCredential(
+ LockscreenCredential.createPattern(stringToPattern("1234")),
+ mUserId, null)).thenReturn(true);
assertEquals(0, mCommand.exec(new Binder(), in, out, err,
new String[] { "set-pattern", "--old", "1234", "4321" },
mShellCallback, mResultReceiver));
- verify(mLockPatternUtils).saveLockPattern(stringToPattern("4321"), "1234".getBytes(),
+ verify(mLockPatternUtils).setLockCredential(
+ LockscreenCredential.createPattern(stringToPattern("4321")),
+ LockscreenCredential.createPattern(stringToPattern("1234")),
mUserId);
}
@@ -165,10 +183,19 @@
public void testClear() throws Exception {
when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(true);
when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(false);
- when(mLockPatternUtils.checkPattern(stringToPattern("1234"), mUserId)).thenReturn(true);
+ when(mLockPatternUtils.checkCredential(
+ LockscreenCredential.createPattern(stringToPattern("1234")),
+ mUserId, null)).thenReturn(true);
assertEquals(0, mCommand.exec(new Binder(), in, out, err,
new String[] { "clear", "--old", "1234" },
mShellCallback, mResultReceiver));
- verify(mLockPatternUtils).clearLock("1234".getBytes(), mUserId);
+ verify(mLockPatternUtils).setLockCredential(
+ LockscreenCredential.createNone(),
+ LockscreenCredential.createPattern(stringToPattern("1234")),
+ mUserId);
+ }
+
+ private List<LockPatternView.Cell> stringToPattern(String str) {
+ return LockPatternUtils.byteArrayToPattern(str.getBytes());
}
}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java
index 2a169b7..cb51897 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java
@@ -434,35 +434,6 @@
assertEquals(2, PersistentData.TYPE_SP_WEAVER);
}
- public void testCredentialHash_serializeUnserialize() {
- byte[] serialized = CredentialHash.create(
- PAYLOAD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD).toBytes();
- CredentialHash deserialized = CredentialHash.fromBytes(serialized);
-
- assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, deserialized.type);
- assertArrayEquals(PAYLOAD, deserialized.hash);
- }
-
- public void testCredentialHash_unserialize_versionGatekeeper() {
- // This test ensures that we can read serialized VERSION_GATEKEEPER CredentialHashes
- // even if we change the wire format in the future.
- byte[] serialized = new byte[] {
- 1, /* VERSION_GATEKEEPER */
- 2, /* CREDENTIAL_TYPE_PASSWORD */
- 0, 0, 0, 5, /* hash length */
- 1, 2, -1, -2, 33, /* hash */
- };
- CredentialHash deserialized = CredentialHash.fromBytes(serialized);
-
- assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, deserialized.type);
- assertArrayEquals(PAYLOAD, deserialized.hash);
-
- // Make sure the constants we use on the wire do not change.
- assertEquals(-1, LockPatternUtils.CREDENTIAL_TYPE_NONE);
- assertEquals(1, LockPatternUtils.CREDENTIAL_TYPE_PATTERN);
- assertEquals(2, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD);
- }
-
private static void assertArrayEquals(byte[] expected, byte[] actual) {
if (!Arrays.equals(expected, actual)) {
fail("expected:<" + Arrays.toString(expected) +
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
index 0776589..42ca42a 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
@@ -39,6 +39,7 @@
import androidx.test.filters.SmallTest;
import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockscreenCredential;
import com.android.internal.widget.VerifyCredentialResponse;
import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationResult;
import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationToken;
@@ -364,7 +365,7 @@
// Verify DPM gets notified about new device lock
flushHandlerTasks();
final PasswordMetrics metric = PasswordMetrics.computeForCredential(
- LockPatternUtils.CREDENTIAL_TYPE_PATTERN, pattern);
+ LockscreenCredential.createPattern(LockPatternUtils.byteArrayToPattern(pattern)));
assertEquals(metric, mService.getUserPasswordMetrics(PRIMARY_USER_ID));
verify(mDevicePolicyManager).reportPasswordChanged(PRIMARY_USER_ID);
@@ -512,7 +513,7 @@
assertFalse(mService.havePattern(PRIMARY_USER_ID));
}
- public void testgetHashFactorPrimaryUser() throws RemoteException {
+ public void testGetHashFactorPrimaryUser() throws RemoteException {
final byte[] password = "password".getBytes();
mService.setLockCredential(password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, false);
@@ -527,7 +528,7 @@
assertArrayEquals(hashFactor, newHashFactor);
}
- public void testgetHashFactorManagedProfileUnifiedChallenge() throws RemoteException {
+ public void testGetHashFactorManagedProfileUnifiedChallenge() throws RemoteException {
final byte[] pattern = "1236".getBytes();
mService.setLockCredential(pattern, LockPatternUtils.CREDENTIAL_TYPE_PATTERN,
null, PASSWORD_QUALITY_SOMETHING, PRIMARY_USER_ID, false);
@@ -535,7 +536,7 @@
assertNotNull(mService.getHashFactor(null, MANAGED_PROFILE_USER_ID));
}
- public void testgetHashFactorManagedProfileSeparateChallenge() throws RemoteException {
+ public void testGetHashFactorManagedProfileSeparateChallenge() throws RemoteException {
final byte[] primaryPassword = "primary".getBytes();
final byte[] profilePassword = "profile".getBytes();
mService.setLockCredential(primaryPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index ba12b73..8a48904 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -113,6 +113,7 @@
import android.net.NetworkTemplate;
import android.net.StringNetworkSpecifier;
import android.os.Binder;
+import android.os.Handler;
import android.os.INetworkManagementService;
import android.os.PersistableBundle;
import android.os.PowerManagerInternal;
@@ -1117,7 +1118,7 @@
// Define simple data plan
final SubscriptionPlan plan = buildMonthlyDataPlan(
ZonedDateTime.parse("2015-11-01T00:00:00.00Z"), DataUnit.MEGABYTES.toBytes(1800));
- mService.setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[] { plan },
+ setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[] { plan },
mServiceContext.getOpPackageName());
// We're 20% through the month (6 days)
@@ -1241,7 +1242,7 @@
// Define simple data plan which gives us effectively 60MB/day
final SubscriptionPlan plan = buildMonthlyDataPlan(
ZonedDateTime.parse("2015-11-01T00:00:00.00Z"), DataUnit.MEGABYTES.toBytes(1800));
- mService.setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[] { plan },
+ setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[] { plan },
mServiceContext.getOpPackageName());
// We're 20% through the month (6 days)
@@ -1457,6 +1458,8 @@
when(mConnManager.getAllNetworkState()).thenReturn(new NetworkState[0]);
when(mSubscriptionManager.getActiveSubscriptionIdList()).thenReturn(new int[]{FAKE_SUB_ID});
when(mTelephonyManager.getSubscriberId(FAKE_SUB_ID)).thenReturn(FAKE_SUBSCRIBER_ID);
+ when(mTelephonyManager.createForSubscriptionId(FAKE_SUB_ID))
+ .thenReturn(mock(TelephonyManager.class));
PersistableBundle bundle = CarrierConfigManager.getDefaultConfig();
when(mCarrierConfigManager.getConfigForSubId(FAKE_SUB_ID)).thenReturn(bundle);
setNetworkPolicies(buildDefaultFakeMobilePolicy());
@@ -1468,6 +1471,8 @@
when(mConnManager.getAllNetworkState()).thenReturn(new NetworkState[0]);
when(mSubscriptionManager.getActiveSubscriptionIdList()).thenReturn(new int[]{FAKE_SUB_ID});
when(mTelephonyManager.getSubscriberId(FAKE_SUB_ID)).thenReturn(FAKE_SUBSCRIBER_ID);
+ when(mTelephonyManager.createForSubscriptionId(FAKE_SUB_ID))
+ .thenReturn(mock(TelephonyManager.class));
when(mCarrierConfigManager.getConfigForSubId(FAKE_SUB_ID)).thenReturn(null);
setNetworkPolicies(buildDefaultFakeMobilePolicy());
// smoke test to make sure no errors are raised
@@ -1653,7 +1658,7 @@
final SubscriptionPlan plan = buildMonthlyDataPlan(
ZonedDateTime.parse("2015-11-01T00:00:00.00Z"),
DataUnit.MEGABYTES.toBytes(1800));
- mService.setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[]{plan},
+ setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[]{plan},
mServiceContext.getOpPackageName());
reset(mTelephonyManager, mNetworkManager, mNotifManager);
@@ -1674,7 +1679,7 @@
final SubscriptionPlan plan = buildMonthlyDataPlan(
ZonedDateTime.parse("2015-11-01T00:00:00.00Z"),
DataUnit.MEGABYTES.toBytes(100));
- mService.setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[]{plan},
+ setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[]{plan},
mServiceContext.getOpPackageName());
reset(mTelephonyManager, mNetworkManager, mNotifManager);
@@ -1690,7 +1695,7 @@
{
final SubscriptionPlan plan = buildMonthlyDataPlan(
ZonedDateTime.parse("2015-11-01T00:00:00.00Z"), BYTES_UNLIMITED);
- mService.setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[]{plan},
+ setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[]{plan},
mServiceContext.getOpPackageName());
reset(mTelephonyManager, mNetworkManager, mNotifManager);
@@ -1707,7 +1712,7 @@
{
final SubscriptionPlan plan = buildMonthlyDataPlan(
ZonedDateTime.parse("2015-11-01T00:00:00.00Z"), BYTES_UNLIMITED);
- mService.setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[]{plan},
+ setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[]{plan},
mServiceContext.getOpPackageName());
reset(mTelephonyManager, mNetworkManager, mNotifManager);
@@ -1923,6 +1928,8 @@
when(mSubscriptionManager.getActiveSubscriptionIdList()).thenReturn(
new int[] { TEST_SUB_ID });
when(mTelephonyManager.getSubscriberId(TEST_SUB_ID)).thenReturn(TEST_IMSI);
+ when(mTelephonyManager.createForSubscriptionId(TEST_SUB_ID))
+ .thenReturn(mock(TelephonyManager.class));
doNothing().when(mTelephonyManager).setPolicyDataEnabled(anyBoolean(), anyInt());
expectNetworkState(false /* roaming */);
}
@@ -2049,6 +2056,23 @@
private FutureIntent mRestrictBackgroundChanged;
+ private void postMsgAndWaitForCompletion() throws InterruptedException {
+ final Handler handler = mService.getHandlerForTesting();
+ final CountDownLatch latch = new CountDownLatch(1);
+ mService.getHandlerForTesting().post(latch::countDown);
+ if (!latch.await(5, TimeUnit.SECONDS)) {
+ fail("Timed out waiting for the test msg to be handled");
+ }
+ }
+
+ private void setSubscriptionPlans(int subId, SubscriptionPlan[] plans, String callingPackage)
+ throws InterruptedException {
+ mService.setSubscriptionPlans(subId, plans, callingPackage);
+ // setSubscriptionPlans() triggers async events, wait for those to be completed before
+ // moving forward as they could interfere with the tests later.
+ postMsgAndWaitForCompletion();
+ }
+
private void setRestrictBackground(boolean flag) throws Exception {
mService.setRestrictBackground(flag);
// Sanity check.
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
index 8c3373f..3ae5674 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -22,6 +22,7 @@
import static android.app.NotificationManager.IMPORTANCE_LOW;
import static android.app.NotificationManager.IMPORTANCE_MIN;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
+import static android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNull;
@@ -105,6 +106,7 @@
private int mUid = 1000;
private int mPid = 2000;
private android.os.UserHandle mUser = UserHandle.of(ActivityManager.getCurrentUser());
+ private NotificationChannel mChannel;
private VibrateRepeatMatcher mVibrateOnceMatcher = new VibrateRepeatMatcher(-1);
private VibrateRepeatMatcher mVibrateLoopMatcher = new VibrateRepeatMatcher(0);
@@ -158,6 +160,8 @@
mService.mScreenOn = false;
mService.mInCallStateOffHook = false;
mService.mNotificationPulseEnabled = true;
+
+ mChannel = new NotificationChannel("test", "test", IMPORTANCE_HIGH);
}
//
@@ -174,13 +178,18 @@
true /* noisy */, false /* buzzy*/, false /* lights */);
}
+ private NotificationRecord getBeepyOtherNotification() {
+ return getNotificationRecord(mOtherId, false /* insistent */, false /* once */,
+ true /* noisy */, false /* buzzy*/, false /* lights */);
+ }
+
private NotificationRecord getBeepyOnceNotification() {
return getNotificationRecord(mId, false /* insistent */, true /* once */,
true /* noisy */, false /* buzzy*/, false /* lights */);
}
private NotificationRecord getQuietNotification() {
- return getNotificationRecord(mId, false /* insistent */, false /* once */,
+ return getNotificationRecord(mId, false /* insistent */, true /* once */,
false /* noisy */, false /* buzzy*/, false /* lights */);
}
@@ -214,6 +223,11 @@
false /* noisy */, true /* buzzy*/, false /* lights */);
}
+ private NotificationRecord getBuzzyOtherNotification() {
+ return getNotificationRecord(mOtherId, false /* insistent */, false /* once */,
+ false /* noisy */, true /* buzzy*/, false /* lights */);
+ }
+
private NotificationRecord getBuzzyOnceNotification() {
return getNotificationRecord(mId, false /* insistent */, true /* once */,
false /* noisy */, true /* buzzy*/, false /* lights */);
@@ -239,22 +253,34 @@
false /* noisy */, false /* buzzy*/, true /* lights */);
}
- private NotificationRecord getCallRecord(int id, boolean insistent) {
- return getNotificationRecord(id, false, false /* once */, true /* noisy */,
- false /* buzzy */, false /* lights */, false /* default vib */,
- false /* default sound */, false /* default lights */, "",
- Notification.GROUP_ALERT_ALL, false);
+ private NotificationRecord getCallRecord(int id, NotificationChannel channel, boolean looping) {
+ final Builder builder = new Builder(getContext())
+ .setContentTitle("foo")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon)
+ .setPriority(Notification.PRIORITY_HIGH);
+ Notification n = builder.build();
+ if (looping) {
+ n.flags |= Notification.FLAG_INSISTENT;
+ }
+ StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, id, mTag, mUid,
+ mPid, n, mUser, null, System.currentTimeMillis());
+ NotificationRecord r = new NotificationRecord(getContext(), sbn, channel);
+ mService.addNotification(r);
+
+ return r;
}
private NotificationRecord getNotificationRecord(int id, boolean insistent, boolean once,
boolean noisy, boolean buzzy, boolean lights) {
- return getNotificationRecord(id, insistent, once, noisy, buzzy, lights, true, true, true,
- null, Notification.GROUP_ALERT_ALL, false);
+ return getNotificationRecord(id, insistent, once, noisy, buzzy, lights, buzzy, noisy,
+ lights, null, Notification.GROUP_ALERT_ALL, false);
}
- private NotificationRecord getLeanbackNotificationRecord(int id, boolean insistent, boolean once,
+ private NotificationRecord getLeanbackNotificationRecord(int id, boolean insistent,
+ boolean once,
boolean noisy, boolean buzzy, boolean lights) {
- return getNotificationRecord(id, insistent, once, noisy, buzzy, lights, true, true, true,
+ return getNotificationRecord(id, insistent, once, noisy, buzzy, lights, true, true,
+ true,
null, Notification.GROUP_ALERT_ALL, true);
}
@@ -265,16 +291,16 @@
private NotificationRecord getLightsNotificationRecord(String groupKey,
int groupAlertBehavior) {
- return getNotificationRecord(mId, false, false, false, false, true /*lights*/, true, true,
- true, groupKey, groupAlertBehavior, false);
+ return getNotificationRecord(mId, false, false, false, false, true /*lights*/, true,
+ true, true, groupKey, groupAlertBehavior, false);
}
- private NotificationRecord getNotificationRecord(int id, boolean insistent, boolean once,
+ private NotificationRecord getNotificationRecord(int id,
+ boolean insistent, boolean once,
boolean noisy, boolean buzzy, boolean lights, boolean defaultVibration,
boolean defaultSound, boolean defaultLights, String groupKey, int groupAlertBehavior,
boolean isLeanback) {
- NotificationChannel channel =
- new NotificationChannel("test", "test", IMPORTANCE_HIGH);
+
final Builder builder = new Builder(getContext())
.setContentTitle("foo")
.setSmallIcon(android.R.drawable.sym_def_app_icon)
@@ -285,31 +311,37 @@
if (noisy) {
if (defaultSound) {
defaults |= Notification.DEFAULT_SOUND;
- channel.setSound(Settings.System.DEFAULT_NOTIFICATION_URI,
+ mChannel.setSound(Settings.System.DEFAULT_NOTIFICATION_URI,
Notification.AUDIO_ATTRIBUTES_DEFAULT);
} else {
builder.setSound(CUSTOM_SOUND);
- channel.setSound(CUSTOM_SOUND, CUSTOM_ATTRIBUTES);
+ mChannel.setSound(CUSTOM_SOUND, CUSTOM_ATTRIBUTES);
}
} else {
- channel.setSound(null, null);
+ mChannel.setSound(null, null);
}
if (buzzy) {
if (defaultVibration) {
defaults |= Notification.DEFAULT_VIBRATE;
} else {
builder.setVibrate(CUSTOM_VIBRATION);
- channel.setVibrationPattern(CUSTOM_VIBRATION);
+ mChannel.setVibrationPattern(CUSTOM_VIBRATION);
}
- channel.enableVibration(true);
+ mChannel.enableVibration(true);
+ } else {
+ mChannel.setVibrationPattern(null);
+ mChannel.enableVibration(false);
}
+
if (lights) {
if (defaultLights) {
defaults |= Notification.DEFAULT_LIGHTS;
} else {
builder.setLights(CUSTOM_LIGHT_COLOR, CUSTOM_LIGHT_ON, CUSTOM_LIGHT_OFF);
}
- channel.enableLights(true);
+ mChannel.enableLights(true);
+ } else {
+ mChannel.enableLights(false);
}
builder.setDefaults(defaults);
@@ -329,7 +361,7 @@
StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, id, mTag, mUid,
mPid, n, mUser, null, System.currentTimeMillis());
- NotificationRecord r = new NotificationRecord(context, sbn, channel);
+ NotificationRecord r = new NotificationRecord(context, sbn, mChannel);
mService.addNotification(r);
return r;
}
@@ -339,18 +371,19 @@
//
private void verifyNeverBeep() throws RemoteException {
- verify(mRingtonePlayer, never()).playAsync((Uri) anyObject(), (UserHandle) anyObject(),
- anyBoolean(), (AudioAttributes) anyObject());
+ verify(mRingtonePlayer, never()).playAsync(any(), any(), anyBoolean(), any());
}
- private void verifyBeep() throws RemoteException {
- verify(mRingtonePlayer, times(1)).playAsync((Uri) anyObject(), (UserHandle) anyObject(),
- eq(true), (AudioAttributes) anyObject());
+ private void verifyBeepUnlooped() throws RemoteException {
+ verify(mRingtonePlayer, times(1)).playAsync(any(), any(), eq(false), any());
}
- private void verifyBeepLooped() throws RemoteException {
- verify(mRingtonePlayer, times(1)).playAsync((Uri) anyObject(), (UserHandle) anyObject(),
- eq(false), (AudioAttributes) anyObject());
+ private void verifyBeepLooped() throws RemoteException {
+ verify(mRingtonePlayer, times(1)).playAsync(any(), any(), eq(true), any());
+ }
+
+ private void verifyBeep(int times) throws RemoteException {
+ verify(mRingtonePlayer, times(times)).playAsync(any(), any(), anyBoolean(), any());
}
private void verifyNeverStopAudio() throws RemoteException {
@@ -362,24 +395,31 @@
}
private void verifyNeverVibrate() {
- verify(mVibrator, never()).vibrate(anyInt(), anyString(), (VibrationEffect) anyObject(),
- anyString(), (AudioAttributes) anyObject());
+ verify(mVibrator, never()).vibrate(anyInt(), anyString(), any(), anyString(), any());
}
private void verifyVibrate() {
verify(mVibrator, times(1)).vibrate(anyInt(), anyString(), argThat(mVibrateOnceMatcher),
- anyString(), (AudioAttributes) anyObject());
+ anyString(), any());
+ }
+
+ private void verifyVibrate(int times) {
+ verify(mVibrator, times(times)).vibrate(anyInt(), anyString(), any(), anyString(), any());
}
private void verifyVibrateLooped() {
verify(mVibrator, times(1)).vibrate(anyInt(), anyString(), argThat(mVibrateLoopMatcher),
- anyString(), (AudioAttributes) anyObject());
+ anyString(), any());
}
private void verifyDelayedVibrateLooped() {
verify(mVibrator, timeout(MAX_VIBRATION_DELAY).times(1)).vibrate(anyInt(), anyString(),
- argThat(mVibrateLoopMatcher), anyString(),
- (AudioAttributes) anyObject());
+ argThat(mVibrateLoopMatcher), anyString(), any());
+ }
+
+ private void verifyDelayedVibrate() {
+ verify(mVibrator, timeout(MAX_VIBRATION_DELAY).times(1)).vibrate(anyInt(), anyString(),
+ argThat(mVibrateOnceMatcher), anyString(), any());
}
private void verifyStopVibrate() {
@@ -398,11 +438,6 @@
verify(mLight, times(1)).setFlashing(anyInt(), anyInt(), anyInt(), anyInt());
}
- private void verifyCustomLights() {
- verify(mLight, times(1)).setFlashing(
- eq(CUSTOM_LIGHT_COLOR), anyInt(), eq(CUSTOM_LIGHT_ON), eq(CUSTOM_LIGHT_OFF));
- }
-
//
// Tests
//
@@ -425,7 +460,7 @@
mService.buzzBeepBlinkLocked(r);
- verifyBeepLooped();
+ verifyBeepUnlooped();
verifyNeverVibrate();
verify(mAccessibilityService, times(1)).sendAccessibilityEvent(any(), anyInt());
assertTrue(r.isInterruptive());
@@ -438,7 +473,7 @@
mService.buzzBeepBlinkLocked(r);
- verifyBeep();
+ verifyBeepLooped();
assertTrue(r.isInterruptive());
assertNotEquals(-1, r.getLastAudiblyAlertedMs());
}
@@ -492,7 +527,7 @@
mService.buzzBeepBlinkLocked(r);
- verifyBeepLooped();
+ verifyBeepUnlooped();
assertTrue(r.isInterruptive());
}
@@ -533,7 +568,7 @@
// update should beep
r.isUpdate = true;
mService.buzzBeepBlinkLocked(r);
- verifyBeepLooped();
+ verifyBeepUnlooped();
verify(mAccessibilityService, times(2)).sendAccessibilityEvent(any(), anyInt());
assertTrue(r.isInterruptive());
assertNotEquals(-1, r.getLastAudiblyAlertedMs());
@@ -723,7 +758,7 @@
mService.buzzBeepBlinkLocked(r);
verifyNeverVibrate();
- verifyBeepLooped();
+ verifyBeepUnlooped();
assertTrue(r.isInterruptive());
assertNotEquals(-1, r.getLastAudiblyAlertedMs());
}
@@ -821,7 +856,7 @@
mService.buzzBeepBlinkLocked(summary);
- verifyBeepLooped();
+ verifyBeepUnlooped();
// summaries are never interruptive for notification counts
assertFalse(summary.isInterruptive());
assertNotEquals(-1, summary.getLastAudiblyAlertedMs());
@@ -833,7 +868,7 @@
mService.buzzBeepBlinkLocked(nonGroup);
- verifyBeepLooped();
+ verifyBeepUnlooped();
assertTrue(nonGroup.isInterruptive());
assertNotEquals(-1, nonGroup.getLastAudiblyAlertedMs());
}
@@ -856,7 +891,7 @@
mService.buzzBeepBlinkLocked(child);
- verifyBeepLooped();
+ verifyBeepUnlooped();
assertTrue(child.isInterruptive());
assertNotEquals(-1, child.getLastAudiblyAlertedMs());
}
@@ -867,7 +902,7 @@
mService.buzzBeepBlinkLocked(nonGroup);
- verifyBeepLooped();
+ verifyBeepUnlooped();
assertTrue(nonGroup.isInterruptive());
assertNotEquals(-1, nonGroup.getLastAudiblyAlertedMs());
}
@@ -878,7 +913,7 @@
mService.buzzBeepBlinkLocked(group);
- verifyBeepLooped();
+ verifyBeepUnlooped();
assertTrue(group.isInterruptive());
assertNotEquals(-1, group.getLastAudiblyAlertedMs());
}
@@ -1293,7 +1328,11 @@
@Test
public void testListenerHintCall() throws Exception {
- NotificationRecord r = getCallRecord(1, true);
+ NotificationChannel ringtoneChannel =
+ new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+ ringtoneChannel.setSound(Settings.System.DEFAULT_RINGTONE_URI,
+ new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
+ NotificationRecord r = getCallRecord(1, ringtoneChannel, true);
mService.setHints(NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS);
@@ -1310,7 +1349,7 @@
mService.buzzBeepBlinkLocked(r);
- verifyBeepLooped();
+ verifyBeepUnlooped();
}
@Test
@@ -1326,7 +1365,11 @@
@Test
public void testListenerHintBoth() throws Exception {
- NotificationRecord r = getCallRecord(1, true);
+ NotificationChannel ringtoneChannel =
+ new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+ ringtoneChannel.setSound(Settings.System.DEFAULT_RINGTONE_URI,
+ new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
+ NotificationRecord r = getCallRecord(1, ringtoneChannel, true);
NotificationRecord s = getBeepyNotification();
mService.setHints(NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS
@@ -1340,7 +1383,11 @@
@Test
public void testListenerHintNotification_callSound() throws Exception {
- NotificationRecord r = getCallRecord(1, true);
+ NotificationChannel ringtoneChannel =
+ new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+ ringtoneChannel.setSound(Settings.System.DEFAULT_RINGTONE_URI,
+ new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
+ NotificationRecord r = getCallRecord(1, ringtoneChannel, true);
mService.setHints(NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS);
@@ -1349,6 +1396,167 @@
verifyBeepLooped();
}
+ @Test
+ public void testCannotInterruptRingtoneInsistentBeep() throws Exception {
+ NotificationChannel ringtoneChannel =
+ new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+ ringtoneChannel.setSound(Settings.System.DEFAULT_RINGTONE_URI,
+ new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
+ NotificationRecord ringtoneNotification = getCallRecord(1, ringtoneChannel, true);
+ mService.addNotification(ringtoneNotification);
+
+ mService.buzzBeepBlinkLocked(ringtoneNotification);
+ verifyBeepLooped();
+
+ NotificationRecord interrupter = getBeepyOtherNotification();
+ assertTrue(mService.shouldMuteNotificationLocked(interrupter));
+ mService.buzzBeepBlinkLocked(interrupter);
+
+ verifyBeep(1);
+
+ assertFalse(interrupter.isInterruptive());
+ assertEquals(-1, interrupter.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testCannotInterruptRingtoneInsistentBuzz() {
+ NotificationChannel ringtoneChannel =
+ new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+ ringtoneChannel.setSound(Uri.EMPTY,
+ new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
+ ringtoneChannel.enableVibration(true);
+ NotificationRecord ringtoneNotification = getCallRecord(1, ringtoneChannel, true);
+ assertFalse(mService.shouldMuteNotificationLocked(ringtoneNotification));
+
+ mService.buzzBeepBlinkLocked(ringtoneNotification);
+ verifyVibrateLooped();
+
+ NotificationRecord interrupter = getBuzzyOtherNotification();
+ assertTrue(mService.shouldMuteNotificationLocked(interrupter));
+ mService.buzzBeepBlinkLocked(interrupter);
+
+ verifyVibrate(1);
+
+ assertFalse(interrupter.isInterruptive());
+ assertEquals(-1, interrupter.getLastAudiblyAlertedMs());
+ }
+
+ @Test
+ public void testCanInterruptRingtoneNonInsistentBeep() throws Exception {
+ NotificationChannel ringtoneChannel =
+ new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+ ringtoneChannel.setSound(Settings.System.DEFAULT_RINGTONE_URI,
+ new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
+ NotificationRecord ringtoneNotification = getCallRecord(1, ringtoneChannel, false);
+
+ mService.buzzBeepBlinkLocked(ringtoneNotification);
+ verifyBeepUnlooped();
+
+ NotificationRecord interrupter = getBeepyOtherNotification();
+ mService.buzzBeepBlinkLocked(interrupter);
+
+ verifyBeep(2);
+
+ assertTrue(interrupter.isInterruptive());
+ }
+
+ @Test
+ public void testCanInterruptRingtoneNonInsistentBuzz() {
+ NotificationChannel ringtoneChannel =
+ new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+ ringtoneChannel.setSound(null,
+ new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
+ ringtoneChannel.enableVibration(true);
+ NotificationRecord ringtoneNotification = getCallRecord(1, ringtoneChannel, false);
+
+ mService.buzzBeepBlinkLocked(ringtoneNotification);
+ verifyVibrate();
+
+ NotificationRecord interrupter = getBuzzyOtherNotification();
+ mService.buzzBeepBlinkLocked(interrupter);
+
+ verifyVibrate(2);
+
+ assertTrue(interrupter.isInterruptive());
+ }
+
+ @Test
+ public void testRingtoneInsistentBeep_doesNotBlockFutureSoundsOnceStopped() throws Exception {
+ NotificationChannel ringtoneChannel =
+ new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+ ringtoneChannel.setSound(Settings.System.DEFAULT_RINGTONE_URI,
+ new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
+ NotificationRecord ringtoneNotification = getCallRecord(1, ringtoneChannel, true);
+
+ mService.buzzBeepBlinkLocked(ringtoneNotification);
+ verifyBeepLooped();
+
+ mService.clearSoundLocked();
+
+ NotificationRecord interrupter = getBeepyOtherNotification();
+ mService.buzzBeepBlinkLocked(interrupter);
+
+ verifyBeep(2);
+
+ assertTrue(interrupter.isInterruptive());
+ }
+
+ @Test
+ public void testRingtoneInsistentBuzz_doesNotBlockFutureSoundsOnceStopped() {
+ NotificationChannel ringtoneChannel =
+ new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+ ringtoneChannel.setSound(null,
+ new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
+ ringtoneChannel.enableVibration(true);
+ NotificationRecord ringtoneNotification = getCallRecord(1, ringtoneChannel, true);
+
+ mService.buzzBeepBlinkLocked(ringtoneNotification);
+ verifyVibrateLooped();
+
+ mService.clearVibrateLocked();
+
+ NotificationRecord interrupter = getBuzzyOtherNotification();
+ mService.buzzBeepBlinkLocked(interrupter);
+
+ verifyVibrate(2);
+
+ assertTrue(interrupter.isInterruptive());
+ }
+
+ @Test
+ public void testCanInterruptNonRingtoneInsistentBeep() throws Exception {
+ NotificationChannel fakeRingtoneChannel =
+ new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+ NotificationRecord ringtoneNotification = getCallRecord(1, fakeRingtoneChannel, true);
+
+ mService.buzzBeepBlinkLocked(ringtoneNotification);
+ verifyBeepLooped();
+
+ NotificationRecord interrupter = getBeepyOtherNotification();
+ mService.buzzBeepBlinkLocked(interrupter);
+
+ verifyBeep(2);
+
+ assertTrue(interrupter.isInterruptive());
+ }
+
+ @Test
+ public void testCanInterruptNonRingtoneInsistentBuzz() throws Exception {
+ NotificationChannel fakeRingtoneChannel =
+ new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+ fakeRingtoneChannel.enableVibration(true);
+ NotificationRecord ringtoneNotification = getCallRecord(1, fakeRingtoneChannel, true);
+
+ mService.buzzBeepBlinkLocked(ringtoneNotification);
+
+ NotificationRecord interrupter = getBuzzyOtherNotification();
+ mService.buzzBeepBlinkLocked(interrupter);
+
+ verifyVibrate(2);
+
+ assertTrue(interrupter.isInterruptive());
+ }
+
static class VibrateRepeatMatcher implements ArgumentMatcher<VibrationEffect> {
private final int mRepeatIndex;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 4ea2fc0..cd0f4f1 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -537,6 +537,18 @@
return new NotificationRecord(mContext, sbn, channel);
}
+ private NotificationRecord generateNotificationRecord(NotificationChannel channel, int userId) {
+ if (channel == null) {
+ channel = mTestNotificationChannel;
+ }
+ Notification.Builder nb = new Notification.Builder(mContext, channel.getId())
+ .setContentTitle("foo")
+ .setSmallIcon(android.R.drawable.sym_def_app_icon);
+ StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, "tag", mUid, 0,
+ nb.build(), new UserHandle(userId), null, 0);
+ return new NotificationRecord(mContext, sbn, channel);
+ }
+
private Map<String, Answer> getSignalExtractorSideEffects() {
Map<String, Answer> answers = new ArrayMap<>();
@@ -5451,6 +5463,112 @@
}
@Test
+ public void testGrantInlineReplyUriPermission_recordExists() throws Exception {
+ NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel, 0);
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+ nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+ waitForIdle();
+
+ // A notification exists for the given record
+ StatusBarNotification[] notifsBefore = mBinderService.getActiveNotifications(PKG);
+ assertEquals(1, notifsBefore.length);
+
+ reset(mPackageManager);
+
+ Uri uri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 1);
+
+ mService.mNotificationDelegate.grantInlineReplyUriPermission(
+ nr.getKey(), uri, nr.sbn.getUid());
+
+ // Grant permission called for the UID of SystemUI under the target user ID
+ verify(mUgm, times(1)).grantUriPermissionFromOwner(any(),
+ eq(nr.sbn.getUid()), eq(nr.sbn.getPackageName()), eq(uri), anyInt(), anyInt(),
+ eq(nr.sbn.getUserId()));
+ }
+
+ @Test
+ public void testGrantInlineReplyUriPermission_userAll() throws Exception {
+ // generate a NotificationRecord for USER_ALL to make sure it's converted into USER_SYSTEM
+ NotificationRecord nr =
+ generateNotificationRecord(mTestNotificationChannel, UserHandle.USER_ALL);
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+ nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+ waitForIdle();
+
+ // A notification exists for the given record
+ StatusBarNotification[] notifsBefore = mBinderService.getActiveNotifications(PKG);
+ assertEquals(1, notifsBefore.length);
+
+ reset(mPackageManager);
+
+ Uri uri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 1);
+
+ mService.mNotificationDelegate.grantInlineReplyUriPermission(
+ nr.getKey(), uri, nr.sbn.getUid());
+
+ // Target user for the grant is USER_ALL instead of USER_SYSTEM
+ verify(mUgm, times(1)).grantUriPermissionFromOwner(any(),
+ eq(nr.sbn.getUid()), eq(nr.sbn.getPackageName()), eq(uri), anyInt(), anyInt(),
+ eq(UserHandle.USER_SYSTEM));
+ }
+
+ @Test
+ public void testGrantInlineReplyUriPermission_acrossUsers() throws Exception {
+ // generate a NotificationRecord for USER_ALL to make sure it's converted into USER_SYSTEM
+ int otherUserId = 11;
+ NotificationRecord nr =
+ generateNotificationRecord(mTestNotificationChannel, otherUserId);
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+ nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+ waitForIdle();
+
+ // A notification exists for the given record
+ StatusBarNotification[] notifsBefore = mBinderService.getActiveNotifications(PKG);
+ assertEquals(1, notifsBefore.length);
+
+ reset(mPackageManager);
+
+ Uri uri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 1);
+
+ int uid = 0; // sysui on primary user
+ int otherUserUid = (otherUserId * 100000) + 1; // SystemUI as a different user
+ String sysuiPackage = "sysui";
+ final String[] sysuiPackages = new String[] { sysuiPackage };
+ when(mPackageManager.getPackagesForUid(uid)).thenReturn(sysuiPackages);
+
+ // Make sure to mock call for USER_SYSTEM and not USER_ALL, since it's been replaced by the
+ // time this is called
+ when(mPackageManager.getPackageUid(sysuiPackage, 0, otherUserId))
+ .thenReturn(otherUserUid);
+
+ mService.mNotificationDelegate.grantInlineReplyUriPermission(nr.getKey(), uri, uid);
+
+ // Target user for the grant is USER_ALL instead of USER_SYSTEM
+ verify(mUgm, times(1)).grantUriPermissionFromOwner(any(),
+ eq(otherUserUid), eq(nr.sbn.getPackageName()), eq(uri), anyInt(), anyInt(),
+ eq(otherUserId));
+ }
+
+ @Test
+ public void testGrantInlineReplyUriPermission_noRecordExists() throws Exception {
+ NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel);
+ waitForIdle();
+
+ // No notifications exist for the given record
+ StatusBarNotification[] notifsBefore = mBinderService.getActiveNotifications(PKG);
+ assertEquals(0, notifsBefore.length);
+
+ Uri uri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 1);
+ int uid = 0; // sysui on primary user
+
+ mService.mNotificationDelegate.grantInlineReplyUriPermission(nr.getKey(), uri, uid);
+
+ // Grant permission not called if no record exists for the given key
+ verify(mUgm, times(0)).grantUriPermissionFromOwner(any(), anyInt(), any(),
+ eq(uri), anyInt(), anyInt(), anyInt());
+ }
+
+ @Test
public void testNotificationBubbles_disabled_lowRamDevice() throws Exception {
// Bubbles are allowed!
setUpPrefsForBubbles(true /* global */, true /* app */, true /* channel */);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
index aaaa7a5..2836e69 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -28,6 +28,7 @@
import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.timeout;
@@ -128,7 +129,7 @@
mActivityMetricsLogger.notifyActivityLaunching(intent);
- verifyAsync(mLaunchObserver).onIntentStarted(eq(intent));
+ verifyAsync(mLaunchObserver).onIntentStarted(eq(intent), anyLong());
verifyNoMoreInteractions(mLaunchObserver);
}
@@ -163,12 +164,12 @@
testOnActivityLaunched();
mActivityMetricsLogger.notifyTransitionStarting(new SparseIntArray(),
- SystemClock.uptimeMillis());
+ SystemClock.elapsedRealtimeNanos());
mActivityMetricsLogger.notifyWindowsDrawn(mActivityRecord.getWindowingMode(),
- SystemClock.uptimeMillis());
+ SystemClock.elapsedRealtimeNanos());
- verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(mActivityRecord));
+ verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(mActivityRecord), anyLong());
verifyNoMoreInteractions(mLaunchObserver);
}
@@ -186,6 +187,16 @@
}
@Test
+ public void testOnReportFullyDrawn() throws Exception {
+ testOnActivityLaunched();
+
+ mActivityMetricsLogger.logAppTransitionReportedDrawn(mActivityRecord, false);
+
+ verifyAsync(mLaunchObserver).onReportFullyDrawn(eqProto(mActivityRecord), anyLong());
+ verifyNoMoreInteractions(mLaunchObserver);
+ }
+
+ @Test
public void testOnActivityLaunchedTrampoline() throws Exception {
testOnIntentStarted();
@@ -206,12 +217,13 @@
testOnActivityLaunchedTrampoline();
mActivityMetricsLogger.notifyTransitionStarting(new SparseIntArray(),
- SystemClock.uptimeMillis());
+ SystemClock.elapsedRealtimeNanos());
mActivityMetricsLogger.notifyWindowsDrawn(mActivityRecordTrampoline.getWindowingMode(),
- SystemClock.uptimeMillis());
+ SystemClock.elapsedRealtimeNanos());
- verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(mActivityRecordTrampoline));
+ verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(mActivityRecordTrampoline),
+ anyLong());
verifyNoMoreInteractions(mLaunchObserver);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
index c2a05c24..2835c1f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -891,7 +891,7 @@
activity.app = null;
overlayActivity.app = null;
- assertEquals(2, mTask.mActivities.size());
+ assertEquals(2, mTask.getChildCount());
mStack.finishDisabledPackageActivitiesLocked(activity.packageName,
null /* filterByClasses */, true /* doit */, true /* evenPersistent */,
@@ -900,7 +900,7 @@
// Although the overlay activity is in another package, the non-overlay activities are
// removed from the task. Since the overlay activity should be removed as well, the task
// should be empty.
- assertThat(mTask.mActivities).isEmpty();
+ assertFalse(mTask.hasChild());
assertThat(mStack.getAllTasks()).isEmpty();
}
@@ -918,11 +918,11 @@
// second activity will be immediately removed as it has no state.
secondActivity.setSavedState(null /* savedState */);
- assertEquals(2, mTask.mActivities.size());
+ assertEquals(2, mTask.getChildCount());
mStack.handleAppDiedLocked(secondActivity.app);
- assertThat(mTask.mActivities).isEmpty();
+ assertFalse(mTask.hasChild());
assertThat(mStack.getAllTasks()).isEmpty();
}
@@ -936,7 +936,7 @@
mStack.handleAppDiedLocked(activity.app);
- assertEquals(1, mTask.mActivities.size());
+ assertEquals(1, mTask.getChildCount());
assertEquals(1, mStack.getAllTasks().size());
}
@@ -950,7 +950,7 @@
mStack.handleAppDiedLocked(activity.app);
- assertThat(mTask.mActivities).isEmpty();
+ assertFalse(mTask.hasChild());
assertThat(mStack.getAllTasks()).isEmpty();
}
@@ -964,7 +964,7 @@
mStack.handleAppDiedLocked(activity.app);
- assertEquals(1, mTask.mActivities.size());
+ assertEquals(1, mTask.getChildCount());
assertEquals(1, mStack.getAllTasks().size());
}
@@ -978,7 +978,7 @@
mStack.handleAppDiedLocked(activity.app);
- assertThat(mTask.mActivities).isEmpty();
+ assertFalse(mTask.hasChild());
assertThat(mStack.getAllTasks()).isEmpty();
}
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 77fbdcf..d43fe63 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -215,23 +215,6 @@
return this;
}
- static Pair<Intent, ActivityInfo> createIntentAndActivityInfo() {
- // TODO: Look into consolidating with dup. code in build() method below.
- final int id = sCurrentActivityId++;
- final ComponentName component = ComponentName.createRelative(
- DEFAULT_COMPONENT_PACKAGE_NAME, DEFAULT_COMPONENT_CLASS_NAME + id);
-
- final Intent intent = new Intent();
- intent.setComponent(component);
-
- final ActivityInfo aInfo = new ActivityInfo();
- aInfo.applicationInfo = new ApplicationInfo();
- aInfo.applicationInfo.packageName = component.getPackageName();
- aInfo.applicationInfo.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
- aInfo.packageName = component.getPackageName();
- return new Pair<>(intent, aInfo);
- }
-
ActivityRecord build() {
if (mComponent == null) {
final int id = sCurrentActivityId++;
@@ -249,6 +232,7 @@
intent.setComponent(mComponent);
final ActivityInfo aInfo = new ActivityInfo();
aInfo.applicationInfo = new ApplicationInfo();
+ aInfo.applicationInfo.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
aInfo.applicationInfo.packageName = mComponent.getPackageName();
aInfo.applicationInfo.uid = mUid;
aInfo.packageName = mComponent.getPackageName();
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
index 650a911..b174251 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java
@@ -57,7 +57,7 @@
private TaskStack mStack;
private Task mTask;
- private AppWindowToken mToken;
+ private ActivityRecord mToken;
public void setUpOnDisplay(DisplayContent dc) {
mStack = createTaskStackOnDisplay(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, dc);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
index 605d520..14939cc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -101,10 +101,10 @@
@Test
@FlakyTest(bugId = 131005232)
public void testTransitWithinTask() {
- final AppWindowToken opening = createAppWindowToken(mDisplayContent,
+ final ActivityRecord opening = createAppWindowToken(mDisplayContent,
WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
opening.setOccludesParent(false);
- final AppWindowToken closing = createAppWindowToken(mDisplayContent,
+ final ActivityRecord closing = createAppWindowToken(mDisplayContent,
WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
closing.setOccludesParent(false);
final Task task = opening.getTask();
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index 72d9bd0..9d53676 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -159,7 +159,7 @@
final TaskStack stack1 = createTaskStackOnDisplay(dc1);
final Task task1 = createTaskInStack(stack1, 0 /* userId */);
- final AppWindowToken token1 =
+ final ActivityRecord token1 =
WindowTestUtils.createTestAppWindowToken(dc1);
task1.addChild(token1, 0);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
index 2661735..b4c978f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -77,7 +77,7 @@
TaskStack mStack;
Task mTask;
- AppWindowToken mToken;
+ ActivityRecord mToken;
private final String mPackageName = getInstrumentation().getTargetContext().getPackageName();
@@ -410,7 +410,7 @@
}
private AppWindowToken createTestAppWindowTokenForGivenTask(Task task) {
- final AppWindowToken appToken =
+ final ActivityRecord appToken =
WindowTestUtils.createTestAppWindowToken(mDisplayContent);
task.addChild(appToken, 0);
waitUntilHandlersIdle();
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 2ba3cbd..f12c349 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -241,7 +241,7 @@
assertEquals(dc, stack.getDisplayContent());
final Task task = createTaskInStack(stack, 0 /* userId */);
- final AppWindowToken token = WindowTestUtils.createTestAppWindowToken(dc);
+ final ActivityRecord token = WindowTestUtils.createTestAppWindowToken(dc);
task.addChild(token, 0);
assertEquals(dc, task.getDisplayContent());
assertEquals(dc, token.getDisplayContent());
@@ -313,7 +313,7 @@
// Add stack with activity.
final TaskStack stack0 = createTaskStackOnDisplay(dc0);
final Task task0 = createTaskInStack(stack0, 0 /* userId */);
- final AppWindowToken token =
+ final ActivityRecord token =
WindowTestUtils.createTestAppWindowToken(dc0);
task0.addChild(token, 0);
dc0.configureDisplayPolicy();
@@ -321,7 +321,7 @@
final TaskStack stack1 = createTaskStackOnDisplay(dc1);
final Task task1 = createTaskInStack(stack1, 0 /* userId */);
- final AppWindowToken token1 =
+ final ActivityRecord token1 =
WindowTestUtils.createTestAppWindowToken(dc0);
task1.addChild(token1, 0);
dc1.configureDisplayPolicy();
@@ -682,16 +682,15 @@
// is appWin & null on the other display.
mDisplayContent.setInputMethodWindowLocked(mImeWindow);
newDisplay.setInputMethodWindowLocked(null);
- assertTrue("appWin should be IME target window",
- appWin.equals(mDisplayContent.mInputMethodTarget));
+ assertEquals("appWin should be IME target window",
+ appWin, mDisplayContent.mInputMethodTarget);
assertNull("newDisplay Ime target: ", newDisplay.mInputMethodTarget);
// Switch input method window on new display & make sure the input method target also
// switched as expected.
newDisplay.setInputMethodWindowLocked(mImeWindow);
mDisplayContent.setInputMethodWindowLocked(null);
- assertTrue("appWin1 should be IME target window",
- appWin1.equals(newDisplay.mInputMethodTarget));
+ assertEquals("appWin1 should be IME target window", appWin1, newDisplay.mInputMethodTarget);
assertNull("default display Ime target: ", mDisplayContent.mInputMethodTarget);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
index 304df22..452e06f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DragDropControllerTests.java
@@ -96,7 +96,7 @@
* Creates a window state which can be used as a drop target.
*/
private WindowState createDropTargetWindow(String name, int ownerId) {
- final AppWindowToken token = WindowTestUtils.createTestAppWindowToken(
+ final ActivityRecord token = WindowTestUtils.createTestAppWindowToken(
mDisplayContent);
final TaskStack stack = createTaskStackOnDisplay(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, mDisplayContent);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java
index 92ddb35..eef680b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java
@@ -54,7 +54,7 @@
// Stack should contain visible app window to be considered visible.
final Task pinnedTask = createTaskInStack(mPinnedStack, 0 /* userId */);
assertFalse(mPinnedStack.isVisible());
- final AppWindowToken pinnedApp =
+ final ActivityRecord pinnedApp =
WindowTestUtils.createTestAppWindowToken(mDisplayContent);
pinnedTask.addChild(pinnedApp, 0 /* addPos */);
assertTrue(mPinnedStack.isVisible());
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
index 2eb6ea4..d045073 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java
@@ -68,13 +68,13 @@
public void testClosingAppDifferentStackOrientation() {
final TaskStack stack = createTaskStackOnDisplay(mDisplayContent);
final Task task1 = createTaskInStack(stack, 0 /* userId */);
- AppWindowToken appWindowToken1 =
+ ActivityRecord appWindowToken1 =
WindowTestUtils.createTestAppWindowToken(mDisplayContent);
task1.addChild(appWindowToken1, 0);
appWindowToken1.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
final Task task2 = createTaskInStack(stack, 1 /* userId */);
- AppWindowToken appWindowToken2 =
+ ActivityRecord appWindowToken2 =
WindowTestUtils.createTestAppWindowToken(mDisplayContent);
task2.addChild(appWindowToken2, 0);
appWindowToken2.setOrientation(SCREEN_ORIENTATION_PORTRAIT);
@@ -88,13 +88,13 @@
public void testMoveTaskToBackDifferentStackOrientation() {
final TaskStack stack = createTaskStackOnDisplay(mDisplayContent);
final Task task1 = createTaskInStack(stack, 0 /* userId */);
- AppWindowToken appWindowToken1 =
+ ActivityRecord appWindowToken1 =
WindowTestUtils.createTestAppWindowToken(mDisplayContent);
task1.addChild(appWindowToken1, 0);
appWindowToken1.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
final Task task2 = createTaskInStack(stack, 1 /* userId */);
- AppWindowToken appWindowToken2 =
+ ActivityRecord appWindowToken2 =
WindowTestUtils.createTestAppWindowToken(mDisplayContent);
task2.addChild(appWindowToken2, 0);
appWindowToken2.setOrientation(SCREEN_ORIENTATION_PORTRAIT);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
index 09e5027..6a94137 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
@@ -115,4 +115,8 @@
@Override
public void showInsets(int types, boolean fromIme) throws RemoteException {
}
+
+ @Override
+ public void hideInsets(int types, boolean fromIme) throws RemoteException {
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
index c627c19..f44c969 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java
@@ -19,17 +19,11 @@
import static android.app.AppOpsManager.OP_NONE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-import static com.android.server.wm.ActivityTestsBase.ActivityBuilder.createIntentAndActivityInfo;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
import android.app.ActivityManager;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
import android.os.IBinder;
-import android.util.Pair;
import android.view.IWindow;
import android.view.WindowManager;
@@ -51,23 +45,22 @@
}
/** Creates an {@link AppWindowToken} and adds it to the specified {@link Task}. */
- static AppWindowToken createAppWindowTokenInTask(DisplayContent dc, Task task) {
- final AppWindowToken newToken = createTestAppWindowToken(dc);
+ static ActivityRecord createAppWindowTokenInTask(DisplayContent dc, Task task) {
+ final ActivityRecord newToken = createTestAppWindowToken(dc);
task.addChild(newToken, POSITION_TOP);
return newToken;
}
- static AppWindowToken createTestAppWindowToken(DisplayContent dc) {
+ static ActivityRecord createTestAppWindowToken(DisplayContent dc) {
synchronized (dc.mWmService.mGlobalLock) {
- Pair<Intent, ActivityInfo> pair = createIntentAndActivityInfo();
- final AppWindowToken token = new AppWindowToken(dc.mWmService,
- dc.mWmService.mAtmService, new ActivityRecord.Token(pair.first), pair.second,
- null, pair.first, dc);
- token.setOccludesParent(true);
- token.setHidden(false);
- token.hiddenRequested = false;
- spyOn(token);
- return token;
+ final ActivityRecord r =
+ new ActivityTestsBase.ActivityBuilder(dc.mWmService.mAtmService)
+ .build();
+ r.onDisplayChanged(dc);
+ r.setOccludesParent(true);
+ r.setHidden(false);
+ r.hiddenRequested = false;
+ return r;
}
}
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 4c4b21e..1fce46c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -204,15 +204,15 @@
}
}
- AppWindowToken createAppWindowToken(DisplayContent dc, int windowingMode, int activityType) {
+ ActivityRecord createAppWindowToken(DisplayContent dc, int windowingMode, int activityType) {
return createTestAppWindowToken(dc, windowingMode, activityType);
}
- AppWindowToken createTestAppWindowToken(DisplayContent dc, int
+ ActivityRecord createTestAppWindowToken(DisplayContent dc, int
windowingMode, int activityType) {
final TaskStack stack = createTaskStackOnDisplay(windowingMode, activityType, dc);
final Task task = createTaskInStack(stack, 0 /* userId */);
- final AppWindowToken appWindowToken =
+ final ActivityRecord appWindowToken =
WindowTestUtils.createTestAppWindowToken(dc);
task.addChild(appWindowToken, 0);
return appWindowToken;
@@ -244,7 +244,7 @@
WindowState createAppWindow(Task task, int type, String name) {
synchronized (mWm.mGlobalLock) {
- final AppWindowToken token = WindowTestUtils.createTestAppWindowToken(mDisplayContent);
+ final ActivityRecord token = WindowTestUtils.createTestAppWindowToken(mDisplayContent);
task.addChild(token, 0);
return createWindow(null, type, token, name);
}
diff --git a/startop/apps/test/Android.bp b/startop/apps/test/Android.bp
index a806320..2ff26b8 100644
--- a/startop/apps/test/Android.bp
+++ b/startop/apps/test/Android.bp
@@ -17,12 +17,14 @@
android_app {
name: "startop_test_app",
srcs: [
+ "src/ComplexLayoutInflationActivity.java",
"src/CPUIntensive.java",
"src/EmptyActivity.java",
- "src/LayoutInflationActivity.java",
- "src/ComplexLayoutInflationActivity.java",
"src/FrameLayoutInflationActivity.java",
+ "src/LayoutInflationActivity.java",
+ "src/NonInteractiveSystemServerBenchmarkActivity.java",
"src/SystemServerBenchmarkActivity.java",
+ "src/SystemServerBenchmarks.java",
"src/TextViewInflationActivity.java",
],
sdk_version: "26", // Android O (8.0) and higher
diff --git a/startop/apps/test/AndroidManifest.xml b/startop/apps/test/AndroidManifest.xml
index 15785d4..a5ac348 100644
--- a/startop/apps/test/AndroidManifest.xml
+++ b/startop/apps/test/AndroidManifest.xml
@@ -84,6 +84,13 @@
</intent-filter>
</activity>
+ <activity
+ android:label="Non-interactive SystemServer Benchmark"
+ android:name=".NonInteractiveSystemServerBenchmarkActivity"
+ android:exported="true" />
+
</application>
+ <uses-permission android:name="android.permission.WAKE_LOCK" />
+
</manifest>
diff --git a/startop/apps/test/README.md b/startop/apps/test/README.md
index dadc66a..949dff7 100644
--- a/startop/apps/test/README.md
+++ b/startop/apps/test/README.md
@@ -24,3 +24,14 @@
inflation.
adb shell am start -n com.android.startop.test/.ComplexLayoutInflationActivity
+
+## NonInteractiveSystemServerBenchmark
+
+This activity is for running microbenchmarks from the command line. Run as follows:
+
+ adb shell am start -W -n com.android.startop.test .NonInteractiveSystemServerBenchmarkActivity
+
+It takes awhile (and there's currently no automated way to make sure it's done),
+but when it finishes, you can get the results like this:
+
+ adb shell cat /sdcard/Android/data/com.android.startop.test/files/benchmark.csv
diff --git a/startop/apps/test/src/NonInteractiveSystemServerBenchmarkActivity.java b/startop/apps/test/src/NonInteractiveSystemServerBenchmarkActivity.java
new file mode 100644
index 0000000..a2dc2cf
--- /dev/null
+++ b/startop/apps/test/src/NonInteractiveSystemServerBenchmarkActivity.java
@@ -0,0 +1,71 @@
+/*
+ * 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.startop.test;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.PrintStream;
+import java.util.ArrayList;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.GridLayout;
+import android.widget.TextView;
+
+public class NonInteractiveSystemServerBenchmarkActivity extends Activity {
+ ArrayList<CharSequence> benchmarkNames = new ArrayList();
+ ArrayList<Runnable> benchmarkThunks = new ArrayList();
+
+ PrintStream out;
+
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ SystemServerBenchmarks.initializeBenchmarks(this, (name, thunk) -> {
+ benchmarkNames.add(name);
+ benchmarkThunks.add(thunk);
+ });
+
+ try {
+ out = new PrintStream(new File(getExternalFilesDir(null), "benchmark.csv"));
+ } catch (FileNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ out.println("Name,Mean,Stdev");
+ runBenchmarks(0);
+ }
+
+ void runBenchmarks(int i) {
+ if (i < benchmarkNames.size()) {
+ SystemServerBenchmarks.runBenchmarkInBackground(benchmarkThunks.get(i),
+ (mean, stdev) -> {
+ out.printf("%s,%.0f,%.0f\n", benchmarkNames.get(i), mean, stdev);
+ runBenchmarks(i + 1);
+ });
+ }
+ }
+}
diff --git a/startop/apps/test/src/SystemServerBenchmarkActivity.java b/startop/apps/test/src/SystemServerBenchmarkActivity.java
index c8d9fde..75ea69b 100644
--- a/startop/apps/test/src/SystemServerBenchmarkActivity.java
+++ b/startop/apps/test/src/SystemServerBenchmarkActivity.java
@@ -31,13 +31,25 @@
import android.widget.GridLayout;
import android.widget.TextView;
+public class SystemServerBenchmarkActivity extends Activity implements BenchmarkRunner {
+ private GridLayout benchmarkList;
-class Benchmark {
- // Time limit to run benchmarks in seconds
- public static final int TIME_LIMIT = 5;
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.system_server_benchmark_page);
- public Benchmark(ViewGroup parent, CharSequence name, Runnable thunk) {
- Context context = parent.getContext();
+ benchmarkList = findViewById(R.id.benchmark_list);
+
+ SystemServerBenchmarks.initializeBenchmarks(this, this);
+ }
+
+ /**
+ * Adds a benchmark to the set to run.
+ *
+ * @param name A short name that shows up in the UI or benchmark results
+ */
+ public void addBenchmark(CharSequence name, Runnable thunk) {
+ Context context = benchmarkList.getContext();
Button button = new Button(context);
TextView mean = new TextView(context);
TextView stdev = new TextView(context);
@@ -50,165 +62,14 @@
mean.setText("Running...");
stdev.setText("");
- new AsyncTask() {
- double resultMean = 0;
- double resultStdev = 0;
-
- @Override
- protected Object doInBackground(Object... _args) {
- long startTime = System.nanoTime();
- int count = 0;
-
- // Run benchmark
- while (true) {
- long elapsed = -System.nanoTime();
- thunk.run();
- elapsed += System.nanoTime();
-
- count++;
- double elapsedVariance = (double) elapsed - resultMean;
- resultMean += elapsedVariance / count;
- resultStdev += elapsedVariance * ((double) elapsed - resultMean);
-
- if (System.nanoTime() - startTime > TIME_LIMIT * 1e9) {
- break;
- }
- }
- resultStdev = Math.sqrt(resultStdev / (count - 1));
-
- return null;
- }
-
- @Override
- protected void onPostExecute(Object _result) {
- mean.setText(String.format("%.3f", resultMean / 1e6));
- stdev.setText(String.format("%.3f", resultStdev / 1e6));
- }
- }.execute(new Object());
- });
-
- parent.addView(button);
- parent.addView(mean);
- parent.addView(stdev);
- }
-}
-
-public class SystemServerBenchmarkActivity extends Activity {
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.system_server_benchmark_page);
-
- GridLayout benchmarkList = findViewById(R.id.benchmark_list);
-
- new Benchmark(benchmarkList, "Empty", () -> {
- });
-
- new Benchmark(benchmarkList, "CPU Intensive (1 thread)", () -> {
- CPUIntensive.doSomeWork(1);
- });
-
- new Benchmark(benchmarkList, "CPU Intensive (2 thread)", () -> {
- CPUIntensive.doSomeWork(2);
- });
-
- new Benchmark(benchmarkList, "CPU Intensive (4 thread)", () -> {
- CPUIntensive.doSomeWork(4);
- });
-
- new Benchmark(benchmarkList, "CPU Intensive (8 thread)", () -> {
- CPUIntensive.doSomeWork(8);
- });
-
- PackageManager pm = getPackageManager();
- new Benchmark(benchmarkList, "getInstalledApplications", () -> {
- pm.getInstalledApplications(PackageManager.MATCH_SYSTEM_ONLY);
- });
-
- new Benchmark(benchmarkList, "getInstalledPackages", () -> {
- pm.getInstalledPackages(PackageManager.GET_ACTIVITIES);
- });
-
- new Benchmark(benchmarkList, "getPackageInfo", () -> {
- try {
- pm.getPackageInfo("com.android.startop.test", 0);
- } catch (NameNotFoundException e) {
- throw new RuntimeException(e);
- }
- });
-
- new Benchmark(benchmarkList, "getApplicationInfo", () -> {
- try {
- pm.getApplicationInfo("com.android.startop.test", 0);
- } catch (NameNotFoundException e) {
- throw new RuntimeException(e);
- }
- });
-
- try {
- ApplicationInfo app = pm.getApplicationInfo("com.android.startop.test", 0);
- new Benchmark(benchmarkList, "getResourcesForApplication", () -> {
- try {
- pm.getResourcesForApplication(app);
- } catch (NameNotFoundException e) {
- throw new RuntimeException(e);
- }
+ SystemServerBenchmarks.runBenchmarkInBackground(thunk, (resultMean, resultStdev) -> {
+ mean.setText(String.format("%.3f", resultMean / 1e6));
+ stdev.setText(String.format("%.3f", resultStdev / 1e6));
});
-
- new Benchmark(benchmarkList, "getPackagesForUid", () -> {
- pm.getPackagesForUid(app.uid);
- });
- } catch (NameNotFoundException e) {
- throw new RuntimeException(e);
- }
-
- ComponentName component = new ComponentName(this, this.getClass());
- new Benchmark(benchmarkList, "getActivityInfo", () -> {
- try {
- pm.getActivityInfo(component, PackageManager.GET_META_DATA);
- } catch (NameNotFoundException e) {
- throw new RuntimeException(e);
- }
});
- new Benchmark(benchmarkList, "getLaunchIntentForPackage", () -> {
- pm.getLaunchIntentForPackage("com.android.startop.test");
- });
-
- new Benchmark(benchmarkList, "getPackageUid", () -> {
- try {
- pm.getPackageUid("com.android.startop.test", 0);
- } catch (NameNotFoundException e) {
- throw new RuntimeException(e);
- }
- });
-
- new Benchmark(benchmarkList, "checkPermission", () -> {
- // Check for the first permission I could find.
- pm.checkPermission("android.permission.SEND_SMS", "com.android.startop.test");
- });
-
- new Benchmark(benchmarkList, "checkSignatures", () -> {
- // Compare with settings, since settings is on both AOSP and Master builds
- pm.checkSignatures("com.android.settings", "com.android.startop.test");
- });
-
- Intent intent = new Intent(Intent.ACTION_BOOT_COMPLETED);
- new Benchmark(benchmarkList, "queryBroadcastReceivers", () -> {
- pm.queryBroadcastReceivers(intent, 0);
- });
-
- new Benchmark(benchmarkList, "hasSystemFeature", () -> {
- pm.hasSystemFeature(PackageManager.FEATURE_CAMERA);
- });
-
- new Benchmark(benchmarkList, "resolveService", () -> {
- pm.resolveService(intent, 0);
- });
-
- ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
- new Benchmark(benchmarkList, "getRunningAppProcesses", () -> {
- am.getRunningAppProcesses();
- });
-
+ benchmarkList.addView(button);
+ benchmarkList.addView(mean);
+ benchmarkList.addView(stdev);
}
}
diff --git a/startop/apps/test/src/SystemServerBenchmarks.java b/startop/apps/test/src/SystemServerBenchmarks.java
new file mode 100644
index 0000000..126c2c8
--- /dev/null
+++ b/startop/apps/test/src/SystemServerBenchmarks.java
@@ -0,0 +1,222 @@
+/*
+ * 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.startop.test;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.AsyncTask;
+import android.os.PowerManager;
+
+/**
+ * An interface for running benchmarks and collecting results. Used so we can have both an
+ * interactive runner and a non-interactive runner.
+ */
+interface BenchmarkRunner {
+ void addBenchmark(CharSequence name, Runnable thunk);
+}
+
+interface ResultListener {
+ /**
+ * Called when a benchmark result is ready
+ *
+ * @param mean The average iteration time in nanoseconds
+ * @param stdev The standard deviation of iteration times in nanoseconds
+ */
+ void onResult(double mean, double stdev);
+}
+
+class SystemServerBenchmarks {
+ // Time limit to run benchmarks in seconds
+ public static final int TIME_LIMIT = 5;
+
+ static void initializeBenchmarks(Activity parent, BenchmarkRunner benchmarks) {
+ benchmarks.addBenchmark("Empty", () -> {
+ });
+
+ benchmarks.addBenchmark("CPU Intensive (1 thread)", () -> {
+ CPUIntensive.doSomeWork(1);
+ });
+
+ benchmarks.addBenchmark("CPU Intensive (2 thread)", () -> {
+ CPUIntensive.doSomeWork(2);
+ });
+
+ benchmarks.addBenchmark("CPU Intensive (4 thread)", () -> {
+ CPUIntensive.doSomeWork(4);
+ });
+
+ benchmarks.addBenchmark("CPU Intensive (8 thread)", () -> {
+ CPUIntensive.doSomeWork(8);
+ });
+
+ PackageManager pm = parent.getPackageManager();
+ benchmarks.addBenchmark("getInstalledApplications", () -> {
+ pm.getInstalledApplications(PackageManager.MATCH_SYSTEM_ONLY);
+ });
+
+ benchmarks.addBenchmark("getInstalledPackages", () -> {
+ pm.getInstalledPackages(PackageManager.GET_ACTIVITIES);
+ });
+
+ benchmarks.addBenchmark("getPackageInfo", () -> {
+ try {
+ pm.getPackageInfo("com.android.startop.test", 0);
+ } catch (NameNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ });
+
+ benchmarks.addBenchmark("getApplicationInfo", () -> {
+ try {
+ pm.getApplicationInfo("com.android.startop.test", 0);
+ } catch (NameNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ });
+
+ try {
+ ApplicationInfo app = pm.getApplicationInfo("com.android.startop.test", 0);
+ benchmarks.addBenchmark("getResourcesForApplication", () -> {
+ try {
+ pm.getResourcesForApplication(app);
+ } catch (NameNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ });
+
+ benchmarks.addBenchmark("getPackagesForUid", () -> {
+ pm.getPackagesForUid(app.uid);
+ });
+ } catch (NameNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+
+ ComponentName component = new ComponentName(parent, parent.getClass());
+ benchmarks.addBenchmark("getActivityInfo", () -> {
+ try {
+ pm.getActivityInfo(component, PackageManager.GET_META_DATA);
+ } catch (NameNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ });
+
+ benchmarks.addBenchmark("getLaunchIntentForPackage", () -> {
+ pm.getLaunchIntentForPackage("com.android.startop.test");
+ });
+
+ benchmarks.addBenchmark("getPackageUid", () -> {
+ try {
+ pm.getPackageUid("com.android.startop.test", 0);
+ } catch (NameNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ });
+
+ benchmarks.addBenchmark("checkPermission", () -> {
+ // Check for the first permission I could find.
+ pm.checkPermission("android.permission.SEND_SMS", "com.android.startop.test");
+ });
+
+ benchmarks.addBenchmark("checkSignatures", () -> {
+ // Compare with settings, since settings is on both AOSP and Master builds
+ pm.checkSignatures("com.android.settings", "com.android.startop.test");
+ });
+
+ Intent intent = new Intent(Intent.ACTION_BOOT_COMPLETED);
+ benchmarks.addBenchmark("queryBroadcastReceivers", () -> {
+ pm.queryBroadcastReceivers(intent, 0);
+ });
+
+ benchmarks.addBenchmark("hasSystemFeature", () -> {
+ pm.hasSystemFeature(PackageManager.FEATURE_CAMERA);
+ });
+
+ benchmarks.addBenchmark("resolveService", () -> {
+ pm.resolveService(intent, 0);
+ });
+
+ ActivityManager am = (ActivityManager) parent.getSystemService(Context.ACTIVITY_SERVICE);
+ benchmarks.addBenchmark("getRunningAppProcesses", () -> {
+ am.getRunningAppProcesses();
+ });
+
+ // We use PendingIntent.getCreatorPackage, since
+ // getPackageIntentForSender is not public to us, but getCreatorPackage
+ // is just a thin wrapper around it.
+ PendingIntent pi = PendingIntent.getActivity(parent, 0, new Intent(), 0);
+ benchmarks.addBenchmark("getPackageIntentForSender", () -> {
+ pi.getCreatorPackage();
+ });
+
+ PowerManager pwr = (PowerManager) parent.getSystemService(Context.POWER_SERVICE);
+ PowerManager.WakeLock wl = pwr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "benchmark tag");
+ benchmarks.addBenchmark("WakeLock Acquire/Release", () -> {
+ wl.acquire();
+ wl.release();
+ });
+ }
+
+ /**
+ * A helper method for benchark runners to actually run the benchmark and gather stats
+ *
+ * @param thunk The code whose performance we want to measure
+ * @param reporter What to do with the results
+ */
+ static void runBenchmarkInBackground(Runnable thunk, ResultListener reporter) {
+ new AsyncTask() {
+ double resultMean = 0;
+ double resultStdev = 0;
+
+ @Override
+ protected Object doInBackground(Object... _args) {
+ long startTime = System.nanoTime();
+ int count = 0;
+
+ // Run benchmark
+ while (true) {
+ long elapsed = -System.nanoTime();
+ thunk.run();
+ elapsed += System.nanoTime();
+
+ count++;
+ double elapsedVariance = (double) elapsed - resultMean;
+ resultMean += elapsedVariance / count;
+ resultStdev += elapsedVariance * ((double) elapsed - resultMean);
+
+ if (System.nanoTime() - startTime > TIME_LIMIT * 1e9) {
+ break;
+ }
+ }
+ resultStdev = Math.sqrt(resultStdev / (count - 1));
+
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Object _result) {
+ reporter.onResult(resultMean, resultStdev);
+ }
+ }.execute(new Object());
+ }
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/AppLaunchEvent.java b/startop/iorap/src/com/google/android/startop/iorap/AppLaunchEvent.java
index acf9946..cf120cf 100644
--- a/startop/iorap/src/com/google/android/startop/iorap/AppLaunchEvent.java
+++ b/startop/iorap/src/com/google/android/startop/iorap/AppLaunchEvent.java
@@ -86,10 +86,14 @@
public static final class IntentStarted extends AppLaunchEvent {
@NonNull
public final Intent intent;
+ public final long timestampNs;
- public IntentStarted(@SequenceId long sequenceId, Intent intent) {
+ public IntentStarted(@SequenceId long sequenceId,
+ Intent intent,
+ long timestampNs) {
super(sequenceId);
this.intent = intent;
+ this.timestampNs = timestampNs;
Objects.requireNonNull(intent, "intent");
}
@@ -98,14 +102,16 @@
public boolean equals(Object other) {
if (other instanceof IntentStarted) {
return intent.equals(((IntentStarted)other).intent) &&
- super.equals(other);
+ timestampNs == ((IntentStarted)other).timestampNs &&
+ super.equals(other);
}
return false;
}
@Override
protected String toStringBody() {
- return ", intent=" + intent.toString();
+ return ", intent=" + intent.toString() +
+ " , timestampNs=" + Long.toString(timestampNs);
}
@@ -113,11 +119,13 @@
protected void writeToParcelImpl(Parcel p, int flags) {
super.writeToParcelImpl(p, flags);
IntentProtoParcelable.write(p, intent, flags);
+ p.writeLong(timestampNs);
}
IntentStarted(Parcel p) {
super(p);
intent = IntentProtoParcelable.create(p);
+ timestampNs = p.readLong();
}
}
@@ -216,18 +224,39 @@
}
public static final class ActivityLaunchFinished extends BaseWithActivityRecordData {
+ public final long timestampNs;
+
public ActivityLaunchFinished(@SequenceId long sequenceId,
- @NonNull @ActivityRecordProto byte[] snapshot) {
+ @NonNull @ActivityRecordProto byte[] snapshot,
+ long timestampNs) {
super(sequenceId, snapshot);
+ this.timestampNs = timestampNs;
}
@Override
public boolean equals(Object other) {
if (other instanceof ActivityLaunched) {
- return super.equals(other);
+ return timestampNs == ((ActivityLaunchFinished)other).timestampNs &&
+ super.equals(other);
}
return false;
}
+
+ @Override
+ protected String toStringBody() {
+ return ", timestampNs=" + Long.toString(timestampNs);
+ }
+
+ @Override
+ protected void writeToParcelImpl(Parcel p, int flags) {
+ super.writeToParcelImpl(p, flags);
+ p.writeLong(timestampNs);
+ }
+
+ ActivityLaunchFinished(Parcel p) {
+ super(p);
+ timestampNs = p.readLong();
+ }
}
public static class ActivityLaunchCancelled extends AppLaunchEvent {
@@ -275,6 +304,42 @@
}
}
+ public static final class ReportFullyDrawn extends BaseWithActivityRecordData {
+ public final long timestampNs;
+
+ public ReportFullyDrawn(@SequenceId long sequenceId,
+ @NonNull @ActivityRecordProto byte[] snapshot,
+ long timestampNs) {
+ super(sequenceId, snapshot);
+ this.timestampNs = timestampNs;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof ReportFullyDrawn) {
+ return timestampNs == ((ReportFullyDrawn)other).timestampNs &&
+ super.equals(other);
+ }
+ return false;
+ }
+
+ @Override
+ protected String toStringBody() {
+ return ", timestampNs=" + Long.toString(timestampNs);
+ }
+
+ @Override
+ protected void writeToParcelImpl(Parcel p, int flags) {
+ super.writeToParcelImpl(p, flags);
+ p.writeLong(timestampNs);
+ }
+
+ ReportFullyDrawn(Parcel p) {
+ super(p);
+ timestampNs = p.readLong();
+ }
+ }
+
@Override
public @ContentsFlags int describeContents() { return 0; }
@@ -348,6 +413,7 @@
ActivityLaunched.class,
ActivityLaunchFinished.class,
ActivityLaunchCancelled.class,
+ ReportFullyDrawn.class,
};
public static class ActivityRecordProtoParcelable {
diff --git a/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java b/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java
index 902da4c..f753548 100644
--- a/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java
+++ b/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java
@@ -315,19 +315,19 @@
// All callbacks occur on the same background thread. Don't synchronize explicitly.
@Override
- public void onIntentStarted(@NonNull Intent intent) {
+ public void onIntentStarted(@NonNull Intent intent, long timestampNs) {
// #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));
+ Log.v(TAG, String.format("AppLaunchObserver#onIntentStarted(%d, %s, %d)",
+ mSequenceId, intent, timestampNs));
}
invokeRemote(mIorapRemote,
(IIorap remote) ->
remote.onAppLaunchEvent(RequestId.nextValueForSequence(),
- new AppLaunchEvent.IntentStarted(mSequenceId, intent))
+ new AppLaunchEvent.IntentStarted(mSequenceId, intent, timestampNs))
);
}
@@ -374,16 +374,34 @@
}
@Override
- public void onActivityLaunchFinished(@NonNull @ActivityRecordProto byte[] activity) {
+ public void onActivityLaunchFinished(@NonNull @ActivityRecordProto byte[] activity,
+ long timestampNs) {
if (DEBUG) {
- Log.v(TAG, String.format("AppLaunchObserver#onActivityLaunchFinished(%d, %s)",
- mSequenceId, activity));
+ Log.v(TAG, String.format("AppLaunchObserver#onActivityLaunchFinished(%d, %s, %d)",
+ mSequenceId, activity, timestampNs));
}
invokeRemote(mIorapRemote,
(IIorap remote) ->
remote.onAppLaunchEvent(RequestId.nextValueForSequence(),
- new AppLaunchEvent.ActivityLaunchFinished(mSequenceId, activity))
+ new AppLaunchEvent.ActivityLaunchFinished(mSequenceId,
+ activity,
+ timestampNs))
+ );
+ }
+
+ @Override
+ public void onReportFullyDrawn(@NonNull @ActivityRecordProto byte[] activity,
+ long timestampNs) {
+ if (DEBUG) {
+ Log.v(TAG, String.format("AppLaunchObserver#onReportFullyDrawn(%d, %s, %d)",
+ mSequenceId, activity, timestampNs));
+ }
+
+ invokeRemote(mIorapRemote,
+ (IIorap remote) ->
+ remote.onAppLaunchEvent(RequestId.nextValueForSequence(),
+ new AppLaunchEvent.ReportFullyDrawn(mSequenceId, activity, timestampNs))
);
}
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 1d5c18d..9e60afc 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -2817,7 +2817,7 @@
"ping_test_before_data_switch_bool";
/**
- * Controls time in milli seconds until DcTracker reevaluates 5G connection state.
+ * Controls time in milliseconds until DcTracker reevaluates 5G connection state.
* @hide
*/
public static final String KEY_5G_WATCHDOG_TIME_MS_LONG =
@@ -3199,6 +3199,13 @@
public static final String KEY_CARRIER_CERTIFICATE_STRING_ARRAY =
"carrier_certificate_string_array";
+ /**
+ * DisconnectCause array to play busy tone. Value should be array of
+ * {@link android.telephony.DisconnectCause}.
+ */
+ public static final String KEY_DISCONNECT_CAUSE_PLAY_BUSYTONE_INT_ARRAY =
+ "disconnect_cause_play_busytone_int_array";
+
/** The default value for every variable. */
private final static PersistableBundle sDefaults;
@@ -3628,6 +3635,8 @@
sDefaults.putBoolean(KEY_SUPPORT_WPS_OVER_IMS_BOOL, true);
sDefaults.putAll(Ims.getDefaults());
sDefaults.putStringArray(KEY_CARRIER_CERTIFICATE_STRING_ARRAY, null);
+ sDefaults.putIntArray(KEY_DISCONNECT_CAUSE_PLAY_BUSYTONE_INT_ARRAY,
+ new int[] {4 /* BUSY */});
}
/**
diff --git a/telephony/java/android/telephony/CellBroadcastService.java b/telephony/java/android/telephony/CellBroadcastService.java
index d5e447e..46eb9df 100644
--- a/telephony/java/android/telephony/CellBroadcastService.java
+++ b/telephony/java/android/telephony/CellBroadcastService.java
@@ -21,6 +21,7 @@
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
+import android.telephony.cdma.CdmaSmsCbProgramData;
/**
* A service which exposes the cell broadcast handling module to the system.
@@ -69,9 +70,11 @@
/**
* Handle a CDMA cell broadcast SMS message forwarded from the system.
* @param slotIndex the index of the slot which received the message
- * @param message the SMS PDU
+ * @param bearerData the CDMA SMS bearer data
+ * @param serviceCategory the CDMA SCPT service category
*/
- public abstract void onCdmaCellBroadcastSms(int slotIndex, byte[] message);
+ public abstract void onCdmaCellBroadcastSms(int slotIndex, byte[] bearerData,
+ @CdmaSmsCbProgramData.Category int serviceCategory);
/**
* If overriding this method, call through to the super method for any unknown actions.
@@ -102,11 +105,14 @@
/**
* Handle a CDMA cell broadcast SMS.
* @param slotIndex the index of the slot which received the broadcast
- * @param message the SMS message PDU
+ * @param bearerData the CDMA SMS bearer data
+ * @param serviceCategory the CDMA SCPT service category
*/
@Override
- public void handleCdmaCellBroadcastSms(int slotIndex, byte[] message) {
- CellBroadcastService.this.onCdmaCellBroadcastSms(slotIndex, message);
+ public void handleCdmaCellBroadcastSms(int slotIndex, byte[] bearerData,
+ int serviceCategory) {
+ CellBroadcastService.this.onCdmaCellBroadcastSms(slotIndex, bearerData,
+ serviceCategory);
}
}
}
diff --git a/telephony/java/android/telephony/CellSignalStrengthWcdma.java b/telephony/java/android/telephony/CellSignalStrengthWcdma.java
index cdf01da..4dc54f0 100644
--- a/telephony/java/android/telephony/CellSignalStrengthWcdma.java
+++ b/telephony/java/android/telephony/CellSignalStrengthWcdma.java
@@ -245,9 +245,12 @@
}
/**
- * Get the Ec/No as dB
+ * Get the Ec/No (Energy per chip over the noise spectral density) as dB.
*
- * @hide
+ * Reference: TS 25.133 Section 9.1.2.3
+ *
+ * @return the Ec/No of the measured cell in the range [-24, 1] or
+ * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable
*/
public int getEcNo() {
return mEcNo;
diff --git a/telephony/java/android/telephony/ICellBroadcastService.aidl b/telephony/java/android/telephony/ICellBroadcastService.aidl
index eff64a2..bcd6cc5 100644
--- a/telephony/java/android/telephony/ICellBroadcastService.aidl
+++ b/telephony/java/android/telephony/ICellBroadcastService.aidl
@@ -28,5 +28,5 @@
oneway void handleGsmCellBroadcastSms(int slotId, in byte[] message);
/** @see android.telephony.CellBroadcastService#onCdmaCellBroadcastSms */
- oneway void handleCdmaCellBroadcastSms(int slotId, in byte[] message);
+ oneway void handleCdmaCellBroadcastSms(int slotId, in byte[] bearerData, int serviceCategory);
}
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index e288f25..40d057f 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -53,7 +53,6 @@
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.telephony.Annotation.NetworkType;
import android.telephony.euicc.EuiccManager;
import android.telephony.ims.ImsMmTelManager;
import android.util.DisplayMetrics;
@@ -2420,8 +2419,12 @@
* @param plans the list of plans. The first plan is always the primary and
* most important plan. Any additional plans are secondary and
* may not be displayed or used by decision making logic.
+ * The list of all plans must meet the requirements defined in
+ * {@link SubscriptionPlan.Builder#setNetworkTypes(int[])}.
* @throws SecurityException if the caller doesn't meet the requirements
* outlined above.
+ * @throws IllegalArgumentException if plans don't meet the requirements
+ * mentioned above.
*/
public void setSubscriptionPlans(int subId, @NonNull List<SubscriptionPlan> plans) {
try {
@@ -2466,51 +2469,10 @@
*/
public void setSubscriptionOverrideUnmetered(int subId, boolean overrideUnmetered,
@DurationMillisLong long timeoutMillis) {
- setSubscriptionOverrideUnmetered(subId, null, overrideUnmetered, timeoutMillis);
- }
-
- /**
- * Temporarily override the billing relationship between a carrier and
- * a specific subscriber to be considered unmetered for the given network
- * types. This will be reflected to apps via
- * {@link NetworkCapabilities#NET_CAPABILITY_NOT_METERED}.
- * This method is only accessible to the following narrow set of apps:
- * <ul>
- * <li>The carrier app for this subscriberId, as determined by
- * {@link TelephonyManager#hasCarrierPrivileges()}.
- * <li>The carrier app explicitly delegated access through
- * {@link CarrierConfigManager#KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING}.
- * </ul>
- *
- * @param subId the subscriber this override applies to.
- * @param networkTypes all network types to set an override for. A null
- * network type means to apply the override to all network types.
- * Any unspecified network types will default to metered.
- * @param overrideUnmetered set if the billing relationship should be
- * considered unmetered.
- * @param timeoutMillis the timeout after which the requested override will
- * be automatically cleared, or {@code 0} to leave in the
- * requested state until explicitly cleared, or the next reboot,
- * whichever happens first.
- * @throws SecurityException if the caller doesn't meet the requirements
- * outlined above.
- * {@hide}
- */
- public void setSubscriptionOverrideUnmetered(int subId,
- @Nullable @NetworkType int[] networkTypes, boolean overrideUnmetered,
- @DurationMillisLong long timeoutMillis) {
try {
- long networkTypeMask = 0;
- if (networkTypes != null) {
- for (int networkType : networkTypes) {
- networkTypeMask |= TelephonyManager.getBitMaskForNetworkType(networkType);
- }
- } else {
- networkTypeMask = ~0;
- }
final int overrideValue = overrideUnmetered ? OVERRIDE_UNMETERED : 0;
getNetworkPolicy().setSubscriptionOverride(subId, OVERRIDE_UNMETERED, overrideValue,
- networkTypeMask, timeoutMillis, mContext.getOpPackageName());
+ timeoutMillis, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2542,52 +2504,10 @@
*/
public void setSubscriptionOverrideCongested(int subId, boolean overrideCongested,
@DurationMillisLong long timeoutMillis) {
- setSubscriptionOverrideCongested(subId, null, overrideCongested, timeoutMillis);
- }
-
- /**
- * Temporarily override the billing relationship plan between a carrier and
- * a specific subscriber to be considered congested. This will cause the
- * device to delay certain network requests when possible, such as developer
- * jobs that are willing to run in a flexible time window.
- * <p>
- * This method is only accessible to the following narrow set of apps:
- * <ul>
- * <li>The carrier app for this subscriberId, as determined by
- * {@link TelephonyManager#hasCarrierPrivileges()}.
- * <li>The carrier app explicitly delegated access through
- * {@link CarrierConfigManager#KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING}.
- * </ul>
- *
- * @param subId the subscriber this override applies to.
- * @param networkTypes all network types to set an override for. A null
- * network type means to apply the override to all network types.
- * Any unspecified network types will default to not congested.
- * @param overrideCongested set if the subscription should be considered
- * congested.
- * @param timeoutMillis the timeout after which the requested override will
- * be automatically cleared, or {@code 0} to leave in the
- * requested state until explicitly cleared, or the next reboot,
- * whichever happens first.
- * @throws SecurityException if the caller doesn't meet the requirements
- * outlined above.
- * @hide
- */
- public void setSubscriptionOverrideCongested(int subId,
- @Nullable @NetworkType int[] networkTypes, boolean overrideCongested,
- @DurationMillisLong long timeoutMillis) {
try {
- long networkTypeMask = 0;
- if (networkTypes != null) {
- for (int networkType : networkTypes) {
- networkTypeMask |= TelephonyManager.getBitMaskForNetworkType(networkType);
- }
- } else {
- networkTypeMask = ~0;
- }
final int overrideValue = overrideCongested ? OVERRIDE_CONGESTED : 0;
getNetworkPolicy().setSubscriptionOverride(subId, OVERRIDE_CONGESTED, overrideValue,
- networkTypeMask, timeoutMillis, mContext.getOpPackageName());
+ timeoutMillis, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/telephony/java/android/telephony/SubscriptionPlan.java b/telephony/java/android/telephony/SubscriptionPlan.java
index ec2050f..e24eb26 100644
--- a/telephony/java/android/telephony/SubscriptionPlan.java
+++ b/telephony/java/android/telephony/SubscriptionPlan.java
@@ -24,6 +24,7 @@
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
+import android.telephony.Annotation.NetworkType;
import android.util.Range;
import android.util.RecurrenceRule;
@@ -33,6 +34,7 @@
import java.lang.annotation.RetentionPolicy;
import java.time.Period;
import java.time.ZonedDateTime;
+import java.util.Arrays;
import java.util.Iterator;
import java.util.Objects;
@@ -80,6 +82,8 @@
private int dataLimitBehavior = LIMIT_BEHAVIOR_UNKNOWN;
private long dataUsageBytes = BYTES_UNKNOWN;
private long dataUsageTime = TIME_UNKNOWN;
+ private @NetworkType int[] networkTypes;
+ private long networkTypesBitMask;
private SubscriptionPlan(RecurrenceRule cycleRule) {
this.cycleRule = Preconditions.checkNotNull(cycleRule);
@@ -93,6 +97,7 @@
dataLimitBehavior = source.readInt();
dataUsageBytes = source.readLong();
dataUsageTime = source.readLong();
+ networkTypes = source.createIntArray();
}
@Override
@@ -109,6 +114,7 @@
dest.writeInt(dataLimitBehavior);
dest.writeLong(dataUsageBytes);
dest.writeLong(dataUsageTime);
+ dest.writeIntArray(networkTypes);
}
@Override
@@ -121,13 +127,14 @@
.append(" dataLimitBehavior=").append(dataLimitBehavior)
.append(" dataUsageBytes=").append(dataUsageBytes)
.append(" dataUsageTime=").append(dataUsageTime)
+ .append(" networkTypes=").append(Arrays.toString(networkTypes))
.append("}").toString();
}
@Override
public int hashCode() {
return Objects.hash(cycleRule, title, summary, dataLimitBytes, dataLimitBehavior,
- dataUsageBytes, dataUsageTime);
+ dataUsageBytes, dataUsageTime, Arrays.hashCode(networkTypes));
}
@Override
@@ -140,7 +147,8 @@
&& dataLimitBytes == other.dataLimitBytes
&& dataLimitBehavior == other.dataLimitBehavior
&& dataUsageBytes == other.dataUsageBytes
- && dataUsageTime == other.dataUsageTime;
+ && dataUsageTime == other.dataUsageTime
+ && Arrays.equals(networkTypes, other.networkTypes);
}
return false;
}
@@ -204,6 +212,32 @@
}
/**
+ * Return an array containing all {@link NetworkType}s this SubscriptionPlan applies to.
+ * A null array means this SubscriptionPlan applies to all network types.
+ */
+ public @Nullable @NetworkType int[] getNetworkTypes() {
+ return networkTypes;
+ }
+
+ /**
+ * Return the networkTypes array converted to a {@link TelephonyManager.NetworkTypeBitMask}
+ * @hide
+ */
+ public long getNetworkTypesBitMask() {
+ // calculate bitmask the first time and save for future calls
+ if (networkTypesBitMask == 0) {
+ if (networkTypes == null) {
+ networkTypesBitMask = ~0;
+ } else {
+ for (int networkType : networkTypes) {
+ networkTypesBitMask |= TelephonyManager.getBitMaskForNetworkType(networkType);
+ }
+ }
+ }
+ return networkTypesBitMask;
+ }
+
+ /**
* Return an iterator that will return all valid data usage cycles based on
* any recurrence rules. The iterator starts from the currently active cycle
* and walks backwards through time.
@@ -335,5 +369,24 @@
plan.dataUsageTime = dataUsageTime;
return this;
}
+
+ /**
+ * Set the network types this SubscriptionPlan applies to.
+ * The developer must supply at least one plan that applies to all network types (default),
+ * and all additional plans may not include a particular network type more than once.
+ * Plan selection will prefer plans that have specific network types defined
+ * over plans that apply to all network types.
+ *
+ * @param networkTypes a set of all {@link NetworkType}s that apply to this plan.
+ * A null value or empty array means the plan applies to all network types.
+ */
+ public @NonNull Builder setNetworkTypes(@Nullable @NetworkType int[] networkTypes) {
+ if (networkTypes == null || networkTypes.length == 0) {
+ plan.networkTypes = null;
+ } else {
+ plan.networkTypes = networkTypes;
+ }
+ return this;
+ }
}
}
diff --git a/telephony/java/android/telephony/ims/ImsReasonInfo.java b/telephony/java/android/telephony/ims/ImsReasonInfo.java
index 1e0d9a78..10251d7 100644
--- a/telephony/java/android/telephony/ims/ImsReasonInfo.java
+++ b/telephony/java/android/telephony/ims/ImsReasonInfo.java
@@ -885,6 +885,12 @@
*/
public static final int CODE_WFC_SERVICE_NOT_AVAILABLE_IN_THIS_LOCATION = 1623;
+ /**
+ * The dialed RTT call should be retried without RTT
+ * @hide
+ */
+ public static final int CODE_RETRY_ON_IMS_WITHOUT_RTT = 3001;
+
/*
* OEM specific error codes. To be used by OEMs when they don't want to reveal error code which
* would be replaced by ERROR_UNSPECIFIED.
@@ -1065,6 +1071,7 @@
CODE_REJECT_VT_AVPF_NOT_ALLOWED,
CODE_REJECT_ONGOING_ENCRYPTED_CALL,
CODE_REJECT_ONGOING_CS_CALL,
+ CODE_RETRY_ON_IMS_WITHOUT_RTT,
CODE_OEM_CAUSE_1,
CODE_OEM_CAUSE_2,
CODE_OEM_CAUSE_3,
diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java
index 668a6af..9e786ce 100644
--- a/telephony/java/com/android/internal/telephony/DctConstants.java
+++ b/telephony/java/com/android/internal/telephony/DctConstants.java
@@ -111,7 +111,7 @@
public static final int EVENT_DATA_SERVICE_BINDING_CHANGED = BASE + 49;
public static final int EVENT_DEVICE_PROVISIONED_CHANGE = BASE + 50;
public static final int EVENT_DATA_ENABLED_OVERRIDE_RULES_CHANGED = BASE + 51;
- public static final int EVENT_5G_NETWORK_CHANGED = BASE + 52;
+ public static final int EVENT_SERVICE_STATE_CHANGED = BASE + 52;
public static final int EVENT_5G_TIMER_HYSTERESIS = BASE + 53;
public static final int EVENT_5G_TIMER_WATCHDOG = BASE + 54;
diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
index 4654437..b357fa4 100644
--- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
@@ -900,6 +900,20 @@
}
/**
+ * @return the bearer data byte array
+ */
+ public byte[] getEnvelopeBearerData() {
+ return mEnvelope.bearerData;
+ }
+
+ /**
+ * @return the 16-bit CDMA SCPT service category
+ */
+ public @CdmaSmsCbProgramData.Category int getEnvelopeServiceCategory() {
+ return mEnvelope.serviceCategory;
+ }
+
+ /**
* {@inheritDoc}
*/
@Override
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java b/telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java
index 2787b24..c924ab3 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java
@@ -57,17 +57,17 @@
// CMAS alert service category assignments, see 3GPP2 C.R1001 table 9.3.3-1
public static final int SERVICE_CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT =
- CdmaSmsCbProgramData.CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT;
+ CdmaSmsCbProgramData.CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT; // = 4096
public static final int SERVICE_CATEGORY_CMAS_EXTREME_THREAT =
- CdmaSmsCbProgramData.CATEGORY_CMAS_EXTREME_THREAT;
+ CdmaSmsCbProgramData.CATEGORY_CMAS_EXTREME_THREAT; // = 4097
public static final int SERVICE_CATEGORY_CMAS_SEVERE_THREAT =
- CdmaSmsCbProgramData.CATEGORY_CMAS_SEVERE_THREAT;
+ CdmaSmsCbProgramData.CATEGORY_CMAS_SEVERE_THREAT; // = 4098
public static final int SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY =
- CdmaSmsCbProgramData.CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY;
+ CdmaSmsCbProgramData.CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY; // = 4099
public static final int SERVICE_CATEGORY_CMAS_TEST_MESSAGE =
- CdmaSmsCbProgramData.CATEGORY_CMAS_TEST_MESSAGE;
+ CdmaSmsCbProgramData.CATEGORY_CMAS_TEST_MESSAGE; // = 4100
public static final int SERVICE_CATEGORY_CMAS_LAST_RESERVED_VALUE =
- CdmaSmsCbProgramData.CATEGORY_CMAS_LAST_RESERVED_VALUE;
+ CdmaSmsCbProgramData.CATEGORY_CMAS_LAST_RESERVED_VALUE; // = 4351
/**
* Provides the type of a SMS message like point to point, broadcast or acknowledge
diff --git a/test-mock/src/android/test/mock/MockContext.java b/test-mock/src/android/test/mock/MockContext.java
index fcd4701..5053cee 100644
--- a/test-mock/src/android/test/mock/MockContext.java
+++ b/test-mock/src/android/test/mock/MockContext.java
@@ -758,7 +758,7 @@
/** {@hide} */
@Override
- public Context createContextAsUser(UserHandle user) {
+ public Context createContextAsUser(UserHandle user, @CreatePackageOptions int flags) {
throw new UnsupportedOperationException();
}
diff --git a/tests/ApkVerityTest/block_device_writer/block_device_writer.cpp b/tests/ApkVerityTest/block_device_writer/block_device_writer.cpp
index b0c7251..02dfd73 100644
--- a/tests/ApkVerityTest/block_device_writer/block_device_writer.cpp
+++ b/tests/ApkVerityTest/block_device_writer/block_device_writer.cpp
@@ -42,6 +42,42 @@
// https://www.kernel.org/doc/Documentation/filesystems/fiemap.txt
// https://git.kernel.org/pub/scm/fs/xfs/xfsprogs-dev.git/tree/io/fiemap.c
+#ifndef F2FS_IOC_SET_PIN_FILE
+#ifndef F2FS_IOCTL_MAGIC
+#define F2FS_IOCTL_MAGIC 0xf5
+#endif
+#define F2FS_IOC_SET_PIN_FILE _IOW(F2FS_IOCTL_MAGIC, 13, __u32)
+#define F2FS_IOC_GET_PIN_FILE _IOR(F2FS_IOCTL_MAGIC, 14, __u32)
+#endif
+
+struct Args {
+ const char* block_device;
+ const char* file_name;
+ uint64_t byte_offset;
+ bool use_f2fs_pinning;
+};
+
+class ScopedF2fsFilePinning {
+ public:
+ explicit ScopedF2fsFilePinning(const char* file_path) {
+ fd_.reset(TEMP_FAILURE_RETRY(open(file_path, O_WRONLY | O_CLOEXEC, 0)));
+ if (fd_.get() == -1) {
+ perror("Failed to open");
+ return;
+ }
+ __u32 set = 1;
+ ioctl(fd_.get(), F2FS_IOC_SET_PIN_FILE, &set);
+ }
+
+ ~ScopedF2fsFilePinning() {
+ __u32 set = 0;
+ ioctl(fd_.get(), F2FS_IOC_SET_PIN_FILE, &set);
+ }
+
+ private:
+ android::base::unique_fd fd_;
+};
+
ssize_t get_logical_block_size(const char* block_device) {
android::base::unique_fd fd(open(block_device, O_RDONLY));
if (fd.get() < 0) {
@@ -138,28 +174,51 @@
return 0;
}
-int main(int argc, const char** argv) {
- if (argc != 4) {
+std::unique_ptr<Args> parse_args(int argc, const char** argv) {
+ if (argc != 4 && argc != 5) {
fprintf(stderr,
- "Usage: %s block_dev filename byte_offset\n"
+ "Usage: %s [--use-f2fs-pinning] block_dev filename byte_offset\n"
"\n"
"This program bypasses filesystem and damages the specified byte\n"
"at the physical position on <block_dev> corresponding to the\n"
"logical byte location in <filename>.\n",
argv[0]);
+ return nullptr;
+ }
+
+ auto args = std::make_unique<Args>();
+ const char** arg = &argv[1];
+ args->use_f2fs_pinning = strcmp(*arg, "--use-f2fs-pinning") == 0;
+ if (args->use_f2fs_pinning) {
+ ++arg;
+ }
+ args->block_device = *(arg++);
+ args->file_name = *(arg++);
+ args->byte_offset = strtoull(*arg, nullptr, 10);
+ if (args->byte_offset == ULLONG_MAX) {
+ perror("Invalid byte offset");
+ return nullptr;
+ }
+ return args;
+}
+
+int main(int argc, const char** argv) {
+ std::unique_ptr<Args> args = parse_args(argc, argv);
+ if (args == nullptr) {
return -1;
}
- const char* block_device = argv[1];
- const char* file_name = argv[2];
- uint64_t byte_offset = strtoull(argv[3], nullptr, 10);
-
- ssize_t block_size = get_logical_block_size(block_device);
+ ssize_t block_size = get_logical_block_size(args->block_device);
if (block_size < 0) {
return -1;
}
- int64_t physical_offset_signed = get_physical_offset(file_name, byte_offset);
+ std::unique_ptr<ScopedF2fsFilePinning> pinned_file;
+ if (args->use_f2fs_pinning) {
+ pinned_file = std::make_unique<ScopedF2fsFilePinning>(args->file_name);
+ }
+
+ int64_t physical_offset_signed = get_physical_offset(args->file_name, args->byte_offset);
if (physical_offset_signed < 0) {
return -1;
}
@@ -172,7 +231,7 @@
std::unique_ptr<char> buf(static_cast<char*>(
aligned_alloc(block_size /* alignment */, block_size /* size */)));
- if (read_block_from_device(block_device, physical_block_offset, block_size,
+ if (read_block_from_device(args->block_device, physical_block_offset, block_size,
buf.get()) < 0) {
return -1;
}
@@ -180,7 +239,7 @@
printf("before: %hhx\n", *p);
*p ^= 0xff;
printf("after: %hhx\n", *p);
- if (write_block_to_device(block_device, physical_block_offset, block_size,
+ if (write_block_to_device(args->block_device, physical_block_offset, block_size,
buf.get()) < 0) {
return -1;
}
diff --git a/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java b/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java
index 761c5ce..2445a6a 100644
--- a/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java
+++ b/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java
@@ -38,6 +38,7 @@
import org.junit.runner.RunWith;
import java.io.FileNotFoundException;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
@@ -440,8 +441,15 @@
throws DeviceNotAvailableException {
assertTrue(path.startsWith("/data/"));
ITestDevice.MountPointInfo mountPoint = mDevice.getMountPointInfo("/data");
- expectRemoteCommandToSucceed(String.join(" ", DAMAGING_EXECUTABLE,
- mountPoint.filesystem, path, Long.toString(offsetOfTargetingByte)));
+ ArrayList<String> args = new ArrayList<>();
+ args.add(DAMAGING_EXECUTABLE);
+ if ("f2fs".equals(mountPoint.type)) {
+ args.add("--use-f2fs-pinning");
+ }
+ args.add(mountPoint.filesystem);
+ args.add(path);
+ args.add(Long.toString(offsetOfTargetingByte));
+ expectRemoteCommandToSucceed(String.join(" ", args));
}
private String getApkPath(String packageName) throws DeviceNotAvailableException {
diff --git a/tests/Codegen/runTest.sh b/tests/Codegen/runTest.sh
index 0e90dea..929f122 100755
--- a/tests/Codegen/runTest.sh
+++ b/tests/Codegen/runTest.sh
@@ -17,11 +17,13 @@
header_and_eval codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java && \
header_and_eval codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java && \
header_and_eval codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java && \
- cd $ANDROID_BUILD_TOP &&
- header_and_eval mmma -j16 frameworks/base/tests/Codegen && \
- header_and_eval adb install -r -t "$(find $ANDROID_TARGET_OUT_TESTCASES -name 'CodegenTests.apk')" && \
- # header_and_eval adb shell am set-debug-app -w com.android.codegentest && \
- header_and_eval adb shell am instrument -w -e package com.android.codegentest com.android.codegentest/androidx.test.runner.AndroidJUnitRunner
+ (
+ cd $ANDROID_BUILD_TOP &&
+ header_and_eval mmma -j16 frameworks/base/tests/Codegen && \
+ header_and_eval adb install -r -t "$(find $ANDROID_TARGET_OUT_TESTCASES -name 'CodegenTests.apk')" && \
+ # header_and_eval adb shell am set-debug-app -w com.android.codegentest && \
+ header_and_eval adb shell am instrument -w -e package com.android.codegentest com.android.codegentest/androidx.test.runner.AndroidJUnitRunner
+ )
exitCode=$?
diff --git a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java
index 10eba6a..3515053 100644
--- a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java
+++ b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java
@@ -32,13 +32,17 @@
- // Code below generated by codegen v1.0.7.
+ // Code below generated by codegen v1.0.8.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
//
// To regenerate run:
// $ codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
@DataClass.Generated.Member
@@ -94,8 +98,8 @@
};
@DataClass.Generated(
- time = 1570576455287L,
- codegenVersion = "1.0.7",
+ time = 1570828332402L,
+ codegenVersion = "1.0.8",
sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java",
inputSignatures = "private int mBaseData\nclass HierrarchicalDataClassBase extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genSetters=true)")
@Deprecated
diff --git a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java
index 1085a6a..c867409 100644
--- a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java
+++ b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java
@@ -46,13 +46,17 @@
- // Code below generated by codegen v1.0.7.
+ // Code below generated by codegen v1.0.8.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
//
// To regenerate run:
// $ codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
@DataClass.Generated.Member
@@ -116,8 +120,8 @@
};
@DataClass.Generated(
- time = 1570576456245L,
- codegenVersion = "1.0.7",
+ time = 1570828333399L,
+ codegenVersion = "1.0.8",
sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java",
inputSignatures = "private @android.annotation.NonNull java.lang.String mChildData\nclass HierrarchicalDataClassChild extends com.android.codegentest.HierrarchicalDataClassBase implements []\n@com.android.internal.util.DataClass(genParcelable=true, genConstructor=false, genSetters=true)")
@Deprecated
diff --git a/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java b/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java
index 75ef963..8d097a0 100644
--- a/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java
+++ b/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java
@@ -16,6 +16,7 @@
package com.android.codegentest;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.SparseArray;
@@ -46,15 +47,22 @@
@NonNull SparseArray<SampleWithCustomBuilder> mSparseArray = null;
@NonNull SparseIntArray mSparseIntArray = null;
+ @SuppressWarnings({"WeakerAccess"})
+ @Nullable Boolean mNullableBoolean = null;
- // Code below generated by codegen v1.0.7.
+
+ // Code below generated by codegen v1.0.8.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
//
// To regenerate run:
// $ codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
@DataClass.Generated.Member
@@ -65,7 +73,8 @@
@NonNull Map<String,SampleWithCustomBuilder> map,
@NonNull Map<String,String> stringMap,
@NonNull SparseArray<SampleWithCustomBuilder> sparseArray,
- @NonNull SparseIntArray sparseIntArray) {
+ @NonNull SparseIntArray sparseIntArray,
+ @SuppressWarnings({ "WeakerAccess" }) @Nullable Boolean nullableBoolean) {
this.mStringArray = stringArray;
AnnotationValidations.validate(
NonNull.class, null, mStringArray);
@@ -87,6 +96,7 @@
this.mSparseIntArray = sparseIntArray;
AnnotationValidations.validate(
NonNull.class, null, mSparseIntArray);
+ this.mNullableBoolean = nullableBoolean;
// onConstructed(); // You can define this method to get a callback
}
@@ -126,6 +136,11 @@
return mSparseIntArray;
}
+ @DataClass.Generated.Member
+ public @SuppressWarnings({ "WeakerAccess" }) @Nullable Boolean getNullableBoolean() {
+ return mNullableBoolean;
+ }
+
@Override
@DataClass.Generated.Member
public String toString() {
@@ -139,7 +154,8 @@
"map = " + mMap + ", " +
"stringMap = " + mStringMap + ", " +
"sparseArray = " + mSparseArray + ", " +
- "sparseIntArray = " + mSparseIntArray +
+ "sparseIntArray = " + mSparseIntArray + ", " +
+ "nullableBoolean = " + mNullableBoolean +
" }";
}
@@ -149,6 +165,9 @@
// You can override field parcelling by defining methods like:
// void parcelFieldName(Parcel dest, int flags) { ... }
+ int flg = 0;
+ if (mNullableBoolean != null) flg |= 0x80;
+ dest.writeInt(flg);
dest.writeStringArray(mStringArray);
dest.writeIntArray(mIntArray);
dest.writeStringList(mStringList);
@@ -156,6 +175,7 @@
dest.writeMap(mStringMap);
dest.writeSparseArray(mSparseArray);
dest.writeSparseIntArray(mSparseIntArray);
+ if (mNullableBoolean != null) dest.writeBoolean(mNullableBoolean);
}
@Override
@@ -169,6 +189,7 @@
// You can override field unparcelling by defining methods like:
// static FieldType unparcelFieldName(Parcel in) { ... }
+ int flg = in.readInt();
String[] stringArray = in.createStringArray();
int[] intArray = in.createIntArray();
List<String> stringList = new java.util.ArrayList<>();
@@ -179,6 +200,7 @@
in.readMap(stringMap, String.class.getClassLoader());
SparseArray<SampleWithCustomBuilder> sparseArray = (SparseArray) in.readSparseArray(SampleWithCustomBuilder.class.getClassLoader());
SparseIntArray sparseIntArray = (SparseIntArray) in.readSparseIntArray();
+ Boolean nullableBoolean = (flg & 0x80) == 0 ? null : (Boolean) in.readBoolean();
this.mStringArray = stringArray;
AnnotationValidations.validate(
@@ -201,6 +223,7 @@
this.mSparseIntArray = sparseIntArray;
AnnotationValidations.validate(
NonNull.class, null, mSparseIntArray);
+ this.mNullableBoolean = nullableBoolean;
// onConstructed(); // You can define this method to get a callback
}
@@ -233,6 +256,7 @@
private @NonNull Map<String,String> mStringMap;
private @NonNull SparseArray<SampleWithCustomBuilder> mSparseArray;
private @NonNull SparseIntArray mSparseIntArray;
+ private @SuppressWarnings({ "WeakerAccess" }) @Nullable Boolean mNullableBoolean;
private long mBuilderFieldsSet = 0L;
@@ -328,10 +352,18 @@
return this;
}
+ @DataClass.Generated.Member
+ public @NonNull Builder setNullableBoolean(@SuppressWarnings({ "WeakerAccess" }) @Nullable Boolean value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x80;
+ mNullableBoolean = value;
+ return this;
+ }
+
/** Builds the instance. This builder should not be touched after calling this! */
public ParcelAllTheThingsDataClass build() {
checkNotUsed();
- mBuilderFieldsSet |= 0x80; // Mark builder used
+ mBuilderFieldsSet |= 0x100; // Mark builder used
if ((mBuilderFieldsSet & 0x1) == 0) {
mStringArray = null;
@@ -354,6 +386,9 @@
if ((mBuilderFieldsSet & 0x40) == 0) {
mSparseIntArray = null;
}
+ if ((mBuilderFieldsSet & 0x80) == 0) {
+ mNullableBoolean = null;
+ }
ParcelAllTheThingsDataClass o = new ParcelAllTheThingsDataClass(
mStringArray,
mIntArray,
@@ -361,12 +396,13 @@
mMap,
mStringMap,
mSparseArray,
- mSparseIntArray);
+ mSparseIntArray,
+ mNullableBoolean);
return o;
}
private void checkNotUsed() {
- if ((mBuilderFieldsSet & 0x80) != 0) {
+ if ((mBuilderFieldsSet & 0x100) != 0) {
throw new IllegalStateException(
"This Builder should not be reused. Use a new Builder instance instead");
}
@@ -374,10 +410,10 @@
}
@DataClass.Generated(
- time = 1570576454326L,
- codegenVersion = "1.0.7",
+ time = 1570828331396L,
+ codegenVersion = "1.0.8",
sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java",
- inputSignatures = " @android.annotation.NonNull java.lang.String[] mStringArray\n @android.annotation.NonNull int[] mIntArray\n @android.annotation.NonNull java.util.List<java.lang.String> mStringList\n @android.annotation.NonNull java.util.Map<java.lang.String,com.android.codegentest.SampleWithCustomBuilder> mMap\n @android.annotation.NonNull java.util.Map<java.lang.String,java.lang.String> mStringMap\n @android.annotation.NonNull android.util.SparseArray<com.android.codegentest.SampleWithCustomBuilder> mSparseArray\n @android.annotation.NonNull android.util.SparseIntArray mSparseIntArray\nclass ParcelAllTheThingsDataClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genAidl=false, genToString=true)")
+ inputSignatures = " @android.annotation.NonNull java.lang.String[] mStringArray\n @android.annotation.NonNull int[] mIntArray\n @android.annotation.NonNull java.util.List<java.lang.String> mStringList\n @android.annotation.NonNull java.util.Map<java.lang.String,com.android.codegentest.SampleWithCustomBuilder> mMap\n @android.annotation.NonNull java.util.Map<java.lang.String,java.lang.String> mStringMap\n @android.annotation.NonNull android.util.SparseArray<com.android.codegentest.SampleWithCustomBuilder> mSparseArray\n @android.annotation.NonNull android.util.SparseIntArray mSparseIntArray\n @java.lang.SuppressWarnings({\"WeakerAccess\"}) @android.annotation.Nullable java.lang.Boolean mNullableBoolean\nclass ParcelAllTheThingsDataClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genAidl=false, genToString=true)")
@Deprecated
private void __metadata() {}
diff --git a/tests/Codegen/src/com/android/codegentest/SampleDataClass.java b/tests/Codegen/src/com/android/codegentest/SampleDataClass.java
index 14010a9..d014d6d 100644
--- a/tests/Codegen/src/com/android/codegentest/SampleDataClass.java
+++ b/tests/Codegen/src/com/android/codegentest/SampleDataClass.java
@@ -342,13 +342,17 @@
- // Code below generated by codegen v1.0.7.
+ // Code below generated by codegen v1.0.8.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
//
// To regenerate run:
// $ codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/SampleDataClass.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
@IntDef(prefix = "STATE_", value = {
@@ -1868,8 +1872,8 @@
}
@DataClass.Generated(
- time = 1570576452225L,
- codegenVersion = "1.0.7",
+ time = 1570828329319L,
+ codegenVersion = "1.0.8",
sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleDataClass.java",
inputSignatures = "public static final java.lang.String STATE_NAME_UNDEFINED\npublic static final java.lang.String STATE_NAME_ON\npublic static final java.lang.String STATE_NAME_OFF\npublic static final int STATE_UNDEFINED\npublic static final int STATE_ON\npublic static final int STATE_OFF\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_AUGMENTED_REQUEST\nprivate int mNum\nprivate int mNum2\nprivate int mNum4\nprivate @android.annotation.Nullable java.lang.String mName\nprivate @android.annotation.NonNull java.lang.String mName2\nprivate @android.annotation.NonNull java.lang.String mName4\nprivate @android.annotation.Nullable android.view.accessibility.AccessibilityNodeInfo mOtherParcelable\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.codegentest.MyDateParcelling.class) @android.annotation.NonNull java.util.Date mDate\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForPattern.class) @android.annotation.NonNull java.util.regex.Pattern mPattern\nprivate @android.annotation.NonNull java.util.List<android.net.LinkAddress> mLinkAddresses2\nprivate @com.android.internal.util.DataClass.PluralOf(\"linkAddress\") @android.annotation.NonNull java.util.ArrayList<android.net.LinkAddress> mLinkAddresses\nprivate @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses4\nprivate @com.android.codegentest.SampleDataClass.StateName @android.annotation.NonNull java.lang.String mStateName\nprivate @com.android.codegentest.SampleDataClass.RequestFlags int mFlags\nprivate @com.android.codegentest.SampleDataClass.State int mState\npublic @android.annotation.NonNull java.lang.CharSequence charSeq\nprivate final @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses5\nprivate transient android.net.LinkAddress[] mLinkAddresses6\ntransient int[] mTmpStorage\nprivate @android.annotation.StringRes int mStringRes\nprivate @android.annotation.IntRange(from=0L, to=6L) int mDayOfWeek\nprivate @android.annotation.Size(2L) @android.annotation.NonNull @com.android.internal.util.DataClass.Each @android.annotation.FloatRange(from=0.0) float[] mCoords\nprivate static java.lang.String defaultName4()\nprivate int[] lazyInitTmpStorage()\npublic android.net.LinkAddress[] getLinkAddresses4()\nprivate boolean patternEquals(java.util.regex.Pattern)\nprivate int patternHashCode()\nprivate void onConstructed()\npublic void dump(java.io.PrintWriter)\nclass SampleDataClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genConstructor=true, genEqualsHashCode=true, genToString=true, genForEachField=true, genSetters=true)")
@Deprecated
diff --git a/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java b/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java
index b5f6c73..1c87e8f 100644
--- a/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java
+++ b/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java
@@ -85,13 +85,17 @@
- // Code below generated by codegen v1.0.7.
+ // Code below generated by codegen v1.0.8.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
//
// To regenerate run:
// $ codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
@DataClass.Generated.Member
@@ -249,8 +253,8 @@
}
@DataClass.Generated(
- time = 1570576453295L,
- codegenVersion = "1.0.7",
+ time = 1570828330331L,
+ codegenVersion = "1.0.8",
sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java",
inputSignatures = " long delayAmount\n @android.annotation.NonNull java.util.concurrent.TimeUnit delayUnit\n long creationTimestamp\nprivate static java.util.concurrent.TimeUnit unparcelDelayUnit(android.os.Parcel)\nprivate void parcelDelayUnit(android.os.Parcel,int)\nclass SampleWithCustomBuilder extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genAidl=false, genToString=true)\nabstract com.android.codegentest.SampleWithCustomBuilder.Builder setDelayAmount(long)\npublic abstract com.android.codegentest.SampleWithCustomBuilder.Builder setDelayUnit(java.util.concurrent.TimeUnit)\npublic com.android.codegentest.SampleWithCustomBuilder.Builder setDelay(long,java.util.concurrent.TimeUnit)\nclass BaseBuilder extends java.lang.Object implements []")
@Deprecated
diff --git a/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java b/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java
index 0ce8aba..27af37f 100644
--- a/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java
+++ b/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java
@@ -51,18 +51,22 @@
- // Code below generated by codegen v1.0.7.
+ // Code below generated by codegen v1.0.8.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
//
// To regenerate run:
// $ codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
@DataClass.Generated(
- time = 1570576457249L,
- codegenVersion = "1.0.7",
+ time = 1570828334384L,
+ codegenVersion = "1.0.8",
sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java",
inputSignatures = "public @android.annotation.NonNull java.lang.String someMethod(int)\nclass StaleDataclassDetectorFalsePositivesTest extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false)")
@Deprecated
diff --git a/tests/Gating/Android.bp b/tests/Gating/Android.bp
new file mode 100644
index 0000000..b6c0094
--- /dev/null
+++ b/tests/Gating/Android.bp
@@ -0,0 +1,17 @@
+android_test {
+ name: "Gating",
+ // Only compile source java files in this apk.
+ srcs: ["src/**/*.java"],
+ certificate: "platform",
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ ],
+ static_libs: [
+ "junit",
+ "android-support-test",
+ "mockito-target-minus-junit4",
+ "truth-prebuilt",
+ "platform_compat-test-rules"
+ ],
+}
diff --git a/tests/Gating/AndroidManifest.xml b/tests/Gating/AndroidManifest.xml
new file mode 100644
index 0000000..7f14b83
--- /dev/null
+++ b/tests/Gating/AndroidManifest.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.tests.gating">
+ <application android:label="GatingTest">
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.tests.gating"/>
+</manifest>
diff --git a/tests/Gating/AndroidTest.xml b/tests/Gating/AndroidTest.xml
new file mode 100644
index 0000000..730e74a
--- /dev/null
+++ b/tests/Gating/AndroidTest.xml
@@ -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.
+-->
+<configuration description="Test compatibility change gating.">
+ <target_preparer class="com.android.tradefed.targetprep.TestFilePushSetup"/>
+ <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="test-file-name" value="Gating.apk"/>
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"/>
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"/>
+ <option name="test-suite-tag" value="apct"/>
+ <option name="test-tag" value="Gating"/>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="com.android.tests.gating"/>
+ <option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/>
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+</configuration>
diff --git a/tests/Gating/src/com/android/compat/DummyApi.java b/tests/Gating/src/com/android/compat/DummyApi.java
new file mode 100644
index 0000000..93d60d0
--- /dev/null
+++ b/tests/Gating/src/com/android/compat/DummyApi.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2016 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.compat;
+
+import android.compat.Compatibility;
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
+import com.android.internal.compat.IPlatformCompat;
+
+/**
+ * This is a dummy API to test gating
+ *
+ * @hide
+ */
+public class DummyApi {
+
+ public static final long CHANGE_ID = 666013;
+ public static final long CHANGE_ID_1 = 666014;
+ public static final long CHANGE_ID_2 = 666015;
+ public static final long CHANGE_SYSTEM_SERVER = 666016;
+
+ /**
+ * Dummy method
+ * @return "A" if change is enabled, "B" otherwise.
+ */
+ public static String dummyFunc() {
+ if (Compatibility.isChangeEnabled(CHANGE_ID)) {
+ return "A";
+ }
+ return "B";
+ }
+
+ /**
+ * Dummy combined method
+ * @return "0" if {@link CHANGE_ID_1} is disabled and {@link CHANGE_ID_2} is disabled,
+ "1" if {@link CHANGE_ID_1} is disabled and {@link CHANGE_ID_2} is enabled,
+ "2" if {@link CHANGE_ID_1} is enabled and {@link CHANGE_ID_2} is disabled,
+ "3" if {@link CHANGE_ID_1} is enabled and {@link CHANGE_ID_2} is enabled.
+ */
+ public static String dummyCombinedFunc() {
+ if (!Compatibility.isChangeEnabled(CHANGE_ID_1)
+ && !Compatibility.isChangeEnabled(CHANGE_ID_2)) {
+ return "0";
+ } else if (!Compatibility.isChangeEnabled(CHANGE_ID_1)
+ && Compatibility.isChangeEnabled(CHANGE_ID_2)) {
+ return "1";
+ } else if (Compatibility.isChangeEnabled(CHANGE_ID_1)
+ && !Compatibility.isChangeEnabled(CHANGE_ID_2)) {
+ return "2";
+ }
+ return "3";
+ }
+
+ /**
+ * Dummy api using system server API.
+ */
+ public static String dummySystemServer(Context context) {
+ IPlatformCompat platformCompat = IPlatformCompat.Stub
+ .asInterface(ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
+ if (platformCompat == null) {
+ throw new RuntimeException("Could not obtain IPlatformCompat instance!");
+ }
+ String packageName = context.getPackageName();
+ try {
+ if (platformCompat.isChangeEnabledByPackageName(CHANGE_SYSTEM_SERVER, packageName)) {
+ return "True";
+ } else {
+ return "False";
+ }
+ } catch (RemoteException e) {
+ throw new RuntimeException("Could not get change value!", e);
+ }
+ }
+}
diff --git a/tests/Gating/src/com/android/tests/gating/GatingTest.java b/tests/Gating/src/com/android/tests/gating/GatingTest.java
new file mode 100644
index 0000000..0867624
--- /dev/null
+++ b/tests/Gating/src/com/android/tests/gating/GatingTest.java
@@ -0,0 +1,95 @@
+/*
+ * 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.gating;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.compat.CompatChangeRule;
+import android.compat.CompatChangeRule.DisableCompatChanges;
+import android.compat.CompatChangeRule.EnableCompatChanges;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.compat.DummyApi;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for platform compatibility change gating.
+ */
+@RunWith(AndroidJUnit4.class)
+public class GatingTest {
+
+ @Rule
+ public TestRule compatChangeRule = new CompatChangeRule();
+
+ @Test
+ @EnableCompatChanges({DummyApi.CHANGE_ID})
+ public void testDummyGatingPositive() {
+ assertThat(DummyApi.dummyFunc()).isEqualTo("A");
+ }
+
+ @Test
+ @DisableCompatChanges({DummyApi.CHANGE_ID})
+ public void testDummyGatingNegative() {
+ assertThat(DummyApi.dummyFunc()).isEqualTo("B");
+ }
+
+ @Test
+ @DisableCompatChanges({DummyApi.CHANGE_ID_1, DummyApi.CHANGE_ID_2})
+ public void testDummyGatingCombined0() {
+ assertThat(DummyApi.dummyCombinedFunc()).isEqualTo("0");
+ }
+
+ @Test
+ @DisableCompatChanges({DummyApi.CHANGE_ID_1})
+ @EnableCompatChanges({DummyApi.CHANGE_ID_2})
+ public void testDummyGatingCombined1() {
+ assertThat(DummyApi.dummyCombinedFunc()).isEqualTo("1");
+ }
+
+ @Test
+ @EnableCompatChanges({DummyApi.CHANGE_ID_1})
+ @DisableCompatChanges({DummyApi.CHANGE_ID_2})
+ public void testDummyGatingCombined2() {
+ assertThat(DummyApi.dummyCombinedFunc()).isEqualTo("2");
+ }
+
+ @Test
+ @EnableCompatChanges({DummyApi.CHANGE_ID_1, DummyApi.CHANGE_ID_2})
+ public void testDummyGatingCombined3() {
+ assertThat(DummyApi.dummyCombinedFunc()).isEqualTo("3");
+ }
+
+ @Test
+ @EnableCompatChanges({DummyApi.CHANGE_SYSTEM_SERVER})
+ public void testDummyGatingPositiveSystemServer() {
+ assertThat(
+ DummyApi.dummySystemServer(InstrumentationRegistry.getTargetContext())).isEqualTo(
+ "True");
+ }
+
+ @Test
+ @DisableCompatChanges({DummyApi.CHANGE_SYSTEM_SERVER})
+ public void testDummyGatingNegativeSystemServer() {
+ assertThat(
+ DummyApi.dummySystemServer(InstrumentationRegistry.getTargetContext())).isEqualTo(
+ "False");
+ }
+}
diff --git a/tests/RollbackTest/Android.bp b/tests/RollbackTest/Android.bp
index 231d045b..085c53c 100644
--- a/tests/RollbackTest/Android.bp
+++ b/tests/RollbackTest/Android.bp
@@ -19,7 +19,6 @@
static_libs: ["androidx.test.rules", "cts-rollback-lib", "cts-install-lib"],
test_suites: ["general-tests"],
test_config: "RollbackTest.xml",
- // TODO: sdk_version: "test_current" when Intent#resolveSystemservice is TestApi
}
java_test_host {
diff --git a/tools/codegen/src/com/android/codegen/FieldInfo.kt b/tools/codegen/src/com/android/codegen/FieldInfo.kt
index ba00264..1a7fd6e 100644
--- a/tools/codegen/src/com/android/codegen/FieldInfo.kt
+++ b/tools/codegen/src/com/android/codegen/FieldInfo.kt
@@ -191,7 +191,7 @@
* Parcel.write* and Parcel.read* method name wildcard values
*/
val ParcelMethodsSuffix = when {
- FieldClass in PRIMITIVE_TYPES - "char" - "boolean" +
+ FieldClass in PRIMITIVE_TYPES - "char" - "boolean" + BOXED_PRIMITIVE_TYPES +
listOf("String", "CharSequence", "Exception", "Size", "SizeF", "Bundle",
"FileDescriptor", "SparseBooleanArray", "SparseIntArray", "SparseArray") ->
FieldClass
diff --git a/tools/codegen/src/com/android/codegen/Generators.kt b/tools/codegen/src/com/android/codegen/Generators.kt
index 5a95676..0ebb3cf 100644
--- a/tools/codegen/src/com/android/codegen/Generators.kt
+++ b/tools/codegen/src/com/android/codegen/Generators.kt
@@ -854,6 +854,7 @@
it.nameAsString == intOrStringDef?.AnnotationName
|| it.nameAsString in knownNonValidationAnnotations
|| it in perElementValidations
+ || it.args.any { (_, value) -> value is ArrayInitializerExpr }
}.forEach { annotation ->
appendValidateCall(annotation,
valueToValidate = if (annotation.nameAsString == Size) sizeExpr else name)
@@ -874,14 +875,7 @@
val validate = memberRef("com.android.internal.util.AnnotationValidations.validate")
"$validate(" {
!"${annotation.nameAsString}.class, null, $valueToValidate"
- val params = when (annotation) {
- is MarkerAnnotationExpr -> emptyMap()
- is SingleMemberAnnotationExpr -> mapOf("value" to annotation.memberValue)
- is NormalAnnotationExpr ->
- annotation.pairs.map { it.name.asString() to it.value }.toMap()
- else -> throw IllegalStateException()
- }
- params.forEach { name, value ->
+ annotation.args.forEach { name, value ->
!",\n\"$name\", $value"
}
}
diff --git a/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt b/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt
index 24cf469..d6953c0 100644
--- a/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt
+++ b/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt
@@ -87,6 +87,14 @@
is IntegerLiteralExpr -> sb.append(ex.asInt()).append("L")
is LongLiteralExpr -> sb.append(ex.asLong()).append("L")
is DoubleLiteralExpr -> sb.append(ex.asDouble())
+ is ArrayInitializerExpr -> {
+ sb.append("{")
+ ex.values.forEachLastAware { arrayElem, isLast ->
+ appendExpr(sb, arrayElem)
+ if (!isLast) sb.append(", ")
+ }
+ sb.append("}")
+ }
else -> sb.append(ex)
}
}
diff --git a/tools/codegen/src/com/android/codegen/Main.kt b/tools/codegen/src/com/android/codegen/Main.kt
index 039f7b2..ce83d3d 100755
--- a/tools/codegen/src/com/android/codegen/Main.kt
+++ b/tools/codegen/src/com/android/codegen/Main.kt
@@ -9,6 +9,7 @@
const val INDENT_SINGLE = " "
val PRIMITIVE_TYPES = listOf("byte", "short", "int", "long", "char", "float", "double", "boolean")
+val BOXED_PRIMITIVE_TYPES = PRIMITIVE_TYPES.map { it.capitalize() } - "Int" + "Integer" - "Char" + "Character"
val BUILTIN_SPECIAL_PARCELLINGS = listOf("Pattern")
@@ -142,6 +143,10 @@
//
// To regenerate run:
// $ $cliExecutable ${cliArgs.dropLast(1).joinToString("") { "$it " }}$fileEscaped
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
"""
diff --git a/tools/codegen/src/com/android/codegen/SharedConstants.kt b/tools/codegen/src/com/android/codegen/SharedConstants.kt
index 47f774f..8c4583f 100644
--- a/tools/codegen/src/com/android/codegen/SharedConstants.kt
+++ b/tools/codegen/src/com/android/codegen/SharedConstants.kt
@@ -1,7 +1,7 @@
package com.android.codegen
const val CODEGEN_NAME = "codegen"
-const val CODEGEN_VERSION = "1.0.7"
+const val CODEGEN_VERSION = "1.0.8"
const val CANONICAL_BUILDER_CLASS = "Builder"
const val BASE_BUILDER_CLASS = "BaseBuilder"
diff --git a/tools/codegen/src/com/android/codegen/Utils.kt b/tools/codegen/src/com/android/codegen/Utils.kt
index a1f068a..e703397 100644
--- a/tools/codegen/src/com/android/codegen/Utils.kt
+++ b/tools/codegen/src/com/android/codegen/Utils.kt
@@ -1,9 +1,6 @@
package com.android.codegen
-import com.github.javaparser.ast.Modifier
-import com.github.javaparser.ast.expr.AnnotationExpr
-import com.github.javaparser.ast.expr.Expression
-import com.github.javaparser.ast.expr.SingleMemberAnnotationExpr
+import com.github.javaparser.ast.expr.*
import com.github.javaparser.ast.nodeTypes.NodeWithModifiers
import java.time.Instant
import java.time.ZoneId
@@ -88,3 +85,10 @@
}
fun bitAtExpr(bitIndex: Int) = "0x${java.lang.Long.toHexString(1L shl bitIndex)}"
+
+val AnnotationExpr.args: Map<String, Expression> get() = when (this) {
+ is MarkerAnnotationExpr -> emptyMap()
+ is SingleMemberAnnotationExpr -> mapOf("value" to memberValue)
+ is NormalAnnotationExpr -> pairs.map { it.name.asString() to it.value }.toMap()
+ else -> throw IllegalArgumentException("Unknown annotation expression: $this")
+}
diff --git a/tools/streaming_proto/Android.bp b/tools/streaming_proto/Android.bp
index 14eead8..1390f63 100644
--- a/tools/streaming_proto/Android.bp
+++ b/tools/streaming_proto/Android.bp
@@ -29,7 +29,7 @@
"-Werror",
],
- shared_libs: ["libprotoc"],
+ static_libs: ["libprotoc"],
}
cc_binary_host {