diff options
353 files changed, 12011 insertions, 3100 deletions
diff --git a/Android.bp b/Android.bp index 7219ef59daa5..7693a66fc1f7 100644 --- a/Android.bp +++ b/Android.bp @@ -1420,7 +1420,6 @@ stubs_defaults { libs: [ "ext", "framework", - "voip-common", ], local_sourcepaths: frameworks_base_subdirs, installable: false, diff --git a/apct-tests/perftests/core/src/android/wm/RelayoutPerfTest.java b/apct-tests/perftests/core/src/android/wm/RelayoutPerfTest.java index 1f12ae6e42c6..f0c474bfc5bb 100644 --- a/apct-tests/perftests/core/src/android/wm/RelayoutPerfTest.java +++ b/apct-tests/perftests/core/src/android/wm/RelayoutPerfTest.java @@ -39,7 +39,6 @@ import android.widget.LinearLayout; import androidx.test.filters.LargeTest; import androidx.test.rule.ActivityTestRule; -import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -51,9 +50,7 @@ import java.util.function.IntSupplier; @RunWith(Parameterized.class) @LargeTest -public class RelayoutPerfTest { - private static final IWindowSession sSession = WindowManagerGlobal.getWindowSession(); - +public class RelayoutPerfTest extends WindowManagerPerfTestBase { private int mIteration; @Rule @@ -85,12 +82,6 @@ public class RelayoutPerfTest { }); } - @Before - public void setUp() { - getInstrumentation().getUiAutomation().executeShellCommand("input keyevent KEYCODE_WAKEUP"); - getInstrumentation().getUiAutomation().executeShellCommand("wm dismiss-keyguard"); - } - @Test public void testRelayout() throws Throwable { final Activity activity = mActivityRule.getActivity(); @@ -154,8 +145,9 @@ public class RelayoutPerfTest { } void runBenchmark(BenchmarkState state) throws RemoteException { + final IWindowSession session = WindowManagerGlobal.getWindowSession(); while (state.keepRunning()) { - sSession.relayout(mWindow, mSeq, mParams, mWidth, mHeight, + session.relayout(mWindow, mSeq, mParams, mWidth, mHeight, mViewVisibility.getAsInt(), mFlags, mFrameNumber, mOutFrame, mOutOverscanInsets, mOutContentInsets, mOutVisibleInsets, mOutStableInsets, mOutOutsets, mOutBackDropFrame, mOutDisplayCutout, mOutMergedConfiguration, diff --git a/apct-tests/perftests/core/src/android/wm/WindowAddRemovePerfTest.java b/apct-tests/perftests/core/src/android/wm/WindowAddRemovePerfTest.java new file mode 100644 index 000000000000..a95186916cea --- /dev/null +++ b/apct-tests/perftests/core/src/android/wm/WindowAddRemovePerfTest.java @@ -0,0 +1,108 @@ +/* + * 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.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; +import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; + +import android.graphics.Rect; +import android.os.RemoteException; +import android.os.SystemClock; +import android.perftests.utils.ManualBenchmarkState; +import android.perftests.utils.ManualBenchmarkState.ManualBenchmarkTest; +import android.perftests.utils.PerfManualStatusReporter; +import android.view.Display; +import android.view.DisplayCutout; +import android.view.IWindowSession; +import android.view.InputChannel; +import android.view.InsetsState; +import android.view.View; +import android.view.WindowManager; +import android.view.WindowManagerGlobal; + +import androidx.test.filters.LargeTest; + +import com.android.internal.view.BaseIWindow; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; + +@LargeTest +public class WindowAddRemovePerfTest extends WindowManagerPerfTestBase { + @Rule + public final PerfManualStatusReporter mPerfStatusReporter = new PerfManualStatusReporter(); + + @BeforeClass + public static void setUpClass() { + // Get the permission to use most window types. + sUiAutomation.adoptShellPermissionIdentity(); + } + + @AfterClass + public static void tearDownClass() { + sUiAutomation.dropShellPermissionIdentity(); + } + + @Test + @ManualBenchmarkTest(warmupDurationNs = WARMUP_DURATION, targetTestDurationNs = TEST_DURATION) + public void testAddRemoveWindow() throws Throwable { + new TestWindow().runBenchmark(mPerfStatusReporter.getBenchmarkState()); + } + + private static class TestWindow extends BaseIWindow { + final WindowManager.LayoutParams mLayoutParams = new WindowManager.LayoutParams(); + final Rect mOutFrame = new Rect(); + final Rect mOutContentInsets = new Rect(); + final Rect mOutStableInsets = new Rect(); + final Rect mOutOutsets = new Rect(); + final DisplayCutout.ParcelableWrapper mOutDisplayCutout = + new DisplayCutout.ParcelableWrapper(); + final InsetsState mOutInsetsState = new InsetsState(); + + TestWindow() { + mLayoutParams.setTitle(TestWindow.class.getName()); + mLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; + // Simulate as common phone window. + mLayoutParams.flags = FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR; + } + + void runBenchmark(ManualBenchmarkState state) throws RemoteException { + final IWindowSession session = WindowManagerGlobal.getWindowSession(); + long elapsedTimeNs = 0; + while (state.keepRunning(elapsedTimeNs)) { + // InputChannel cannot be reused. + final InputChannel inputChannel = new InputChannel(); + + long startTime = SystemClock.elapsedRealtimeNanos(); + session.addToDisplay(this, mSeq, mLayoutParams, View.VISIBLE, + Display.DEFAULT_DISPLAY, mOutFrame, mOutContentInsets, mOutStableInsets, + mOutOutsets, mOutDisplayCutout, inputChannel, mOutInsetsState); + final long elapsedTimeNsOfAdd = SystemClock.elapsedRealtimeNanos() - startTime; + state.addExtraResult("add", elapsedTimeNsOfAdd); + + startTime = SystemClock.elapsedRealtimeNanos(); + session.remove(this); + final long elapsedTimeNsOfRemove = SystemClock.elapsedRealtimeNanos() - startTime; + state.addExtraResult("remove", elapsedTimeNsOfRemove); + + elapsedTimeNs = elapsedTimeNsOfAdd + elapsedTimeNsOfRemove; + } + } + } +} diff --git a/apct-tests/perftests/core/src/android/wm/WindowManagerPerfTestBase.java b/apct-tests/perftests/core/src/android/wm/WindowManagerPerfTestBase.java new file mode 100644 index 000000000000..b2c61688ef59 --- /dev/null +++ b/apct-tests/perftests/core/src/android/wm/WindowManagerPerfTestBase.java @@ -0,0 +1,37 @@ +/* + * 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 androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + +import android.app.UiAutomation; + +import org.junit.Before; + +public class WindowManagerPerfTestBase { + static final UiAutomation sUiAutomation = getInstrumentation().getUiAutomation(); + static final long NANOS_PER_S = 1000L * 1000 * 1000; + static final long WARMUP_DURATION = 1 * NANOS_PER_S; + static final long TEST_DURATION = 5 * NANOS_PER_S; + + @Before + public void setUp() { + // In order to be closer to the real use case. + sUiAutomation.executeShellCommand("input keyevent KEYCODE_WAKEUP"); + sUiAutomation.executeShellCommand("wm dismiss-keyguard"); + } +} 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 40778de4e521..dd43ae70cc5c 100644 --- a/apct-tests/perftests/utils/src/android/perftests/utils/ManualBenchmarkState.java +++ b/apct-tests/perftests/utils/src/android/perftests/utils/ManualBenchmarkState.java @@ -19,8 +19,13 @@ package android.perftests.utils; import android.app.Activity; import android.app.Instrumentation; import android.os.Bundle; +import android.util.ArrayMap; import android.util.Log; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.ArrayList; import java.util.concurrent.TimeUnit; @@ -71,6 +76,8 @@ public final class ManualBenchmarkState { private int mState = NOT_STARTED; // Current benchmark state. + private long mWarmupDurationNs = WARMUP_DURATION_NS; + private long mTargetTestDurationNs = TARGET_TEST_DURATION_NS; private long mWarmupStartTime = 0; private int mWarmupIterations = 0; @@ -79,12 +86,30 @@ public final class ManualBenchmarkState { // Individual duration in nano seconds. private ArrayList<Long> mResults = new ArrayList<>(); + /** @see #addExtraResult(String, long) */ + private ArrayMap<String, ArrayList<Long>> mExtraResults; + // Statistics. These values will be filled when the benchmark has finished. // The computation needs double precision, but long int is fine for final reporting. private Stats mStats; + void configure(ManualBenchmarkTest testAnnotation) { + if (testAnnotation == null) { + return; + } + + final long warmupDurationNs = testAnnotation.warmupDurationNs(); + if (warmupDurationNs >= 0) { + mWarmupDurationNs = warmupDurationNs; + } + final long targetTestDurationNs = testAnnotation.targetTestDurationNs(); + if (targetTestDurationNs >= 0) { + mTargetTestDurationNs = targetTestDurationNs; + } + } + private void beginBenchmark(long warmupDuration, int iterations) { - mMaxIterations = (int) (TARGET_TEST_DURATION_NS / (warmupDuration / iterations)); + mMaxIterations = (int) (mTargetTestDurationNs / (warmupDuration / iterations)); mMaxIterations = Math.min(MAX_TEST_ITERATIONS, Math.max(mMaxIterations, MIN_TEST_ITERATIONS)); mState = RUNNING; @@ -108,7 +133,7 @@ public final class ManualBenchmarkState { final long timeSinceStartingWarmup = System.nanoTime() - mWarmupStartTime; ++mWarmupIterations; if (mWarmupIterations >= WARMUP_MIN_ITERATIONS - && timeSinceStartingWarmup >= WARMUP_DURATION_NS) { + && timeSinceStartingWarmup >= mWarmupDurationNs) { beginBenchmark(timeSinceStartingWarmup, mWarmupIterations); } return true; @@ -129,31 +154,69 @@ public final class ManualBenchmarkState { } } - private String summaryLine() { - final StringBuilder sb = new StringBuilder(); - sb.append("Summary: "); - sb.append("median=").append(mStats.getMedian()).append("ns, "); - sb.append("mean=").append(mStats.getMean()).append("ns, "); - sb.append("min=").append(mStats.getMin()).append("ns, "); - sb.append("max=").append(mStats.getMax()).append("ns, "); - sb.append("sigma=").append(mStats.getStandardDeviation()).append(", "); - sb.append("iteration=").append(mResults.size()).append(", "); - sb.append("values=").append(mResults.toString()); + /** + * Adds additional result while this benchmark is running. 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) { + return; + } + if (mExtraResults == null) { + mExtraResults = new ArrayMap<>(); + } + mExtraResults.computeIfAbsent(key, k -> new ArrayList<>()).add(duration); + } + + private static String summaryLine(String key, Stats stats, ArrayList<Long> results) { + final StringBuilder sb = new StringBuilder(key); + sb.append(" Summary: "); + sb.append("median=").append(stats.getMedian()).append("ns, "); + sb.append("mean=").append(stats.getMean()).append("ns, "); + sb.append("min=").append(stats.getMin()).append("ns, "); + sb.append("max=").append(stats.getMax()).append("ns, "); + sb.append("sigma=").append(stats.getStandardDeviation()).append(", "); + sb.append("iteration=").append(results.size()).append(", "); + sb.append("values="); + if (results.size() > 100) { + sb.append(results.subList(0, 100)).append(" ..."); + } else { + sb.append(results); + } return sb.toString(); } + private static void fillStatus(Bundle status, String key, Stats stats) { + status.putLong(key + "_median", stats.getMedian()); + status.putLong(key + "_mean", (long) stats.getMean()); + status.putLong(key + "_percentile90", stats.getPercentile90()); + status.putLong(key + "_percentile95", stats.getPercentile95()); + status.putLong(key + "_stddev", (long) stats.getStandardDeviation()); + } + public void sendFullStatusReport(Instrumentation instrumentation, String key) { if (mState != FINISHED) { throw new IllegalStateException("The benchmark hasn't finished"); } - Log.i(TAG, key + summaryLine()); + Log.i(TAG, summaryLine(key, mStats, mResults)); final Bundle status = new Bundle(); - status.putLong(key + "_median", mStats.getMedian()); - status.putLong(key + "_mean", (long) mStats.getMean()); - status.putLong(key + "_percentile90", mStats.getPercentile90()); - status.putLong(key + "_percentile95", mStats.getPercentile95()); - status.putLong(key + "_stddev", (long) mStats.getStandardDeviation()); + fillStatus(status, key, mStats); + if (mExtraResults != null) { + for (int i = 0; i < mExtraResults.size(); i++) { + final String subKey = key + "_" + mExtraResults.keyAt(i); + final Stats stats = new Stats(mExtraResults.valueAt(i)); + Log.i(TAG, summaryLine(subKey, mStats, mResults)); + fillStatus(status, subKey, stats); + } + } instrumentation.sendStatus(Activity.RESULT_OK, status); } -} + /** The annotation to customize the test, e.g. the duration of warm-up and target test. */ + @Target(ElementType.METHOD) + @Retention(RetentionPolicy.RUNTIME) + public @interface ManualBenchmarkTest { + long warmupDurationNs() default -1; + long targetTestDurationNs() default -1; + } +} diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/PerfManualStatusReporter.java b/apct-tests/perftests/utils/src/android/perftests/utils/PerfManualStatusReporter.java index 8187c6fa63d5..8ff6a16e8b7e 100644 --- a/apct-tests/perftests/utils/src/android/perftests/utils/PerfManualStatusReporter.java +++ b/apct-tests/perftests/utils/src/android/perftests/utils/PerfManualStatusReporter.java @@ -16,7 +16,7 @@ package android.perftests.utils; -import androidx.test.InstrumentationRegistry; +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; import org.junit.rules.TestRule; import org.junit.runner.Description; @@ -59,15 +59,15 @@ public class PerfManualStatusReporter implements TestRule { @Override public Statement apply(Statement base, Description description) { + mState.configure(description.getAnnotation(ManualBenchmarkState.ManualBenchmarkTest.class)); + return new Statement() { @Override public void evaluate() throws Throwable { base.evaluate(); - mState.sendFullStatusReport(InstrumentationRegistry.getInstrumentation(), - description.getMethodName()); + mState.sendFullStatusReport(getInstrumentation(), description.getMethodName()); } }; } } - diff --git a/api/current.txt b/api/current.txt index 25801cac203e..a7b38a5d5b10 100644 --- a/api/current.txt +++ b/api/current.txt @@ -5842,12 +5842,19 @@ package android.app { method public boolean updateAutomaticZenRule(String, android.app.AutomaticZenRule); field public static final String ACTION_APP_BLOCK_STATE_CHANGED = "android.app.action.APP_BLOCK_STATE_CHANGED"; field public static final String ACTION_AUTOMATIC_ZEN_RULE = "android.app.action.AUTOMATIC_ZEN_RULE"; + field public static final String ACTION_AUTOMATIC_ZEN_RULE_STATUS_CHANGED = "android.app.action.AUTOMATIC_ZEN_RULE_STATUS_CHANGED"; field public static final String ACTION_INTERRUPTION_FILTER_CHANGED = "android.app.action.INTERRUPTION_FILTER_CHANGED"; field public static final String ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED = "android.app.action.NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED"; field public static final String ACTION_NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED = "android.app.action.NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED"; field public static final String ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED = "android.app.action.NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED"; field public static final String ACTION_NOTIFICATION_POLICY_CHANGED = "android.app.action.NOTIFICATION_POLICY_CHANGED"; + field public static final int AUTOMATIC_RULE_STATUS_DISABLED = 2; // 0x2 + field public static final int AUTOMATIC_RULE_STATUS_ENABLED = 1; // 0x1 + field public static final int AUTOMATIC_RULE_STATUS_REMOVED = 3; // 0x3 + field public static final int AUTOMATIC_RULE_STATUS_UNKNOWN = -1; // 0xffffffff field public static final String EXTRA_AUTOMATIC_RULE_ID = "android.app.extra.AUTOMATIC_RULE_ID"; + field public static final String EXTRA_AUTOMATIC_ZEN_RULE_ID = "android.app.extra.AUTOMATIC_ZEN_RULE_ID"; + field public static final String EXTRA_AUTOMATIC_ZEN_RULE_STATUS = "android.app.extra.AUTOMATIC_ZEN_RULE_STATUS"; field public static final String EXTRA_BLOCKED_STATE = "android.app.extra.BLOCKED_STATE"; field public static final String EXTRA_NOTIFICATION_CHANNEL_GROUP_ID = "android.app.extra.NOTIFICATION_CHANNEL_GROUP_ID"; field public static final String EXTRA_NOTIFICATION_CHANNEL_ID = "android.app.extra.NOTIFICATION_CHANNEL_ID"; @@ -10827,7 +10834,9 @@ package android.content { method @Nullable public String getString(String, @Nullable String); method @Nullable public java.util.Set<java.lang.String> getStringSet(String, @Nullable java.util.Set<java.lang.String>); method public void registerOnSharedPreferenceChangeListener(android.content.SharedPreferences.OnSharedPreferenceChangeListener); + method public default void registerOnSharedPreferencesClearListener(@NonNull android.content.SharedPreferences.OnSharedPreferencesClearListener); method public void unregisterOnSharedPreferenceChangeListener(android.content.SharedPreferences.OnSharedPreferenceChangeListener); + method public default void unregisterOnSharedPreferencesClearListener(@NonNull android.content.SharedPreferences.OnSharedPreferencesClearListener); } public static interface SharedPreferences.Editor { @@ -10847,6 +10856,10 @@ package android.content { method public void onSharedPreferenceChanged(android.content.SharedPreferences, String); } + public static interface SharedPreferences.OnSharedPreferencesClearListener { + method public void onSharedPreferencesClear(@NonNull android.content.SharedPreferences, @NonNull java.util.Set<java.lang.String>); + } + public class SyncAdapterType implements android.os.Parcelable { ctor public SyncAdapterType(String, String, boolean, boolean); ctor public SyncAdapterType(android.os.Parcel); @@ -13116,8 +13129,11 @@ package android.database.sqlite { method @Nullable public android.database.sqlite.SQLiteDatabase.CursorFactory getCursorFactory(); method @Nullable public java.util.Map<java.lang.String,java.lang.String> getProjectionMap(); method @Nullable public String getTables(); + method public long insert(@NonNull android.database.sqlite.SQLiteDatabase, @NonNull android.content.ContentValues); method public boolean isDistinct(); method public boolean isStrict(); + method public boolean isStrictColumns(); + method public boolean isStrictGrammar(); method public android.database.Cursor query(android.database.sqlite.SQLiteDatabase, String[], String, String[], String, String, String); method public android.database.Cursor query(android.database.sqlite.SQLiteDatabase, String[], String, String[], String, String, String, String); method public android.database.Cursor query(android.database.sqlite.SQLiteDatabase, String[], String, String[], String, String, String, String, android.os.CancellationSignal); @@ -13125,6 +13141,8 @@ package android.database.sqlite { method public void setDistinct(boolean); method public void setProjectionMap(@Nullable java.util.Map<java.lang.String,java.lang.String>); method public void setStrict(boolean); + method public void setStrictColumns(boolean); + method public void setStrictGrammar(boolean); method public void setTables(@Nullable String); method public int update(@NonNull android.database.sqlite.SQLiteDatabase, @NonNull android.content.ContentValues, @Nullable String, @Nullable String[]); } @@ -41046,6 +41064,7 @@ package android.service.autofill { method public int getType(); field public static final int TYPE_AUTHENTICATION_SELECTED = 2; // 0x2 field public static final int TYPE_CONTEXT_COMMITTED = 4; // 0x4 + field public static final int TYPE_DATASETS_SHOWN = 5; // 0x5 field public static final int TYPE_DATASET_AUTHENTICATION_SELECTED = 1; // 0x1 field public static final int TYPE_DATASET_SELECTED = 0; // 0x0 field public static final int TYPE_SAVE_SHOWN = 3; // 0x3 @@ -52428,6 +52447,7 @@ package android.view.accessibility { method public int getId(); method public int getLayer(); method public android.view.accessibility.AccessibilityWindowInfo getParent(); + method public void getRegionInScreen(@NonNull android.graphics.Region); method public android.view.accessibility.AccessibilityNodeInfo getRoot(); method @Nullable public CharSequence getTitle(); method public int getType(); diff --git a/api/removed.txt b/api/removed.txt index 7b1d241fdd6a..b075f9ef3e51 100644 --- a/api/removed.txt +++ b/api/removed.txt @@ -73,10 +73,6 @@ package android.app.usage { package android.content { - public class ClipData implements android.os.Parcelable { - method @Deprecated public void addItem(android.content.ClipData.Item, android.content.ContentResolver); - } - public abstract class Context { method public abstract android.content.SharedPreferences getSharedPreferences(java.io.File, int); method public abstract java.io.File getSharedPreferencesPath(String); diff --git a/api/system-current.txt b/api/system-current.txt index 48804b6be72a..eef6b3f98e61 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -5179,7 +5179,8 @@ package android.os { method @NonNull public static java.io.File getOdmDirectory(); method @NonNull public static java.io.File getOemDirectory(); method @NonNull public static java.io.File getProductDirectory(); - method @NonNull public static java.io.File getProductServicesDirectory(); + method @Deprecated @NonNull public static java.io.File getProductServicesDirectory(); + method @NonNull public static java.io.File getSystemExtDirectory(); method @NonNull public static java.io.File getVendorDirectory(); } diff --git a/api/test-current.txt b/api/test-current.txt index 050ec520059c..b393fccdb366 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -54,6 +54,7 @@ package android.app { method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getPackageImportance(String); method public long getTotalRam(); method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getUidImportance(int); + method public static boolean isHighEndGfx(); method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void removeOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener); method public static void resumeAppSwitches() throws android.os.RemoteException; method @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public void scheduleApplicationInfoChanged(java.util.List<java.lang.String>, int); @@ -696,6 +697,7 @@ package android.content.pm { method public void setEnableRollback(boolean); method @RequiresPermission("android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS") public void setGrantedRuntimePermissions(String[]); method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setInstallAsApex(); + method public void setRequestDowngrade(boolean); method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setStaged(); } @@ -3492,11 +3494,6 @@ package android.view.inputmethod { method public boolean isInputMethodPickerShown(); } - public class InputMethodSystemProperty { - ctor public InputMethodSystemProperty(); - field public static final boolean MULTI_CLIENT_IME_ENABLED; - } - } package android.view.inspector { diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java index 680ccfc57ddf..76b905d8985f 100644 --- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java +++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java @@ -64,6 +64,10 @@ public class Bmgr { private static final String BMGR_NOT_RUNNING_ERR = "Error: Could not access the Backup Manager. Is the system running?"; + private static final String BMGR_NOT_ACTIVATED_FOR_USER = + "Error: Backup Manager is not activated for user "; + private static final String BMGR_ERR_NO_RESTORESESSION_FOR_USER = + "Error: Could not get restore session for user "; private static final String TRANSPORT_NOT_RUNNING_ERR = "Error: Could not access the backup transport. Is the system running?"; private static final String PM_NOT_RUNNING_ERR = @@ -190,15 +194,19 @@ public class Bmgr { showUsage(); } - boolean isBackupActive(@UserIdInt int userId) { + private void handleRemoteException(RemoteException e) { + System.err.println(e.toString()); + System.err.println(BMGR_NOT_RUNNING_ERR); + } + + private boolean isBackupActive(@UserIdInt int userId) { try { if (!mBmgr.isBackupServiceActive(userId)) { - System.err.println(BMGR_NOT_RUNNING_ERR); + System.err.println(BMGR_NOT_ACTIVATED_FOR_USER + userId); return false; } } catch (RemoteException e) { - System.err.println(e.toString()); - System.err.println(BMGR_NOT_RUNNING_ERR); + handleRemoteException(e); return false; } @@ -214,8 +222,7 @@ public class Bmgr { System.out.println("Backup Manager currently " + activatedToString(mBmgr.isBackupServiceActive(userId))); } catch (RemoteException e) { - System.err.println(e.toString()); - System.err.println(BMGR_NOT_RUNNING_ERR); + handleRemoteException(e); } } @@ -230,8 +237,7 @@ public class Bmgr { System.out.println("Backup Manager currently " + enableToString(isEnabled)); } catch (RemoteException e) { - System.err.println(e.toString()); - System.err.println(BMGR_NOT_RUNNING_ERR); + handleRemoteException(e); } } @@ -250,8 +256,7 @@ public class Bmgr { showUsage(); return; } catch (RemoteException e) { - System.err.println(e.toString()); - System.err.println(BMGR_NOT_RUNNING_ERR); + handleRemoteException(e); } } @@ -259,8 +264,7 @@ public class Bmgr { try { mBmgr.backupNowForUser(userId); } catch (RemoteException e) { - System.err.println(e.toString()); - System.err.println(BMGR_NOT_RUNNING_ERR); + handleRemoteException(e); } } @@ -274,8 +278,7 @@ public class Bmgr { try { mBmgr.dataChangedForUser(userId, pkg); } catch (RemoteException e) { - System.err.println(e.toString()); - System.err.println(BMGR_NOT_RUNNING_ERR); + handleRemoteException(e); } } @@ -292,8 +295,7 @@ public class Bmgr { mBmgr.fullTransportBackupForUser( userId, allPkgs.toArray(new String[allPkgs.size()])); } catch (RemoteException e) { - System.err.println(e.toString()); - System.err.println(BMGR_NOT_RUNNING_ERR); + handleRemoteException(e); } } } @@ -421,8 +423,7 @@ public class Bmgr { try { filteredPackages = mBmgr.filterAppsEligibleForBackupForUser(userId, packages); } catch (RemoteException e) { - System.err.println(e.toString()); - System.err.println(BMGR_NOT_RUNNING_ERR); + handleRemoteException(e); } backupNowPackages(userId, Arrays.asList(filteredPackages), nonIncrementalBackup, monitorState); @@ -455,8 +456,7 @@ public class Bmgr { System.err.println("Unable to run backup"); } } catch (RemoteException e) { - System.err.println(e.toString()); - System.err.println(BMGR_NOT_RUNNING_ERR); + handleRemoteException(e); } } @@ -506,8 +506,7 @@ public class Bmgr { try { mBmgr.cancelBackupsForUser(userId); } catch (RemoteException e) { - System.err.println(e.toString()); - System.err.println(BMGR_NOT_RUNNING_ERR); + handleRemoteException(e); } return; } @@ -537,8 +536,7 @@ public class Bmgr { } } catch (RemoteException e) { - System.err.println(e.toString()); - System.err.println(BMGR_NOT_RUNNING_ERR); + handleRemoteException(e); } } @@ -569,8 +567,7 @@ public class Bmgr { } }); } catch (RemoteException e) { - System.err.println(e.toString()); - System.err.println(BMGR_NOT_RUNNING_ERR); + handleRemoteException(e); return; } @@ -598,8 +595,7 @@ public class Bmgr { mBmgr.clearBackupDataForUser(userId, transport, pkg); System.out.println("Wiped backup data for " + pkg + " on " + transport); } catch (RemoteException e) { - System.err.println(e.toString()); - System.err.println(BMGR_NOT_RUNNING_ERR); + handleRemoteException(e); } } @@ -632,8 +628,7 @@ public class Bmgr { observer.waitForCompletion(30*1000L); System.out.println("Initialization result: " + observer.result); } catch (RemoteException e) { - System.err.println(e.toString()); - System.err.println(BMGR_NOT_RUNNING_ERR); + handleRemoteException(e); } } @@ -648,7 +643,7 @@ public class Bmgr { try { mRestore = mBmgr.beginRestoreSessionForUser(userId, null, null); if (mRestore == null) { - System.err.println(BMGR_NOT_RUNNING_ERR); + System.err.println(BMGR_ERR_NO_RESTORESESSION_FOR_USER + userId); return; } @@ -658,8 +653,7 @@ public class Bmgr { mRestore.endRestoreSession(); } catch (RemoteException e) { - System.err.println(e.toString()); - System.err.println(BMGR_NOT_RUNNING_ERR); + handleRemoteException(e); } } @@ -686,8 +680,7 @@ public class Bmgr { System.out.println(pad + t); } } catch (RemoteException e) { - System.err.println(e.toString()); - System.err.println(BMGR_NOT_RUNNING_ERR); + handleRemoteException(e); } } @@ -805,7 +798,7 @@ public class Bmgr { boolean didRestore = false; mRestore = mBmgr.beginRestoreSessionForUser(userId, null, null); if (mRestore == null) { - System.err.println(BMGR_NOT_RUNNING_ERR); + System.err.println(BMGR_ERR_NO_RESTORESESSION_FOR_USER + userId); return; } RestoreSet[] sets = null; @@ -851,8 +844,7 @@ public class Bmgr { System.out.println("done"); } catch (RemoteException e) { - System.err.println(e.toString()); - System.err.println(BMGR_NOT_RUNNING_ERR); + handleRemoteException(e); } } @@ -865,8 +857,7 @@ public class Bmgr { } } } catch (RemoteException e) { - System.err.println(e.toString()); - System.err.println(BMGR_NOT_RUNNING_ERR); + handleRemoteException(e); } } @@ -886,8 +877,7 @@ public class Bmgr { + " for user " + userId); } catch (RemoteException e) { - System.err.println(e.toString()); - System.err.println(BMGR_NOT_RUNNING_ERR); + handleRemoteException(e); } } diff --git a/cmds/bootanimation/Android.bp b/cmds/bootanimation/Android.bp index 31bd612cc2c7..c1f86544070e 100644 --- a/cmds/bootanimation/Android.bp +++ b/cmds/bootanimation/Android.bp @@ -79,7 +79,6 @@ cc_library_shared { "libEGL", "libGLESv1_CM", "libgui", - "libtinyalsa", ], product_variables: { diff --git a/cmds/idmap2/idmap2/Scan.cpp b/cmds/idmap2/idmap2/Scan.cpp index cfac5f31e2e6..0b349e10a118 100644 --- a/cmds/idmap2/idmap2/Scan.cpp +++ b/cmds/idmap2/idmap2/Scan.cpp @@ -103,6 +103,7 @@ std::vector<std::string> PoliciesForPath(const std::string& apk_path) { {"/oem/", kPolicyOem}, {"/product/", kPolicyProduct}, {"/system/", kPolicySystem}, + {"/system_ext/", kPolicySystem}, {"/vendor/", kPolicyVendor}, }; diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp index 2f0dc3cb1e98..6df0a8e14f6a 100644 --- a/cmds/statsd/Android.bp +++ b/cmds/statsd/Android.bp @@ -71,7 +71,6 @@ cc_defaults { "src/config/ConfigManager.cpp", "src/external/GpuStatsPuller.cpp", "src/external/Perfetto.cpp", - "src/external/Perfprofd.cpp", "src/external/StatsPuller.cpp", "src/external/StatsCallbackPuller.cpp", "src/external/StatsCompanionServicePuller.cpp", @@ -109,8 +108,6 @@ cc_defaults { "src/socket/StatsSocketListener.cpp", "src/shell/ShellSubscriber.cpp", "src/shell/shell_config.proto", - - ":perfprofd_aidl", ], local_include_dirs: [ diff --git a/cmds/statsd/src/anomaly/subscriber_util.cpp b/cmds/statsd/src/anomaly/subscriber_util.cpp index 548a6869436d..e09d5751d323 100644 --- a/cmds/statsd/src/anomaly/subscriber_util.cpp +++ b/cmds/statsd/src/anomaly/subscriber_util.cpp @@ -22,7 +22,6 @@ #include <binder/IServiceManager.h> #include "external/Perfetto.h" -#include "external/Perfprofd.h" #include "subscriber/IncidentdReporter.h" #include "subscriber/SubscriberReporter.h" @@ -64,12 +63,6 @@ void triggerSubscribers(int64_t ruleId, int64_t metricId, const MetricDimensionK SubscriberReporter::getInstance().alertBroadcastSubscriber(configKey, subscription, dimensionKey); break; - case Subscription::SubscriberInformationCase::kPerfprofdDetails: - if (!CollectPerfprofdTraceAndUploadToDropbox(subscription.perfprofd_details(), - ruleId, configKey)) { - ALOGW("Failed to generate perfprofd traces."); - } - break; default: break; } diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index ceabd39aee8b..f6c72ea9d3b9 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -5309,6 +5309,9 @@ message NetworkDnsEventReported { // Additional pass-through fields opaque to statsd. // The DNS resolver Mainline module can add new fields here without requiring an OS update. optional android.stats.dnsresolver.DnsQueryEvents dns_query_events = 8 [(log_mode) = MODE_BYTES]; + + // The sample rate of DNS stats (to statsd) is 1/sampling_rate_denom. + optional int32 sampling_rate_denom = 9; } /** diff --git a/cmds/statsd/src/external/Perfprofd.cpp b/cmds/statsd/src/external/Perfprofd.cpp deleted file mode 100644 index 1678f104a07a..000000000000 --- a/cmds/statsd/src/external/Perfprofd.cpp +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Perfprofd.h" - -#define DEBUG false // STOPSHIP if true -#include "config/ConfigKey.h" -#include "Log.h" - -#include <errno.h> -#include <fcntl.h> -#include <sys/types.h> -#include <sys/wait.h> -#include <unistd.h> - -#include <string> - -#include <binder/IServiceManager.h> - -#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" // Alert - -#include "android/os/IPerfProfd.h" - -namespace android { -namespace os { -namespace statsd { - -bool CollectPerfprofdTraceAndUploadToDropbox(const PerfprofdDetails& config, - int64_t alert_id, - const ConfigKey& configKey) { - VLOG("Starting trace collection through perfprofd"); - - if (!config.has_perfprofd_config()) { - ALOGE("The perfprofd trace config is empty, aborting"); - return false; - } - - sp<IPerfProfd> service = interface_cast<IPerfProfd>( - defaultServiceManager()->getService(android::String16("perfprofd"))); - if (service == NULL) { - ALOGE("Could not find perfprofd service"); - return false; - } - - auto* data = reinterpret_cast<const uint8_t*>(config.perfprofd_config().data()); - std::vector<uint8_t> proto_serialized(data, data + config.perfprofd_config().size()); - - // TODO: alert-id etc? - - binder::Status status = service->startProfilingProtobuf(proto_serialized); - if (status.isOk()) { - return true; - } - - ALOGE("Error starting perfprofd profiling: %s", status.toString8().c_str()); - return false; -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/external/Perfprofd.h b/cmds/statsd/src/external/Perfprofd.h deleted file mode 100644 index b93fdf8e1cb2..000000000000 --- a/cmds/statsd/src/external/Perfprofd.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include <inttypes.h> - -namespace android { -namespace os { -namespace statsd { - -class ConfigKey; -class PerfprofdDetails; // Declared in statsd_config.pb.h - -// Starts the collection of a Perfprofd trace with the given |config|. -// The trace is uploaded to Dropbox by the perfprofd service once done. -// This method returns immediately after passing the config and does NOT wait -// for the full duration of the trace. -bool CollectPerfprofdTraceAndUploadToDropbox(const PerfprofdDetails& config, - int64_t alert_id, - const ConfigKey& configKey); - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto index a2fd9d42f488..79c06b98a82d 100644 --- a/cmds/statsd/src/statsd_config.proto +++ b/cmds/statsd/src/statsd_config.proto @@ -343,15 +343,6 @@ message PerfettoDetails { optional bytes trace_config = 1; } -message PerfprofdDetails { - // The |perfprofd_config| field is a proto-encoded message of type - // android.perfprofd.ProfilingConfig defined in - // //system/extras/perfprofd/. On device, statsd doesn't need to - // deserialize the message as it's just passed binary-encoded to - // the perfprofd service. - optional bytes perfprofd_config = 1; -} - message BroadcastSubscriberDetails { optional int64 subscriber_id = 1; repeated string cookie = 2; @@ -373,10 +364,12 @@ message Subscription { IncidentdDetails incidentd_details = 4; PerfettoDetails perfetto_details = 5; BroadcastSubscriberDetails broadcast_subscriber_details = 6; - PerfprofdDetails perfprofd_details = 8; } optional float probability_of_informing = 7 [default = 1.1]; + + // This was used for perfprofd historically. + reserved 8; } enum ActivationType { diff --git a/config/preloaded-classes b/config/preloaded-classes index b4fd031bf5a2..03e3b82c75f7 100644 --- a/config/preloaded-classes +++ b/config/preloaded-classes @@ -2696,6 +2696,7 @@ android.os.-$$Lambda$ThreadLocalWorkSource$IP9vRFCDG5YwbWbXAEGHH52B9IE android.os.-$$Lambda$Trace$2zLZ-Lc2kAXsVjw_nLYeNhqmGq0 android.os.-$$Lambda$q1UvBdLgHRZVzc68BxdksTmbuCw android.os.AsyncResult +android.os.AsyncTask android.os.AsyncTask$1 android.os.AsyncTask$2 android.os.AsyncTask$3 diff --git a/config/preloaded-classes-blacklist b/config/preloaded-classes-blacklist index b5f995088977..f05edee9c42d 100644 --- a/config/preloaded-classes-blacklist +++ b/config/preloaded-classes-blacklist @@ -1,6 +1,5 @@ android.content.AsyncTaskLoader$LoadTask android.net.ConnectivityThread$Singleton -android.os.AsyncTask android.os.FileObserver android.speech.tts.TextToSpeech$Connection$SetupConnectionAsyncTask android.widget.Magnifier diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index cebe6e1211e0..4aafa53805e1 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -644,6 +644,32 @@ public abstract class AccessibilityService extends Service { } } + @Override + public Context createDisplayContext(Display display) { + final Context context = super.createDisplayContext(display); + final int displayId = display.getDisplayId(); + setDefaultTokenInternal(context, displayId); + return context; + } + + private void setDefaultTokenInternal(Context context, int displayId) { + final WindowManagerImpl wm = (WindowManagerImpl) context.getSystemService(WINDOW_SERVICE); + final IAccessibilityServiceConnection connection = + AccessibilityInteractionClient.getInstance().getConnection(mConnectionId); + IBinder token = null; + if (connection != null) { + synchronized (mLock) { + try { + token = connection.getOverlayWindowToken(displayId); + } catch (RemoteException re) { + Log.w(LOG_TAG, "Failed to get window token", re); + re.rethrowFromSystemServer(); + } + } + wm.setDefaultToken(token); + } + } + /** * Returns the magnification controller, which may be used to query and * modify the state of display magnification. diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl index 8c38fe4aaf6f..c8e01321812c 100644 --- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl +++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl @@ -96,4 +96,6 @@ interface IAccessibilityServiceConnection { void sendGesture(int sequence, in ParceledListSlice gestureSteps); boolean isFingerprintGestureDetectionAvailable(); + + IBinder getOverlayWindowToken(int displayid); } diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 17368b789645..cb99a3aa11c6 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -925,7 +925,7 @@ public class ActivityManager { * (which tends to consume a lot more RAM). * @hide */ - @UnsupportedAppUsage + @TestApi static public boolean isHighEndGfx() { return !isLowRamDeviceStatic() && !RoSystemProperties.CONFIG_AVOID_GFX_ACCEL @@ -1822,7 +1822,8 @@ public class ActivityManager { * @hide */ public static class TaskSnapshot implements Parcelable { - + // Identifier of this snapshot + private final long mId; // Top activity in task when snapshot was taken private final ComponentName mTopActivityComponent; private final GraphicBuffer mSnapshot; @@ -1841,10 +1842,12 @@ public class ActivityManager { // Must be one of the named color spaces, otherwise, always use SRGB color space. private final ColorSpace mColorSpace; - public TaskSnapshot(@NonNull ComponentName topActivityComponent, GraphicBuffer snapshot, + public TaskSnapshot(long id, + @NonNull ComponentName topActivityComponent, GraphicBuffer snapshot, @NonNull ColorSpace colorSpace, int orientation, Rect contentInsets, boolean reducedResolution, float scale, boolean isRealSnapshot, int windowingMode, int systemUiVisibility, boolean isTranslucent) { + mId = id; mTopActivityComponent = topActivityComponent; mSnapshot = snapshot; mColorSpace = colorSpace.getId() < 0 @@ -1860,6 +1863,7 @@ public class ActivityManager { } private TaskSnapshot(Parcel source) { + mId = source.readLong(); mTopActivityComponent = ComponentName.readFromParcel(source); mSnapshot = source.readParcelable(null /* classLoader */); int colorSpaceId = source.readInt(); @@ -1877,6 +1881,13 @@ public class ActivityManager { } /** + * @return Identifier of this snapshot. + */ + public long getId() { + return mId; + } + + /** * @return The top activity component for the task at the point this snapshot was taken. */ public ComponentName getTopActivityComponent() { @@ -1970,6 +1981,7 @@ public class ActivityManager { @Override public void writeToParcel(Parcel dest, int flags) { + dest.writeLong(mId); ComponentName.writeToParcel(mTopActivityComponent, dest); dest.writeParcelable(mSnapshot, 0); dest.writeInt(mColorSpace.getId()); @@ -1988,6 +2000,7 @@ public class ActivityManager { final int width = mSnapshot != null ? mSnapshot.getWidth() : 0; final int height = mSnapshot != null ? mSnapshot.getHeight() : 0; return "TaskSnapshot{" + + " mId=" + mId + " mTopActivityComponent=" + mTopActivityComponent.flattenToShortString() + " mSnapshot=" + mSnapshot + " (" + width + "x" + height + ")" + " mColorSpace=" + mColorSpace.toString() diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index f8e6ae50d765..69e71185f228 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -26,6 +26,7 @@ import android.content.pm.ActivityInfo; import android.content.pm.ActivityPresentationInfo; import android.content.pm.ApplicationInfo; import android.content.pm.UserInfo; +import android.net.Uri; import android.os.Bundle; import android.os.IBinder; import android.os.TransactionTooLargeException; @@ -52,6 +53,12 @@ public abstract class ActivityManagerInternal { */ public abstract String checkContentProviderAccess(String authority, int userId); + /** + * Verify that calling UID has access to the given provider. + */ + public abstract int checkContentProviderUriPermission(Uri uri, int userId, + int callingUid, int modeFlags); + // Called by the power manager. public abstract void onWakefulnessChanged(int wakefulness); diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index d741b610a0e2..2b4d4f8bc6eb 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -791,6 +791,8 @@ public final class ActivityThread extends ClientTransactionHandler { @Nullable ContentCaptureOptions contentCaptureOptions; + long[] disabledCompatChanges; + @Override public String toString() { return "AppBindData{appInfo=" + appInfo + "}"; @@ -1003,7 +1005,7 @@ public final class ActivityThread extends ClientTransactionHandler { boolean isRestrictedBackupMode, boolean persistent, Configuration config, CompatibilityInfo compatInfo, Map services, Bundle coreSettings, String buildSerial, AutofillOptions autofillOptions, - ContentCaptureOptions contentCaptureOptions) { + ContentCaptureOptions contentCaptureOptions, long[] disabledCompatChanges) { if (services != null) { if (false) { // Test code to make sure the app could see the passed-in services. @@ -1051,6 +1053,7 @@ public final class ActivityThread extends ClientTransactionHandler { data.buildSerial = buildSerial; data.autofillOptions = autofillOptions; data.contentCaptureOptions = contentCaptureOptions; + data.disabledCompatChanges = disabledCompatChanges; sendMessage(H.BIND_APPLICATION, data); } @@ -6144,10 +6147,10 @@ public final class ActivityThread extends ClientTransactionHandler { if (data.trackAllocation) { DdmVmInternal.enableRecentAllocations(true); } - // Note when this process has started. Process.setStartTimes(SystemClock.elapsedRealtime(), SystemClock.uptimeMillis()); + AppCompatCallbacks.install(data.disabledCompatChanges); mBoundApplication = data; mConfiguration = new Configuration(data.config); mCompatConfiguration = new Configuration(data.config); diff --git a/core/java/android/app/AppCompatCallbacks.java b/core/java/android/app/AppCompatCallbacks.java new file mode 100644 index 000000000000..17697dba9ccd --- /dev/null +++ b/core/java/android/app/AppCompatCallbacks.java @@ -0,0 +1,64 @@ +/* + * 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.app; + +import android.compat.Compatibility; +import android.os.Process; +import android.util.Log; + +import java.util.Arrays; + +/** + * App process implementation of the {@link Compatibility} API. + * + * @hide + */ +public final class AppCompatCallbacks extends Compatibility.Callbacks { + + private static final String TAG = "Compatibility"; + + private final long[] mDisabledChanges; + + /** + * Install this class into the current process. + * + * @param disabledChanges Set of compatibility changes that are disabled for this process. + */ + public static void install(long[] disabledChanges) { + Compatibility.setCallbacks(new AppCompatCallbacks(disabledChanges)); + } + + private AppCompatCallbacks(long[] disabledChanges) { + mDisabledChanges = Arrays.copyOf(disabledChanges, disabledChanges.length); + Arrays.sort(mDisabledChanges); + } + + protected void reportChange(long changeId) { + Log.d(TAG, "Compat change reported: " + changeId + "; UID " + Process.myUid()); + // TODO log via StatsLog + } + + protected boolean isChangeEnabled(long changeId) { + if (Arrays.binarySearch(mDisabledChanges, changeId) < 0) { + // Not present in the disabled array + reportChange(changeId); + return true; + } + return false; + } + +} diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 416aaa37f064..0478ac89fbbb 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -635,7 +635,7 @@ public class ApplicationPackageManager extends PackageManager { @Override public boolean isPermissionRevokedByPolicy(String permName, String pkgName) { try { - return mPM.isPermissionRevokedByPolicy(permName, pkgName, getUserId()); + return mPermissionManager.isPermissionRevokedByPolicy(permName, pkgName, getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -795,10 +795,11 @@ public class ApplicationPackageManager extends PackageManager { @Override @UnsupportedAppUsage - public boolean shouldShowRequestPermissionRationale(String permission) { + public boolean shouldShowRequestPermissionRationale(String permName) { try { - return mPM.shouldShowRequestPermissionRationale(permission, - mContext.getPackageName(), getUserId()); + final String packageName = mContext.getPackageName(); + return mPermissionManager + .shouldShowRequestPermissionRationale(permName, packageName, getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -2049,7 +2050,7 @@ public class ApplicationPackageManager extends PackageManager { @Override public String getDefaultBrowserPackageNameAsUser(int userId) { try { - return mPM.getDefaultBrowserPackageName(userId); + return mPermissionManager.getDefaultBrowser(userId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -2058,7 +2059,7 @@ public class ApplicationPackageManager extends PackageManager { @Override public boolean setDefaultBrowserPackageNameAsUser(String packageName, int userId) { try { - return mPM.setDefaultBrowserPackageName(packageName, userId); + return mPermissionManager.setDefaultBrowser(packageName, userId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl index 66c438393bd6..cfa065ba5bc9 100644 --- a/core/java/android/app/IApplicationThread.aidl +++ b/core/java/android/app/IApplicationThread.aidl @@ -73,7 +73,7 @@ oneway interface IApplicationThread { boolean restrictedBackupMode, boolean persistent, in Configuration config, in CompatibilityInfo compatInfo, in Map services, in Bundle coreSettings, in String buildSerial, in AutofillOptions autofillOptions, - in ContentCaptureOptions contentCaptureOptions); + in ContentCaptureOptions contentCaptureOptions, in long[] disabledCompatChanges); void runIsolatedEntryPoint(in String entryPoint, in String[] entryPointArgs); void scheduleExit(); void scheduleServiceArgs(IBinder token, in ParceledListSlice args); diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index ccea964b1213..5aa1f8f07f60 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -8101,8 +8101,7 @@ public class Notification implements Parcelable */ @Override public RemoteViews makeHeadsUpContentView(boolean increasedHeight) { - RemoteViews expanded = makeMediaBigContentView(); - return expanded != null ? expanded : makeMediaContentView(); + return makeMediaContentView(); } /** @hide */ diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index c1cee77be16d..316cab8d600b 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -171,6 +171,78 @@ public class NotificationManager { "android.app.action.NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED"; /** + * Intent that is broadcast when the status of an {@link AutomaticZenRule} has changed. + * + * <p>Use this to know whether you need to continue monitor to device state in order to + * provide up-to-date states (with {@link #setAutomaticZenRuleState(String, Condition)}) for + * this rule.</p> + * + * Input: nothing + * Output: {@link #EXTRA_AUTOMATIC_ZEN_RULE_ID} + * Output: {@link #EXTRA_AUTOMATIC_ZEN_RULE_STATUS} + */ + @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_AUTOMATIC_ZEN_RULE_STATUS_CHANGED = + "android.app.action.AUTOMATIC_ZEN_RULE_STATUS_CHANGED"; + + /** + * Integer extra for {@link #ACTION_AUTOMATIC_ZEN_RULE_STATUS_CHANGED} containing the state of + * the {@link AutomaticZenRule}. + * + * <p> + * The value will be one of {@link #AUTOMATIC_RULE_STATUS_ENABLED}, + * {@link #AUTOMATIC_RULE_STATUS_DISABLED}, {@link #AUTOMATIC_RULE_STATUS_REMOVED}, + * {@link #AUTOMATIC_RULE_STATUS_UNKNOWN}. + * </p> + */ + public static final String EXTRA_AUTOMATIC_ZEN_RULE_STATUS = + "android.app.extra.AUTOMATIC_ZEN_RULE_STATUS"; + + /** + * String extra for {@link #ACTION_AUTOMATIC_ZEN_RULE_STATUS_CHANGED} containing the id of the + * {@link AutomaticZenRule} (see {@link #addAutomaticZenRule(AutomaticZenRule)}) that has + * changed. + */ + public static final String EXTRA_AUTOMATIC_ZEN_RULE_ID = + "android.app.extra.AUTOMATIC_ZEN_RULE_ID"; + + /** @hide */ + @IntDef(prefix = { "AUTOMATIC_RULE_STATUS" }, value = { + AUTOMATIC_RULE_STATUS_ENABLED, AUTOMATIC_RULE_STATUS_DISABLED, + AUTOMATIC_RULE_STATUS_REMOVED, AUTOMATIC_RULE_STATUS_UNKNOWN + }) + @Retention(RetentionPolicy.SOURCE) + public @interface AutomaticZenRuleStatus {} + + /** + * Constant value for {@link #EXTRA_AUTOMATIC_ZEN_RULE_STATUS} - the current status of the + * rule is unknown at your target sdk version, and you should continue to provide state changes + * via {@link #setAutomaticZenRuleState(String, Condition)}. + */ + public static final int AUTOMATIC_RULE_STATUS_UNKNOWN = -1; + + /** + * Constant value for {@link #EXTRA_AUTOMATIC_ZEN_RULE_STATUS} - the given rule currently + * exists and is enabled. You should continue to provide state changes via + * {@link #setAutomaticZenRuleState(String, Condition)}. + */ + public static final int AUTOMATIC_RULE_STATUS_ENABLED = 1; + + /** + * Constant value for {@link #EXTRA_AUTOMATIC_ZEN_RULE_STATUS} - the given rule currently + * exists but is disabled. You do not need to continue to provide state changes via + * {@link #setAutomaticZenRuleState(String, Condition)} until the rule is reenabled. + */ + public static final int AUTOMATIC_RULE_STATUS_DISABLED = 2; + + /** + * Constant value for {@link #EXTRA_AUTOMATIC_ZEN_RULE_STATUS} - the given rule has been + * deleted. Further calls to {@link #setAutomaticZenRuleState(String, Condition)} will be + * ignored. + */ + public static final int AUTOMATIC_RULE_STATUS_REMOVED = 3; + + /** * Intent that is broadcast when the state of {@link #getEffectsSuppressor()} changes. * This broadcast is only sent to registered receivers. * diff --git a/core/java/android/app/SharedPreferencesImpl.java b/core/java/android/app/SharedPreferencesImpl.java index 0f8976fe924a..9162626e1b37 100644 --- a/core/java/android/app/SharedPreferencesImpl.java +++ b/core/java/android/app/SharedPreferencesImpl.java @@ -16,6 +16,7 @@ package android.app; +import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UnsupportedAppUsage; import android.content.SharedPreferences; @@ -25,6 +26,7 @@ import android.system.ErrnoException; import android.system.Os; import android.system.StructStat; import android.system.StructTimespec; +import android.util.ArraySet; import android.util.Log; import com.android.internal.annotations.GuardedBy; @@ -92,6 +94,10 @@ final class SharedPreferencesImpl implements SharedPreferences { private final WeakHashMap<OnSharedPreferenceChangeListener, Object> mListeners = new WeakHashMap<OnSharedPreferenceChangeListener, Object>(); + @GuardedBy("mLock") + private final WeakHashMap<OnSharedPreferencesClearListener, Object> mClearListeners = + new WeakHashMap<>(); + /** Current memory state (always increasing) */ @GuardedBy("this") private long mCurrentMemoryStateGeneration; @@ -252,6 +258,28 @@ final class SharedPreferencesImpl implements SharedPreferences { } } + @Override + public void registerOnSharedPreferencesClearListener( + @NonNull OnSharedPreferencesClearListener listener) { + if (listener == null) { + throw new IllegalArgumentException("listener cannot be null."); + } + synchronized (mLock) { + mClearListeners.put(listener, CONTENT); + } + } + + @Override + public void unregisterOnSharedPreferencesClearListener( + @NonNull OnSharedPreferencesClearListener listener) { + if (listener == null) { + throw new IllegalArgumentException("listener cannot be null."); + } + synchronized (mLock) { + mClearListeners.remove(listener); + } + } + @GuardedBy("mLock") private void awaitLoadedLocked() { if (!mLoaded) { @@ -361,7 +389,9 @@ final class SharedPreferencesImpl implements SharedPreferences { private static class MemoryCommitResult { final long memoryStateGeneration; @Nullable final List<String> keysModified; + @Nullable final Set<String> keysCleared; @Nullable final Set<OnSharedPreferenceChangeListener> listeners; + @Nullable final Set<OnSharedPreferencesClearListener> clearListeners; final Map<String, Object> mapToWriteToDisk; final CountDownLatch writtenToDiskLatch = new CountDownLatch(1); @@ -371,10 +401,14 @@ final class SharedPreferencesImpl implements SharedPreferences { private MemoryCommitResult(long memoryStateGeneration, @Nullable List<String> keysModified, @Nullable Set<OnSharedPreferenceChangeListener> listeners, + @Nullable Set<String> keysCleared, + @Nullable Set<OnSharedPreferencesClearListener> clearListeners, Map<String, Object> mapToWriteToDisk) { this.memoryStateGeneration = memoryStateGeneration; this.keysModified = keysModified; this.listeners = listeners; + this.keysCleared = keysCleared; + this.clearListeners = clearListeners; this.mapToWriteToDisk = mapToWriteToDisk; } @@ -492,13 +526,16 @@ final class SharedPreferencesImpl implements SharedPreferences { // SharedPreferences instance back, which has the // changes reflected in memory. notifyListeners(mcr); + notifyClearListeners(mcr); } // Returns true if any changes were made private MemoryCommitResult commitToMemory() { long memoryStateGeneration; List<String> keysModified = null; + Set<String> keysCleared = null; Set<OnSharedPreferenceChangeListener> listeners = null; + Set<OnSharedPreferencesClearListener> clearListeners = null; Map<String, Object> mapToWriteToDisk; synchronized (SharedPreferencesImpl.this.mLock) { @@ -520,12 +557,20 @@ final class SharedPreferencesImpl implements SharedPreferences { keysModified = new ArrayList<String>(); listeners = new HashSet<OnSharedPreferenceChangeListener>(mListeners.keySet()); } + boolean hasClearListeners = !mClearListeners.isEmpty(); + if (hasClearListeners) { + keysCleared = new ArraySet<>(); + clearListeners = new HashSet<>(mClearListeners.keySet()); + } synchronized (mEditorLock) { boolean changesMade = false; if (mClear) { if (!mapToWriteToDisk.isEmpty()) { + if (hasClearListeners) { + keysCleared.addAll(mapToWriteToDisk.keySet()); + } changesMade = true; mapToWriteToDisk.clear(); } @@ -569,7 +614,7 @@ final class SharedPreferencesImpl implements SharedPreferences { } } return new MemoryCommitResult(memoryStateGeneration, keysModified, listeners, - mapToWriteToDisk); + keysCleared, clearListeners, mapToWriteToDisk); } @Override @@ -596,6 +641,7 @@ final class SharedPreferencesImpl implements SharedPreferences { } } notifyListeners(mcr); + notifyClearListeners(mcr); return mcr.writeToDiskResult; } @@ -618,6 +664,24 @@ final class SharedPreferencesImpl implements SharedPreferences { ActivityThread.sMainThreadHandler.post(() -> notifyListeners(mcr)); } } + + private void notifyClearListeners(final MemoryCommitResult mcr) { + if (mcr.clearListeners == null || mcr.keysCleared == null + || mcr.keysCleared.isEmpty()) { + return; + } + if (Looper.myLooper() == Looper.getMainLooper()) { + for (OnSharedPreferencesClearListener listener : mcr.clearListeners) { + if (listener != null) { + listener.onSharedPreferencesClear(SharedPreferencesImpl.this, + mcr.keysCleared); + } + } + } else { + // Run this function on the main thread. + ActivityThread.sMainThreadHandler.post(() -> notifyClearListeners(mcr)); + } + } } /** diff --git a/core/java/android/content/ClipData.java b/core/java/android/content/ClipData.java index 88e2c220bb8d..fdef2a143fc4 100644 --- a/core/java/android/content/ClipData.java +++ b/core/java/android/content/ClipData.java @@ -892,12 +892,6 @@ public class ClipData implements Parcelable { mItems.add(item); } - /** @removed use #addItem(ContentResolver, Item) instead */ - @Deprecated - public void addItem(Item item, ContentResolver resolver) { - addItem(resolver, item); - } - /** * Add a new Item to the overall ClipData container. * <p> Unlike {@link #addItem(Item)}, this method will update the list of available MIME types diff --git a/core/java/android/content/ContentInterface.java b/core/java/android/content/ContentInterface.java index d41d8d9cc1e2..197de9711296 100644 --- a/core/java/android/content/ContentInterface.java +++ b/core/java/android/content/ContentInterface.java @@ -56,6 +56,9 @@ public interface ContentInterface { public boolean refresh(@NonNull Uri uri, @Nullable Bundle args, @Nullable CancellationSignal cancellationSignal) throws RemoteException; + public int checkUriPermission(@NonNull Uri uri, int uid, @Intent.AccessUriMode int modeFlags) + throws RemoteException; + public @Nullable Uri insert(@NonNull Uri uri, @Nullable ContentValues initialValues) throws RemoteException; diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java index 7cdd2683905f..3c799915b871 100644 --- a/core/java/android/content/ContentProvider.java +++ b/core/java/android/content/ContentProvider.java @@ -28,6 +28,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UnsupportedAppUsage; import android.app.AppOpsManager; +import android.content.pm.PackageManager; import android.content.pm.PathPermission; import android.content.pm.ProviderInfo; import android.content.res.AssetFileDescriptor; @@ -582,6 +583,22 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall } } + @Override + public int checkUriPermission(String callingPkg, Uri uri, int uid, int modeFlags) { + uri = validateIncomingUri(uri); + uri = maybeGetUriWithoutUserId(uri); + Trace.traceBegin(TRACE_TAG_DATABASE, "checkUriPermission"); + final String original = setCallingPackage(callingPkg); + try { + return mInterface.checkUriPermission(uri, uid, modeFlags); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } finally { + setCallingPackage(original); + Trace.traceEnd(TRACE_TAG_DATABASE); + } + } + private void enforceFilePermission(String callingPkg, Uri uri, String mode, IBinder callerToken) throws FileNotFoundException, SecurityException { if (mode != null && mode.indexOf('w') != -1) { @@ -1416,6 +1433,12 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall return false; } + /** {@hide} */ + @Override + public int checkUriPermission(@NonNull Uri uri, int uid, @Intent.AccessUriMode int modeFlags) { + return PackageManager.PERMISSION_DENIED; + } + /** * @hide * Implementation when a caller has performed an insert on the content diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java index 93bf5188705c..8a4330ec0ede 100644 --- a/core/java/android/content/ContentProviderClient.java +++ b/core/java/android/content/ContentProviderClient.java @@ -307,6 +307,25 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { } } + /** {@hide} */ + @Override + public int checkUriPermission(@NonNull Uri uri, int uid, @Intent.AccessUriMode int modeFlags) + throws RemoteException { + Preconditions.checkNotNull(uri, "uri"); + + beforeRemote(); + try { + return mContentProvider.checkUriPermission(mPackageName, uri, uid, modeFlags); + } catch (DeadObjectException e) { + if (!mStable) { + mContentResolver.unstableProviderDied(mContentProvider); + } + throw e; + } finally { + afterRemote(); + } + } + /** See {@link ContentProvider#insert ContentProvider.insert} */ @Override public @Nullable Uri insert(@NonNull Uri url, @Nullable ContentValues initialValues) diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java index 994833866f40..cd735d4b10a3 100644 --- a/core/java/android/content/ContentProviderNative.java +++ b/core/java/android/content/ContentProviderNative.java @@ -363,6 +363,19 @@ abstract public class ContentProviderNative extends Binder implements IContentPr reply.writeInt(out ? 0 : -1); return true; } + + case CHECK_URI_PERMISSION_TRANSACTION: { + data.enforceInterface(IContentProvider.descriptor); + String callingPkg = data.readString(); + Uri uri = Uri.CREATOR.createFromParcel(data); + int uid = data.readInt(); + int modeFlags = data.readInt(); + + int out = checkUriPermission(callingPkg, uri, uid, modeFlags); + reply.writeNoException(); + reply.writeInt(out); + return true; + } } } catch (Exception e) { DatabaseUtils.writeExceptionToParcel(reply, e); @@ -800,6 +813,29 @@ final class ContentProviderProxy implements IContentProvider } } + @Override + public int checkUriPermission(String callingPkg, Uri url, int uid, int modeFlags) + throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + try { + data.writeInterfaceToken(IContentProvider.descriptor); + + data.writeString(callingPkg); + url.writeToParcel(data, 0); + data.writeInt(uid); + data.writeInt(modeFlags); + + mRemote.transact(IContentProvider.CHECK_URI_PERMISSION_TRANSACTION, data, reply, 0); + + DatabaseUtils.readExceptionFromParcel(reply); + return reply.readInt(); + } finally { + data.recycle(); + reply.recycle(); + } + } + @UnsupportedAppUsage private IBinder mRemote; } diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index 0a1bc85202ff..9c863591f16f 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -31,6 +31,7 @@ import android.app.ActivityManager; import android.app.ActivityThread; import android.app.AppGlobals; import android.app.UriGrantsManager; +import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.AssetFileDescriptor; import android.content.res.Resources; @@ -1146,6 +1147,24 @@ public abstract class ContentResolver implements ContentInterface { } } + /** {@hide} */ + @Override + public int checkUriPermission(@NonNull Uri uri, int uid, @Intent.AccessUriMode int modeFlags) { + Preconditions.checkNotNull(uri, "uri"); + + try { + if (mWrapped != null) return mWrapped.checkUriPermission(uri, uid, modeFlags); + } catch (RemoteException e) { + return PackageManager.PERMISSION_DENIED; + } + + try (ContentProviderClient client = acquireUnstableContentProviderClient(uri)) { + return client.checkUriPermission(uri, uid, modeFlags); + } catch (RemoteException e) { + return PackageManager.PERMISSION_DENIED; + } + } + /** * Open a stream on to the content associated with a content URI. If there * is no data associated with the URI, FileNotFoundException is thrown. diff --git a/core/java/android/content/IContentProvider.java b/core/java/android/content/IContentProvider.java index 0427c2f52415..fade0ab6bb5c 100644 --- a/core/java/android/content/IContentProvider.java +++ b/core/java/android/content/IContentProvider.java @@ -16,6 +16,7 @@ package android.content; +import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UnsupportedAppUsage; import android.content.res.AssetFileDescriptor; @@ -82,6 +83,9 @@ public interface IContentProvider extends IInterface { public Bundle call(String callingPkg, String authority, String method, @Nullable String arg, @Nullable Bundle extras) throws RemoteException; + public int checkUriPermission(String callingPkg, Uri uri, int uid, int modeFlags) + throws RemoteException; + public ICancellationSignal createCancellationSignal() throws RemoteException; public Uri canonicalize(String callingPkg, Uri uri) throws RemoteException; @@ -116,4 +120,5 @@ public interface IContentProvider extends IInterface { static final int CANONICALIZE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 24; static final int UNCANONICALIZE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 25; static final int REFRESH_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 26; + static final int CHECK_URI_PERMISSION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 27; } diff --git a/core/java/android/content/LoggingContentInterface.java b/core/java/android/content/LoggingContentInterface.java index 83c0c9112387..1df1c4faf2fe 100644 --- a/core/java/android/content/LoggingContentInterface.java +++ b/core/java/android/content/LoggingContentInterface.java @@ -165,6 +165,19 @@ public class LoggingContentInterface implements ContentInterface { } @Override + public int checkUriPermission(@NonNull Uri uri, int uid, @Intent.AccessUriMode int modeFlags) + throws RemoteException { + try (Logger l = new Logger("checkUriPermission", uri, uid, modeFlags)) { + try { + return l.setResult(delegate.checkUriPermission(uri, uid, modeFlags)); + } catch (Exception res) { + l.setResult(res); + throw res; + } + } + } + + @Override public @Nullable Uri insert(@NonNull Uri uri, @Nullable ContentValues initialValues) throws RemoteException { try (Logger l = new Logger("insert", uri, initialValues)) { diff --git a/core/java/android/content/SharedPreferences.java b/core/java/android/content/SharedPreferences.java index 877dfee138da..9d87e2550c95 100644 --- a/core/java/android/content/SharedPreferences.java +++ b/core/java/android/content/SharedPreferences.java @@ -16,6 +16,7 @@ package android.content; +import android.annotation.NonNull; import android.annotation.Nullable; import java.util.Map; @@ -58,7 +59,9 @@ public interface SharedPreferences { * <p>This callback will be run on your main thread. * * <p><em>Note: This callback will not be triggered when preferences are cleared via - * {@link Editor#clear()}.</em> + * {@link Editor#clear()}. However, from {@link android.os.Build.VERSION_CODES#R Android R} + * onwards, you can use {@link OnSharedPreferencesClearListener} to register for + * {@link Editor#clear()} callbacks.</em> * * @param sharedPreferences The {@link SharedPreferences} that received * the change. @@ -67,7 +70,23 @@ public interface SharedPreferences { */ void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key); } - + + /** + * Interface definition for a callback to be invoked when shared preferences are cleared. + */ + public interface OnSharedPreferencesClearListener { + /** + * Called when shared preferences are cleared via {@link Editor#clear()}. + * + * <p>This callback will be run on your main thread. + * + * @param sharedPreferences The {@link SharedPreferences} that received the change. + * @param keys The set of keys that were cleared. + */ + void onSharedPreferencesClear(@NonNull SharedPreferences sharedPreferences, + @NonNull Set<String> keys); + } + /** * Interface used for modifying values in a {@link SharedPreferences} * object. All changes you make in an editor are batched, and not copied @@ -378,12 +397,43 @@ public interface SharedPreferences { * @see #unregisterOnSharedPreferenceChangeListener */ void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener); - + /** * Unregisters a previous callback. - * + * * @param listener The callback that should be unregistered. * @see #registerOnSharedPreferenceChangeListener */ void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener); + + /** + * Registers a callback to be invoked when preferences are cleared via {@link Editor#clear()}. + * + * <p class="caution"><strong>Caution:</strong> The preference manager does + * not currently store a strong reference to the listener. You must store a + * strong reference to the listener, or it will be susceptible to garbage + * collection. We recommend you keep a reference to the listener in the + * instance data of an object that will exist as long as you need the + * listener.</p> + * + * @param listener The callback that will run. + * @see #unregisterOnSharedPreferencesClearListener + */ + default void registerOnSharedPreferencesClearListener( + @NonNull OnSharedPreferencesClearListener listener) { + throw new UnsupportedOperationException( + "registerOnSharedPreferencesClearListener not implemented"); + } + + /** + * Unregisters a previous callback for {@link Editor#clear()}. + * + * @param listener The callback that should be unregistered. + * @see #registerOnSharedPreferencesClearListener + */ + default void unregisterOnSharedPreferencesClearListener( + @NonNull OnSharedPreferencesClearListener listener) { + throw new UnsupportedOperationException( + "unregisterOnSharedPreferencesClearListener not implemented"); + } } diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index f75cd236eb36..d0a61eb38141 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -614,10 +614,10 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { /** * Value for {@link #privateFlags}: whether this app is pre-installed on the - * google partition of the system image. + * system_ext partition of the system image. * @hide */ - public static final int PRIVATE_FLAG_PRODUCT_SERVICES = 1 << 21; + public static final int PRIVATE_FLAG_SYSTEM_EXT = 1 << 21; /** * Indicates whether this package requires access to non-SDK APIs. @@ -713,7 +713,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { PRIVATE_FLAG_USE_EMBEDDED_DEX, PRIVATE_FLAG_PRIVILEGED, PRIVATE_FLAG_PRODUCT, - PRIVATE_FLAG_PRODUCT_SERVICES, + PRIVATE_FLAG_SYSTEM_EXT, PRIVATE_FLAG_PROFILEABLE_BY_SHELL, PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER, PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY, @@ -2046,8 +2046,8 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { } /** @hide */ - public boolean isProductServices() { - return (privateFlags & ApplicationInfo.PRIVATE_FLAG_PRODUCT_SERVICES) != 0; + public boolean isSystemExt() { + return (privateFlags & ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT) != 0; } /** @hide */ diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 904bd1682f9a..c6beee2f898a 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -94,8 +94,6 @@ interface IPackageManager { @UnsupportedAppUsage ProviderInfo getProviderInfo(in ComponentName className, int flags, int userId); - boolean shouldShowRequestPermissionRationale(String permName, - String packageName, int userId); boolean isProtectedBroadcast(String actionName); @@ -594,18 +592,12 @@ interface IPackageManager { ParceledListSlice getIntentFilterVerifications(String packageName); ParceledListSlice getAllIntentFilters(String packageName); - boolean setDefaultBrowserPackageName(String packageName, int userId); - String getDefaultBrowserPackageName(int userId); - VerifierDeviceIdentity getVerifierDeviceIdentity(); boolean isFirstBoot(); boolean isOnlyCoreApps(); boolean isDeviceUpgrading(); - void setPermissionEnforced(String permission, boolean enforced); - boolean isPermissionEnforced(String permission); - /** Reflects current DeviceStorageMonitorService state */ @UnsupportedAppUsage boolean isStorageLow(); @@ -629,17 +621,6 @@ interface IPackageManager { boolean isPackageSignedByKeySet(String packageName, in KeySet ks); boolean isPackageSignedByKeySetExactly(String packageName, in KeySet ks); - void grantDefaultPermissionsToEnabledCarrierApps(in String[] packageNames, int userId); - void grantDefaultPermissionsToEnabledImsServices(in String[] packageNames, int userId); - void grantDefaultPermissionsToEnabledTelephonyDataServices( - in String[] packageNames, int userId); - void revokeDefaultPermissionsFromDisabledTelephonyDataServices( - in String[] packageNames, int userId); - void grantDefaultPermissionsToActiveLuiApp(in String packageName, int userId); - void revokeDefaultPermissionsFromLuiApps(in String[] packageNames, int userId); - - boolean isPermissionRevokedByPolicy(String permission, String packageName, int userId); - @UnsupportedAppUsage String getPermissionControllerPackageName(); diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index 3cecd7f88045..1099d8bdc7dd 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -1565,7 +1565,7 @@ public class PackageInstaller { } /** {@hide} */ - @SystemApi + @SystemApi @TestApi public void setRequestDowngrade(boolean requestDowngrade) { if (requestDowngrade) { installFlags |= PackageManager.INSTALL_REQUEST_DOWNGRADE; diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index b845a37b0342..4d8a0c46508e 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -3571,7 +3571,7 @@ public abstract class PackageManager { /** * Retrieve all of the information we know about a particular permission. * - * @param permissionName The fully qualified name (i.e. com.google.permission.LOGIN) + * @param permName The fully qualified name (i.e. com.google.permission.LOGIN) * of the permission you are interested in. * @param flags Additional option flags to modify the data returned. * @return Returns a {@link PermissionInfo} containing information about the @@ -3579,7 +3579,7 @@ public abstract class PackageManager { * @throws NameNotFoundException if a package with the given name cannot be * found on the system. */ - public abstract PermissionInfo getPermissionInfo(@NonNull String permissionName, + public abstract PermissionInfo getPermissionInfo(@NonNull String permName, @PermissionInfoFlags int flags) throws NameNotFoundException; /** @@ -3620,7 +3620,7 @@ public abstract class PackageManager { * Retrieve all of the information we know about a particular group of * permissions. * - * @param permissionName The fully qualified name (i.e. + * @param permName The fully qualified name (i.e. * com.google.permission_group.APPS) of the permission you are * interested in. * @param flags Additional option flags to modify the data returned. @@ -3630,7 +3630,7 @@ public abstract class PackageManager { * found on the system. */ @NonNull - public abstract PermissionGroupInfo getPermissionGroupInfo(@NonNull String permissionName, + public abstract PermissionGroupInfo getPermissionGroupInfo(@NonNull String permName, @PermissionGroupInfoFlags int flags) throws NameNotFoundException; /** @@ -3858,7 +3858,7 @@ public abstract class PackageManager { * Check whether a particular package has been granted a particular * permission. * - * @param permissionName The name of the permission you are checking for. + * @param permName The name of the permission you are checking for. * @param packageName The name of the package you are checking against. * * @return If the package has the permission, PERMISSION_GRANTED is @@ -3870,7 +3870,7 @@ public abstract class PackageManager { */ @CheckResult @PermissionResult - public abstract int checkPermission(@NonNull String permissionName, + public abstract int checkPermission(@NonNull String permName, @NonNull String packageName); /** @@ -3880,13 +3880,13 @@ public abstract class PackageManager { * permissions, hence the only way for an app to get such a permission * is by a policy change. * - * @param permissionName The name of the permission you are checking for. + * @param permName The name of the permission you are checking for. * @param packageName The name of the package you are checking against. * * @return Whether the permission is restricted by policy. */ @CheckResult - public abstract boolean isPermissionRevokedByPolicy(@NonNull String permissionName, + public abstract boolean isPermissionRevokedByPolicy(@NonNull String permName, @NonNull String packageName); /** @@ -3949,14 +3949,14 @@ public abstract class PackageManager { * -- you are only allowed to remove permissions that you are allowed * to add. * - * @param permissionName The name of the permission to remove. + * @param permName The name of the permission to remove. * * @throws SecurityException if you are not allowed to remove the * given permission name. * * @see #addPermission(PermissionInfo) */ - public abstract void removePermission(@NonNull String permissionName); + public abstract void removePermission(@NonNull String permName); /** * Permission flags set when granting or revoking a permission. @@ -3998,7 +3998,7 @@ public abstract class PackageManager { * </p> * * @param packageName The package to which to grant the permission. - * @param permissionName The permission name to grant. + * @param permName The permission name to grant. * @param user The user for which to grant the permission. * * @see #revokeRuntimePermission(String, String, android.os.UserHandle) @@ -4009,7 +4009,7 @@ public abstract class PackageManager { @SystemApi @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public abstract void grantRuntimePermission(@NonNull String packageName, - @NonNull String permissionName, @NonNull UserHandle user); + @NonNull String permName, @NonNull UserHandle user); /** * Revoke a runtime permission that was previously granted by {@link @@ -4025,7 +4025,7 @@ public abstract class PackageManager { * </p> * * @param packageName The package from which to revoke the permission. - * @param permissionName The permission name to revoke. + * @param permName The permission name to revoke. * @param user The user for which to revoke the permission. * * @see #grantRuntimePermission(String, String, android.os.UserHandle) @@ -4036,12 +4036,12 @@ public abstract class PackageManager { @SystemApi @RequiresPermission(android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS) public abstract void revokeRuntimePermission(@NonNull String packageName, - @NonNull String permissionName, @NonNull UserHandle user); + @NonNull String permName, @NonNull UserHandle user); /** * Gets the state flags associated with a permission. * - * @param permissionName The permission for which to get the flags. + * @param permName The permission for which to get the flags. * @param packageName The package name for which to get the flags. * @param user The user for which to get permission flags. * @return The permission flags. @@ -4056,14 +4056,14 @@ public abstract class PackageManager { android.Manifest.permission.GET_RUNTIME_PERMISSIONS }) @PermissionFlags - public abstract int getPermissionFlags(@NonNull String permissionName, + public abstract int getPermissionFlags(@NonNull String permName, @NonNull String packageName, @NonNull UserHandle user); /** * Updates the flags associated with a permission by replacing the flags in * the specified mask with the provided flag values. * - * @param permissionName The permission for which to update the flags. + * @param permName The permission for which to update the flags. * @param packageName The package name for which to update the flags. * @param flagMask The flags which to replace. * @param flagValues The flags with which to replace. @@ -4077,7 +4077,7 @@ public abstract class PackageManager { android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS }) - public abstract void updatePermissionFlags(@NonNull String permissionName, + public abstract void updatePermissionFlags(@NonNull String permName, @NonNull String packageName, @PermissionFlags int flagMask, @PermissionFlags int flagValues, @NonNull UserHandle user); @@ -4164,7 +4164,7 @@ public abstract class PackageManager { * provided ones. * * @param packageName The app for which to get whitelisted permissions. - * @param permission The whitelisted permission to add. + * @param permName The whitelisted permission to add. * @param whitelistFlags The whitelists to which to add. Passing multiple flags * updates all specified whitelists. * @return Whether the permission was added to the whitelist. @@ -4180,7 +4180,7 @@ public abstract class PackageManager { @RequiresPermission(value = Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS, conditional = true) public boolean addWhitelistedRestrictedPermission(@NonNull String packageName, - @NonNull String permission, @PermissionWhitelistFlags int whitelistFlags) { + @NonNull String permName, @PermissionWhitelistFlags int whitelistFlags) { return false; } @@ -4218,7 +4218,7 @@ public abstract class PackageManager { * provided ones. * * @param packageName The app for which to get whitelisted permissions. - * @param permission The whitelisted permission to remove. + * @param permName The whitelisted permission to remove. * @param whitelistFlags The whitelists from which to remove. Passing multiple flags * updates all specified whitelists. * @return Whether the permission was removed from the whitelist. @@ -4234,7 +4234,7 @@ public abstract class PackageManager { @RequiresPermission(value = Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS, conditional = true) public boolean removeWhitelistedRestrictedPermission(@NonNull String packageName, - @NonNull String permission, @PermissionWhitelistFlags int whitelistFlags) { + @NonNull String permName, @PermissionWhitelistFlags int whitelistFlags) { return false; } @@ -4244,13 +4244,13 @@ public abstract class PackageManager { * which the permission is requested does not clearly communicate to the user * what would be the benefit from grating this permission. * - * @param permissionName A permission your app wants to request. + * @param permName A permission your app wants to request. * @return Whether you can show permission rationale UI. * * @hide */ @UnsupportedAppUsage - public abstract boolean shouldShowRequestPermissionRationale(@NonNull String permissionName); + public abstract boolean shouldShowRequestPermissionRationale(@NonNull String permName); /** * Returns an {@link android.content.Intent} suitable for passing to diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java index e21d4c41811c..2f198acde06d 100644 --- a/core/java/android/content/pm/PackageManagerInternal.java +++ b/core/java/android/content/pm/PackageManagerInternal.java @@ -84,139 +84,6 @@ public abstract class PackageManagerInternal { } /** - * Provider for package names. - */ - public interface PackagesProvider { - - /** - * Gets the packages for a given user. - * @param userId The user id. - * @return The package names. - */ - public String[] getPackages(int userId); - } - - /** - * Provider for package names. - */ - public interface SyncAdapterPackagesProvider { - - /** - * Gets the sync adapter packages for given authority and user. - * @param authority The authority. - * @param userId The user id. - * @return The package names. - */ - public String[] getPackages(String authority, int userId); - } - - /** - * Provider for default browser - */ - public interface DefaultBrowserProvider { - - /** - * Get the package name of the default browser. - * - * @param userId the user id - * - * @return the package name of the default browser, or {@code null} if none - */ - @Nullable - String getDefaultBrowser(@UserIdInt int userId); - - /** - * Set the package name of the default browser. - * - * @param packageName package name of the default browser, or {@code null} to remove - * @param userId the user id - * - * @return whether the default browser was successfully set. - */ - boolean setDefaultBrowser(@Nullable String packageName, @UserIdInt int userId); - - /** - * Set the package name of the default browser asynchronously. - * - * @param packageName package name of the default browser, or {@code null} to remove - * @param userId the user id - */ - void setDefaultBrowserAsync(@Nullable String packageName, @UserIdInt int userId); - } - - /** - * Provider for default dialer - */ - public interface DefaultDialerProvider { - - /** - * Get the package name of the default dialer. - * - * @param userId the user id - * - * @return the package name of the default dialer, or {@code null} if none - */ - @Nullable - String getDefaultDialer(@UserIdInt int userId); - } - - /** - * Provider for default home - */ - public interface DefaultHomeProvider { - - /** - * Get the package name of the default home. - * - * @param userId the user id - * - * @return the package name of the default home, or {@code null} if none - */ - @Nullable - String getDefaultHome(@UserIdInt int userId); - - /** - * Set the package name of the default home. - * - * @param packageName package name of the default home, or {@code null} to remove - * @param userId the user id - * @param callback the callback made after the default home as been updated - */ - void setDefaultHomeAsync(@Nullable String packageName, @UserIdInt int userId, - @NonNull Consumer<Boolean> callback); - } - - /** - * Sets the location provider packages provider. - * @param provider The packages provider. - */ - public abstract void setLocationPackagesProvider(PackagesProvider provider); - - /** - * Set the location extra packages provider. - * @param provider The packages provider. - */ - public abstract void setLocationExtraPackagesProvider(PackagesProvider provider); - - /** - * Sets the voice interaction packages provider. - * @param provider The packages provider. - */ - public abstract void setVoiceInteractionPackagesProvider(PackagesProvider provider); - - /** - * Sets the Use Open Wifi packages provider. - * @param provider The packages provider. - */ - public abstract void setUseOpenWifiAppPackagesProvider(PackagesProvider provider); - - /** - * Sets the sync adapter packages provider. - * @param provider The provider. - */ - public abstract void setSyncAdapterPackagesprovider(SyncAdapterPackagesProvider provider); - - /** * Called when the package for the default SMS handler changed * * @param packageName the new sms package @@ -233,14 +100,6 @@ public abstract class PackageManagerInternal { public void onDefaultSimCallManagerAppChanged(String packageName, int userId) {} /** - * Requests granting of the default permissions to the current default Use Open Wifi app. - * @param packageName The default use open wifi package name. - * @param userId The user for which to grant the permissions. - */ - public abstract void grantDefaultPermissionsToDefaultUseOpenWifiApp(String packageName, - int userId); - - /** * Sets a list of apps to keep in PM's internal data structures and as APKs even if no user has * currently installed it. The apps are not preloaded. * @param packageList List of package names to keep cached. @@ -697,7 +556,15 @@ public abstract class PackageManagerInternal { * @see #canAccessInstantApps */ public abstract boolean filterAppAccess( - @Nullable PackageParser.Package pkg, int callingUid, int userId); + @NonNull PackageParser.Package pkg, int callingUid, int userId); + + /** + * Returns whether or not access to the application should be filtered. + * + * @see #filterAppAccess(android.content.pm.PackageParser.Package, int, int) + */ + public abstract boolean filterAppAccess( + @NonNull String packageName, int callingUid, int userId); /** Returns whether the given package was signed by the platform */ public abstract boolean isPlatformSigned(String pkg); @@ -872,27 +739,6 @@ public abstract class PackageManagerInternal { public abstract String removeLegacyDefaultBrowserPackageName(int userId); /** - * Sets the default browser provider. - * - * @param provider the provider - */ - public abstract void setDefaultBrowserProvider(@NonNull DefaultBrowserProvider provider); - - /** - * Sets the default dialer provider. - * - * @param provider the provider - */ - public abstract void setDefaultDialerProvider(@NonNull DefaultDialerProvider provider); - - /** - * Sets the default home provider. - * - * @param provider the provider - */ - public abstract void setDefaultHomeProvider(@NonNull DefaultHomeProvider provider); - - /** * Returns {@code true} if given {@code packageName} is an apex package. */ public abstract boolean isApexPackage(String packageName); @@ -910,15 +756,6 @@ public abstract class PackageManagerInternal { IntentSender intentSender); /** - * Whether default permission grants have been performed for a user - * since the device booted. - * - * @param userId The user id. - * @return true if default permissions - */ - public abstract boolean wereDefaultPermissionsGrantedSinceBoot(int userId); - - /** * Get fingerprint of build that updated the runtime permissions for a user. * * @param userId The user to update @@ -960,4 +797,10 @@ public abstract class PackageManagerInternal { */ public abstract boolean isCallerInstallerOfRecord( @NonNull PackageParser.Package pkg, int callingUid); + + /** Returns whether or not default runtime permissions are granted for the given user */ + public abstract boolean areDefaultRuntimePermissionsGranted(@UserIdInt int userId); + + /** Sets the enforcement of reading external storage */ + public abstract void setReadExternalStorageEnforced(boolean enforced); } diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index a74c34ff4a88..e690a7fd783b 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -254,6 +254,8 @@ public class PackageParser { /** @hide */ public static final String APK_FILE_EXTENSION = ".apk"; + /** @hide */ + public static final String APEX_FILE_EXTENSION = ".apex"; /** @hide */ public static class NewPermissionInfo { @@ -6897,8 +6899,8 @@ public class PackageParser { } /** @hide */ - public boolean isProductServices() { - return applicationInfo.isProductServices(); + public boolean isSystemExt() { + return applicationInfo.isSystemExt(); } /** @hide */ diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java index 2b1b32ed3df2..7865d75d839c 100644 --- a/core/java/android/content/pm/UserInfo.java +++ b/core/java/android/content/pm/UserInfo.java @@ -19,7 +19,6 @@ package android.content.pm; import android.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; -import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; @@ -232,7 +231,7 @@ public class UserInfo implements Parcelable { if (isManagedProfile() || isGuest() || isRestricted()) { return false; } - if (UserManager.isSplitSystemUser()) { + if (UserManager.isSplitSystemUser() || UserManager.isHeadlessSystemUserMode()) { return id != UserHandle.USER_SYSTEM; } else { return id == UserHandle.USER_SYSTEM; diff --git a/core/java/android/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java index a99a0b5a7a90..f3b7553577ca 100644 --- a/core/java/android/content/res/CompatibilityInfo.java +++ b/core/java/android/content/res/CompatibilityInfo.java @@ -32,8 +32,8 @@ import android.view.WindowManager; import android.view.WindowManager.LayoutParams; /** - * CompatibilityInfo class keeps the information about compatibility mode that the application is - * running under. + * CompatibilityInfo class keeps the information about the screen compatibility mode that the + * application is running under. * * {@hide} */ diff --git a/core/java/android/database/sqlite/SQLiteQueryBuilder.java b/core/java/android/database/sqlite/SQLiteQueryBuilder.java index 3523e956656a..48d88678f721 100644 --- a/core/java/android/database/sqlite/SQLiteQueryBuilder.java +++ b/core/java/android/database/sqlite/SQLiteQueryBuilder.java @@ -30,11 +30,14 @@ import android.text.TextUtils; import android.util.ArrayMap; import android.util.Log; +import com.android.internal.util.ArrayUtils; + import libcore.util.EmptyArray; import java.util.Arrays; import java.util.Iterator; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; @@ -49,14 +52,11 @@ import java.util.regex.Pattern; public class SQLiteQueryBuilder { private static final String TAG = "SQLiteQueryBuilder"; - private static final Pattern sLimitPattern = - Pattern.compile("\\s*\\d+\\s*(,\\s*\\d+\\s*)?"); private static final Pattern sAggregationPattern = Pattern.compile( "(?i)(AVG|COUNT|MAX|MIN|SUM|TOTAL|GROUP_CONCAT)\\((.+)\\)"); private Map<String, String> mProjectionMap = null; private List<Pattern> mProjectionGreylist = null; - private boolean mProjectionAggregationAllowed = false; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) private String mTables = ""; @@ -65,7 +65,12 @@ public class SQLiteQueryBuilder { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) private boolean mDistinct; private SQLiteDatabase.CursorFactory mFactory; - private boolean mStrict; + + private static final int STRICT_PARENTHESES = 1 << 0; + private static final int STRICT_COLUMNS = 1 << 1; + private static final int STRICT_GRAMMAR = 1 << 2; + + private int mStrictFlags; public SQLiteQueryBuilder() { mDistinct = false; @@ -209,13 +214,14 @@ public class SQLiteQueryBuilder { } /** {@hide} */ + @Deprecated public void setProjectionAggregationAllowed(boolean projectionAggregationAllowed) { - mProjectionAggregationAllowed = projectionAggregationAllowed; } /** {@hide} */ + @Deprecated public boolean isProjectionAggregationAllowed() { - return mProjectionAggregationAllowed; + return true; } /** @@ -258,8 +264,12 @@ public class SQLiteQueryBuilder { * </ul> * By default, this value is false. */ - public void setStrict(boolean flag) { - mStrict = flag; + public void setStrict(boolean strict) { + if (strict) { + mStrictFlags |= STRICT_PARENTHESES; + } else { + mStrictFlags &= ~STRICT_PARENTHESES; + } } /** @@ -267,7 +277,67 @@ public class SQLiteQueryBuilder { * {@link #setStrict(boolean)}. */ public boolean isStrict() { - return mStrict; + return (mStrictFlags & STRICT_PARENTHESES) != 0; + } + + /** + * When enabled, verify that all projections and {@link ContentValues} only + * contain valid columns as defined by {@link #setProjectionMap(Map)}. + * <p> + * This enforcement applies to {@link #insert}, {@link #query}, and + * {@link #update} operations. Any enforcement failures will throw an + * {@link IllegalArgumentException}. + */ + public void setStrictColumns(boolean strictColumns) { + if (strictColumns) { + mStrictFlags |= STRICT_COLUMNS; + } else { + mStrictFlags &= ~STRICT_COLUMNS; + } + } + + /** + * Get if the query is marked as strict, as last configured by + * {@link #setStrictColumns(boolean)}. + */ + public boolean isStrictColumns() { + return (mStrictFlags & STRICT_COLUMNS) != 0; + } + + /** + * When enabled, verify that all untrusted SQL conforms to a restricted SQL + * grammar. Here are the restrictions applied: + * <ul> + * <li>In {@code WHERE} and {@code HAVING} clauses: subqueries, raising, and + * windowing terms are rejected. + * <li>In {@code GROUP BY} clauses: only valid columns are allowed. + * <li>In {@code ORDER BY} clauses: only valid columns, collation, and + * ordering terms are allowed. + * <li>In {@code LIMIT} clauses: only numerical values and offset terms are + * allowed. + * </ul> + * All column references must be valid as defined by + * {@link #setProjectionMap(Map)}. + * <p> + * This enforcement applies to {@link #query}, {@link #update} and + * {@link #delete} operations. This enforcement does not apply to trusted + * inputs, such as those provided by {@link #appendWhere}. Any enforcement + * failures will throw an {@link IllegalArgumentException}. + */ + public void setStrictGrammar(boolean strictGrammar) { + if (strictGrammar) { + mStrictFlags |= STRICT_GRAMMAR; + } else { + mStrictFlags &= ~STRICT_GRAMMAR; + } + } + + /** + * Get if the query is marked as strict, as last configured by + * {@link #setStrictGrammar(boolean)}. + */ + public boolean isStrictGrammar() { + return (mStrictFlags & STRICT_GRAMMAR) != 0; } /** @@ -303,9 +373,6 @@ public class SQLiteQueryBuilder { throw new IllegalArgumentException( "HAVING clauses are only permitted when using a groupBy clause"); } - if (!TextUtils.isEmpty(limit) && !sLimitPattern.matcher(limit).matches()) { - throw new IllegalArgumentException("invalid LIMIT clauses:" + limit); - } StringBuilder query = new StringBuilder(120); @@ -479,7 +546,13 @@ public class SQLiteQueryBuilder { projectionIn, selection, groupBy, having, sortOrder, limit); - if (mStrict && selection != null && selection.length() > 0) { + if (isStrictColumns()) { + enforceStrictColumns(projectionIn); + } + if (isStrictGrammar()) { + enforceStrictGrammar(selection, groupBy, having, sortOrder, limit); + } + if (isStrict()) { // Validate the user-supplied selection to detect syntactic anomalies // in the selection string that could indicate a SQL injection attempt. // The idea is to ensure that the selection clause is a valid SQL expression @@ -497,7 +570,7 @@ public class SQLiteQueryBuilder { // Execute wrapped query for extra protection final String wrappedSql = buildQuery(projectionIn, wrap(selection), groupBy, - having, sortOrder, limit); + wrap(having), sortOrder, limit); sql = wrappedSql; } else { // Execute unwrapped query @@ -519,6 +592,40 @@ public class SQLiteQueryBuilder { } /** + * Perform an insert by combining all current settings and the + * information passed into this method. + * + * @param db the database to insert on + * @return the row ID of the newly inserted row, or -1 if an error occurred + */ + public long insert(@NonNull SQLiteDatabase db, @NonNull ContentValues values) { + Objects.requireNonNull(mTables, "No tables defined"); + Objects.requireNonNull(db, "No database defined"); + Objects.requireNonNull(values, "No values defined"); + + if (isStrictColumns()) { + enforceStrictColumns(values); + } + + final String sql = buildInsert(values); + + final ArrayMap<String, Object> rawValues = values.getValues(); + final int valuesLength = rawValues.size(); + final Object[] sqlArgs = new Object[valuesLength]; + for (int i = 0; i < sqlArgs.length; i++) { + sqlArgs[i] = rawValues.valueAt(i); + } + if (Log.isLoggable(TAG, Log.DEBUG)) { + if (Build.IS_DEBUGGABLE) { + Log.d(TAG, sql + " with args " + Arrays.toString(sqlArgs)); + } else { + Log.d(TAG, sql); + } + } + return db.executeSql(sql, sqlArgs); + } + + /** * Perform an update by combining all current settings and the * information passed into this method. * @@ -541,7 +648,13 @@ public class SQLiteQueryBuilder { final String sql; final String unwrappedSql = buildUpdate(values, selection); - if (mStrict) { + if (isStrictColumns()) { + enforceStrictColumns(values); + } + if (isStrictGrammar()) { + enforceStrictGrammar(selection, null, null, null, null); + } + if (isStrict()) { // Validate the user-supplied selection to detect syntactic anomalies // in the selection string that could indicate a SQL injection attempt. // The idea is to ensure that the selection clause is a valid SQL expression @@ -610,7 +723,10 @@ public class SQLiteQueryBuilder { final String sql; final String unwrappedSql = buildDelete(selection); - if (mStrict) { + if (isStrictGrammar()) { + enforceStrictGrammar(selection, null, null, null, null); + } + if (isStrict()) { // Validate the user-supplied selection to detect syntactic anomalies // in the selection string that could indicate a SQL injection attempt. // The idea is to ensure that the selection clause is a valid SQL expression @@ -645,6 +761,81 @@ public class SQLiteQueryBuilder { return db.executeSql(sql, sqlArgs); } + private void enforceStrictColumns(@Nullable String[] projection) { + Objects.requireNonNull(mProjectionMap, "No projection map defined"); + + computeProjection(projection); + } + + private void enforceStrictColumns(@NonNull ContentValues values) { + Objects.requireNonNull(mProjectionMap, "No projection map defined"); + + final ArrayMap<String, Object> rawValues = values.getValues(); + for (int i = 0; i < rawValues.size(); i++) { + final String column = rawValues.keyAt(i); + if (!mProjectionMap.containsKey(column)) { + throw new IllegalArgumentException("Invalid column " + column); + } + } + } + + private void enforceStrictGrammar(@Nullable String selection, @Nullable String groupBy, + @Nullable String having, @Nullable String sortOrder, @Nullable String limit) { + SQLiteTokenizer.tokenize(selection, SQLiteTokenizer.OPTION_NONE, + this::enforceStrictGrammarWhereHaving); + SQLiteTokenizer.tokenize(groupBy, SQLiteTokenizer.OPTION_NONE, + this::enforceStrictGrammarGroupBy); + SQLiteTokenizer.tokenize(having, SQLiteTokenizer.OPTION_NONE, + this::enforceStrictGrammarWhereHaving); + SQLiteTokenizer.tokenize(sortOrder, SQLiteTokenizer.OPTION_NONE, + this::enforceStrictGrammarOrderBy); + SQLiteTokenizer.tokenize(limit, SQLiteTokenizer.OPTION_NONE, + this::enforceStrictGrammarLimit); + } + + private void enforceStrictGrammarWhereHaving(@NonNull String token) { + if (isTableOrColumn(token)) return; + if (SQLiteTokenizer.isFunction(token)) return; + if (SQLiteTokenizer.isType(token)) return; + + // NOTE: we explicitly don't allow SELECT subqueries, since they could + // leak data that should have been filtered by the trusted where clause + switch (token.toUpperCase(Locale.US)) { + case "AND": case "AS": case "BETWEEN": case "BINARY": + case "CASE": case "CAST": case "COLLATE": case "DISTINCT": + case "ELSE": case "END": case "ESCAPE": case "EXISTS": + case "GLOB": case "IN": case "IS": case "ISNULL": + case "LIKE": case "MATCH": case "NOCASE": case "NOT": + case "NOTNULL": case "NULL": case "OR": case "REGEXP": + case "RTRIM": case "THEN": case "WHEN": + return; + } + throw new IllegalArgumentException("Invalid token " + token); + } + + private void enforceStrictGrammarGroupBy(@NonNull String token) { + if (isTableOrColumn(token)) return; + throw new IllegalArgumentException("Invalid token " + token); + } + + private void enforceStrictGrammarOrderBy(@NonNull String token) { + if (isTableOrColumn(token)) return; + switch (token.toUpperCase(Locale.US)) { + case "COLLATE": case "ASC": case "DESC": + case "BINARY": case "RTRIM": case "NOCASE": + return; + } + throw new IllegalArgumentException("Invalid token " + token); + } + + private void enforceStrictGrammarLimit(@NonNull String token) { + switch (token.toUpperCase(Locale.US)) { + case "OFFSET": + return; + } + throw new IllegalArgumentException("Invalid token " + token); + } + /** * Construct a {@code SELECT} statement suitable for use in a group of * {@code SELECT} statements that will be joined through {@code UNION} operators @@ -698,6 +889,35 @@ public class SQLiteQueryBuilder { } /** {@hide} */ + public String buildInsert(ContentValues values) { + if (values == null || values.isEmpty()) { + throw new IllegalArgumentException("Empty values"); + } + + StringBuilder sql = new StringBuilder(120); + sql.append("INSERT INTO "); + sql.append(SQLiteDatabase.findEditTable(mTables)); + sql.append(" ("); + + final ArrayMap<String, Object> rawValues = values.getValues(); + for (int i = 0; i < rawValues.size(); i++) { + if (i > 0) { + sql.append(','); + } + sql.append(rawValues.keyAt(i)); + } + sql.append(") VALUES ("); + for (int i = 0; i < rawValues.size(); i++) { + if (i > 0) { + sql.append(','); + } + sql.append('?'); + } + sql.append(")"); + return sql.toString(); + } + + /** {@hide} */ public String buildUpdate(ContentValues values, String selection) { if (values == null || values.isEmpty()) { throw new IllegalArgumentException("Empty values"); @@ -705,7 +925,7 @@ public class SQLiteQueryBuilder { StringBuilder sql = new StringBuilder(120); sql.append("UPDATE "); - sql.append(mTables); + sql.append(SQLiteDatabase.findEditTable(mTables)); sql.append(" SET "); final ArrayMap<String, Object> rawValues = values.getValues(); @@ -726,7 +946,7 @@ public class SQLiteQueryBuilder { public String buildDelete(String selection) { StringBuilder sql = new StringBuilder(120); sql.append("DELETE FROM "); - sql.append(mTables); + sql.append(SQLiteDatabase.findEditTable(mTables)); final String where = computeWhere(selection); appendClause(sql, " WHERE ", where); @@ -868,65 +1088,13 @@ public class SQLiteQueryBuilder { /** {@hide} */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) - public String[] computeProjection(String[] projectionIn) { - if (projectionIn != null && projectionIn.length > 0) { - if (mProjectionMap != null) { - String[] projection = new String[projectionIn.length]; - int length = projectionIn.length; - - for (int i = 0; i < length; i++) { - String operator = null; - String userColumn = projectionIn[i]; - String column = mProjectionMap.get(userColumn); - - // If aggregation is allowed, extract the underlying column - // that may be aggregated - if (mProjectionAggregationAllowed) { - final Matcher matcher = sAggregationPattern.matcher(userColumn); - if (matcher.matches()) { - operator = matcher.group(1); - userColumn = matcher.group(2); - column = mProjectionMap.get(userColumn); - } - } - - if (column != null) { - projection[i] = maybeWithOperator(operator, column); - continue; - } - - if (!mStrict && - ( userColumn.contains(" AS ") || userColumn.contains(" as "))) { - /* A column alias already exist */ - projection[i] = maybeWithOperator(operator, userColumn); - continue; - } - - // If greylist is configured, we might be willing to let - // this custom column bypass our strict checks. - if (mProjectionGreylist != null) { - boolean match = false; - for (Pattern p : mProjectionGreylist) { - if (p.matcher(userColumn).matches()) { - match = true; - break; - } - } - - if (match) { - Log.w(TAG, "Allowing abusive custom column: " + userColumn); - projection[i] = maybeWithOperator(operator, userColumn); - continue; - } - } - - throw new IllegalArgumentException("Invalid column " - + projectionIn[i]); - } - return projection; - } else { - return projectionIn; + public @Nullable String[] computeProjection(@Nullable String[] projectionIn) { + if (!ArrayUtils.isEmpty(projectionIn)) { + String[] projectionOut = new String[projectionIn.length]; + for (int i = 0; i < projectionIn.length; i++) { + projectionOut[i] = computeSingleProjectionOrThrow(projectionIn[i]); } + return projectionOut; } else if (mProjectionMap != null) { // Return all columns in projection map. Set<Entry<String, String>> entrySet = mProjectionMap.entrySet(); @@ -948,6 +1116,69 @@ public class SQLiteQueryBuilder { return null; } + private @NonNull String computeSingleProjectionOrThrow(@NonNull String userColumn) { + final String column = computeSingleProjection(userColumn); + if (column != null) { + return column; + } else { + throw new IllegalArgumentException("Invalid column " + userColumn); + } + } + + private @Nullable String computeSingleProjection(@NonNull String userColumn) { + // When no mapping provided, anything goes + if (mProjectionMap == null) { + return userColumn; + } + + String operator = null; + String column = mProjectionMap.get(userColumn); + + // When no direct match found, look for aggregation + if (column == null) { + final Matcher matcher = sAggregationPattern.matcher(userColumn); + if (matcher.matches()) { + operator = matcher.group(1); + userColumn = matcher.group(2); + column = mProjectionMap.get(userColumn); + } + } + + if (column != null) { + return maybeWithOperator(operator, column); + } + + if (mStrictFlags == 0 && + (userColumn.contains(" AS ") || userColumn.contains(" as "))) { + /* A column alias already exist */ + return maybeWithOperator(operator, userColumn); + } + + // If greylist is configured, we might be willing to let + // this custom column bypass our strict checks. + if (mProjectionGreylist != null) { + boolean match = false; + for (Pattern p : mProjectionGreylist) { + if (p.matcher(userColumn).matches()) { + match = true; + break; + } + } + + if (match) { + Log.w(TAG, "Allowing abusive custom column: " + userColumn); + return maybeWithOperator(operator, userColumn); + } + } + + return null; + } + + private boolean isTableOrColumn(String token) { + if (mTables.equals(token)) return true; + return computeSingleProjection(token) != null; + } + /** {@hide} */ public @Nullable String computeWhere(@Nullable String selection) { final boolean hasInternal = !TextUtils.isEmpty(mWhereClause); diff --git a/core/java/android/database/sqlite/SQLiteTokenizer.java b/core/java/android/database/sqlite/SQLiteTokenizer.java new file mode 100644 index 000000000000..7e7c3fb976c7 --- /dev/null +++ b/core/java/android/database/sqlite/SQLiteTokenizer.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 android.database.sqlite; + +import android.annotation.NonNull; +import android.annotation.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.function.Consumer; + +/** + * SQL Tokenizer specialized to extract tokens from SQL (snippets). + * <p> + * Based on sqlite3GetToken() in tokenzie.c in SQLite. + * <p> + * Source for v3.8.6 (which android uses): http://www.sqlite.org/src/artifact/ae45399d6252b4d7 + * (Latest source as of now: http://www.sqlite.org/src/artifact/78c8085bc7af1922) + * <p> + * Also draft spec: http://www.sqlite.org/draft/tokenreq.html + * + * @hide + */ +public class SQLiteTokenizer { + private static boolean isAlpha(char ch) { + return ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') || (ch == '_'); + } + + private static boolean isNum(char ch) { + return ('0' <= ch && ch <= '9'); + } + + private static boolean isAlNum(char ch) { + return isAlpha(ch) || isNum(ch); + } + + private static boolean isAnyOf(char ch, String set) { + return set.indexOf(ch) >= 0; + } + + private static IllegalArgumentException genException(String message, String sql) { + throw new IllegalArgumentException(message + " in '" + sql + "'"); + } + + private static char peek(String s, int index) { + return index < s.length() ? s.charAt(index) : '\0'; + } + + public static final int OPTION_NONE = 0; + + /** + * Require that SQL contains only tokens; any comments or values will result + * in an exception. + */ + public static final int OPTION_TOKEN_ONLY = 1 << 0; + + /** + * Tokenize the given SQL, returning the list of each encountered token. + * + * @throws IllegalArgumentException if invalid SQL is encountered. + */ + public static List<String> tokenize(@Nullable String sql, int options) { + final ArrayList<String> res = new ArrayList<>(); + tokenize(sql, options, res::add); + return res; + } + + /** + * Tokenize the given SQL, sending each encountered token to the given + * {@link Consumer}. + * + * @throws IllegalArgumentException if invalid SQL is encountered. + */ + public static void tokenize(@Nullable String sql, int options, Consumer<String> checker) { + if (sql == null) { + return; + } + int pos = 0; + final int len = sql.length(); + while (pos < len) { + final char ch = peek(sql, pos); + + // Regular token. + if (isAlpha(ch)) { + final int start = pos; + pos++; + while (isAlNum(peek(sql, pos))) { + pos++; + } + final int end = pos; + + final String token = sql.substring(start, end); + checker.accept(token); + + continue; + } + + // Handle quoted tokens + if (isAnyOf(ch, "'\"`")) { + final int quoteStart = pos; + pos++; + + for (;;) { + pos = sql.indexOf(ch, pos); + if (pos < 0) { + throw genException("Unterminated quote", sql); + } + if (peek(sql, pos + 1) != ch) { + break; + } + // Quoted quote char -- e.g. "abc""def" is a single string. + pos += 2; + } + final int quoteEnd = pos; + pos++; + + if (ch != '\'') { + // Extract the token + final String tokenUnquoted = sql.substring(quoteStart + 1, quoteEnd); + + final String token; + + // Unquote if needed. i.e. "aa""bb" -> aa"bb + if (tokenUnquoted.indexOf(ch) >= 0) { + token = tokenUnquoted.replaceAll( + String.valueOf(ch) + ch, String.valueOf(ch)); + } else { + token = tokenUnquoted; + } + checker.accept(token); + } else { + if ((options &= OPTION_TOKEN_ONLY) != 0) { + throw genException("Non-token detected", sql); + } + } + continue; + } + // Handle tokens enclosed in [...] + if (ch == '[') { + final int quoteStart = pos; + pos++; + + pos = sql.indexOf(']', pos); + if (pos < 0) { + throw genException("Unterminated quote", sql); + } + final int quoteEnd = pos; + pos++; + + final String token = sql.substring(quoteStart + 1, quoteEnd); + + checker.accept(token); + continue; + } + if ((options &= OPTION_TOKEN_ONLY) != 0) { + throw genException("Non-token detected", sql); + } + + // Detect comments. + if (ch == '-' && peek(sql, pos + 1) == '-') { + pos += 2; + pos = sql.indexOf('\n', pos); + if (pos < 0) { + // We disallow strings ending in an inline comment. + throw genException("Unterminated comment", sql); + } + pos++; + + continue; + } + if (ch == '/' && peek(sql, pos + 1) == '*') { + pos += 2; + pos = sql.indexOf("*/", pos); + if (pos < 0) { + throw genException("Unterminated comment", sql); + } + pos += 2; + + continue; + } + + // Semicolon is never allowed. + if (ch == ';') { + throw genException("Semicolon is not allowed", sql); + } + + // For this purpose, we can simply ignore other characters. + // (Note it doesn't handle the X'' literal properly and reports this X as a token, + // but that should be fine...) + pos++; + } + } + + /** + * Test if given token is a + * <a href="https://www.sqlite.org/lang_keywords.html">SQLite reserved + * keyword</a>. + */ + public static boolean isKeyword(@NonNull String token) { + switch (token.toUpperCase(Locale.US)) { + case "ABORT": case "ACTION": case "ADD": case "AFTER": + case "ALL": case "ALTER": case "ANALYZE": case "AND": + case "AS": case "ASC": case "ATTACH": case "AUTOINCREMENT": + case "BEFORE": case "BEGIN": case "BETWEEN": case "BINARY": + case "BY": case "CASCADE": case "CASE": case "CAST": + case "CHECK": case "COLLATE": case "COLUMN": case "COMMIT": + case "CONFLICT": case "CONSTRAINT": case "CREATE": case "CROSS": + case "CURRENT": case "CURRENT_DATE": case "CURRENT_TIME": case "CURRENT_TIMESTAMP": + case "DATABASE": case "DEFAULT": case "DEFERRABLE": case "DEFERRED": + case "DELETE": case "DESC": case "DETACH": case "DISTINCT": + case "DO": case "DROP": case "EACH": case "ELSE": + case "END": case "ESCAPE": case "EXCEPT": case "EXCLUDE": + case "EXCLUSIVE": case "EXISTS": case "EXPLAIN": case "FAIL": + case "FILTER": case "FOLLOWING": case "FOR": case "FOREIGN": + case "FROM": case "FULL": case "GLOB": case "GROUP": + case "GROUPS": case "HAVING": case "IF": case "IGNORE": + case "IMMEDIATE": case "IN": case "INDEX": case "INDEXED": + case "INITIALLY": case "INNER": case "INSERT": case "INSTEAD": + case "INTERSECT": case "INTO": case "IS": case "ISNULL": + case "JOIN": case "KEY": case "LEFT": case "LIKE": + case "LIMIT": case "MATCH": case "NATURAL": case "NO": + case "NOCASE": case "NOT": case "NOTHING": case "NOTNULL": + case "NULL": case "OF": case "OFFSET": case "ON": + case "OR": case "ORDER": case "OTHERS": case "OUTER": + case "OVER": case "PARTITION": case "PLAN": case "PRAGMA": + case "PRECEDING": case "PRIMARY": case "QUERY": case "RAISE": + case "RANGE": case "RECURSIVE": case "REFERENCES": case "REGEXP": + case "REINDEX": case "RELEASE": case "RENAME": case "REPLACE": + case "RESTRICT": case "RIGHT": case "ROLLBACK": case "ROW": + case "ROWS": case "RTRIM": case "SAVEPOINT": case "SELECT": + case "SET": case "TABLE": case "TEMP": case "TEMPORARY": + case "THEN": case "TIES": case "TO": case "TRANSACTION": + case "TRIGGER": case "UNBOUNDED": case "UNION": case "UNIQUE": + case "UPDATE": case "USING": case "VACUUM": case "VALUES": + case "VIEW": case "VIRTUAL": case "WHEN": case "WHERE": + case "WINDOW": case "WITH": case "WITHOUT": + return true; + default: + return false; + } + } + + /** + * Test if given token is a + * <a href="https://www.sqlite.org/lang_corefunc.html">SQLite reserved + * function</a>. + */ + public static boolean isFunction(@NonNull String token) { + switch (token.toLowerCase(Locale.US)) { + case "abs": case "avg": case "char": case "coalesce": + case "count": case "glob": case "group_concat": case "hex": + case "ifnull": case "instr": case "length": case "like": + case "likelihood": case "likely": case "lower": case "ltrim": + case "max": case "min": case "nullif": case "random": + case "randomblob": case "replace": case "round": case "rtrim": + case "substr": case "sum": case "total": case "trim": + case "typeof": case "unicode": case "unlikely": case "upper": + case "zeroblob": + return true; + default: + return false; + } + } + + /** + * Test if given token is a + * <a href="https://www.sqlite.org/datatype3.html">SQLite reserved type</a>. + */ + public static boolean isType(@NonNull String token) { + switch (token.toUpperCase(Locale.US)) { + case "INT": case "INTEGER": case "TINYINT": case "SMALLINT": + case "MEDIUMINT": case "BIGINT": case "INT2": case "INT8": + case "CHARACTER": case "VARCHAR": case "NCHAR": case "NVARCHAR": + case "TEXT": case "CLOB": case "BLOB": case "REAL": + case "DOUBLE": case "FLOAT": case "NUMERIC": case "DECIMAL": + case "BOOLEAN": case "DATE": case "DATETIME": + return true; + default: + return false; + } + } +} diff --git a/core/java/android/hardware/biometrics/IBiometricEnabledOnKeyguardCallback.aidl b/core/java/android/hardware/biometrics/IBiometricEnabledOnKeyguardCallback.aidl index d22e7e295b77..62d727c080e3 100644 --- a/core/java/android/hardware/biometrics/IBiometricEnabledOnKeyguardCallback.aidl +++ b/core/java/android/hardware/biometrics/IBiometricEnabledOnKeyguardCallback.aidl @@ -22,5 +22,5 @@ import android.hardware.biometrics.BiometricSourceType; * @hide */ oneway interface IBiometricEnabledOnKeyguardCallback { - void onChanged(in BiometricSourceType type, boolean enabled); + void onChanged(in BiometricSourceType type, boolean enabled, int userId); }
\ No newline at end of file diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index 9f4118e83e47..b3125d89a1a7 100755 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -1191,7 +1191,7 @@ public class Build { ArrayList<Partition> partitions = new ArrayList(); String[] names = new String[] { - "bootimage", "odm", "product", "product_services", Partition.PARTITION_NAME_SYSTEM, + "bootimage", "odm", "product", "system_ext", Partition.PARTITION_NAME_SYSTEM, "vendor" }; for (String name : names) { diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java index 0ee9a1192a5f..3462d1f56b67 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -54,7 +54,7 @@ public class Environment { private static final String ENV_ODM_ROOT = "ODM_ROOT"; private static final String ENV_VENDOR_ROOT = "VENDOR_ROOT"; private static final String ENV_PRODUCT_ROOT = "PRODUCT_ROOT"; - private static final String ENV_PRODUCT_SERVICES_ROOT = "PRODUCT_SERVICES_ROOT"; + private static final String ENV_SYSTEM_EXT_ROOT = "SYSTEM_EXT_ROOT"; /** {@hide} */ public static final String DIR_ANDROID = "Android"; @@ -77,8 +77,8 @@ public class Environment { private static final File DIR_ODM_ROOT = getDirectory(ENV_ODM_ROOT, "/odm"); private static final File DIR_VENDOR_ROOT = getDirectory(ENV_VENDOR_ROOT, "/vendor"); private static final File DIR_PRODUCT_ROOT = getDirectory(ENV_PRODUCT_ROOT, "/product"); - private static final File DIR_PRODUCT_SERVICES_ROOT = getDirectory(ENV_PRODUCT_SERVICES_ROOT, - "/product_services"); + private static final File DIR_SYSTEM_EXT_ROOT = getDirectory(ENV_SYSTEM_EXT_ROOT, + "/system_ext"); @UnsupportedAppUsage private static UserEnvironment sCurrentUser; @@ -222,11 +222,26 @@ public class Environment { * Return root directory of the "product_services" partition holding middleware * services if any. If present, the partition is mounted read-only. * + * @deprecated This directory is not guaranteed to exist. + * Its name is changed to "system_ext" because the partition's purpose is changed. + * {@link #getSystemExtDirectory()} * @hide */ @SystemApi + @Deprecated public static @NonNull File getProductServicesDirectory() { - return DIR_PRODUCT_SERVICES_ROOT; + return getDirectory("PRODUCT_SERVICES_ROOT", "/product_services"); + } + + /** + * Return root directory of the "system_ext" partition holding system partition's extension + * If present, the partition is mounted read-only. + * + * @hide + */ + @SystemApi + public static @NonNull File getSystemExtDirectory() { + return DIR_SYSTEM_EXT_ROOT; } /** diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 9c9829fb08e2..a7fa96bc1d90 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -1283,6 +1283,16 @@ public class UserManager { } /** + * @hide + * @return Whether the device is running in a headless system user mode. It means the headless + * user (system user) runs system services and system UI, but is not associated with any real + * person. Secondary users can be created to be associated with real person. + */ + public static boolean isHeadlessSystemUserMode() { + return RoSystemProperties.MULTIUSER_HEADLESS_SYSTEM_USER; + } + + /** * @deprecated use {@link #getUserSwitchability()} instead. * * @removed diff --git a/core/java/android/permission/IPermissionManager.aidl b/core/java/android/permission/IPermissionManager.aidl index d31cee0a1dac..9fa5f1647431 100644 --- a/core/java/android/permission/IPermissionManager.aidl +++ b/core/java/android/permission/IPermissionManager.aidl @@ -70,4 +70,31 @@ interface IPermissionManager { void revokeRuntimePermission(String packageName, String permName, int userId); void resetRuntimePermissions(); + + boolean setDefaultBrowser(String packageName, int userId); + + String getDefaultBrowser(int userId); + + void grantDefaultPermissionsToEnabledCarrierApps(in String[] packageNames, int userId); + + void grantDefaultPermissionsToEnabledImsServices(in String[] packageNames, int userId); + + void grantDefaultPermissionsToEnabledTelephonyDataServices( + in String[] packageNames, int userId); + + void revokeDefaultPermissionsFromDisabledTelephonyDataServices( + in String[] packageNames, int userId); + + void grantDefaultPermissionsToActiveLuiApp(in String packageName, int userId); + + void revokeDefaultPermissionsFromLuiApps(in String[] packageNames, int userId); + + void setPermissionEnforced(String permName, boolean enforced); + + boolean isPermissionEnforced(String permName); + + boolean shouldShowRequestPermissionRationale(String permName, + String packageName, int userId); + + boolean isPermissionRevokedByPolicy(String permName, String packageName, int userId); } diff --git a/core/java/android/service/autofill/FillEventHistory.java b/core/java/android/service/autofill/FillEventHistory.java index c65e773283cb..1cd2d62ce55f 100644 --- a/core/java/android/service/autofill/FillEventHistory.java +++ b/core/java/android/service/autofill/FillEventHistory.java @@ -224,13 +224,21 @@ public final class FillEventHistory implements Parcelable { */ public static final int TYPE_CONTEXT_COMMITTED = 4; + /** + * A dataset selector was shown. + * + * <p>This event is fired whenever the autofill UI was presented to the user.</p> + */ + public static final int TYPE_DATASETS_SHOWN = 5; + /** @hide */ @IntDef(prefix = { "TYPE_" }, value = { TYPE_DATASET_SELECTED, TYPE_DATASET_AUTHENTICATION_SELECTED, TYPE_AUTHENTICATION_SELECTED, TYPE_SAVE_SHOWN, - TYPE_CONTEXT_COMMITTED + TYPE_CONTEXT_COMMITTED, + TYPE_DATASETS_SHOWN }) @Retention(RetentionPolicy.SOURCE) @interface EventIds{} @@ -473,7 +481,7 @@ public final class FillEventHistory implements Parcelable { @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds, @Nullable AutofillId[] detectedFieldIds, @Nullable FieldClassification[] detectedFieldClassifications) { - mEventType = Preconditions.checkArgumentInRange(eventType, 0, TYPE_CONTEXT_COMMITTED, + mEventType = Preconditions.checkArgumentInRange(eventType, 0, TYPE_DATASETS_SHOWN, "eventType"); mDatasetId = datasetId; mClientState = clientState; diff --git a/core/java/android/view/DisplayCutout.java b/core/java/android/view/DisplayCutout.java index 715181f28076..797c128e514c 100644 --- a/core/java/android/view/DisplayCutout.java +++ b/core/java/android/view/DisplayCutout.java @@ -319,18 +319,23 @@ public final class DisplayCutout { sortedBounds[i] = ZERO_RECT; } if (safeInsets != null && boundingRects != null) { + // There is at most one non-functional area per short edge of the device, but none + // on the long edges, so either a) safeInsets.top and safeInsets.bottom is 0, or + // b) safeInsets.left and safeInset.right is 0. + final boolean topBottomInset = safeInsets.top > 0 || safeInsets.bottom > 0; for (Rect bound : boundingRects) { - // There is at most one non-functional area per short edge of the device, but none - // on the long edges, so either safeInsets.right or safeInsets.bottom must be 0. - // TODO(b/117199965): Refine the logic to handle edge cases. - if (bound.left == 0) { - sortedBounds[BOUNDS_POSITION_LEFT] = bound; - } else if (bound.top == 0) { - sortedBounds[BOUNDS_POSITION_TOP] = bound; - } else if (safeInsets.right > 0) { - sortedBounds[BOUNDS_POSITION_RIGHT] = bound; - } else if (safeInsets.bottom > 0) { - sortedBounds[BOUNDS_POSITION_BOTTOM] = bound; + if (topBottomInset) { + if (bound.top == 0) { + sortedBounds[BOUNDS_POSITION_TOP] = bound; + } else { + sortedBounds[BOUNDS_POSITION_BOTTOM] = bound; + } + } else { + if (bound.left == 0) { + sortedBounds[BOUNDS_POSITION_LEFT] = bound; + } else { + sortedBounds[BOUNDS_POSITION_RIGHT] = bound; + } } } } diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index 4bfd1387bef4..f73f28a943c3 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -308,7 +308,7 @@ interface IWindowSession { /** * Called when the system gesture exclusion has changed. */ - void reportSystemGestureExclusionChanged(IWindow window, in List<Rect> exclusionRects); + oneway void reportSystemGestureExclusionChanged(IWindow window, in List<Rect> exclusionRects); /** * Request the server to call setInputWindowInfo on a given Surface, and return diff --git a/core/java/android/view/WindowInfo.java b/core/java/android/view/WindowInfo.java index abf5e3f47e45..57dfc62b4f8f 100644 --- a/core/java/android/view/WindowInfo.java +++ b/core/java/android/view/WindowInfo.java @@ -16,7 +16,7 @@ package android.view; -import android.graphics.Rect; +import android.graphics.Region; import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; @@ -44,7 +44,7 @@ public class WindowInfo implements Parcelable { public IBinder parentToken; public IBinder activityToken; public boolean focused; - public final Rect boundsInScreen = new Rect(); + public Region regionInScreen = new Region(); public List<IBinder> childTokens; public CharSequence title; public long accessibilityIdOfAnchor = AccessibilityNodeInfo.UNDEFINED_NODE_ID; @@ -73,7 +73,7 @@ public class WindowInfo implements Parcelable { window.parentToken = other.parentToken; window.activityToken = other.activityToken; window.focused = other.focused; - window.boundsInScreen.set(other.boundsInScreen); + window.regionInScreen.set(other.regionInScreen); window.title = other.title; window.accessibilityIdOfAnchor = other.accessibilityIdOfAnchor; window.inPictureInPicture = other.inPictureInPicture; @@ -109,7 +109,7 @@ public class WindowInfo implements Parcelable { parcel.writeStrongBinder(parentToken); parcel.writeStrongBinder(activityToken); parcel.writeInt(focused ? 1 : 0); - boundsInScreen.writeToParcel(parcel, flags); + regionInScreen.writeToParcel(parcel, flags); parcel.writeCharSequence(title); parcel.writeLong(accessibilityIdOfAnchor); parcel.writeInt(inPictureInPicture ? 1 : 0); @@ -132,7 +132,8 @@ public class WindowInfo implements Parcelable { builder.append(", type=").append(type); builder.append(", layer=").append(layer); builder.append(", token=").append(token); - builder.append(", bounds=").append(boundsInScreen); + builder.append(", region=").append(regionInScreen); + builder.append(", bounds=").append(regionInScreen.getBounds()); builder.append(", parent=").append(parentToken); builder.append(", focused=").append(focused); builder.append(", children=").append(childTokens); @@ -151,7 +152,7 @@ public class WindowInfo implements Parcelable { parentToken = parcel.readStrongBinder(); activityToken = parcel.readStrongBinder(); focused = (parcel.readInt() == 1); - boundsInScreen.readFromParcel(parcel); + regionInScreen = Region.CREATOR.createFromParcel(parcel); title = parcel.readCharSequence(); accessibilityIdOfAnchor = parcel.readLong(); inPictureInPicture = (parcel.readInt() == 1); @@ -174,7 +175,7 @@ public class WindowInfo implements Parcelable { parentToken = null; activityToken = null; focused = false; - boundsInScreen.setEmpty(); + regionInScreen.setEmpty(); if (childTokens != null) { childTokens.clear(); } diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java index dfeb4b5c6edf..584436542875 100644 --- a/core/java/android/view/WindowlessWindowManager.java +++ b/core/java/android/view/WindowlessWindowManager.java @@ -20,8 +20,8 @@ import android.content.res.Configuration; import android.graphics.Rect; import android.os.IBinder; import android.os.RemoteException; -import android.util.MergedConfiguration; import android.util.Log; +import android.util.MergedConfiguration; import android.view.IWindowSession; import android.view.SurfaceControl; import android.view.SurfaceSession; @@ -35,7 +35,7 @@ import java.util.HashMap; * By parcelling the root surface, the app can offer another app content for embedding. * @hide */ -class WindowlessWindowManager extends IWindowSession.Default { +class WindowlessWindowManager implements IWindowSession { private final static String TAG = "WindowlessWindowManager"; /** @@ -83,6 +83,17 @@ class WindowlessWindowManager extends IWindowSession.Default { } @Override + public int addToDisplayWithoutInputChannel(android.view.IWindow window, int seq, + android.view.WindowManager.LayoutParams attrs, int viewVisibility, int layerStackId, + android.graphics.Rect outContentInsets, android.graphics.Rect outStableInsets, + android.view.InsetsState insetsState) { + return 0; + } + + @Override + public void remove(android.view.IWindow window) {} + + @Override public int relayout(IWindow window, int seq, WindowManager.LayoutParams attrs, int requestedWidth, int requestedHeight, int viewFlags, int flags, long frameNumber, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets, Rect outVisibleInsets, @@ -106,4 +117,154 @@ class WindowlessWindowManager extends IWindowSession.Default { return 0; } + + @Override + public void prepareToReplaceWindows(android.os.IBinder appToken, boolean childrenOnly) { + } + + @Override + public boolean outOfMemory(android.view.IWindow window) { + return false; + } + + @Override + public void setTransparentRegion(android.view.IWindow window, android.graphics.Region region) { + } + + @Override + public void setInsets(android.view.IWindow window, int touchableInsets, + android.graphics.Rect contentInsets, android.graphics.Rect visibleInsets, + android.graphics.Region touchableRegion) { + } + + @Override + public void getDisplayFrame(android.view.IWindow window, + android.graphics.Rect outDisplayFrame) { + } + + @Override + public void finishDrawing(android.view.IWindow window, + android.view.SurfaceControl.Transaction postDrawTransaction) { + } + + @Override + public void setInTouchMode(boolean showFocus) { + } + + @Override + public boolean getInTouchMode() { + return false; + } + + @Override + public boolean performHapticFeedback(int effectId, boolean always) { + return false; + } + + @Override + public android.os.IBinder performDrag(android.view.IWindow window, int flags, + android.view.SurfaceControl surface, int touchSource, float touchX, float touchY, + float thumbCenterX, float thumbCenterY, android.content.ClipData data) { + return null; + } + + @Override + public void reportDropResult(android.view.IWindow window, boolean consumed) { + } + + @Override + public void cancelDragAndDrop(android.os.IBinder dragToken, boolean skipAnimation) { + } + + @Override + public void dragRecipientEntered(android.view.IWindow window) { + } + + @Override + public void dragRecipientExited(android.view.IWindow window) { + } + + @Override + public void setWallpaperPosition(android.os.IBinder windowToken, float x, float y, + float xstep, float ystep) { + } + + @Override + public void wallpaperOffsetsComplete(android.os.IBinder window) { + } + + @Override + public void setWallpaperDisplayOffset(android.os.IBinder windowToken, int x, int y) { + } + + @Override + public android.os.Bundle sendWallpaperCommand(android.os.IBinder window, + java.lang.String action, int x, int y, int z, android.os.Bundle extras, boolean sync) { + return null; + } + + @Override + public void wallpaperCommandComplete(android.os.IBinder window, android.os.Bundle result) { + } + + @Override + public void onRectangleOnScreenRequested(android.os.IBinder token, + android.graphics.Rect rectangle) { + } + + @Override + public android.view.IWindowId getWindowId(android.os.IBinder window) { + return null; + } + + @Override + public void pokeDrawLock(android.os.IBinder window) { + } + + @Override + public boolean startMovingTask(android.view.IWindow window, float startX, float startY) { + return false; + } + + @Override + public void finishMovingTask(android.view.IWindow window) { + } + + @Override + public void updatePointerIcon(android.view.IWindow window) { + } + + @Override + public void reparentDisplayContent(android.view.IWindow window, android.view.SurfaceControl sc, + int displayId) { + } + + @Override + public void updateDisplayContentLocation(android.view.IWindow window, int x, int y, + int displayId) { + } + + @Override + public void updateTapExcludeRegion(android.view.IWindow window, int regionId, + android.graphics.Region region) { + } + + @Override + public void insetsModified(android.view.IWindow window, android.view.InsetsState state) { + } + + @Override + public void reportSystemGestureExclusionChanged(android.view.IWindow window, + java.util.List<android.graphics.Rect> exclusionRects) { + } + + @Override + public void blessInputSurface(int displayId, SurfaceControl surface, + InputChannel outInputChannel) { + } + + @Override + public android.os.IBinder asBinder() { + return null; + } } diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java index 985effbb9423..fd09a87aaa70 100644 --- a/core/java/android/view/accessibility/AccessibilityEvent.java +++ b/core/java/android/view/accessibility/AccessibilityEvent.java @@ -622,6 +622,10 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par /** * Change type for {@link #TYPE_WINDOWS_CHANGED} event: * The window's bounds changed. + * <p> + * Starting in {@link android.os.Build.VERSION_CODES#R R}, this event implies the window's + * region changed. It's also possible that region changed but bounds doesn't. + * </p> */ public static final int WINDOWS_CHANGE_BOUNDS = 0x00000008; diff --git a/core/java/android/view/accessibility/AccessibilityWindowInfo.java b/core/java/android/view/accessibility/AccessibilityWindowInfo.java index ea61ef86c1b1..6a3af3478449 100644 --- a/core/java/android/view/accessibility/AccessibilityWindowInfo.java +++ b/core/java/android/view/accessibility/AccessibilityWindowInfo.java @@ -16,9 +16,11 @@ package android.view.accessibility; +import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.TestApi; import android.graphics.Rect; +import android.graphics.Region; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; @@ -107,7 +109,7 @@ public final class AccessibilityWindowInfo implements Parcelable { private int mBooleanProperties; private int mId = UNDEFINED_WINDOW_ID; private int mParentId = UNDEFINED_WINDOW_ID; - private final Rect mBoundsInScreen = new Rect(); + private Region mRegionInScreen = new Region(); private LongArray mChildIds; private CharSequence mTitle; private long mAnchorId = AccessibilityNodeInfo.UNDEFINED_NODE_ID; @@ -305,23 +307,33 @@ public final class AccessibilityWindowInfo implements Parcelable { } /** - * Gets the bounds of this window in the screen. + * Gets the touchable region of this window in the screen. * - * @param outBounds The out window bounds. + * @param outRegion The out window region. */ - public void getBoundsInScreen(Rect outBounds) { - outBounds.set(mBoundsInScreen); + public void getRegionInScreen(@NonNull Region outRegion) { + outRegion.set(mRegionInScreen); } /** - * Sets the bounds of this window in the screen. + * Sets the touchable region of this window in the screen. * - * @param bounds The out window bounds. + * @param region The window region. * * @hide */ - public void setBoundsInScreen(Rect bounds) { - mBoundsInScreen.set(bounds); + public void setRegionInScreen(Region region) { + mRegionInScreen.set(region); + } + + /** + * Gets the bounds of this window in the screen. This is equivalent to get the bounds of the + * Region from {@link #getRegionInScreen(Region)}. + * + * @param outBounds The out window bounds. + */ + public void getBoundsInScreen(Rect outBounds) { + outBounds.set(mRegionInScreen.getBounds()); } /** @@ -522,7 +534,7 @@ public final class AccessibilityWindowInfo implements Parcelable { parcel.writeInt(mBooleanProperties); parcel.writeInt(mId); parcel.writeInt(mParentId); - mBoundsInScreen.writeToParcel(parcel, flags); + mRegionInScreen.writeToParcel(parcel, flags); parcel.writeCharSequence(mTitle); parcel.writeLong(mAnchorId); @@ -552,7 +564,7 @@ public final class AccessibilityWindowInfo implements Parcelable { mBooleanProperties = other.mBooleanProperties; mId = other.mId; mParentId = other.mParentId; - mBoundsInScreen.set(other.mBoundsInScreen); + mRegionInScreen.set(other.mRegionInScreen); mTitle = other.mTitle; mAnchorId = other.mAnchorId; @@ -574,7 +586,7 @@ public final class AccessibilityWindowInfo implements Parcelable { mBooleanProperties = parcel.readInt(); mId = parcel.readInt(); mParentId = parcel.readInt(); - mBoundsInScreen.readFromParcel(parcel); + mRegionInScreen = Region.CREATOR.createFromParcel(parcel); mTitle = parcel.readCharSequence(); mAnchorId = parcel.readLong(); @@ -621,7 +633,8 @@ public final class AccessibilityWindowInfo implements Parcelable { builder.append(", id=").append(mId); builder.append(", type=").append(typeToString(mType)); builder.append(", layer=").append(mLayer); - builder.append(", bounds=").append(mBoundsInScreen); + builder.append(", region=").append(mRegionInScreen); + builder.append(", bounds=").append(mRegionInScreen.getBounds()); builder.append(", focused=").append(isFocused()); builder.append(", active=").append(isActive()); builder.append(", pictureInPicture=").append(isInPictureInPictureMode()); @@ -661,7 +674,7 @@ public final class AccessibilityWindowInfo implements Parcelable { mBooleanProperties = 0; mId = UNDEFINED_WINDOW_ID; mParentId = UNDEFINED_WINDOW_ID; - mBoundsInScreen.setEmpty(); + mRegionInScreen.setEmpty(); mChildIds = null; mConnectionId = UNDEFINED_WINDOW_ID; mAnchorId = AccessibilityNodeInfo.UNDEFINED_NODE_ID; @@ -716,7 +729,6 @@ public final class AccessibilityWindowInfo implements Parcelable { } } - /** * Reports how this window differs from a possibly different state of the same window. The * argument must have the same id and type as neither of those properties may change. @@ -739,8 +751,7 @@ public final class AccessibilityWindowInfo implements Parcelable { if (!TextUtils.equals(mTitle, other.mTitle)) { changes |= AccessibilityEvent.WINDOWS_CHANGE_TITLE; } - - if (!mBoundsInScreen.equals(other.mBoundsInScreen)) { + if (!mRegionInScreen.equals(other.mRegionInScreen)) { changes |= AccessibilityEvent.WINDOWS_CHANGE_BOUNDS; } if (mLayer != other.mLayer) { diff --git a/core/java/android/view/inputmethod/InputMethodSystemProperty.java b/core/java/android/view/inputmethod/InputMethodSystemProperty.java index 0689806bf832..e20c2fdc8074 100644 --- a/core/java/android/view/inputmethod/InputMethodSystemProperty.java +++ b/core/java/android/view/inputmethod/InputMethodSystemProperty.java @@ -17,7 +17,6 @@ package android.view.inputmethod; import android.annotation.Nullable; -import android.annotation.TestApi; import android.content.ComponentName; import android.os.Build; import android.os.SystemProperties; @@ -27,7 +26,6 @@ import android.os.SystemProperties; * * @hide */ -@TestApi public class InputMethodSystemProperty { /** * System property key for the production use. The value must be either empty or a valid @@ -78,6 +76,5 @@ public class InputMethodSystemProperty { * * @hide */ - @TestApi public static final boolean MULTI_CLIENT_IME_ENABLED = (sMultiClientImeComponentName != null); } diff --git a/core/java/android/widget/ArrayAdapter.java b/core/java/android/widget/ArrayAdapter.java index c3c2c0db9a77..2bf1ba5cf017 100644 --- a/core/java/android/widget/ArrayAdapter.java +++ b/core/java/android/widget/ArrayAdapter.java @@ -50,7 +50,7 @@ import java.util.List; * override {@link #getView(int, View, ViewGroup)} * and inflate a view resource. * For a code example, see - * the <a href="https://developer.android.com/samples/CustomChoiceList/index.html"> + * the <a href="https://github.com/googlesamples/android-CustomChoiceList/#readme"> * CustomChoiceList</a> sample. * </p> * <p> diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java index d985528c38fb..6b324a541c42 100644 --- a/core/java/android/widget/RelativeLayout.java +++ b/core/java/android/widget/RelativeLayout.java @@ -1202,13 +1202,13 @@ public class RelativeLayout extends ViewGroup { * determine where to position the view on the screen. If the view is not contained * within a relative layout, these attributes are ignored. * - * See the <a href="/guide/topics/ui/layout/relative.html"> - * Relative Layout</a> guide for example code demonstrating how to use relative layout’s + * See the <a href="{@docRoot}guide/topics/ui/layout/relative.html">Relative + * Layout</a> guide for example code demonstrating how to use relative layout's * layout parameters in a layout XML. * * To learn more about layout parameters and how they differ from typical view attributes, - * see the <a href="/guide/topics/ui/declaring-layout.html#attributes"> - * Layouts guide</a>. + * see the <a href="{@docRoot}guide/topics/ui/declaring-layout.html#attributes">Layouts + * guide</a>. * * * @attr ref android.R.styleable#RelativeLayout_Layout_layout_alignWithParentIfMissing diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java index 430fa610c76e..ee4666ff368c 100644 --- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java +++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java @@ -198,6 +198,89 @@ public final class SystemUiDeviceConfigFlags { */ public static final String BRIGHTLINE_FALSING_MANAGER_ENABLED = "brightline_falsing_manager_enabled"; + /** + * (float) Maximum fraction of the screen required to qualify as a real swipe. + */ + public static final String BRIGHTLINE_FALSING_DISTANCE_SCREEN_FRACTION_MAX_DISTANCE = + "brightline_falsing_distance_screen_fraction_max_distance"; + + /** + * (float) Multiplier for swipe velocity to convert it to pixels for a fling. + */ + public static final String BRIGHTLINE_FALSING_DISTANCE_VELOCITY_TO_DISTANCE = + "brightline_falsing_distance_velcoity_to_distance"; + + /** + * (float) How far, in inches, must a fling travel horizontally to qualify as intentional. + */ + public static final String BRIGHTLINE_FALSING_DISTANCE_HORIZONTAL_FLING_THRESHOLD_IN = + "brightline_falsing_distance_horizontal_fling_threshold_in"; + + /** + * (float) Maximum fraction of the screen required to qualify as a real swipe. + */ + public static final String BRIGHTLINE_FALSING_DISTANCE_VERTICAL_FLING_THRESHOLD_IN = + "brightline_falsing_distance_vertical_fling_threshold_in"; + + /** + * (float) How far, in inches, must a continuous swipe travel horizontally to be intentional. + */ + public static final String BRIGHTLINE_FALSING_DISTANCE_HORIZONTAL_SWIPE_THRESHOLD_IN = + "brightline_falsing_distance_horizontal_swipe_threshold_in"; + + /** + * (float) How far, in inches, must a continuous swipe travel vertically to be intentional. + */ + public static final String BRIGHTLINE_FALSING_DISTANCE_VERTICAL_SWIPE_THRESHOLD_IN = + "brightline_falsing_distance_horizontal_swipe_threshold_in"; + + /** + * (float) Percentage of swipe with the proximity sensor covered that triggers a higher + * swipe distance requirement. + */ + public static final String BRIGHTLINE_FALSING_PROXIMITY_PERCENT_COVERED_THRESHOLD = + "brightline_falsing_proximity_percent_covered_threshold"; + + /** + * (float) Angle, in radians, that a swipe can vary from horizontal and sill be intentional. + */ + public static final String BRIGHTLINE_FALSING_DIAGONAL_HORIZONTAL_ANGLE_RANGE = + "brightline_falsing_diagonal_horizontal_angle_range"; + + /** + * (float) Angle, in radians, that a swipe can vary from vertical and sill be intentional. + */ + public static final String BRIGHTLINE_FALSING_DIAGONAL_VERTICAL_ANGLE_RANGE = + "brightline_falsing_diagonal_horizontal_angle_range"; + + /** + * (float) Distance, in inches, that a swipe is allowed to vary in the horizontal direction for + * horizontal swipes. + */ + public static final String BRIGHTLINE_FALSING_ZIGZAG_X_PRIMARY_DEVIANCE = + "brightline_falsing_zigzag_x_primary_deviance"; + + /** + * (float) Distance, in inches, that a swipe is allowed to vary in the vertical direction for + * vertical swipes. + */ + public static final String BRIGHTLINE_FALSING_ZIGZAG_Y_PRIMARY_DEVIANCE = + "brightline_falsing_zigzag_y_primary_deviance"; + + /** + * (float) Distance, in inches, that a swipe is allowed to vary in the horizontal direction for + * horizontal swipes. + */ + public static final String BRIGHTLINE_FALSING_ZIGZAG_X_SECONDARY_DEVIANCE = + "brightline_falsing_zigzag_x_secondary_deviance"; + + /** + * (float) Distance, in inches, that a swipe is allowed to vary in the vertical direction for + * vertical swipes. + */ + public static final String BRIGHTLINE_FALSING_ZIGZAG_Y_SECONDARY_DEVIANCE = + "brightline_falsing_zigzag_y_secondary_deviance"; + private SystemUiDeviceConfigFlags() { } } diff --git a/core/java/com/android/internal/infra/ServiceConnector.java b/core/java/com/android/internal/infra/ServiceConnector.java index 8136cfc09733..d6862f0188ce 100644 --- a/core/java/com/android/internal/infra/ServiceConnector.java +++ b/core/java/com/android/internal/infra/ServiceConnector.java @@ -228,7 +228,7 @@ public interface ServiceConnector<I extends IInterface> { private final int mUserId; private final @Nullable Function<IBinder, I> mBinderAsInterface; - private I mService = null; + private volatile I mService = null; private boolean mBinding = false; private boolean mUnbinding = false; @@ -506,11 +506,12 @@ public interface ServiceConnector<I extends IInterface> { void unbindJobThread() { cancelTimeout(); - boolean wasBound = isBound(); + I service = mService; + boolean wasBound = service != null; if (wasBound) { - onServiceConnectionStatusChanged(mService, false); + onServiceConnectionStatusChanged(service, false); mContext.unbindService(mServiceConnection); - mService.asBinder().unlinkToDeath(this, 0); + service.asBinder().unlinkToDeath(this, 0); mService = null; } mBinding = false; @@ -543,7 +544,7 @@ public interface ServiceConnector<I extends IInterface> { } @Override - public void onServiceConnected(@NonNull ComponentName name, @NonNull IBinder service) { + public void onServiceConnected(@NonNull ComponentName name, @NonNull IBinder binder) { if (mUnbinding) { Log.i(LOG_TAG, "Ignoring onServiceConnected due to ongoing unbinding: " + this); return; @@ -551,14 +552,15 @@ public interface ServiceConnector<I extends IInterface> { if (DEBUG) { logTrace(); } - mService = binderAsInterface(service); + I service = binderAsInterface(binder); + mService = service; mBinding = false; try { - service.linkToDeath(ServiceConnector.Impl.this, 0); + binder.linkToDeath(ServiceConnector.Impl.this, 0); } catch (RemoteException e) { Log.e(LOG_TAG, "onServiceConnected " + name + ": ", e); } - onServiceConnectionStatusChanged(mService, true); + onServiceConnectionStatusChanged(service, true); processQueue(); } @@ -568,8 +570,11 @@ public interface ServiceConnector<I extends IInterface> { logTrace(); } mBinding = true; - onServiceConnectionStatusChanged(mService, false); - mService = null; + I service = mService; + if (service != null) { + onServiceConnectionStatusChanged(service, false); + mService = null; + } } @Override diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java index 9b51c1239648..9d4cdc73b452 100644 --- a/core/java/com/android/internal/os/Zygote.java +++ b/core/java/com/android/internal/os/Zygote.java @@ -106,6 +106,14 @@ public final class Zygote { */ public static final int USE_APP_IMAGE_STARTUP_CACHE = 1 << 16; + /** + * When set, application specified signal handlers are not chained (i.e, ignored) + * by the runtime. + * + * Used for debugging only. Usage: set debug.ignoreappsignalhandler to 1. + */ + public static final int DEBUG_IGNORE_APP_SIGNAL_HANDLER = 1 << 17; + /** No external storage should be mounted. */ public static final int MOUNT_EXTERNAL_NONE = IVold.REMOUNT_MODE_NONE; /** Default external storage should be mounted. */ diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java index fe66cf9aab7d..599c354f0a1a 100644 --- a/core/java/com/android/internal/policy/DecorView.java +++ b/core/java/com/android/internal/policy/DecorView.java @@ -249,6 +249,14 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind private Drawable mOriginalBackgroundDrawable; private Drawable mLastOriginalBackgroundDrawable; private Drawable mResizingBackgroundDrawable; + + /** + * Temporary holder for a window background when it is set before {@link #mWindow} is + * initialized. It will be set as the actual background once {@link #setWindow(PhoneWindow)} is + * called. + */ + @Nullable + private Drawable mPendingWindowBackground; private Drawable mCaptionBackgroundDrawable; private Drawable mUserCaptionBackgroundDrawable; @@ -961,6 +969,10 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind } public void setWindowBackground(Drawable drawable) { + if (mWindow == null) { + mPendingWindowBackground = drawable; + return; + } if (mOriginalBackgroundDrawable != drawable) { mOriginalBackgroundDrawable = drawable; updateBackgroundDrawable(); @@ -983,14 +995,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind @Override public void setBackgroundDrawable(Drawable background) { - - // TODO: This should route through setWindowBackground, but late in the release to make this - // change. - if (mOriginalBackgroundDrawable != background) { - mOriginalBackgroundDrawable = background; - updateBackgroundDrawable(); - drawableChanged(); - } + setWindowBackground(background); } public void setWindowFrame(Drawable drawable) { @@ -2010,6 +2015,11 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind DecorContext decorContext = (DecorContext) context; decorContext.setPhoneWindow(mWindow); } + if (mPendingWindowBackground != null) { + Drawable background = mPendingWindowBackground; + mPendingWindowBackground = null; + setWindowBackground(background); + } } @Override diff --git a/core/java/com/android/internal/util/AnnotationValidations.java b/core/java/com/android/internal/util/AnnotationValidations.java new file mode 100644 index 000000000000..c8afdd47f295 --- /dev/null +++ b/core/java/com/android/internal/util/AnnotationValidations.java @@ -0,0 +1,193 @@ +/* + * 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.util; + +import static com.android.internal.util.BitUtils.flagsUpTo; + +import android.annotation.AppIdInt; +import android.annotation.ColorInt; +import android.annotation.FloatRange; +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.Size; +import android.annotation.UserIdInt; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.PackageInfoFlags; +import android.content.pm.PackageManager.PermissionResult; +import android.os.UserHandle; + +import java.lang.annotation.Annotation; + +/** + * Validations for common annotations, e.g. {@link IntRange}, {@link UserIdInt}, etc. + * + * For usability from generated {@link DataClass} code, all validations are overloads of + * {@link #validate} with the following shape: + * {@code + * <A extends Annotation> void validate( + * Class<A> cls, A ignored, Object value[, (String, Object)... annotationParams]) + * } + * The ignored {@link Annotation} parameter is used to differentiate between overloads that would + * otherwise have the same jvm signature. It's usually null at runtime. + */ +public class AnnotationValidations { + private AnnotationValidations() {} + + public static void validate(Class<UserIdInt> annotation, UserIdInt ignored, int value) { + if ((value != UserHandle.USER_NULL && value < -3) + || value > Integer.MAX_VALUE / UserHandle.PER_USER_RANGE) { + invalid(annotation, value); + } + } + + public static void validate(Class<AppIdInt> annotation, AppIdInt ignored, int value) { + if (value / UserHandle.PER_USER_RANGE != 0 || value < 0) { + invalid(annotation, value); + } + } + + public static void validate(Class<IntRange> annotation, IntRange ignored, int value, + String paramName1, int param1, String paramName2, int param2) { + validate(annotation, ignored, value, paramName1, param1); + validate(annotation, ignored, value, paramName2, param2); + } + + public static void validate(Class<IntRange> annotation, IntRange ignored, int value, + String paramName, int param) { + switch (paramName) { + case "from": if (value < param) invalid(annotation, value, paramName, param); break; + case "to": if (value > param) invalid(annotation, value, paramName, param); break; + } + } + + public static void validate(Class<FloatRange> annotation, FloatRange ignored, float value, + String paramName1, float param1, String paramName2, float param2) { + validate(annotation, ignored, value, paramName1, param1); + validate(annotation, ignored, value, paramName2, param2); + } + + public static void validate(Class<FloatRange> annotation, FloatRange ignored, float value, + String paramName, float param) { + switch (paramName) { + case "from": if (value < param) invalid(annotation, value, paramName, param); break; + case "to": if (value > param) invalid(annotation, value, paramName, param); break; + } + } + + public static void validate(Class<NonNull> annotation, NonNull ignored, Object value) { + if (value == null) { + throw new NullPointerException(); + } + } + + public static void validate(Class<Size> annotation, Size ignored, int value, + String paramName1, int param1, String paramName2, int param2) { + validate(annotation, ignored, value, paramName1, param1); + validate(annotation, ignored, value, paramName2, param2); + } + + public static void validate(Class<Size> annotation, Size ignored, int value, + String paramName, int param) { + switch (paramName) { + case "value": { + if (param != -1 && value != param) invalid(annotation, value, paramName, param); + } break; + case "min": { + if (value < param) invalid(annotation, value, paramName, param); + } break; + case "max": { + if (value > param) invalid(annotation, value, paramName, param); + } break; + case "multiple": { + if (value % param != 0) invalid(annotation, value, paramName, param); + } break; + } + } + + public static void validate( + Class<PermissionResult> annotation, PermissionResult ignored, int value) { + validateIntEnum(annotation, value, PackageManager.PERMISSION_GRANTED); + } + + public static void validate( + Class<PackageInfoFlags> annotation, PackageInfoFlags ignored, int value) { + validateIntFlags(annotation, value, + flagsUpTo(PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS)); + } + + public static void validate( + Class<Intent.Flags> annotation, Intent.Flags ignored, int value) { + validateIntFlags(annotation, value, flagsUpTo(Intent.FLAG_RECEIVER_OFFLOAD)); + } + + + @Deprecated + public static void validate(Class<? extends Annotation> annotation, + Annotation ignored, Object value, Object... params) {} + @Deprecated + public static void validate(Class<? extends Annotation> annotation, + Annotation ignored, Object value) {} + @Deprecated + public static void validate(Class<? extends Annotation> annotation, + Annotation ignored, int value, Object... params) {} + public static void validate(Class<? extends Annotation> annotation, + Annotation ignored, int value) { + if (("android.annotation".equals(annotation.getPackageName$()) + && annotation.getSimpleName().endsWith("Res")) + || ColorInt.class.equals(annotation)) { + if (value < 0) { + invalid(annotation, value); + } + } + } + public static void validate(Class<? extends Annotation> annotation, + Annotation ignored, long value) { + if ("android.annotation".equals(annotation.getPackageName$()) + && annotation.getSimpleName().endsWith("Long")) { + if (value < 0L) { + invalid(annotation, value); + } + } + } + + private static void validateIntEnum( + Class<? extends Annotation> annotation, int value, int lastValid) { + if (value > lastValid) { + invalid(annotation, value); + } + } + private static void validateIntFlags( + Class<? extends Annotation> annotation, int value, int validBits) { + if ((validBits & value) != validBits) { + invalid(annotation, "0x" + Integer.toHexString(value)); + } + } + + private static void invalid(Class<? extends Annotation> annotation, Object value) { + invalid("@" + annotation.getSimpleName(), value); + } + + private static void invalid(Class<? extends Annotation> annotation, Object value, + String paramName, Object param) { + String paramPrefix = "value".equals(paramName) ? "" : paramName + " = "; + invalid("@" + annotation.getSimpleName() + "(" + paramPrefix + param + ")", value); + } + + private static void invalid(String valueKind, Object value) { + throw new IllegalStateException("Invalid " + valueKind + ": " + value); + } +} diff --git a/core/java/com/android/internal/util/BitUtils.java b/core/java/com/android/internal/util/BitUtils.java index 61581458f98a..b4bab809cc00 100644 --- a/core/java/com/android/internal/util/BitUtils.java +++ b/core/java/com/android/internal/util/BitUtils.java @@ -158,4 +158,18 @@ public final class BitUtils { public static byte[] toBytes(long l) { return ByteBuffer.allocate(8).putLong(l).array(); } + + /** + * 0b01000 -> 0b01111 + */ + public static int flagsUpTo(int lastFlag) { + return lastFlag <= 0 ? 0 : lastFlag | flagsUpTo(lastFlag >> 1); + } + + /** + * 0b00010, 0b01000 -> 0b01110 + */ + public static int flagsWithin(int firstFlag, int lastFlag) { + return (flagsUpTo(lastFlag) & ~flagsUpTo(firstFlag)) | firstFlag; + } } diff --git a/core/java/com/android/internal/util/DataClass.java b/core/java/com/android/internal/util/DataClass.java new file mode 100644 index 000000000000..146f54684c02 --- /dev/null +++ b/core/java/com/android/internal/util/DataClass.java @@ -0,0 +1,219 @@ +/* + * 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.util; + +import static java.lang.annotation.ElementType.*; + +import android.annotation.IntDef; +import android.annotation.Nullable; +import android.annotation.StringDef; +import android.os.Parcelable; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + + +@Retention(RetentionPolicy.SOURCE) +@Target(ElementType.TYPE) +public @interface DataClass { + + /** + * Generates {@link Parcelable#writeToParcel}, {@link Parcelable#describeContents} and a + * {@link Parcelable.Creator}. + * + * Can be implicitly requested by adding "implements Parcelable" to class signature + * + * You can provide custom parceling logic by using a {@link ParcelWith} annotation with a + * custom {@link Parcelling} subclass. + * + * Alternatively, for one-off customizations you can declare methods like: + * {@code void parcelFieldName(Parcel dest, int flags)} + * {@code static FieldType unparcelFieldName(Parcel in)} + */ + boolean genParcelable() default false; + + /** + * Generates a simple "parcelable" .aidl file alongside the original .java file + * + * If not explicitly requested/suppressed, is on iff {@link #genParcelable} is on + */ + boolean genAidl() default false; + + /** + * Generates getters for each field. + * + * You can request for getter to lazily initialize your field by declaring a method like: + * {@code FieldType lazyInitFieldName()} + * + * You can request for the lazy initialization to be thread safe my marking the field volatile. + */ + boolean genGetters() default true; + + /** + * Generates setters for each field. + */ + boolean genSetters() default false; + + /** + * Generates a public constructor with each field initialized from a parameter and optionally + * some user-defined state validation at the end. + * + * Uses field {@link Nullable nullability}/default value presence to determine optional + * parameters. + * + * Requesting a {@link #genBuilder} suppresses public constructor generation by default. + * + * You receive a callback at the end of constructor call by declaring the method: + * {@code void onConstructed()} + * This is the place to put any custom validation logic. + */ + boolean genConstructor() default true; + + /** + * Generates a Builder for your class. + * + * Uses a package-private constructor under the hood, so same rules hold as for + * {@link #genConstructor()} + */ + boolean genBuilder() default false; + + /** + * Generates a structural {@link Object#equals} + {@link Object#hashCode}. + * + * You can customize individual fields' logic by declaring methods like: + * {@link boolean fieldNameEquals(ClassName otherInstance)} + * {@link boolean fieldNameEquals(FieldType otherValue)} + * {@link int fieldNameHashCode()} + */ + boolean genEqualsHashCode() default false; + + /** + * Generates a structural {@link Object#toString}. + * + * You can customize individual fields' logic by declaring methods like: + * {@link String fieldNameToString()} + */ + boolean genToString() default false; + + /** + * Generates a utility method that takes a {@link PerObjectFieldAction per-field callback} + * and calls it once for each field with its name and value. + * + * If some fields are of primitive types, and additional overload is generated that takes + * multiple callbacks, specialized for used primitive types to avoid auto-boxing, e.g. + * {@link PerIntFieldAction}. + */ + boolean genForEachField() default false; + + /** + * Generates a constructor that copies the given instance of the same class. + */ + boolean genCopyConstructor() default false; + + /** + * Generates constant annotations({@link IntDef}/{@link StringDef}) for any constant groups + * with common prefix. + * The annotation names are based on the common prefix. + * + * For int constants this additionally generates the corresponding static *ToString method and + * uses it in {@link Object#toString}. + * + * Additionally, any fields you annotate with the generated constants will be automatically + * validated in constructor. + * + * Int constants specified as hex(0x..) are considered to be flags, which is taken into account + * for in their *ToString and validation. + * + * You can optionally override the name of the generated annotation by annotating each constant + * with the desired annotation name. + * + * Unless suppressed, is implied by presence of constants with common prefix. + */ + boolean genConstDefs() default true; + + + /** + * Allows specifying custom parcelling logic based on reusable + * {@link Parcelling} implementations + */ + @Retention(RetentionPolicy.SOURCE) + @Target(FIELD) + @interface ParcelWith { + Class<? extends Parcelling> value(); + } + + /** + * Allows specifying a singular name for a builder's plural field name e.g. 'name' for 'mNames' + * Used for Builder's {@code addName(String name)} methods + */ + @Retention(RetentionPolicy.SOURCE) + @Target(FIELD) + @interface PluralOf { + String value(); + } + + /** + * Marks that any annotations following it are applicable to each element of the + * collection/array, as opposed to itself. + */ + @Retention(RetentionPolicy.SOURCE) + @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE}) + @interface Each {} + + /** + * @deprecated to be used by code generator exclusively + * @hide + */ + @Deprecated + @Retention(RetentionPolicy.SOURCE) + @Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, ANNOTATION_TYPE, CONSTRUCTOR, TYPE}) + @interface Generated { + long time(); + String codegenVersion(); + String sourceFile(); + String inputSignatures() default ""; + + /** + * @deprecated to be used by code generator exclusively + * @hide + */ + @Deprecated + @Retention(RetentionPolicy.SOURCE) + @Target({FIELD, METHOD, ANNOTATION_TYPE, CONSTRUCTOR, TYPE}) + @interface Member {} + } + + /** + * Callback used by {@link #genForEachField}. + * + * @param <THIS> The enclosing data class instance. + * Can be used to try and avoid capturing values from outside of the lambda, + * minimizing allocations. + */ + interface PerObjectFieldAction<THIS> { + void acceptObject(THIS self, String fieldName, Object fieldValue); + } + + /** + * A specialization of {@link PerObjectFieldAction} called exclusively for int fields to avoid + * boxing. + */ + interface PerIntFieldAction<THIS> { + void acceptInt(THIS self, String fieldName, int fieldValue); + } +} diff --git a/core/java/com/android/internal/util/Parcelling.java b/core/java/com/android/internal/util/Parcelling.java new file mode 100644 index 000000000000..63530dc389bc --- /dev/null +++ b/core/java/com/android/internal/util/Parcelling.java @@ -0,0 +1,104 @@ +/* + * 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.util; + +import android.annotation.Nullable; +import android.os.Parcel; +import android.util.ArrayMap; + +import java.util.regex.Pattern; + +/** + * Describes a 2-way parcelling contract of type {@code T} into/out of a {@link Parcel} + * + * @param <T> the type being [un]parcelled + */ +public interface Parcelling<T> { + + /** + * Write an item into parcel. + */ + void parcel(T item, Parcel dest, int parcelFlags); + + /** + * Read an item from parcel. + */ + T unparcel(Parcel source); + + + /** + * A registry of {@link Parcelling} singletons. + */ + class Cache { + private Cache() {} + + private static ArrayMap<Class, Parcelling> sCache = new ArrayMap<>(); + + /** + * Retrieves an instance of a given {@link Parcelling} class if present. + */ + public static @Nullable <P extends Parcelling<?>> P get(Class<P> clazz) { + return (P) sCache.get(clazz); + } + + /** + * Stores an instance of a given {@link Parcelling}. + * + * @return the provided parcelling for convenience. + */ + public static <P extends Parcelling<?>> P put(P parcelling) { + sCache.put(parcelling.getClass(), parcelling); + return parcelling; + } + + /** + * Produces an instance of a given {@link Parcelling} class, by either retrieving a cached + * instance or reflectively creating one. + */ + public static <P extends Parcelling<?>> P getOrCreate(Class<P> clazz) { + P cached = get(clazz); + if (cached != null) { + return cached; + } else { + try { + return put(clazz.newInstance()); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + } + + /** + * Common {@link Parcelling} implementations. + */ + interface BuiltIn { + + class ForPattern implements Parcelling<Pattern> { + + @Override + public void parcel(Pattern item, Parcel dest, int parcelFlags) { + dest.writeString(item == null ? null : item.pattern()); + } + + @Override + public Pattern unparcel(Parcel source) { + String s = source.readString(); + return s == null ? null : Pattern.compile(s); + } + } + } +} diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java index 9fc79cb606e6..364278d1a09e 100644 --- a/core/java/com/android/server/SystemConfig.java +++ b/core/java/com/android/server/SystemConfig.java @@ -194,9 +194,8 @@ public class SystemConfig { final ArrayMap<String, ArraySet<String>> mProductPrivAppPermissions = new ArrayMap<>(); final ArrayMap<String, ArraySet<String>> mProductPrivAppDenyPermissions = new ArrayMap<>(); - final ArrayMap<String, ArraySet<String>> mProductServicesPrivAppPermissions = new ArrayMap<>(); - final ArrayMap<String, ArraySet<String>> mProductServicesPrivAppDenyPermissions = - new ArrayMap<>(); + final ArrayMap<String, ArraySet<String>> mSystemExtPrivAppPermissions = new ArrayMap<>(); + final ArrayMap<String, ArraySet<String>> mSystemExtPrivAppDenyPermissions = new ArrayMap<>(); final ArrayMap<String, ArrayMap<String, Boolean>> mOemPermissions = new ArrayMap<>(); @@ -321,12 +320,20 @@ public class SystemConfig { return mProductPrivAppDenyPermissions.get(packageName); } - public ArraySet<String> getProductServicesPrivAppPermissions(String packageName) { - return mProductServicesPrivAppPermissions.get(packageName); + /** + * Read from "permission" tags in /system_ext/etc/permissions/*.xml + * @return Set of privileged permissions that are explicitly granted. + */ + public ArraySet<String> getSystemExtPrivAppPermissions(String packageName) { + return mSystemExtPrivAppPermissions.get(packageName); } - public ArraySet<String> getProductServicesPrivAppDenyPermissions(String packageName) { - return mProductServicesPrivAppDenyPermissions.get(packageName); + /** + * Read from "deny-permission" tags in /system_ext/etc/permissions/*.xml + * @return Set of privileged permissions that are explicitly denied. + */ + public ArraySet<String> getSystemExtPrivAppDenyPermissions(String packageName) { + return mSystemExtPrivAppDenyPermissions.get(packageName); } public Map<String, Boolean> getOemPermissions(String packageName) { @@ -398,11 +405,11 @@ public class SystemConfig { readPermissions(Environment.buildPath( Environment.getProductDirectory(), "etc", "permissions"), ALLOW_ALL); - // Allow /product_services to customize all system configs + // Allow /system_ext to customize all system configs readPermissions(Environment.buildPath( - Environment.getProductServicesDirectory(), "etc", "sysconfig"), ALLOW_ALL); + Environment.getSystemExtDirectory(), "etc", "sysconfig"), ALLOW_ALL); readPermissions(Environment.buildPath( - Environment.getProductServicesDirectory(), "etc", "permissions"), ALLOW_ALL); + Environment.getSystemExtDirectory(), "etc", "permissions"), ALLOW_ALL); } void readPermissions(File libraryDir, int permissionFlag) { @@ -848,7 +855,7 @@ public class SystemConfig { } break; case "privapp-permissions": { if (allowPrivappPermissions) { - // privapp permissions from system, vendor, product and product_services + // privapp permissions from system, vendor, product and system_ext // partitions are stored separately. This is to prevent xml files in // the vendor partition from granting permissions to priv apps in the // system partition and vice versa. @@ -858,17 +865,17 @@ public class SystemConfig { Environment.getOdmDirectory().toPath() + "/"); boolean product = permFile.toPath().startsWith( Environment.getProductDirectory().toPath() + "/"); - boolean productServices = permFile.toPath().startsWith( - Environment.getProductServicesDirectory().toPath() + "/"); + boolean systemExt = permFile.toPath().startsWith( + Environment.getSystemExtDirectory().toPath() + "/"); if (vendor) { readPrivAppPermissions(parser, mVendorPrivAppPermissions, mVendorPrivAppDenyPermissions); } else if (product) { readPrivAppPermissions(parser, mProductPrivAppPermissions, mProductPrivAppDenyPermissions); - } else if (productServices) { - readPrivAppPermissions(parser, mProductServicesPrivAppPermissions, - mProductServicesPrivAppDenyPermissions); + } else if (systemExt) { + readPrivAppPermissions(parser, mSystemExtPrivAppPermissions, + mSystemExtPrivAppDenyPermissions); } else { readPrivAppPermissions(parser, mPrivAppPermissions, mPrivAppDenyPermissions); diff --git a/core/jni/android/graphics/Bitmap.h b/core/jni/android/graphics/Bitmap.h index 6934d26cdc78..06e31a1518ca 100644 --- a/core/jni/android/graphics/Bitmap.h +++ b/core/jni/android/graphics/Bitmap.h @@ -18,8 +18,9 @@ #include <jni.h> #include <android/bitmap.h> -#include <SkBitmap.h> -#include <SkImageInfo.h> + +class SkBitmap; +struct SkImageInfo; namespace android { @@ -34,8 +35,8 @@ enum BitmapCreateFlags { }; jobject createBitmap(JNIEnv* env, Bitmap* bitmap, - int bitmapCreateFlags, jbyteArray ninePatchChunk = NULL, - jobject ninePatchInsets = NULL, int density = -1); + int bitmapCreateFlags, jbyteArray ninePatchChunk = nullptr, + jobject ninePatchInsets = nullptr, int density = -1); void toSkBitmap(jlong bitmapHandle, SkBitmap* outBitmap); diff --git a/core/jni/android/opengl/util.cpp b/core/jni/android/opengl/util.cpp index 55abc932cff2..58c5871aba28 100644 --- a/core/jni/android/opengl/util.cpp +++ b/core/jni/android/opengl/util.cpp @@ -16,7 +16,6 @@ #include "jni.h" #include <nativehelper/JNIHelp.h> -#include "GraphicsJNI.h" #include <math.h> #include <stdio.h> @@ -33,6 +32,7 @@ #include <SkBitmap.h> #include "core_jni_helpers.h" +#include "android/graphics/Bitmap.h" #undef LOG_TAG #define LOG_TAG "OpenGLUtil" @@ -43,6 +43,10 @@ namespace android { +static void doThrowIAE(JNIEnv* env, const char* msg = nullptr) { + jniThrowException(env, "java/lang/IllegalArgumentException", msg); +} + static inline void mx4transform(float x, float y, float z, float w, const float* pM, float* pDest) { pDest[0] = pM[0 + 4 * 0] * x + pM[0 + 4 * 1] * y + pM[0 + 4 * 2] * z + pM[0 + 4 * 3] * w; @@ -706,7 +710,7 @@ static jint util_getInternalFormat(JNIEnv *env, jclass clazz, jlong bitmapPtr) { SkBitmap nativeBitmap; - bitmap::toBitmap(bitmapPtr).getSkBitmap(&nativeBitmap); + bitmap::toSkBitmap(bitmapPtr, &nativeBitmap); return getInternalFormat(nativeBitmap.colorType()); } @@ -714,7 +718,7 @@ static jint util_getType(JNIEnv *env, jclass clazz, jlong bitmapPtr) { SkBitmap nativeBitmap; - bitmap::toBitmap(bitmapPtr).getSkBitmap(&nativeBitmap); + bitmap::toSkBitmap(bitmapPtr, &nativeBitmap); return getType(nativeBitmap.colorType()); } @@ -723,7 +727,7 @@ static jint util_texImage2D(JNIEnv *env, jclass clazz, jlong bitmapPtr, jint type, jint border) { SkBitmap bitmap; - bitmap::toBitmap(bitmapPtr).getSkBitmap(&bitmap); + bitmap::toSkBitmap(bitmapPtr, &bitmap); SkColorType colorType = bitmap.colorType(); if (internalformat < 0) { internalformat = getInternalFormat(colorType); @@ -751,7 +755,7 @@ static jint util_texSubImage2D(JNIEnv *env, jclass clazz, jlong bitmapPtr, jint format, jint type) { SkBitmap bitmap; - bitmap::toBitmap(bitmapPtr).getSkBitmap(&bitmap); + bitmap::toSkBitmap(bitmapPtr, &bitmap); SkColorType colorType = bitmap.colorType(); int internalFormat = getInternalFormat(colorType); if (format < 0) { diff --git a/core/jni/android_app_ActivityThread.cpp b/core/jni/android_app_ActivityThread.cpp index 93f2525eb29d..3a0814875531 100644 --- a/core/jni/android_app_ActivityThread.cpp +++ b/core/jni/android_app_ActivityThread.cpp @@ -15,7 +15,6 @@ */ #include "jni.h" -#include "GraphicsJNI.h" #include <nativehelper/JNIHelp.h> #include <minikin/Layout.h> diff --git a/core/jni/android_nio_utils.h b/core/jni/android_nio_utils.h index aa75dd0c7e32..4aaa0a78c276 100644 --- a/core/jni/android_nio_utils.h +++ b/core/jni/android_nio_utils.h @@ -17,7 +17,7 @@ #ifndef _ANDROID_NIO_UTILS_H_ #define _ANDROID_NIO_UTILS_H_ -#include <android_runtime/AndroidRuntime.h> +#include <nativehelper/JNIHelp.h> namespace android { @@ -68,12 +68,12 @@ private: AutoBufferPointer() = delete; AutoBufferPointer(AutoBufferPointer&) = delete; AutoBufferPointer& operator=(AutoBufferPointer&) = delete; - static void* operator new(std::size_t); - static void* operator new[](std::size_t); - static void* operator new(std::size_t, void*); - static void* operator new[](std::size_t, void*); - static void operator delete(void*, std::size_t); - static void operator delete[](void*, std::size_t); + static void* operator new(size_t); + static void* operator new[](size_t); + static void* operator new(size_t, void*); + static void* operator new[](size_t, void*); + static void operator delete(void*, size_t); + static void operator delete[](void*, size_t); }; } /* namespace android */ diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp index d6d939183a0d..bf4ffc7e42e0 100644 --- a/core/jni/android_util_AssetManager.cpp +++ b/core/jni/android_util_AssetManager.cpp @@ -161,7 +161,7 @@ static void NativeVerifySystemIdmaps(JNIEnv* /*env*/, jclass /*clazz*/) { } // Generic idmap parameters - const char* argv[10]; + const char* argv[11]; int argc = 0; struct stat st; @@ -193,8 +193,8 @@ static void NativeVerifySystemIdmaps(JNIEnv* /*env*/, jclass /*clazz*/) { argv[argc++] = AssetManager::PRODUCT_OVERLAY_DIR; } - if (stat(AssetManager::PRODUCT_SERVICES_OVERLAY_DIR, &st) == 0) { - argv[argc++] = AssetManager::PRODUCT_SERVICES_OVERLAY_DIR; + if (stat(AssetManager::SYSTEM_EXT_OVERLAY_DIR, &st) == 0) { + argv[argc++] = AssetManager::SYSTEM_EXT_OVERLAY_DIR; } if (stat(AssetManager::ODM_OVERLAY_DIR, &st) == 0) { @@ -235,8 +235,8 @@ static jobjectArray NativeCreateIdmapsForStaticOverlaysTargetingAndroid(JNIEnv* input_dirs.push_back(AssetManager::PRODUCT_OVERLAY_DIR); } - if (stat(AssetManager::PRODUCT_SERVICES_OVERLAY_DIR, &st) == 0) { - input_dirs.push_back(AssetManager::PRODUCT_SERVICES_OVERLAY_DIR); + if (stat(AssetManager::SYSTEM_EXT_OVERLAY_DIR, &st) == 0) { + input_dirs.push_back(AssetManager::SYSTEM_EXT_OVERLAY_DIR); } if (stat(AssetManager::ODM_OVERLAY_DIR, &st) == 0) { diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp index df98cdc9f1f9..0afbaa0e174c 100644 --- a/core/jni/android_util_Binder.cpp +++ b/core/jni/android_util_Binder.cpp @@ -99,7 +99,9 @@ static struct sparseintarray_offsets_t static struct error_offsets_t { - jclass mClass; + jclass mError; + jclass mOutOfMemory; + jclass mStackOverflow; } gErrorOffsets; // ---------------------------------------------------------------------------- @@ -207,6 +209,16 @@ static JNIEnv* javavm_to_jnienv(JavaVM* vm) return vm->GetEnv((void **)&env, JNI_VERSION_1_4) >= 0 ? env : NULL; } +static const char* GetErrorTypeName(JNIEnv* env, jthrowable error) { + if (env->IsInstanceOf(error, gErrorOffsets.mOutOfMemory)) { + return "OutOfMemoryError"; + } + if (env->IsInstanceOf(error, gErrorOffsets.mStackOverflow)) { + return "StackOverflowError"; + } + return nullptr; +} + // Report a java.lang.Error (or subclass). This will terminate the runtime by // calling FatalError with a message derived from the given error. static void report_java_lang_error_fatal_error(JNIEnv* env, jthrowable error, @@ -216,7 +228,7 @@ static void report_java_lang_error_fatal_error(JNIEnv* env, jthrowable error, // Try to get the exception string. Sometimes logcat isn't available, // so try to add it to the abort message. - std::string exc_msg = "(Unknown exception message)"; + std::string exc_msg; { ScopedLocalRef<jclass> exc_class(env, env->GetObjectClass(error)); jmethodID method_id = env->GetMethodID(exc_class.get(), "toString", @@ -225,15 +237,36 @@ static void report_java_lang_error_fatal_error(JNIEnv* env, jthrowable error, env, reinterpret_cast<jstring>( env->CallObjectMethod(error, method_id))); - env->ExceptionClear(); // Just for good measure. + ScopedLocalRef<jthrowable> new_error(env, nullptr); + bool got_jstr = false; + if (env->ExceptionCheck()) { + new_error = ScopedLocalRef<jthrowable>(env, env->ExceptionOccurred()); + env->ExceptionClear(); + } if (jstr.get() != nullptr) { ScopedUtfChars jstr_utf(env, jstr.get()); if (jstr_utf.c_str() != nullptr) { exc_msg = jstr_utf.c_str(); + got_jstr = true; } else { + new_error = ScopedLocalRef<jthrowable>(env, env->ExceptionOccurred()); env->ExceptionClear(); } } + if (!got_jstr) { + exc_msg = "(Unknown exception message)"; + const char* orig_type = GetErrorTypeName(env, error); + if (orig_type != nullptr) { + exc_msg = base::StringPrintf("%s (Error was %s)", exc_msg.c_str(), orig_type); + } + const char* new_type = + new_error == nullptr ? nullptr : GetErrorTypeName(env, new_error.get()); + if (new_type != nullptr) { + exc_msg = base::StringPrintf("%s (toString() error was %s)", + exc_msg.c_str(), + new_type); + } + } } env->Throw(error); @@ -291,7 +324,7 @@ static void report_exception(JNIEnv* env, jthrowable excep, const char* msg) ALOGE("%s", msg); } - if (env->IsInstanceOf(excep, gErrorOffsets.mClass)) { + if (env->IsInstanceOf(excep, gErrorOffsets.mError)) { report_java_lang_error(env, excep, msg); } } @@ -1440,10 +1473,13 @@ const char* const kBinderProxyPathName = "android/os/BinderProxy"; static int int_register_android_os_BinderProxy(JNIEnv* env) { - jclass clazz = FindClassOrDie(env, "java/lang/Error"); - gErrorOffsets.mClass = MakeGlobalRefOrDie(env, clazz); + gErrorOffsets.mError = MakeGlobalRefOrDie(env, FindClassOrDie(env, "java/lang/Error")); + gErrorOffsets.mOutOfMemory = + MakeGlobalRefOrDie(env, FindClassOrDie(env, "java/lang/OutOfMemoryError")); + gErrorOffsets.mStackOverflow = + MakeGlobalRefOrDie(env, FindClassOrDie(env, "java/lang/StackOverflowError")); - clazz = FindClassOrDie(env, kBinderProxyPathName); + jclass clazz = FindClassOrDie(env, kBinderProxyPathName); gBinderProxyOffsets.mClass = MakeGlobalRefOrDie(env, clazz); gBinderProxyOffsets.mGetInstance = GetStaticMethodIDOrDie(env, clazz, "getInstance", "(JJ)Landroid/os/BinderProxy;"); diff --git a/core/jni/android_view_RenderNodeAnimator.cpp b/core/jni/android_view_RenderNodeAnimator.cpp index c9eac79ef9b6..ca32b0051bf5 100644 --- a/core/jni/android_view_RenderNodeAnimator.cpp +++ b/core/jni/android_view_RenderNodeAnimator.cpp @@ -17,7 +17,6 @@ #define LOG_TAG "OpenGLRenderer" #include "jni.h" -#include "GraphicsJNI.h" #include <nativehelper/JNIHelp.h> #include <android_runtime/AndroidRuntime.h> diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index 78dd1aa85ae0..a5792293e4bd 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -21,10 +21,10 @@ #include "android_util_Binder.h" #include "android_hardware_input_InputWindowHandle.h" #include "android/graphics/Bitmap.h" -#include "android/graphics/GraphicsJNI.h" #include "android/graphics/Region.h" #include "core_jni_helpers.h" +#include <android_runtime/AndroidRuntime.h> #include <android-base/chrono_utils.h> #include <nativehelper/JNIHelp.h> #include <nativehelper/ScopedUtfChars.h> @@ -50,6 +50,14 @@ namespace android { +static void doThrowNPE(JNIEnv* env) { + jniThrowNullPointerException(env, NULL); +} + +static void doThrowIAE(JNIEnv* env, const char* msg = nullptr) { + jniThrowException(env, "java/lang/IllegalArgumentException", msg); +} + static const char* const OutOfResourcesException = "android/view/Surface$OutOfResourcesException"; diff --git a/core/jni/com_android_internal_util_VirtualRefBasePtr.cpp b/core/jni/com_android_internal_util_VirtualRefBasePtr.cpp index d20bae232656..d629d63cd2c4 100644 --- a/core/jni/com_android_internal_util_VirtualRefBasePtr.cpp +++ b/core/jni/com_android_internal_util_VirtualRefBasePtr.cpp @@ -14,8 +14,8 @@ * limitations under the License. */ -#include "jni.h" #include <nativehelper/JNIHelp.h> +#include <utils/LightRefBase.h> #include "core_jni_helpers.h" namespace android { diff --git a/core/jni/com_google_android_gles_jni_EGLImpl.cpp b/core/jni/com_google_android_gles_jni_EGLImpl.cpp index 940ac22193b3..5aea8485d0c1 100644 --- a/core/jni/com_google_android_gles_jni_EGLImpl.cpp +++ b/core/jni/com_google_android_gles_jni_EGLImpl.cpp @@ -30,10 +30,6 @@ #include <gui/GLConsumer.h> #include <gui/Surface.h> -#include <GraphicsJNI.h> -#include <SkBitmap.h> -#include <SkPixelRef.h> - #include <ui/ANativeObjectBase.h> namespace android { diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp index e5d6393530e4..ea4b25267512 100644 --- a/core/jni/fd_utils.cpp +++ b/core/jni/fd_utils.cpp @@ -100,8 +100,8 @@ bool FileDescriptorWhitelist::IsAllowed(const std::string& path) const { static const char* kVendorOverlaySubdir = "/system/vendor/overlay-subdir/"; static const char* kSystemProductOverlayDir = "/system/product/overlay/"; static const char* kProductOverlayDir = "/product/overlay"; - static const char* kSystemProductServicesOverlayDir = "/system/product_services/overlay/"; - static const char* kProductServicesOverlayDir = "/product_services/overlay"; + static const char* kSystemSystemExtOverlayDir = "/system/system_ext/overlay/"; + static const char* kSystemExtOverlayDir = "/system_ext/overlay"; static const char* kSystemOdmOverlayDir = "/system/odm/overlay"; static const char* kOdmOverlayDir = "/odm/overlay"; static const char* kSystemOemOverlayDir = "/system/oem/overlay"; @@ -113,8 +113,8 @@ bool FileDescriptorWhitelist::IsAllowed(const std::string& path) const { || android::base::StartsWith(path, kVendorOverlayDir) || android::base::StartsWith(path, kSystemProductOverlayDir) || android::base::StartsWith(path, kProductOverlayDir) - || android::base::StartsWith(path, kSystemProductServicesOverlayDir) - || android::base::StartsWith(path, kProductServicesOverlayDir) + || android::base::StartsWith(path, kSystemSystemExtOverlayDir) + || android::base::StartsWith(path, kSystemExtOverlayDir) || android::base::StartsWith(path, kSystemOdmOverlayDir) || android::base::StartsWith(path, kOdmOverlayDir) || android::base::StartsWith(path, kSystemOemOverlayDir) diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto index 784e85414e00..c534aa419595 100644 --- a/core/proto/android/server/jobscheduler.proto +++ b/core/proto/android/server/jobscheduler.proto @@ -231,15 +231,6 @@ message ConstantsProto { // will use heartbeats, false will use a rolling window. optional bool use_heartbeats = 23; - message TimeController { - option (.android.msg_privacy).dest = DEST_AUTOMATIC; - - // Whether or not TimeController should skip setting wakeup alarms for jobs that aren't - // ready now. - optional bool skip_not_ready_jobs = 1; - } - optional TimeController time_controller = 25; - message QuotaController { option (.android.msg_privacy).dest = DEST_AUTOMATIC; @@ -304,6 +295,15 @@ message ConstantsProto { } optional QuotaController quota_controller = 24; + message TimeController { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + + // Whether or not TimeController should skip setting wakeup alarms for jobs that aren't + // ready now. + reserved 1; // skip_not_ready_jobs + } + optional TimeController time_controller = 25; + // Max number of jobs, when screen is ON. optional MaxJobCountsPerMemoryTrimLevelProto max_job_counts_screen_on = 26; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 119d5ea33dc3..40acebc40abd 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -536,6 +536,7 @@ <protected-broadcast android:name="android.app.action.INTERRUPTION_FILTER_CHANGED_INTERNAL" /> <protected-broadcast android:name="android.app.action.NOTIFICATION_POLICY_CHANGED" /> <protected-broadcast android:name="android.app.action.NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED" /> + <protected-broadcast android:name="android.app.action.AUTOMATIC_ZEN_RULE_STATUS_CHANGED" /> <protected-broadcast android:name="android.os.action.ACTION_EFFECTS_SUPPRESSOR_CHANGED" /> <protected-broadcast android:name="android.app.action.NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED" /> <protected-broadcast android:name="android.app.action.NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED" /> @@ -4980,6 +4981,7 @@ <service android:name="com.android.server.autofill.AutofillCompatAccessibilityService" android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE" + android:visibleToInstantApps="true" android:exported="true"> <meta-data android:name="android.accessibilityservice" diff --git a/core/res/res/layout/notification_template_material_media.xml b/core/res/res/layout/notification_template_material_media.xml index 13fef67c5e2f..575295b1be45 100644 --- a/core/res/res/layout/notification_template_material_media.xml +++ b/core/res/res/layout/notification_template_material_media.xml @@ -15,7 +15,7 @@ ~ limitations under the License --> -<FrameLayout +<com.android.internal.widget.MediaNotificationView android:id="@+id/status_bar_latest_event_content" xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" @@ -24,8 +24,8 @@ android:tag="media" > <ImageView android:id="@+id/right_icon" - android:layout_width="wrap_content" - android:layout_height="match_parent" + android:layout_width="0dp" + android:layout_height="0dp" android:adjustViewBounds="true" android:layout_gravity="top|end" android:scaleType="centerCrop" @@ -91,4 +91,4 @@ android:layout_alignParentBottom="true" /> </LinearLayout> -</FrameLayout> +</com.android.internal.widget.MediaNotificationView> diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml index 7cfcaf4c8809..bbf1736bb913 100644 --- a/core/res/res/values-ca/strings.xml +++ b/core/res/res/values-ca/strings.xml @@ -595,7 +595,7 @@ </string-array> <string name="face_icon_content_description" msgid="4024817159806482191">"Icona facial"</string> <string name="permlab_readSyncSettings" msgid="6201810008230503052">"llegir la configuració de sincronització"</string> - <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Permet que l\'aplicació llegeixi la configuració de sincronització d\'un compte. Per exemple, això pot determinar que l\'aplicació Persones estigui sincronitzada amb un compte."</string> + <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Permet que l\'aplicació llegeixi la configuració de sincronització d\'un compte. Per exemple, això pot determinar que l\'aplicació Contactes estigui sincronitzada amb un compte."</string> <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"activar o desactivar la sincronització"</string> <string name="permdesc_writeSyncSettings" msgid="8956262591306369868">"Permet que una aplicació modifiqui la configuració de sincronització d\'un compte. Per exemple, aquesta acció es pot fer servir per activar la sincronització de l\'aplicació Persones amb un compte."</string> <string name="permlab_readSyncStats" msgid="7396577451360202448">"llegir les estadístiques de sincronització"</string> diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml index 5316d13a9fcf..114d8019207d 100644 --- a/core/res/res/values-de/strings.xml +++ b/core/res/res/values-de/strings.xml @@ -429,7 +429,7 @@ <string name="permdesc_recordAudio" msgid="4245930455135321433">"Diese App kann jederzeit Audio über das Mikrofon aufnehmen."</string> <string name="permlab_sim_communication" msgid="2935852302216852065">"Befehle an die SIM senden"</string> <string name="permdesc_sim_communication" msgid="5725159654279639498">"Ermöglicht der App das Senden von Befehlen an die SIM-Karte. Dies ist äußerst risikoreich."</string> - <string name="permlab_activityRecognition" msgid="3634590230567608356">"körperlichen Aktivitäten erkennen"</string> + <string name="permlab_activityRecognition" msgid="3634590230567608356">"Körperliche Aktivitäten erkennen"</string> <string name="permdesc_activityRecognition" msgid="3143453925156552894">"Diese App kann deine körperliche Aktivität erkennen."</string> <string name="permlab_camera" msgid="3616391919559751192">"Bilder und Videos aufnehmen"</string> <string name="permdesc_camera" msgid="5392231870049240670">"Diese App kann mit der Kamera jederzeit Bilder und Videos aufnehmen."</string> diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml index 18bba90202ca..b3eed4a59e9f 100644 --- a/core/res/res/values-gl/strings.xml +++ b/core/res/res/values-gl/strings.xml @@ -1985,7 +1985,7 @@ <string name="harmful_app_warning_uninstall" msgid="4837672735619532931">"DESINSTALAR"</string> <string name="harmful_app_warning_open_anyway" msgid="596432803680914321">"ABRIR IGUALMENTE"</string> <string name="harmful_app_warning_title" msgid="8982527462829423432">"Detectouse unha aplicación daniña"</string> - <string name="slices_permission_request" msgid="8484943441501672932">"A aplicación <xliff:g id="APP_0">%1$s</xliff:g> quere mostrar partes de <xliff:g id="APP_2">%2$s</xliff:g>"</string> + <string name="slices_permission_request" msgid="8484943441501672932">"<xliff:g id="APP_0">%1$s</xliff:g> quere mostrar fragmentos de aplicación de <xliff:g id="APP_2">%2$s</xliff:g>"</string> <string name="screenshot_edit" msgid="7867478911006447565">"Editar"</string> <string name="volume_dialog_ringer_guidance_vibrate" msgid="8902050240801159042">"As chamadas e as notificacións vibrarán"</string> <string name="volume_dialog_ringer_guidance_silent" msgid="2128975224280276122">"As chamadas e as notificacións estarán silenciadas"</string> diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml index cb4886a6a408..7165df91b170 100644 --- a/core/res/res/values-hy/strings.xml +++ b/core/res/res/values-hy/strings.xml @@ -1614,7 +1614,7 @@ <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"Ներկառուցված էկրան"</string> <string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"HDMI էկրան"</string> <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"Վերածածկ #<xliff:g id="ID">%1$d</xliff:g>"</string> - <string name="display_manager_overlay_display_title" msgid="652124517672257172">"<xliff:g id="NAME">%1$s</xliff:g>. <xliff:g id="WIDTH">%2$d</xliff:g>x<xliff:g id="HEIGHT">%3$d</xliff:g>, <xliff:g id="DPI">%4$d</xliff:g> կմվ"</string> + <string name="display_manager_overlay_display_title" msgid="652124517672257172">"<xliff:g id="NAME">%1$s</xliff:g>. <xliff:g id="WIDTH">%2$d</xliff:g>x<xliff:g id="HEIGHT">%3$d</xliff:g>, <xliff:g id="DPI">%4$d</xliff:g> կմչ"</string> <string name="display_manager_overlay_display_secure_suffix" msgid="6022119702628572080">", անվտանգ"</string> <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Մոռացել եմ սխեման"</string> <string name="kg_wrong_pattern" msgid="1850806070801358830">"Սխալ սխեմա"</string> diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml index a90acface07c..054a45626778 100644 --- a/core/res/res/values-pl/strings.xml +++ b/core/res/res/values-pl/strings.xml @@ -1712,7 +1712,7 @@ <string name="accessibility_shortcut_toogle_warning" msgid="7256507885737444807">"Gdy skrót jest włączony, jednoczesne naciśnięcie przez trzy sekundy obu klawiszy sterowania głośnością uruchomi funkcję ułatwień dostępu.\n\nBieżąca funkcja ułatwień dostępu:\n<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\n\nFunkcję możesz zmienić, wybierając Ustawienia > Ułatwienia dostępu."</string> <string name="disable_accessibility_shortcut" msgid="627625354248453445">"Wyłącz skrót"</string> <string name="leave_accessibility_shortcut_on" msgid="7653111894438512680">"Użyj skrótu"</string> - <string name="color_inversion_feature_name" msgid="4231186527799958644">"Inwersja kolorów"</string> + <string name="color_inversion_feature_name" msgid="4231186527799958644">"Odwrócenie kolorów"</string> <string name="color_correction_feature_name" msgid="6779391426096954933">"Korekcja kolorów"</string> <string name="accessibility_shortcut_enabling_service" msgid="7771852911861522636">"Skrót ułatwień dostępu wyłączył usługę <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> <string name="accessibility_shortcut_disabling_service" msgid="2747243438223109821">"Skrót ułatwień dostępu wyłączył usługę <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string> diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml index f2eb9d4ecb51..78c429010f0f 100644 --- a/core/res/res/values-vi/strings.xml +++ b/core/res/res/values-vi/strings.xml @@ -521,7 +521,7 @@ <string name="permdesc_imagesWrite" msgid="7073662756617474375">"Cho phép ứng dụng này sửa đổi bộ sưu tập ảnh của bạn."</string> <string name="permlab_mediaLocation" msgid="8675148183726247864">"đọc vị trí từ bộ sưu tập phương tiện"</string> <string name="permdesc_mediaLocation" msgid="2237023389178865130">"Cho phép ứng dụng này đọc vị trí từ bộ sưu tập phương tiện của bạn."</string> - <string name="biometric_dialog_default_title" msgid="881952973720613213">"Hãy xác minh đó là bạn"</string> + <string name="biometric_dialog_default_title" msgid="881952973720613213">"Xác minh danh tính của bạn"</string> <string name="biometric_error_hw_unavailable" msgid="645781226537551036">"Không có phần cứng sinh trắc học"</string> <string name="biometric_error_user_canceled" msgid="2260175018114348727">"Đã hủy xác thực"</string> <string name="biometric_not_recognized" msgid="5770511773560736082">"Không nhận dạng được"</string> diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml index 9ac4582fca26..884772e7b628 100644 --- a/core/res/res/values-zh-rHK/strings.xml +++ b/core/res/res/values-zh-rHK/strings.xml @@ -551,11 +551,11 @@ <string-array name="fingerprint_error_vendor"> </string-array> <string name="fingerprint_icon_content_description" msgid="2340202869968465936">"指紋圖示"</string> - <string name="permlab_manageFace" msgid="7262837876352591553">"管理臉容解鎖硬件"</string> + <string name="permlab_manageFace" msgid="7262837876352591553">"管理臉孔解鎖硬件"</string> <string name="permdesc_manageFace" msgid="8919637120670185330">"允許應用程式調用方法,以加入和刪除可用的臉孔範本。"</string> - <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"使用臉容解鎖硬件"</string> - <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"允許應用程式使用臉容解鎖硬件來驗證"</string> - <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"臉容解鎖"</string> + <string name="permlab_useFaceAuthentication" msgid="2565716575739037572">"使用臉孔解鎖硬件"</string> + <string name="permdesc_useFaceAuthentication" msgid="4712947955047607722">"允許應用程式使用臉孔解鎖硬件來驗證"</string> + <string name="face_recalibrate_notification_name" msgid="1913676850645544352">"臉孔解鎖"</string> <string name="face_recalibrate_notification_title" msgid="4087620069451499365">"重新註冊臉孔"</string> <string name="face_recalibrate_notification_content" msgid="5530308842361499835">"如要提高識別能力,請重新註冊您的臉孔"</string> <string name="face_acquired_insufficient" msgid="2767330364802375742">"無法擷取準確的臉容資料。請再試一次。"</string> @@ -581,15 +581,15 @@ <string-array name="face_acquired_vendor"> </string-array> <string name="face_error_hw_not_available" msgid="396883585636963908">"無法驗證臉孔,硬件無法使用。"</string> - <string name="face_error_timeout" msgid="981512090365729465">"請再次嘗試「臉容解鎖」。"</string> + <string name="face_error_timeout" msgid="981512090365729465">"請再次嘗試「臉孔解鎖」。"</string> <string name="face_error_no_space" msgid="2712120617457553825">"無法儲存新的臉容資料,請先刪除舊資料。"</string> <string name="face_error_canceled" msgid="283945501061931023">"臉孔操作已取消。"</string> - <string name="face_error_user_canceled" msgid="5317030072349668946">"使用者已取消「臉容解鎖」。"</string> + <string name="face_error_user_canceled" msgid="5317030072349668946">"使用者已取消「臉孔解鎖」。"</string> <string name="face_error_lockout" msgid="3407426963155388504">"嘗試次數過多,請稍後再試。"</string> - <string name="face_error_lockout_permanent" msgid="4723594314443097159">"嘗試次數過多,「臉容解鎖」已停用。"</string> + <string name="face_error_lockout_permanent" msgid="4723594314443097159">"嘗試次數過多,「臉孔解鎖」已停用。"</string> <string name="face_error_unable_to_process" msgid="4940944939691171539">"無法驗證臉孔。請再試一次。"</string> - <string name="face_error_not_enrolled" msgid="4016937174832839540">"您尚未設定「臉容解鎖」。"</string> - <string name="face_error_hw_not_present" msgid="8302690289757559738">"此裝置不支援「臉容解鎖」。"</string> + <string name="face_error_not_enrolled" msgid="4016937174832839540">"您尚未設定「臉孔解鎖」。"</string> + <string name="face_error_hw_not_present" msgid="8302690289757559738">"此裝置不支援「臉孔解鎖」。"</string> <string name="face_name_template" msgid="7004562145809595384">"臉孔 <xliff:g id="FACEID">%d</xliff:g>"</string> <string-array name="face_error_vendor"> </string-array> @@ -818,7 +818,7 @@ <string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"再試一次"</string> <string name="lockscreen_password_wrong" msgid="5737815393253165301">"再試一次"</string> <string name="lockscreen_storage_locked" msgid="9167551160010625200">"解鎖即可使用所有功能和資料"</string> - <string name="faceunlock_multiple_failures" msgid="754137583022792429">"已超過臉容解鎖嘗試次數上限"</string> + <string name="faceunlock_multiple_failures" msgid="754137583022792429">"已超過臉孔解鎖嘗試次數上限"</string> <string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"找不到 SIM 卡"</string> <string name="lockscreen_missing_sim_message" product="tablet" msgid="151659196095791474">"平板電腦中沒有 SIM 卡。"</string> <string name="lockscreen_missing_sim_message" product="tv" msgid="1943633865476989599">"電視沒有 SIM 卡。"</string> @@ -888,7 +888,7 @@ <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"展開解鎖區域。"</string> <string name="keyguard_accessibility_slide_unlock" msgid="2959928478764697254">"滑動解鎖。"</string> <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"圖案解鎖。"</string> - <string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"臉容解鎖。"</string> + <string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"臉孔解鎖。"</string> <string name="keyguard_accessibility_pin_unlock" msgid="2469687111784035046">"PIN 解鎖。"</string> <string name="keyguard_accessibility_sim_pin_unlock" msgid="9149698847116962307">"SIM 卡 PIN 碼解鎖。"</string> <string name="keyguard_accessibility_sim_puk_unlock" msgid="9106899279724723341">"SIM 卡 PUK 解鎖。"</string> diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 77fca8fe4d8e..be6cdcf3f098 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -1315,7 +1315,11 @@ it will yield the baseIntent to any activity that it launches in the same task. This continues until an activity is encountered which has this attribute set to false. False is the default. This attribute set to true also permits activity's use of the - TaskDescription to change labels, colors and icons in the recent task list. --> + TaskDescription to change labels, colors and icons in the recent task list. + + <p>NOTE: Setting this flag to <code>true</code> will not change the affinity of the task, + which is used for intent resolution during activity launch. The task's root activity will + always define its affinity. --> <attr name="relinquishTaskIdentity" format="boolean" /> <!-- Indicate that it is okay for this activity be resumed while the previous diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index bc7e3b7e9058..3b1275354d1f 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1115,6 +1115,22 @@ regularly selected color mode will be used if this value is negative. --> <integer name="config_accessibilityColorMode">-1</integer> + <!-- The following two arrays specify which color space to use for display composition when a + certain color mode is active. + Composition color spaces are defined in android.view.Display.COLOR_MODE_xxx, and color + modes are defined in ColorDisplayManager.COLOR_MODE_xxx and + ColorDisplayManager.VENDOR_COLOR_MODE_xxx. + The color space COLOR_MODE_DEFAULT (0) lets the system select the most appropriate + composition color space for currently displayed content. Other values (e.g., + COLOR_MODE_SRGB) override system selection; these other color spaces must be supported by + the device for for display composition. + If a color mode does not have a corresponding color space specified in this array, the + currently set composition color space will not be modified.--> + <integer-array name="config_displayCompositionColorModes"> + </integer-array> + <integer-array name="config_displayCompositionColorSpaces"> + </integer-array> + <!-- Indicate whether to allow the device to suspend when the screen is off due to the proximity sensor. This resource should only be set to true if the sensor HAL correctly handles the proximity sensor as a wake-up source. @@ -4133,8 +4149,27 @@ for higher refresh rates to be automatically used out of the box --> <integer name="config_defaultPeakRefreshRate">60</integer> - <!-- The default brightness threshold that allows to switch to higher refresh rate --> - <integer name="config_brightnessThresholdOfPeakRefreshRate">-1</integer> + <!-- The display uses different gamma curves for different refresh rates. It's hard for panel + vendor to tune the curves to have exact same brightness for different refresh rate. So + flicker could be observed at switch time. The issue is worse at the gamma lower end. + In addition, human eyes are more sensitive to the flicker at darker environment. + To prevent flicker, we only support higher refresh rates if the display brightness is above + a threshold. And the darker environment could have higher threshold. + For example, no higher refresh rate if + display brightness <= disp0 && ambient brightness <= amb0 + || display brightness <= disp1 && ambient brightness <= amb1 --> + <integer-array translatable="false" name="config_brightnessThresholdsOfPeakRefreshRate"> + <!-- + <item>disp0</item> + <item>disp1</item> + --> + </integer-array> + <integer-array translatable="false" name="config_ambientThresholdsOfPeakRefreshRate"> + <!-- + <item>amb0</item> + <item>amb1</item> + --> + </integer-array> <!-- The type of the light sensor to be used by the display framework for things like auto-brightness. If unset, then it just gets the default sensor of type TYPE_LIGHT. --> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 039bc4d925a1..665387943bbd 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -279,7 +279,7 @@ <dimen name="notification_expand_button_padding_top">1dp</dimen> <!-- Height of a small notification in the status bar --> - <dimen name="notification_min_height">92dp</dimen> + <dimen name="notification_min_height">106dp</dimen> <!-- The width of the big icons in notifications. --> <dimen name="notification_large_icon_width">64dp</dimen> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index b3b1b9fc01f2..d91bbd006318 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3196,6 +3196,8 @@ <java-symbol type="array" name="config_nightDisplayColorTemperatureCoefficientsNative" /> <java-symbol type="array" name="config_availableColorModes" /> <java-symbol type="integer" name="config_accessibilityColorMode" /> + <java-symbol type="array" name="config_displayCompositionColorModes" /> + <java-symbol type="array" name="config_displayCompositionColorSpaces" /> <java-symbol type="bool" name="config_displayWhiteBalanceAvailable" /> <java-symbol type="bool" name="config_displayWhiteBalanceEnabledDefault" /> <java-symbol type="integer" name="config_displayWhiteBalanceColorTemperatureMin" /> @@ -3793,12 +3795,14 @@ <!-- For high refresh rate displays --> <java-symbol type="integer" name="config_defaultPeakRefreshRate" /> - <java-symbol type="integer" name="config_brightnessThresholdOfPeakRefreshRate" /> + <java-symbol type="array" name="config_brightnessThresholdsOfPeakRefreshRate" /> + <java-symbol type="array" name="config_ambientThresholdsOfPeakRefreshRate" /> <!-- For Auto-Brightness --> <java-symbol type="string" name="config_displayLightSensorType" /> <java-symbol type="drawable" name="iconfactory_adaptive_icon_drawable_wrapper"/> + <java-symbol type="dimen" name="notification_min_height" /> <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"/> diff --git a/core/tests/coretests/res/values/styles.xml b/core/tests/coretests/res/values/styles.xml index dbc46265a96f..0bf4b9278446 100644 --- a/core/tests/coretests/res/values/styles.xml +++ b/core/tests/coretests/res/values/styles.xml @@ -39,4 +39,7 @@ <item name="android:colorBackground">@null</item> <item name="android:windowBackgroundFallback">#0000FF</item> </style> + <style name="ViewDefaultBackground"> + <item name="android:background">#00000000</item> + </style> </resources> diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java index d2b18cb0bcb8..51da0c871c4d 100644 --- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java @@ -416,7 +416,8 @@ public class TransactionParcelTests { IUiAutomationConnection iUiAutomationConnection, int i, boolean b, boolean b1, boolean b2, boolean b3, Configuration configuration, CompatibilityInfo compatibilityInfo, Map map, Bundle bundle1, String s1, - AutofillOptions ao, ContentCaptureOptions co) throws RemoteException { + AutofillOptions ao, ContentCaptureOptions co, long[] disableCompatChanges) + throws RemoteException { } @Override diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteTokenizerTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteTokenizerTest.java new file mode 100644 index 000000000000..a9d148289262 --- /dev/null +++ b/core/tests/coretests/src/android/database/sqlite/SQLiteTokenizerTest.java @@ -0,0 +1,173 @@ +/* + * 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.database.sqlite; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +@RunWith(AndroidJUnit4.class) +public class SQLiteTokenizerTest { + private List<String> getTokens(String sql) { + return SQLiteTokenizer.tokenize(sql, SQLiteTokenizer.OPTION_NONE); + } + + private void checkTokens(String sql, String spaceSeparatedExpectedTokens) { + final List<String> expected = spaceSeparatedExpectedTokens == null + ? new ArrayList<>() + : Arrays.asList(spaceSeparatedExpectedTokens.split(" +")); + + assertEquals(expected, getTokens(sql)); + } + + private void assertInvalidSql(String sql, String message) { + try { + getTokens(sql); + fail("Didn't throw InvalidSqlException"); + } catch (IllegalArgumentException e) { + assertTrue("Expected " + e.getMessage() + " to contain " + message, + e.getMessage().contains(message)); + } + } + + @Test + public void testWhitespaces() { + checkTokens(" select \t\r\n a\n\n ", "select a"); + checkTokens("a b", "a b"); + } + + @Test + public void testComment() { + checkTokens("--\n", null); + checkTokens("a--\n", "a"); + checkTokens("a--abcdef\n", "a"); + checkTokens("a--abcdef\nx", "a x"); + checkTokens("a--\nx", "a x"); + assertInvalidSql("a--abcdef", "Unterminated comment"); + assertInvalidSql("a--abcdef\ndef--", "Unterminated comment"); + + checkTokens("/**/", null); + assertInvalidSql("/*", "Unterminated comment"); + assertInvalidSql("/*/", "Unterminated comment"); + assertInvalidSql("/*\n* /*a", "Unterminated comment"); + checkTokens("a/**/", "a"); + checkTokens("/**/b", "b"); + checkTokens("a/**/b", "a b"); + checkTokens("a/* -- \n* /* **/b", "a b"); + } + + @Test + public void testStrings() { + assertInvalidSql("'", "Unterminated quote"); + assertInvalidSql("a'", "Unterminated quote"); + assertInvalidSql("a'''", "Unterminated quote"); + assertInvalidSql("a''' ", "Unterminated quote"); + checkTokens("''", null); + checkTokens("''''", null); + checkTokens("a''''b", "a b"); + checkTokens("a' '' 'b", "a b"); + checkTokens("'abc'", null); + checkTokens("'abc\ndef'", null); + checkTokens("a'abc\ndef'", "a"); + checkTokens("'abc\ndef'b", "b"); + checkTokens("a'abc\ndef'b", "a b"); + checkTokens("a'''abc\nd''ef'''b", "a b"); + } + + @Test + public void testDoubleQuotes() { + assertInvalidSql("\"", "Unterminated quote"); + assertInvalidSql("a\"", "Unterminated quote"); + assertInvalidSql("a\"\"\"", "Unterminated quote"); + assertInvalidSql("a\"\"\" ", "Unterminated quote"); + checkTokens("\"\"", ""); + checkTokens("\"\"\"\"", "\""); + checkTokens("a\"\"\"\"b", "a \" b"); + checkTokens("a\"\t\"\"\t\"b", "a \t\"\t b"); + checkTokens("\"abc\"", "abc"); + checkTokens("\"abc\ndef\"", "abc\ndef"); + checkTokens("a\"abc\ndef\"", "a abc\ndef"); + checkTokens("\"abc\ndef\"b", "abc\ndef b"); + checkTokens("a\"abc\ndef\"b", "a abc\ndef b"); + checkTokens("a\"\"\"abc\nd\"\"ef\"\"\"b", "a \"abc\nd\"ef\" b"); + } + + @Test + public void testBackQuotes() { + assertInvalidSql("`", "Unterminated quote"); + assertInvalidSql("a`", "Unterminated quote"); + assertInvalidSql("a```", "Unterminated quote"); + assertInvalidSql("a``` ", "Unterminated quote"); + checkTokens("``", ""); + checkTokens("````", "`"); + checkTokens("a````b", "a ` b"); + checkTokens("a`\t``\t`b", "a \t`\t b"); + checkTokens("`abc`", "abc"); + checkTokens("`abc\ndef`", "abc\ndef"); + checkTokens("a`abc\ndef`", "a abc\ndef"); + checkTokens("`abc\ndef`b", "abc\ndef b"); + checkTokens("a`abc\ndef`b", "a abc\ndef b"); + checkTokens("a```abc\nd``ef```b", "a `abc\nd`ef` b"); + } + + @Test + public void testBrackets() { + assertInvalidSql("[", "Unterminated quote"); + assertInvalidSql("a[", "Unterminated quote"); + assertInvalidSql("a[ ", "Unterminated quote"); + assertInvalidSql("a[[ ", "Unterminated quote"); + checkTokens("[]", ""); + checkTokens("[[]", "["); + checkTokens("a[[]b", "a [ b"); + checkTokens("a[\t[\t]b", "a \t[\t b"); + checkTokens("[abc]", "abc"); + checkTokens("[abc\ndef]", "abc\ndef"); + checkTokens("a[abc\ndef]", "a abc\ndef"); + checkTokens("[abc\ndef]b", "abc\ndef b"); + checkTokens("a[abc\ndef]b", "a abc\ndef b"); + checkTokens("a[[abc\nd[ef[]b", "a [abc\nd[ef[ b"); + } + + @Test + public void testSemicolons() { + assertInvalidSql(";", "Semicolon is not allowed"); + assertInvalidSql(" ;", "Semicolon is not allowed"); + assertInvalidSql("; ", "Semicolon is not allowed"); + assertInvalidSql("-;-", "Semicolon is not allowed"); + checkTokens("--;\n", null); + checkTokens("/*;*/", null); + checkTokens("';'", null); + checkTokens("[;]", ";"); + checkTokens("`;`", ";"); + } + + @Test + public void testTokens() { + checkTokens("a,abc,a00b,_1,_123,abcdef", "a abc a00b _1 _123 abcdef"); + checkTokens("a--\nabc/**/a00b''_1'''ABC'''`_123`abc[d]\"e\"f", + "a abc a00b _1 _123 abc d e f"); + } +} diff --git a/core/tests/coretests/src/android/view/DisplayCutoutTest.java b/core/tests/coretests/src/android/view/DisplayCutoutTest.java index 182fe78dfa7a..d5a0dfadcbe5 100644 --- a/core/tests/coretests/src/android/view/DisplayCutoutTest.java +++ b/core/tests/coretests/src/android/view/DisplayCutoutTest.java @@ -104,8 +104,8 @@ public class DisplayCutoutTest { @Test public void testExtractBoundsFromList_top_and_bottom() { - Rect safeInsets = new Rect(0, 1, 0, 10); - Rect boundTop = new Rect(80, 0, 120, 10); + Rect safeInsets = new Rect(0, 10, 0, 10); + Rect boundTop = new Rect(0, 0, 120, 10); Rect boundBottom = new Rect(80, 190, 120, 200); assertThat(extractBoundsFromList(safeInsets, Arrays.asList(new Rect[]{boundTop, boundBottom})), diff --git a/core/tests/coretests/src/android/view/WindowInfoTest.java b/core/tests/coretests/src/android/view/WindowInfoTest.java index 037a0d9a1207..05e8bd8b6cab 100644 --- a/core/tests/coretests/src/android/view/WindowInfoTest.java +++ b/core/tests/coretests/src/android/view/WindowInfoTest.java @@ -91,7 +91,7 @@ public class WindowInfoTest { assertFalse(w.focused); assertFalse(w.inPictureInPicture); assertFalse(w.hasFlagWatchOutsideTouch); - assertTrue(w.boundsInScreen.isEmpty()); + assertTrue(w.regionInScreen.isEmpty()); } @SmallTest @@ -114,7 +114,7 @@ public class WindowInfoTest { equality &= w1.childTokens.equals(w2.childTokens); equality &= w1.parentToken == w2.parentToken; equality &= w1.activityToken == w2.activityToken; - equality &= w1.boundsInScreen.equals(w2.boundsInScreen); + equality &= w1.regionInScreen.equals(w2.regionInScreen); return equality; } @@ -132,6 +132,6 @@ public class WindowInfoTest { windowInfo.focused = true; windowInfo.inPictureInPicture = true; windowInfo.hasFlagWatchOutsideTouch = true; - windowInfo.boundsInScreen.set(0, 0, 1080, 1080); + windowInfo.regionInScreen.set(0, 0, 1080, 1080); } } diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java index 683d16b1dea1..adaae5cdba8d 100644 --- a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java +++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java @@ -21,6 +21,7 @@ import android.accessibilityservice.IAccessibilityServiceConnection; import android.content.pm.ParceledListSlice; import android.graphics.Region; import android.os.Bundle; +import android.os.IBinder; import java.util.List; @@ -134,4 +135,8 @@ public class AccessibilityServiceConnectionImpl extends IAccessibilityServiceCon public boolean isFingerprintGestureDetectionAvailable() { return false; } + + public IBinder getOverlayWindowToken(int displayId) { + return null; + } } 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 aadfcbc84f76..abee1da2ed7a 100644 --- a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java +++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java @@ -71,7 +71,7 @@ import com.android.internal.R; import com.android.internal.accessibility.AccessibilityShortcutController.FrameworkObjectProvider; import com.android.internal.util.test.FakeSettingsProvider; -import org.junit.After; +import org.junit.AfterClass; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -136,6 +136,7 @@ public class AccessibilityShortcutControllerTest { mContentResolver = new MockContentResolver(mContext); mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); + FakeSettingsProvider.clearSettingsProvider(); when(mContext.getContentResolver()).thenReturn(mContentResolver); when(mAccessibilityManagerService.getInstalledAccessibilityServiceList(anyInt())) @@ -193,8 +194,9 @@ public class AccessibilityShortcutControllerTest { when(mTextToSpeech.getVoice()).thenReturn(mVoice); } - @After - public void tearDown() { + @AfterClass + public static void cleanUpSettingsProvider() { + FakeSettingsProvider.clearSettingsProvider(); } @Test diff --git a/core/tests/coretests/src/com/android/internal/policy/DecorViewTest.java b/core/tests/coretests/src/com/android/internal/policy/DecorViewTest.java new file mode 100644 index 000000000000..62e4efe7a543 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/policy/DecorViewTest.java @@ -0,0 +1,88 @@ +/* + * 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.policy; + +import static com.google.common.truth.Truth.assertThat; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.drawable.Drawable; + +import androidx.test.platform.app.InstrumentationRegistry; + +import com.android.frameworks.coretests.R; + +import org.junit.Before; +import org.junit.Test; + +import java.util.Arrays; + +public class DecorViewTest { + + private Context mContext; + private DecorView mDecorView; + + @Before + public void setUp() { + mContext = InstrumentationRegistry.getInstrumentation().getContext(); + PhoneWindow phoneWindow = new PhoneWindow(mContext); + mDecorView = (DecorView) phoneWindow.getDecorView(); + } + + @Test + public void setBackgroundDrawableSameAsSetWindowBackground() { + Drawable bitmapDrawable = mContext.getResources() + .getDrawable(R.drawable.test16x12, mContext.getTheme()); + int w = 16; + int h = 12; + Bitmap expectedBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); + + mDecorView.setWindowBackground(bitmapDrawable); + Canvas testCanvas = new Canvas(expectedBitmap); + mDecorView.draw(testCanvas); + testCanvas.release(); + + Drawable expectedBackground = mDecorView.getBackground(); + + mDecorView.setBackgroundDrawable(bitmapDrawable); + Bitmap resultBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); + Canvas resCanvas = new Canvas(resultBitmap); + mDecorView.draw(resCanvas); + resCanvas.release(); + + // Check that the drawable is the same. + assertThat(mDecorView.getBackground()).isEqualTo(expectedBackground); + assertThat(mDecorView.getBackground()).isEqualTo(bitmapDrawable); + + // Check that canvas is the same. + int[] expPixels = new int[w * h]; + int[] resPixels = new int[w * h]; + resultBitmap.getPixels(resPixels, 0, w, 0, 0, w, h); + expectedBitmap.getPixels(expPixels, 0, w, 0, 0, w, h); + assertThat(Arrays.toString(expPixels)).isEqualTo(Arrays.toString(resPixels)); + } + + @Test + public void setBackgroundWithNoWindow() { + PhoneWindow phoneWindow = new PhoneWindow(mContext); + // Set a theme that defines a non-null value for android:background + mContext.setTheme(R.style.ViewDefaultBackground); + DecorView decorView = (DecorView) phoneWindow.getDecorView(); + assertThat(decorView.getBackground()).isNotNull(); + } +} diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp index 4755cb866310..f7c83371f79c 100644 --- a/libs/androidfw/AssetManager.cpp +++ b/libs/androidfw/AssetManager.cpp @@ -74,7 +74,7 @@ const char* AssetManager::RESOURCES_FILENAME = "resources.arsc"; const char* AssetManager::IDMAP_BIN = "/system/bin/idmap"; const char* AssetManager::VENDOR_OVERLAY_DIR = "/vendor/overlay"; const char* AssetManager::PRODUCT_OVERLAY_DIR = "/product/overlay"; -const char* AssetManager::PRODUCT_SERVICES_OVERLAY_DIR = "/product_services/overlay"; +const char* AssetManager::SYSTEM_EXT_OVERLAY_DIR = "/system_ext/overlay"; const char* AssetManager::ODM_OVERLAY_DIR = "/odm/overlay"; const char* AssetManager::OEM_OVERLAY_DIR = "/oem/overlay"; const char* AssetManager::OVERLAY_THEME_DIR_PROPERTY = "ro.boot.vendor.overlay.theme"; @@ -575,7 +575,7 @@ bool AssetManager::appendPathToResTable(asset_path& ap, bool appAsLib) const { mZipSet.setZipResourceTableAsset(ap.path, ass); } } - + if (nextEntryIdx == 0 && ass != NULL) { // If this is the first resource table in the asset // manager, then we are going to cache it so that we diff --git a/libs/androidfw/include/androidfw/AssetManager.h b/libs/androidfw/include/androidfw/AssetManager.h index 66fba26b7289..ce0985b38986 100644 --- a/libs/androidfw/include/androidfw/AssetManager.h +++ b/libs/androidfw/include/androidfw/AssetManager.h @@ -61,7 +61,7 @@ public: static const char* IDMAP_BIN; static const char* VENDOR_OVERLAY_DIR; static const char* PRODUCT_OVERLAY_DIR; - static const char* PRODUCT_SERVICES_OVERLAY_DIR; + static const char* SYSTEM_EXT_OVERLAY_DIR; static const char* ODM_OVERLAY_DIR; static const char* OEM_OVERLAY_DIR; /* diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h index 7269bcad3d7a..16ec877002f7 100644 --- a/libs/hwui/RecordingCanvas.h +++ b/libs/hwui/RecordingCanvas.h @@ -29,7 +29,6 @@ #include "SkPaint.h" #include "SkPath.h" #include "SkRect.h" -#include "SkTDArray.h" #include "SkTemplates.h" #include <vector> diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp index eed19420a78a..96b17e1d7975 100644 --- a/libs/hwui/pipeline/skia/LayerDrawable.cpp +++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp @@ -33,21 +33,47 @@ void LayerDrawable::onDraw(SkCanvas* canvas) { } } -// This is a less-strict matrix.isTranslate() that will still report being translate-only -// on imperceptibly small scaleX & scaleY values. -static bool isBasicallyTranslate(const SkMatrix& matrix) { - if (!matrix.isScaleTranslate()) return false; - return MathUtils::isOne(matrix.getScaleX()) && MathUtils::isOne(matrix.getScaleY()); -} - -static bool shouldFilter(const SkMatrix& matrix) { - if (!matrix.isScaleTranslate()) return true; - - // We only care about meaningful scale here - bool noScale = MathUtils::isOne(matrix.getScaleX()) && MathUtils::isOne(matrix.getScaleY()); - bool pixelAligned = - SkScalarIsInt(matrix.getTranslateX()) && SkScalarIsInt(matrix.getTranslateY()); - return !(noScale && pixelAligned); +// Disable filtering when there is no scaling in screen coordinates and the corners have the same +// fraction (for translate) or zero fraction (for any other rect-to-rect transform). +static bool shouldFilterRect(const SkMatrix& matrix, const SkRect& srcRect, const SkRect& dstRect) { + if (!matrix.rectStaysRect()) return true; + SkRect dstDevRect = matrix.mapRect(dstRect); + float dstW, dstH; + bool requiresIntegerTranslate = false; + if (MathUtils::isZero(matrix.getScaleX()) && MathUtils::isZero(matrix.getScaleY())) { + // Has a 90 or 270 degree rotation, although total matrix may also have scale factors + // in m10 and m01. Those scalings are automatically handled by mapRect so comparing + // dimensions is sufficient, but swap width and height comparison. + dstW = dstDevRect.height(); + dstH = dstDevRect.width(); + requiresIntegerTranslate = true; + } else { + // Handle H/V flips or 180 rotation matrices. Axes may have been mirrored, but + // dimensions are still safe to compare directly. + dstW = dstDevRect.width(); + dstH = dstDevRect.height(); + requiresIntegerTranslate = + matrix.getScaleX() < -NON_ZERO_EPSILON || matrix.getScaleY() < -NON_ZERO_EPSILON; + } + if (!(MathUtils::areEqual(dstW, srcRect.width()) && + MathUtils::areEqual(dstH, srcRect.height()))) { + return true; + } + if (requiresIntegerTranslate) { + // Device rect and source rect should be integer aligned to ensure there's no difference + // in how nearest-neighbor sampling is resolved. + return !(MathUtils::isZero(SkScalarFraction(srcRect.x())) && + MathUtils::isZero(SkScalarFraction(srcRect.y())) && + MathUtils::isZero(SkScalarFraction(dstDevRect.x())) && + MathUtils::isZero(SkScalarFraction(dstDevRect.y()))); + } else { + // As long as src and device rects are translated by the same fractional amount, + // filtering won't be needed + return !(MathUtils::areEqual(SkScalarFraction(srcRect.x()), + SkScalarFraction(dstDevRect.x())) && + MathUtils::areEqual(SkScalarFraction(srcRect.y()), + SkScalarFraction(dstDevRect.y()))); + } } bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer, @@ -114,24 +140,21 @@ bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer skiaDestRect = SkRect::MakeIWH(layerWidth, layerHeight); } matrixInv.mapRect(&skiaDestRect); - // If (matrix is identity or an integer translation) and (src/dst buffers size match), + // If (matrix is a rect-to-rect transform) + // and (src/dst buffers size match in screen coordinates) + // and (src/dst corners align fractionally), // then use nearest neighbor, otherwise use bilerp sampling. - // Integer translation is defined as when src rect and dst rect align fractionally. // Skia TextureOp has the above logic build-in, but not NonAAFillRectOp. TextureOp works // only for SrcOver blending and without color filter (readback uses Src blending). - bool isIntegerTranslate = - isBasicallyTranslate(totalMatrix) && - SkScalarFraction(skiaDestRect.fLeft + totalMatrix[SkMatrix::kMTransX]) == - SkScalarFraction(skiaSrcRect.fLeft) && - SkScalarFraction(skiaDestRect.fTop + totalMatrix[SkMatrix::kMTransY]) == - SkScalarFraction(skiaSrcRect.fTop); - if (layer->getForceFilter() || !isIntegerTranslate) { + if (layer->getForceFilter() || + shouldFilterRect(totalMatrix, skiaSrcRect, skiaDestRect)) { paint.setFilterQuality(kLow_SkFilterQuality); } canvas->drawImageRect(layerImage.get(), skiaSrcRect, skiaDestRect, &paint, SkCanvas::kFast_SrcRectConstraint); } else { - if (layer->getForceFilter() || shouldFilter(totalMatrix)) { + SkRect imageRect = SkRect::MakeIWH(layerImage->width(), layerImage->height()); + if (layer->getForceFilter() || shouldFilterRect(totalMatrix, imageRect, imageRect)) { paint.setFilterQuality(kLow_SkFilterQuality); } canvas->drawImage(layerImage.get(), 0, 0, &paint); diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 550c27d7a051..d97c5ed4c7f5 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -145,7 +145,8 @@ void CanvasContext::setSurface(sp<Surface>&& surface) { if (surface) { mNativeSurface = new ReliableSurface{std::move(surface)}; - mNativeSurface->setDequeueTimeout(500_ms); + // TODO: Fix error handling & re-shorten timeout + mNativeSurface->setDequeueTimeout(4000_ms); } else { mNativeSurface = nullptr; } diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index e50428b2ee62..40fbdffaf90a 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -16,6 +16,8 @@ #include "RenderProxy.h" +#include <gui/Surface.h> + #include "DeferredLayerUpdater.h" #include "DisplayList.h" #include "Properties.h" diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index a0f08cbd26f9..c3eb6ede9bf2 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -19,7 +19,6 @@ #include <SkBitmap.h> #include <cutils/compiler.h> -#include <gui/Surface.h> #include <utils/Functor.h> #include "../FrameMetricsObserver.h" @@ -30,6 +29,7 @@ namespace android { class GraphicBuffer; +class Surface; namespace uirenderer { diff --git a/libs/hwui/renderthread/VulkanSurface.h b/libs/hwui/renderthread/VulkanSurface.h index 5fa860a20916..5717bb3afe5b 100644 --- a/libs/hwui/renderthread/VulkanSurface.h +++ b/libs/hwui/renderthread/VulkanSurface.h @@ -17,6 +17,7 @@ #include <system/graphics.h> #include <system/window.h> +#include <ui/BufferQueueDefs.h> #include <vulkan/vulkan.h> #include <SkRefCnt.h> diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index f5330b444818..1cc246b51765 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -191,19 +191,6 @@ public class LocationManager { public static final String MODE_CHANGED_ACTION = "android.location.MODE_CHANGED"; /** - * Broadcast intent action when {@link android.provider.Settings.Secure#LOCATION_MODE} is - * about to be changed through Settings app or Quick Settings. - * For use with the {@link android.provider.Settings.Secure#LOCATION_MODE} API. - * If you're interacting with {@link #isProviderEnabled(String)}, use - * {@link #PROVIDERS_CHANGED_ACTION} instead. - * - * @deprecated Do not use. - * @hide - */ - @Deprecated - public static final String MODE_CHANGING_ACTION = "com.android.settings.location.MODE_CHANGING"; - - /** * Broadcast intent action indicating that a high power location requests * has either started or stopped being active. The current state of * active location requests should be read from AppOpsManager using @@ -216,17 +203,10 @@ public class LocationManager { /** * Broadcast intent action for Settings app to inject a footer at the bottom of location - * settings. - * - * <p>This broadcast is used for two things: - * <ol> - * <li>For receivers to inject a footer with provided text. This is for use only by apps - * that are included in the system image. </li> - * <li>For receivers to know their footer is injected under location settings.</li> - * </ol> + * settings. This is for use only by apps that are included in the system image. * - * <p>To inject a footer to location settings, you must declare a broadcast receiver of - * {@link LocationManager#SETTINGS_FOOTER_DISPLAYED_ACTION} in the manifest as so: + * <p>To inject a footer to location settings, you must declare a broadcast receiver for + * this action in the manifest: * <pre> * <receiver android:name="com.example.android.footer.MyFooterInjector"> * <intent-filter> @@ -238,10 +218,8 @@ public class LocationManager { * </receiver> * </pre> * - * <p>On entering location settings, Settings app will send a - * {@link #SETTINGS_FOOTER_DISPLAYED_ACTION} broadcast to receivers whose footer is successfully - * injected. On leaving location settings, the footer becomes not visible to users. Settings app - * will send a {@link #SETTINGS_FOOTER_REMOVED_ACTION} broadcast to those receivers. + * <p>This broadcast receiver will never actually be invoked. See also + * {#METADATA_SETTINGS_FOOTER_STRING}. * * @hide */ @@ -249,16 +227,6 @@ public class LocationManager { "com.android.settings.location.DISPLAYED_FOOTER"; /** - * Broadcast intent action when location settings footer is not visible to users. - * - * <p>See {@link #SETTINGS_FOOTER_DISPLAYED_ACTION} for more detail on how to use. - * - * @hide - */ - public static final String SETTINGS_FOOTER_REMOVED_ACTION = - "com.android.settings.location.REMOVED_FOOTER"; - - /** * Metadata name for {@link LocationManager#SETTINGS_FOOTER_DISPLAYED_ACTION} broadcast * receivers to specify a string resource id as location settings footer text. This is for use * only by apps that are included in the system image. diff --git a/media/Android.bp b/media/Android.bp index 4f9671f9936b..29064adf8a01 100644 --- a/media/Android.bp +++ b/media/Android.bp @@ -33,6 +33,8 @@ java_library { "framework_media_annotation", "android_system_stubs_current", ], + + plugins: ["java_api_finder"], } filegroup { diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java index ce9b07dd0c0e..0254c9721019 100644 --- a/media/java/android/media/AudioRecord.java +++ b/media/java/android/media/AudioRecord.java @@ -1788,6 +1788,9 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection, * @hide */ public int getPortId() { + if (mNativeRecorderInJavaObj == 0) { + return 0; + } return native_getPortId(); } diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index 353e58e98efa..7906fa34d844 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -246,7 +246,7 @@ import java.util.Vector; * via {@link #setOnBufferingUpdateListener(OnBufferingUpdateListener)}. * This callback allows applications to keep track of the buffering status * while streaming audio/video.</li> - * <li>Calling {@link #start()} has not effect + * <li>Calling {@link #start()} has no effect * on a MediaPlayer object that is already in the <em>Started</em> state.</li> * </ul> * </li> diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java index 63b22df12953..bf7da23323a1 100644 --- a/media/java/android/media/MediaRecorder.java +++ b/media/java/android/media/MediaRecorder.java @@ -720,7 +720,7 @@ public class MediaRecorder implements AudioRouting, /** * Sets the width and height of the video to be captured. Must be called - * after setVideoSource(). Call this after setOutFormat() but before + * after setVideoSource(). Call this after setOutputFormat() but before * prepare(). * * @param width the width of the video to be captured @@ -733,7 +733,7 @@ public class MediaRecorder implements AudioRouting, /** * Sets the frame rate of the video to be captured. Must be called - * after setVideoSource(). Call this after setOutFormat() but before + * after setVideoSource(). Call this after setOutputFormat() but before * prepare(). * * @param rate the number of frames per second of video to capture @@ -748,7 +748,7 @@ public class MediaRecorder implements AudioRouting, /** * Sets the maximum duration (in ms) of the recording session. - * Call this after setOutFormat() but before prepare(). + * Call this after setOutputFormat() but before prepare(). * After recording reaches the specified duration, a notification * will be sent to the {@link android.media.MediaRecorder.OnInfoListener} * with a "what" code of {@link #MEDIA_RECORDER_INFO_MAX_DURATION_REACHED} @@ -769,7 +769,7 @@ public class MediaRecorder implements AudioRouting, /** * Sets the maximum filesize (in bytes) of the recording session. - * Call this after setOutFormat() but before prepare(). + * Call this after setOutputFormat() but before prepare(). * After recording reaches the specified filesize, a notification * will be sent to the {@link android.media.MediaRecorder.OnInfoListener} * with a "what" code of {@link #MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED} @@ -1624,6 +1624,9 @@ public class MediaRecorder implements AudioRouting, * @hide */ public int getPortId() { + if (mNativeContext == 0) { + return 0; + } return native_getPortId(); } diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java index f13a64c1520b..5dcbb05184fe 100644 --- a/media/java/android/media/MediaRoute2Info.java +++ b/media/java/android/media/MediaRoute2Info.java @@ -23,6 +23,9 @@ import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; import java.util.Objects; /** @@ -53,6 +56,8 @@ public final class MediaRoute2Info implements Parcelable { final String mDescription; @Nullable final String mClientPackageName; + @NonNull + final List<String> mSupportedCategories; @Nullable final Bundle mExtras; @@ -62,6 +67,7 @@ public final class MediaRoute2Info implements Parcelable { mName = builder.mName; mDescription = builder.mDescription; mClientPackageName = builder.mClientPackageName; + mSupportedCategories = builder.mSupportedCategories; mExtras = builder.mExtras; } @@ -71,6 +77,7 @@ public final class MediaRoute2Info implements Parcelable { mName = in.readString(); mDescription = in.readString(); mClientPackageName = in.readString(); + mSupportedCategories = in.createStringArrayList(); mExtras = in.readBundle(); } @@ -103,13 +110,14 @@ public final class MediaRoute2Info implements Parcelable { && Objects.equals(mName, other.mName) && Objects.equals(mDescription, other.mDescription) && Objects.equals(mClientPackageName, other.mClientPackageName) + && Objects.equals(mSupportedCategories, other.mSupportedCategories) //TODO: This will be evaluated as false in most cases. Try not to. && Objects.equals(mExtras, other.mExtras); } @Override public int hashCode() { - return Objects.hash(mId, mName, mDescription); + return Objects.hash(mId, mName, mDescription, mSupportedCategories); } @NonNull @@ -146,11 +154,38 @@ public final class MediaRoute2Info implements Parcelable { return mClientPackageName; } + /** + * Gets the supported categories of the route. + */ + @NonNull + public List<String> getSupportedCategories() { + return mSupportedCategories; + } + @Nullable public Bundle getExtras() { return mExtras; } + //TODO: Move this if we re-define control category / selector things. + /** + * Returns true if the route supports at least one of the specified control categories + * + * @param controlCategories the list of control categories to consider + * @return true if the route supports at least one category + */ + public boolean supportsControlCategory(@NonNull Collection<String> controlCategories) { + Objects.requireNonNull(controlCategories, "control categories must not be null"); + for (String controlCategory : controlCategories) { + for (String supportedCategory : getSupportedCategories()) { + if (TextUtils.equals(controlCategory, supportedCategory)) { + return true; + } + } + } + return false; + } + @Override public int describeContents() { return 0; @@ -163,6 +198,7 @@ public final class MediaRoute2Info implements Parcelable { dest.writeString(mName); dest.writeString(mDescription); dest.writeString(mClientPackageName); + dest.writeStringList(mSupportedCategories); dest.writeBundle(mExtras); } @@ -187,6 +223,7 @@ public final class MediaRoute2Info implements Parcelable { String mName; String mDescription; String mClientPackageName; + List<String> mSupportedCategories; Bundle mExtras; public Builder(@NonNull String id, @NonNull String name) { @@ -198,6 +235,7 @@ public final class MediaRoute2Info implements Parcelable { } setId(id); setName(name); + mSupportedCategories = new ArrayList<>(); } public Builder(@NonNull MediaRoute2Info routeInfo) { @@ -212,6 +250,7 @@ public final class MediaRoute2Info implements Parcelable { setName(routeInfo.mName); mDescription = routeInfo.mDescription; setClientPackageName(routeInfo.mClientPackageName); + setSupportedCategories(routeInfo.mSupportedCategories); if (routeInfo.mExtras != null) { mExtras = new Bundle(routeInfo.mExtras); } @@ -273,6 +312,39 @@ public final class MediaRoute2Info implements Parcelable { } /** + * Sets the supported categories of the route. + */ + @NonNull + public Builder setSupportedCategories(@NonNull Collection<String> categories) { + mSupportedCategories = new ArrayList<>(); + return addSupportedCategories(categories); + } + + /** + * Adds supported categories for the route. + */ + @NonNull + public Builder addSupportedCategories(@NonNull Collection<String> categories) { + Objects.requireNonNull(categories, "categories must not be null"); + for (String category: categories) { + addSupportedCategory(category); + } + return this; + } + + /** + * Add a supported category for the route. + */ + @NonNull + public Builder addSupportedCategory(@NonNull String category) { + if (TextUtils.isEmpty(category)) { + throw new IllegalArgumentException("category must not be null or empty"); + } + mSupportedCategories.add(category); + return this; + } + + /** * Sets a bundle of extras for the route. */ @NonNull diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java index 5fc37a579d92..85105e069aaa 100644 --- a/media/java/android/media/MediaRouter2Manager.java +++ b/media/java/android/media/MediaRouter2Manager.java @@ -38,6 +38,8 @@ import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Executor; @@ -66,6 +68,8 @@ public class MediaRouter2Manager { List<MediaRoute2ProviderInfo> mProviders = Collections.emptyList(); @NonNull List<MediaRoute2Info> mRoutes = Collections.emptyList(); + @NonNull + ConcurrentMap<String, List<String>> mControlCategoryMap = new ConcurrentHashMap<>(); /** * Gets an instance of media router manager that controls media route of other applications. @@ -160,6 +164,8 @@ public class MediaRouter2Manager { return -1; } + //TODO: Use cache not to create array. For now, it's unclear when to purge the cache. + //Do this when we finalize how to set control categories. /** * Gets available routes for an application. * @@ -167,8 +173,17 @@ public class MediaRouter2Manager { */ @NonNull public List<MediaRoute2Info> getAvailableRoutes(@NonNull String packageName) { - //TODO: filter irrelevant routes. - return Collections.unmodifiableList(mRoutes); + List<String> controlCategories = mControlCategoryMap.get(packageName); + if (controlCategories == null) { + return Collections.emptyList(); + } + List<MediaRoute2Info> routes = new ArrayList<>(); + for (MediaRoute2Info route : mRoutes) { + if (route.supportsControlCategory(controlCategories)) { + routes.add(route); + } + } + return routes; } /** @@ -310,11 +325,8 @@ public class MediaRouter2Manager { } } - void notifyControlCategoriesChanged(String packageName, List<String> categories) { - for (CallbackRecord record : mCallbacks) { - record.mExecutor.execute( - () -> record.mCallback.onControlCategoriesChanged(packageName, categories)); - } + void updateControlCategories(String packageName, List<String> categories) { + mControlCategoryMap.put(packageName, categories); } /** @@ -346,15 +358,6 @@ public class MediaRouter2Manager { public void onRouteSelected(@NonNull String packageName, @Nullable MediaRoute2Info route) {} /** - * Called when the control categories of an application is changed. - * - * @param packageName the package name of the app that changed control categories - * @param categories the changed categories - */ - public void onControlCategoriesChanged(@NonNull String packageName, - @NonNull List<String> categories) {} - - /** * Called when the list of routes are changed. * A client may refresh available routes for each application. */ @@ -389,7 +392,7 @@ public class MediaRouter2Manager { @Override public void notifyControlCategoriesChanged(String packageName, List<String> categories) { - mHandler.sendMessage(obtainMessage(MediaRouter2Manager::notifyControlCategoriesChanged, + mHandler.sendMessage(obtainMessage(MediaRouter2Manager::updateControlCategories, MediaRouter2Manager.this, packageName, categories)); } diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp index 77878f83b072..0a02156ccf76 100644 --- a/media/jni/android_media_ImageReader.cpp +++ b/media/jni/android_media_ImageReader.cpp @@ -416,11 +416,13 @@ static void ImageReader_init(JNIEnv* env, jobject thiz, jobject weakThiz, jint w if (res != OK) { jniThrowExceptionFmt(env, "java/lang/IllegalStateException", "Failed to set buffer consumer default format 0x%x", nativeFormat); + return; } res = bufferConsumer->setDefaultBufferDataSpace(nativeDataspace); if (res != OK) { jniThrowExceptionFmt(env, "java/lang/IllegalStateException", "Failed to set buffer consumer default dataSpace 0x%x", nativeDataspace); + return; } } @@ -704,7 +706,7 @@ static void Image_getLockedImage(JNIEnv* env, jobject thiz, LockedImage *image) // and we don't set them here. } -static void Image_getLockedImageInfo(JNIEnv* env, LockedImage* buffer, int idx, +static bool Image_getLockedImageInfo(JNIEnv* env, LockedImage* buffer, int idx, int32_t writerFormat, uint8_t **base, uint32_t *size, int *pixelStride, int *rowStride) { ALOGV("%s", __FUNCTION__); @@ -713,7 +715,9 @@ static void Image_getLockedImageInfo(JNIEnv* env, LockedImage* buffer, int idx, if (res != OK) { jniThrowExceptionFmt(env, "java/lang/UnsupportedOperationException", "Pixel format: 0x%x is unsupported", buffer->flexFormat); + return false; } + return true; } static jobjectArray Image_createSurfacePlanes(JNIEnv* env, jobject thiz, @@ -756,8 +760,10 @@ static jobjectArray Image_createSurfacePlanes(JNIEnv* env, jobject thiz, } // Create all SurfacePlanes for (int i = 0; i < numPlanes; i++) { - Image_getLockedImageInfo(env, &lockedImg, i, halReaderFormat, - &pData, &dataSize, &pixelStride, &rowStride); + if (!Image_getLockedImageInfo(env, &lockedImg, i, halReaderFormat, + &pData, &dataSize, &pixelStride, &rowStride)) { + return NULL; + } byteBuffer = env->NewDirectByteBuffer(pData, dataSize); if ((byteBuffer == NULL) && (env->ExceptionCheck() == false)) { jniThrowException(env, "java/lang/IllegalStateException", diff --git a/media/jni/android_media_ImageWriter.cpp b/media/jni/android_media_ImageWriter.cpp index 728c55e5e6f7..6d8b966f35d5 100644 --- a/media/jni/android_media_ImageWriter.cpp +++ b/media/jni/android_media_ImageWriter.cpp @@ -777,6 +777,7 @@ static void Image_unlockIfLocked(JNIEnv* env, jobject thiz) { status_t res = buffer->unlock(); if (res != OK) { jniThrowRuntimeException(env, "unlock buffer failed"); + return; } ALOGV("Successfully unlocked the image"); } @@ -872,7 +873,7 @@ static void Image_getLockedImage(JNIEnv* env, jobject thiz, LockedImage *image) // and we don't set them here. } -static void Image_getLockedImageInfo(JNIEnv* env, LockedImage* buffer, int idx, +static bool Image_getLockedImageInfo(JNIEnv* env, LockedImage* buffer, int idx, int32_t writerFormat, uint8_t **base, uint32_t *size, int *pixelStride, int *rowStride) { ALOGV("%s", __FUNCTION__); @@ -880,8 +881,10 @@ static void Image_getLockedImageInfo(JNIEnv* env, LockedImage* buffer, int idx, pixelStride, rowStride); if (res != OK) { jniThrowExceptionFmt(env, "java/lang/UnsupportedOperationException", - "Pixel format: 0x%x is unsupported", buffer->flexFormat); + "Pixel format: 0x%x is unsupported", writerFormat); + return false; } + return true; } static jobjectArray Image_createSurfacePlanes(JNIEnv* env, jobject thiz, @@ -920,8 +923,10 @@ static jobjectArray Image_createSurfacePlanes(JNIEnv* env, jobject thiz, PublicFormat publicWriterFormat = static_cast<PublicFormat>(writerFormat); writerFormat = mapPublicFormatToHalFormat(publicWriterFormat); for (int i = 0; i < numPlanes; i++) { - Image_getLockedImageInfo(env, &lockedImg, i, writerFormat, - &pData, &dataSize, &pixelStride, &rowStride); + if (!Image_getLockedImageInfo(env, &lockedImg, i, writerFormat, + &pData, &dataSize, &pixelStride, &rowStride)) { + return NULL; + } byteBuffer = env->NewDirectByteBuffer(pData, dataSize); if ((byteBuffer == NULL) && (env->ExceptionCheck() == false)) { jniThrowException(env, "java/lang/IllegalStateException", 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 2cdc6a8101d7..1267aa88a939 100644 --- a/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java +++ b/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java @@ -32,19 +32,35 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService public static final String ROUTE_NAME1 = "Sample Route 1"; public static final String ROUTE_ID2 = "route_id2"; public static final String ROUTE_NAME2 = "Sample Route 2"; + + public static final String ROUTE_ID_SPECIAL_CATEGORY = "route_special_category"; + public static final String ROUTE_NAME_SPECIAL_CATEGORY = "Special Category Route"; + public static final String ACTION_REMOVE_ROUTE = "com.android.mediarouteprovider.action_remove_route"; + public static final String CATEGORY_SAMPLE = + "com.android.mediarouteprovider.CATEGORY_SAMPLE"; + public static final String CATEGORY_SPECIAL = + "com.android.mediarouteprovider.CATEGORY_SPECIAL"; + Map<String, MediaRoute2Info> mRoutes = new HashMap<>(); private void initializeRoutes() { MediaRoute2Info route1 = new MediaRoute2Info.Builder(ROUTE_ID1, ROUTE_NAME1) + .addSupportedCategory(CATEGORY_SAMPLE) .build(); MediaRoute2Info route2 = new MediaRoute2Info.Builder(ROUTE_ID2, ROUTE_NAME2) + .addSupportedCategory(CATEGORY_SAMPLE) .build(); - + MediaRoute2Info routeSpecial = + new MediaRoute2Info.Builder(ROUTE_ID_SPECIAL_CATEGORY, ROUTE_NAME_SPECIAL_CATEGORY) + .addSupportedCategory(CATEGORY_SAMPLE) + .addSupportedCategory(CATEGORY_SPECIAL) + .build(); mRoutes.put(route1.getId(), route1); mRoutes.put(route2.getId(), route2); + mRoutes.put(routeSpecial.getId(), routeSpecial); } @Override diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java index 03b43e215c25..4282a5bcea01 100644 --- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java +++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java @@ -58,9 +58,18 @@ public class MediaRouterManagerTest { public static final String ROUTE_NAME1 = "Sample Route 1"; public static final String ROUTE_ID2 = "route_id2"; public static final String ROUTE_NAME2 = "Sample Route 2"; + + public static final String ROUTE_ID_SPECIAL_CATEGORY = "route_special_category"; + public static final String ROUTE_NAME_SPECIAL_CATEGORY = "Special Category Route"; + public static final String ACTION_REMOVE_ROUTE = "com.android.mediarouteprovider.action_remove_route"; + public static final String CATEGORY_SAMPLE = + "com.android.mediarouteprovider.CATEGORY_SAMPLE"; + public static final String CATEGORY_SPECIAL = + "com.android.mediarouteprovider.CATEGORY_SPECIAL"; + private static final int TIMEOUT_MS = 5000; private Context mContext; @@ -69,12 +78,13 @@ public class MediaRouterManagerTest { private Executor mExecutor; private String mPackageName; - private static final List<String> TEST_CONTROL_CATEGORIES = new ArrayList(); - private static final String CONTROL_CATEGORY_1 = "android.media.mediarouter.MEDIA1"; - private static final String CONTROL_CATEGORY_2 = "android.media.mediarouter.MEDIA2"; + private static final List<String> CONTROL_CATEGORIES_ALL = new ArrayList(); + private static final List<String> CONTROL_CATEGORIES_SPECIAL = new ArrayList(); static { - TEST_CONTROL_CATEGORIES.add(CONTROL_CATEGORY_1); - TEST_CONTROL_CATEGORIES.add(CONTROL_CATEGORY_2); + CONTROL_CATEGORIES_ALL.add(CATEGORY_SAMPLE); + CONTROL_CATEGORIES_ALL.add(CATEGORY_SPECIAL); + + CONTROL_CATEGORIES_SPECIAL.add(CATEGORY_SPECIAL); } @Before @@ -125,7 +135,7 @@ public class MediaRouterManagerTest { // (Control requests shouldn't be used in this way.) InstrumentationRegistry.getInstrumentation().runOnMainSync( (Runnable) () -> { - mRouter.addCallback(TEST_CONTROL_CATEGORIES, mExecutor, mockRouterCallback); + mRouter.addCallback(CONTROL_CATEGORIES_ALL, mExecutor, mockRouterCallback); mRouter.sendControlRequest( new MediaRoute2Info.Builder(ROUTE_ID2, ROUTE_NAME2).build(), new Intent(ACTION_REMOVE_ROUTE)); @@ -138,8 +148,11 @@ public class MediaRouterManagerTest { mManager.removeCallback(mockCallback); } + /** + * Tests if we get proper routes for application that has special control category. + */ @Test - public void controlCategoryTest() throws Exception { + public void testControlCategory() throws Exception { MediaRouter2Manager.Callback mockCallback = mock(MediaRouter2Manager.Callback.class); mManager.addCallback(mExecutor, mockCallback); @@ -147,12 +160,19 @@ public class MediaRouterManagerTest { InstrumentationRegistry.getInstrumentation().runOnMainSync( () -> { - mRouter.addCallback(TEST_CONTROL_CATEGORIES, mExecutor, mockRouterCallback); + mRouter.addCallback(CONTROL_CATEGORIES_SPECIAL, + mExecutor, mockRouterCallback); mRouter.removeCallback(mockRouterCallback); } ); - verify(mockCallback, timeout(TIMEOUT_MS).atLeastOnce()) - .onControlCategoriesChanged(mPackageName, TEST_CONTROL_CATEGORIES); + verify(mockCallback, timeout(TIMEOUT_MS)) + .onRouteListChanged(argThat(routes -> routes.size() > 0)); + + Map<String, MediaRoute2Info> routes = + createRouteMap(mManager.getAvailableRoutes(mPackageName)); + + Assert.assertEquals(1, routes.size()); + Assert.assertNotNull(routes.get(ROUTE_ID_SPECIAL_CATEGORY)); mManager.removeCallback(mockCallback); } @@ -164,7 +184,7 @@ public class MediaRouterManagerTest { MediaRouter2.Callback mockRouterCallback = mock(MediaRouter2.Callback.class); InstrumentationRegistry.getInstrumentation().runOnMainSync( () -> { - mRouter.addCallback(TEST_CONTROL_CATEGORIES, mExecutor, mockRouterCallback); + mRouter.addCallback(CONTROL_CATEGORIES_ALL, mExecutor, mockRouterCallback); } ); @@ -197,10 +217,10 @@ public class MediaRouterManagerTest { mManager.removeCallback(managerCallback); } - @Test /** * Tests selecting and unselecting routes of a single provider. */ + @Test public void testSingleProviderSelect() { MediaRouter2Manager.Callback managerCallback = mock(MediaRouter2Manager.Callback.class); MediaRouter2.Callback routerCallback = mock(MediaRouter2.Callback.class); @@ -208,7 +228,7 @@ public class MediaRouterManagerTest { mManager.addCallback(mExecutor, managerCallback); InstrumentationRegistry.getInstrumentation().runOnMainSync( () -> { - mRouter.addCallback(TEST_CONTROL_CATEGORIES, mExecutor, routerCallback); + mRouter.addCallback(CONTROL_CATEGORIES_ALL, mExecutor, routerCallback); } ); verify(managerCallback, timeout(TIMEOUT_MS)) @@ -218,7 +238,8 @@ public class MediaRouterManagerTest { createRouteMap(mManager.getAvailableRoutes(mPackageName)); 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))); diff --git a/packages/CarSystemUI/res/values/dimens.xml b/packages/CarSystemUI/res/values/dimens.xml index 7592bd227a94..fb422aff51ef 100644 --- a/packages/CarSystemUI/res/values/dimens.xml +++ b/packages/CarSystemUI/res/values/dimens.xml @@ -114,10 +114,10 @@ <!-- Car notification shade--> <dimen name="notification_shade_handle_bar_height">10dp</dimen> <dimen name="notification_shade_handle_bar_radius">20dp</dimen> - <dimen name="notification_shade_handle_bar_margin_start">500dp</dimen> - <dimen name="notification_shade_handle_bar_margin_end">500dp</dimen> + <dimen name="notification_shade_handle_bar_margin_start">200dp</dimen> + <dimen name="notification_shade_handle_bar_margin_end">200dp</dimen> <dimen name="notification_shade_handle_bar_margin_top">20dp</dimen> <dimen name="notification_shade_handle_bar_margin_bottom">10dp</dimen> - <dimen name="notification_shade_list_padding_bottom">0dp</dimen> + <dimen name="notification_shade_list_padding_bottom">50dp</dimen> </resources> diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java index fc5e791ff7c5..c0dcbbcf30d4 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java @@ -70,13 +70,9 @@ public class CarNavigationButton extends com.android.keyguard.AlphaOptimizedImag R.styleable.CarNavigationButton_unselectedAlpha, mUnselectedAlpha); mSelectedIconResourceId = typedArray.getResourceId( R.styleable.CarNavigationButton_selectedIcon, mIconResourceId); + mIconResourceId = typedArray.getResourceId( + R.styleable.CarNavigationButton_icon, 0); typedArray.recycle(); - - // ImageView attrs - TypedArray a = context.obtainStyledAttributes( - attrs, com.android.internal.R.styleable.ImageView); - mIconResourceId = a.getResourceId(com.android.internal.R.styleable.ImageView_src, 0); - a.recycle(); } 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 a452baeac5c1..b1f979708615 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java @@ -1073,9 +1073,10 @@ public class CarStatusBar extends StatusBar implements // shade is visible to the user. When the notification shade is completely open then // alpha value will be 1. float alpha = (float) height / mNotificationView.getHeight(); - Drawable background = mNotificationView.getBackground(); + Drawable background = mNotificationView.getBackground().mutate(); background.setAlpha((int) (alpha * 255)); + mNotificationView.setBackground(background); } } diff --git a/packages/SettingsLib/res/values-hi/arrays.xml b/packages/SettingsLib/res/values-hi/arrays.xml index 5ad9b0191c77..3d9a78e99204 100644 --- a/packages/SettingsLib/res/values-hi/arrays.xml +++ b/packages/SettingsLib/res/values-hi/arrays.xml @@ -76,7 +76,7 @@ <item msgid="3422726142222090896">"avrcp16"</item> </string-array> <string-array name="bluetooth_a2dp_codec_titles"> - <item msgid="7065842274271279580">"सिस्टम चयन का उपयोग करें (डिफ़ॉल्ट)"</item> + <item msgid="7065842274271279580">"सिस्टम से चुने जाने का उपयोग करें (डिफ़ॉल्ट)"</item> <item msgid="7539690996561263909">"SBC"</item> <item msgid="686685526567131661">"AAC"</item> <item msgid="5254942598247222737">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ऑडियो"</item> @@ -86,7 +86,7 @@ <item msgid="3304843301758635896">"वैकल्पिक कोडेक अक्षम करें"</item> </string-array> <string-array name="bluetooth_a2dp_codec_summaries"> - <item msgid="5062108632402595000">"सिस्टम चयन का उपयोग करें (डिफ़ॉल्ट)"</item> + <item msgid="5062108632402595000">"सिस्टम से चुने जाने का उपयोग करें (डिफ़ॉल्ट)"</item> <item msgid="6898329690939802290">"SBC"</item> <item msgid="6839647709301342559">"AAC"</item> <item msgid="7848030269621918608">"<xliff:g id="QUALCOMM">Qualcomm®</xliff:g> <xliff:g id="APTX">aptX™</xliff:g> ऑडियो"</item> @@ -96,38 +96,38 @@ <item msgid="741805482892725657">"वैकल्पिक कोडेक अक्षम करें"</item> </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> - <item msgid="3093023430402746802">"सिस्टम चयन का उपयोग करें (डिफ़ॉल्ट)"</item> + <item msgid="3093023430402746802">"सिस्टम से चुने जाने का उपयोग करें (डिफ़ॉल्ट)"</item> <item msgid="8895532488906185219">"44.1 kHz"</item> <item msgid="2909915718994807056">"48.0 kHz"</item> <item msgid="3347287377354164611">"88.2 kHz"</item> <item msgid="1234212100239985373">"96.0 kHz"</item> </string-array> <string-array name="bluetooth_a2dp_codec_sample_rate_summaries"> - <item msgid="3214516120190965356">"सिस्टम चयन का उपयोग करें (डिफ़ॉल्ट)"</item> + <item msgid="3214516120190965356">"सिस्टम से चुने जाने का उपयोग करें (डिफ़ॉल्ट)"</item> <item msgid="4482862757811638365">"44.1 kHz"</item> <item msgid="354495328188724404">"48.0 kHz"</item> <item msgid="7329816882213695083">"88.2 kHz"</item> <item msgid="6967397666254430476">"96.0 kHz"</item> </string-array> <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles"> - <item msgid="2684127272582591429">"सिस्टम चयन का उपयोग करें (डिफ़ॉल्ट)"</item> + <item msgid="2684127272582591429">"सिस्टम से चुने जाने का उपयोग करें (डिफ़ॉल्ट)"</item> <item msgid="5618929009984956469">"16 बिट/नमूना"</item> <item msgid="3412640499234627248">"24 बिट/नमूना"</item> <item msgid="121583001492929387">"32 बिट/नमूना"</item> </string-array> <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries"> - <item msgid="1081159789834584363">"सिस्टम चयन का उपयोग करें (डिफ़ॉल्ट)"</item> + <item msgid="1081159789834584363">"सिस्टम से चुने जाने का उपयोग करें (डिफ़ॉल्ट)"</item> <item msgid="4726688794884191540">"16 बिट/नमूना"</item> <item msgid="305344756485516870">"24 बिट/नमूना"</item> <item msgid="244568657919675099">"32 बिट/नमूना"</item> </string-array> <string-array name="bluetooth_a2dp_codec_channel_mode_titles"> - <item msgid="5226878858503393706">"सिस्टम चयन का उपयोग करें (डिफ़ॉल्ट)"</item> + <item msgid="5226878858503393706">"सिस्टम से चुने जाने का उपयोग करें (डिफ़ॉल्ट)"</item> <item msgid="4106832974775067314">"मोनो"</item> <item msgid="5571632958424639155">"स्टीरियो"</item> </string-array> <string-array name="bluetooth_a2dp_codec_channel_mode_summaries"> - <item msgid="4118561796005528173">"सिस्टम चयन का उपयोग करें (डिफ़ॉल्ट)"</item> + <item msgid="4118561796005528173">"सिस्टम चुनाव का उपयोग करें (डिफ़ॉल्ट)"</item> <item msgid="8900559293912978337">"मोनो"</item> <item msgid="8883739882299884241">"स्टीरियो"</item> </string-array> diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml index 5d512a84ef99..238eba58e0a5 100644 --- a/packages/SettingsLib/res/values-hi/strings.xml +++ b/packages/SettingsLib/res/values-hi/strings.xml @@ -361,7 +361,7 @@ <string name="runningservices_settings_summary" msgid="854608995821032748">"इस समय चल रही सेवाओं को देखें और नियंत्रित करें"</string> <string name="select_webview_provider_title" msgid="4628592979751918907">"वेबव्यू लागू करें"</string> <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"वेबव्यू सेट करें"</string> - <string name="select_webview_provider_toast_text" msgid="5466970498308266359">"यह चयन अब मान्य नहीं है. पुनः प्रयास करें."</string> + <string name="select_webview_provider_toast_text" msgid="5466970498308266359">"यह चुनाव अब मान्य नहीं है. दोबारा कोशिश करें."</string> <string name="convert_to_file_encryption" msgid="3060156730651061223">"फ़ाइल आधारित सुरक्षित करने के तरीके में बदलें"</string> <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"रूपांतरित करें..."</string> <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"फ़ाइल पहले से एन्क्रिप्ट की हुई है"</string> diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml index fd45cb0a1f3c..f1934c37369e 100644 --- a/packages/SettingsLib/res/values-ne/strings.xml +++ b/packages/SettingsLib/res/values-ne/strings.xml @@ -248,7 +248,7 @@ <string name="wifi_display_certification_summary" msgid="1155182309166746973">"ताररहित प्रदर्शन प्रमाणीकरणका लागि विकल्पहरू देखाउनुहोस्"</string> <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi-Fi लग स्तर बढाउनुहोस्, Wi-Fi चयनकर्तामा प्रति SSID RSSI देखाइन्छ"</string> <string name="wifi_scan_throttling_summary" msgid="4461922728822495763">"ब्याट्रीको खपत कम गरी नेटवर्कको कार्यसम्पादनमा सुधार गर्दछ"</string> - <string name="wifi_metered_label" msgid="4514924227256839725">"मिटर गरिएको जडान भनी चिन्ह लगाइएको"</string> + <string name="wifi_metered_label" msgid="4514924227256839725">"सशुल्क वाइफाइ"</string> <string name="wifi_unmetered_label" msgid="6124098729457992931">"मिटर नगरिएको"</string> <string name="select_logd_size_title" msgid="7433137108348553508">"लगर बफर आकारहरू"</string> <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"लग बफर प्रति लगर आकार चयन गर्नुहोस्"</string> diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java index 9a41f1d6a2b1..9672fea1cc19 100644 --- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java +++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java @@ -663,7 +663,8 @@ public class RestrictedLockUtilsInternal extends RestrictedLockUtils { final SpannableStringBuilder sb = new SpannableStringBuilder(textView.getText()); removeExistingRestrictedSpans(sb); if (disabled) { - final int disabledColor = context.getColor(R.color.disabled_text_color); + final int disabledColor = Utils.getDisabled(context, + textView.getCurrentTextColor()); sb.setSpan(new ForegroundColorSpan(disabledColor), 0, sb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); textView.setCompoundDrawables(null, null, getRestrictedPadlock(context), null); diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java index d32e85f01962..14f233d958e6 100644 --- a/packages/SettingsLib/src/com/android/settingslib/Utils.java +++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java @@ -35,8 +35,6 @@ import java.text.NumberFormat; public class Utils { - private static final String CURRENT_MODE_KEY = "CURRENT_MODE"; - private static final String NEW_MODE_KEY = "NEW_MODE"; @VisibleForTesting static final String STORAGE_MANAGER_ENABLED_PROPERTY = "ro.storage_manager.enabled"; @@ -56,24 +54,11 @@ public class Utils { public static void updateLocationEnabled(Context context, boolean enabled, int userId, int source) { - LocationManager locationManager = context.getSystemService(LocationManager.class); - Settings.Secure.putIntForUser( context.getContentResolver(), Settings.Secure.LOCATION_CHANGER, source, userId); - Intent intent = new Intent(LocationManager.MODE_CHANGING_ACTION); - final int oldMode = locationManager.isLocationEnabled() - ? Settings.Secure.LOCATION_MODE_ON - : Settings.Secure.LOCATION_MODE_OFF; - final int newMode = enabled - ? Settings.Secure.LOCATION_MODE_ON - : Settings.Secure.LOCATION_MODE_OFF; - intent.putExtra(CURRENT_MODE_KEY, oldMode); - intent.putExtra(NEW_MODE_KEY, newMode); - context.sendBroadcastAsUser( - intent, UserHandle.of(userId), android.Manifest.permission.WRITE_SECURE_SETTINGS); - + LocationManager locationManager = context.getSystemService(LocationManager.class); locationManager.setLocationEnabledForUser(enabled, UserHandle.of(userId)); } @@ -218,6 +203,13 @@ public class Utils { return list.getDefaultColor(); } + /** + * This method computes disabled color from normal color + * + * @param context + * @param inputColor normal color. + * @return disabled color. + */ @ColorInt public static int getDisabled(Context context, int inputColor) { return applyAlphaAttr(context, android.R.attr.disabledAlpha, inputColor); diff --git a/packages/SettingsLib/src/com/android/settingslib/utils/ColorUtil.java b/packages/SettingsLib/src/com/android/settingslib/utils/ColorUtil.java new file mode 100644 index 000000000000..c54b471135d1 --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/utils/ColorUtil.java @@ -0,0 +1,35 @@ +/* + * 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.settingslib.utils; + +import android.content.Context; +import android.content.res.TypedArray; + +/** Utility class for getting color attribute **/ +public class ColorUtil { + + /** + * Returns android:disabledAlpha value in context + */ + public static float getDisabledAlpha(Context context) { + final TypedArray ta = context.obtainStyledAttributes( + new int[]{android.R.attr.disabledAlpha}); + final float alpha = ta.getFloat(0, 0); + ta.recycle(); + return alpha; + } +} diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java index 4d76e44a3c50..fc69b1a657d4 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java @@ -15,17 +15,13 @@ */ package com.android.settingslib; -import static android.Manifest.permission.WRITE_SECURE_SETTINGS; - import static com.android.settingslib.Utils.STORAGE_MANAGER_ENABLED_PROPERTY; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.ActivityManager; @@ -45,7 +41,6 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentMatcher; -import org.mockito.ArgumentMatchers; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; @@ -85,15 +80,11 @@ public class UtilsTest { } @Test - public void testUpdateLocationEnabled_sendBroadcast() { + public void testUpdateLocationEnabled() { int currentUserId = ActivityManager.getCurrentUser(); Utils.updateLocationEnabled(mContext, true, currentUserId, Settings.Secure.LOCATION_CHANGER_QUICK_SETTINGS); - verify(mContext).sendBroadcastAsUser( - argThat(actionMatches(LocationManager.MODE_CHANGING_ACTION)), - ArgumentMatchers.eq(UserHandle.of(currentUserId)), - ArgumentMatchers.eq(WRITE_SECURE_SETTINGS)); assertThat(Settings.Secure.getInt(mContext.getContentResolver(), Settings.Secure.LOCATION_CHANGER, Settings.Secure.LOCATION_CHANGER_UNKNOWN)) .isEqualTo(Settings.Secure.LOCATION_CHANGER_QUICK_SETTINGS); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java b/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java index 044ec867aef4..56173310a8bb 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java @@ -22,14 +22,16 @@ import android.provider.Settings; import android.util.MemoryIntArray; import android.util.Slog; import android.util.SparseIntArray; + import com.android.internal.annotations.GuardedBy; import java.io.IOException; /** - * This class tracks changes for global/secure/system tables on a - * per user basis and updates a shared memory region which client - * processes can read to determine if their local caches are stale, + * This class tracks changes for config/global/secure/system tables + * on a per user basis and updates a shared memory region which + * client processes can read to determine if their local caches are + * stale. */ final class GenerationRegistry { private static final String LOG_TAG = "GenerationRegistry"; @@ -114,11 +116,12 @@ final class GenerationRegistry { @GuardedBy("mLock") private MemoryIntArray getBackingStoreLocked() { if (mBackingStore == null) { - // One for the global table, two for system and secure tables for a - // managed profile (managed profile is not included in the max user - // count), ten for partially deleted users if users are quickly removed, - // and twice max user count for system and secure. - final int size = 1 + 2 + 10 + 2 * UserManager.getMaxSupportedUsers(); + // One for the config table, one for the global table, two for system + // and secure tables for a managed profile (managed profile is not + // included in the max user count), ten for partially deleted users if + // users are quickly removed, and twice max user count for system and + // secure. + final int size = 1 + 1 + 2 + 10 + 2 * UserManager.getMaxSupportedUsers(); try { mBackingStore = new MemoryIntArray(size); if (DEBUG) { diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java index 9d398b5b69a1..b11df4890e98 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java @@ -16,7 +16,6 @@ package com.android.providers.settings; -import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.backup.BackupAgentHelper; import android.app.backup.BackupDataInput; @@ -41,6 +40,7 @@ import android.util.ArraySet; import android.util.BackupUtils; import android.util.Log; +import com.android.internal.util.ArrayUtils; import com.android.internal.widget.LockPatternUtils; import java.io.BufferedOutputStream; @@ -586,15 +586,15 @@ public class SettingsBackupAgent extends BackupAgentHelper { final String[] whitelist; Map<String, Validator> validators = null; if (contentUri.equals(Settings.Secure.CONTENT_URI)) { - whitelist = concat(Settings.Secure.SETTINGS_TO_BACKUP, + whitelist = ArrayUtils.concatElements(String.class, Settings.Secure.SETTINGS_TO_BACKUP, Settings.Secure.LEGACY_RESTORE_SETTINGS); validators = Settings.Secure.VALIDATORS; } else if (contentUri.equals(Settings.System.CONTENT_URI)) { - whitelist = concat(Settings.System.SETTINGS_TO_BACKUP, + whitelist = ArrayUtils.concatElements(String.class, Settings.System.SETTINGS_TO_BACKUP, Settings.System.LEGACY_RESTORE_SETTINGS); validators = Settings.System.VALIDATORS; } else if (contentUri.equals(Settings.Global.CONTENT_URI)) { - whitelist = concat(Settings.Global.SETTINGS_TO_BACKUP, + whitelist = ArrayUtils.concatElements(String.class, Settings.Global.SETTINGS_TO_BACKUP, Settings.Global.LEGACY_RESTORE_SETTINGS); validators = Settings.Global.VALIDATORS; } else { @@ -672,18 +672,6 @@ public class SettingsBackupAgent extends BackupAgentHelper { return (validator != null) && validator.validate(value); } - private final String[] concat(String[] first, @Nullable String[] second) { - if (second == null || second.length == 0) { - return first; - } - final int firstLen = first.length; - final int secondLen = second.length; - String[] both = new String[firstLen + secondLen]; - System.arraycopy(first, 0, both, 0, firstLen); - System.arraycopy(second, 0, both, firstLen, secondLen); - return both; - } - /** * Restores the owner info enabled and other settings in LockSettings. * diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java index ea878703f3d3..c9bb83c0b986 100644 --- a/packages/Shell/src/com/android/shell/BugreportProgressService.java +++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java @@ -50,7 +50,6 @@ import android.os.BugreportManager; import android.os.BugreportManager.BugreportCallback; import android.os.BugreportManager.BugreportCallback.BugreportErrorCode; import android.os.BugreportParams; -import android.os.BugreportParams.BugreportMode; import android.os.Bundle; import android.os.FileUtils; import android.os.Handler; @@ -354,7 +353,7 @@ public class BugreportProgressService extends Service { private final int mId; private final BugreportInfo mInfo; - BugreportCallbackImpl(String name, int id, @BugreportMode int bugreportType) { + BugreportCallbackImpl(String name, int id) { mId = id; // pid not used in this workflow, so setting default = 0 mInfo = new BugreportInfo(mContext, mId, 0 /* pid */, name, @@ -363,8 +362,6 @@ public class BugreportProgressService extends Service { @Override public void onProgress(float progress) { - // TODO: Make dumpstate call onProgress at 0% progress to trigger the - // progress notification instantly. checkProgressUpdated(mInfo, (int) progress); } @@ -544,21 +541,21 @@ public class BugreportProgressService extends Service { private void startBugreportAPI(Intent intent) { mUsingBugreportApi = true; - String currentTimeStamp = new SimpleDateFormat("yyyy-MM-dd-HH-mm").format( + String bugreportName = "bugreport-" + new SimpleDateFormat("yyyy-MM-dd-HH-mm").format( new Date()); // TODO(b/126862297): Make file naming same as dumpstate triggered bugreports ParcelFileDescriptor bugreportFd = createReadWriteFile(BUGREPORT_DIR, - "bugreport-" + currentTimeStamp + ".zip"); + bugreportName + ".zip"); if (bugreportFd == null) { Log.e(TAG, "Bugreport parcel file descriptor is null."); return; } + int bugreportType = intent.getIntExtra(EXTRA_BUGREPORT_TYPE, + BugreportParams.BUGREPORT_MODE_INTERACTIVE); - // TODO(b/126862297): Screenshot file is not needed for INTERACTIVE_BUGREPORTS - // Add logic to pass screenshot file only for specific bugreports. ParcelFileDescriptor screenshotFd = createReadWriteFile(BUGREPORT_DIR, - "screenshot-" + currentTimeStamp + ".png"); + bugreportName + ".png"); if (screenshotFd == null) { Log.e(TAG, "Screenshot parcel file descriptor is null."); // TODO(b/123617758): Delete bugreport file created above @@ -572,16 +569,13 @@ public class BugreportProgressService extends Service { // Dumpstate increments PROPERTY_LAST_ID, may be racy if multiple calls // to dumpstate are made simultaneously. final int id = SystemProperties.getInt(PROPERTY_LAST_ID, 0) + 1; - int bugreportType = intent.getIntExtra(EXTRA_BUGREPORT_TYPE, - BugreportParams.BUGREPORT_MODE_INTERACTIVE); Log.i(TAG, "bugreport type = " + bugreportType + " bugreport file fd: " + bugreportFd + " screenshot file fd: " + screenshotFd); - BugreportCallbackImpl bugreportCallback = new BugreportCallbackImpl("bugreport-" - + currentTimeStamp, id, bugreportType); + BugreportCallbackImpl bugreportCallback = new BugreportCallbackImpl(bugreportName, id); try { - mBugreportManager.startBugreport(bugreportFd, null, + mBugreportManager.startBugreport(bugreportFd, screenshotFd, new BugreportParams(bugreportType), executor, bugreportCallback); mBugreportInfos.put(bugreportCallback.mInfo.id, bugreportCallback.mInfo); } catch (RuntimeException e) { @@ -963,14 +957,20 @@ public class BugreportProgressService extends Service { return; } final int max = -1; // this is to log metrics for dumpstate duration. - final File screenshotFile = new File(BUGREPORT_DIR, info.name + ".png"); - // TODO(b/126862297): Screenshot file is not needed for INTERACTIVE_BUGREPORTS - // Add logic to null check screenshot file only for specific bugreports. + File screenshotFile = new File(BUGREPORT_DIR, info.name + ".png"); if (screenshotFile == null) { // Should never happen, an id always has a file linked to it. Log.wtf(TAG, "Missing file " + screenshotFile.getPath() + " does not exist."); return; } + // If the screenshot file did not get populated implies this type of bugreport does not + // need the screenshot file; setting the file to null so that empty file doesnt get shared + if (screenshotFile.length() == 0) { + if (screenshotFile.delete()) { + Log.d(TAG, "screenshot file deleted successfully."); + } + screenshotFile = null; + } onBugreportFinished(id, bugreportFile, screenshotFile, info.title, info.description, max); } @@ -2195,7 +2195,7 @@ public class BugreportProgressService extends Service { max = CAPPED_MAX; } - if (newPercentage > oldPercentage) { + if (progress == 0 || newPercentage > oldPercentage) { updateProgressInfo(info, progress, max); } } diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java index 28d5402bf707..52ec1f0bb330 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java @@ -30,7 +30,7 @@ import java.io.PrintWriter; */ @ProvidesInterface(version = FalsingManager.VERSION) public interface FalsingManager { - int VERSION = 1; + int VERSION = 2; void onSucccessfulUnlock(); @@ -103,4 +103,6 @@ public interface FalsingManager { void onTouchEvent(MotionEvent ev, int width, int height); void dump(PrintWriter pw); + + void cleanup(); } diff --git a/packages/SystemUI/res/layout/biometric_dialog.xml b/packages/SystemUI/res/layout/biometric_dialog.xml index c560d7e8f126..e687cdfac356 100644 --- a/packages/SystemUI/res/layout/biometric_dialog.xml +++ b/packages/SystemUI/res/layout/biometric_dialog.xml @@ -55,7 +55,8 @@ android:id="@+id/left_space" android:layout_weight="1" android:layout_width="0dp" - android:layout_height="match_parent"/> + android:layout_height="match_parent" + android:contentDescription="@string/biometric_dialog_empty_space_description"/> <LinearLayout android:id="@+id/dialog" @@ -177,7 +178,8 @@ android:id="@+id/right_space" android:layout_weight="1" android:layout_width="0dp" - android:layout_height="match_parent" /> + android:layout_height="match_parent" + android:contentDescription="@string/biometric_dialog_empty_space_description"/> </LinearLayout> diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml index 7fc2066c7176..21c2c6b879f8 100644 --- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml +++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml @@ -46,6 +46,8 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" + android:paddingStart="@dimen/keyguard_indication_text_padding" + android:paddingEnd="@dimen/keyguard_indication_text_padding" android:textAppearance="@style/TextAppearance.Keyguard.BottomArea" android:visibility="gone" /> @@ -54,6 +56,8 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" + android:paddingStart="@dimen/keyguard_indication_text_padding" + android:paddingEnd="@dimen/keyguard_indication_text_padding" android:textAppearance="@style/TextAppearance.Keyguard.BottomArea" android:accessibilityLiveRegion="polite" /> diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml index 82252ba862a2..dc661dd14a4e 100644 --- a/packages/SystemUI/res/values-en-rAU/strings.xml +++ b/packages/SystemUI/res/values-en-rAU/strings.xml @@ -401,7 +401,7 @@ <string name="speed_bump_explanation" msgid="1288875699658819755">"Less urgent notifications below"</string> <string name="notification_tap_again" msgid="7590196980943943842">"Tap again to open"</string> <string name="keyguard_unlock" msgid="6035822649218712063">"Swipe up to open"</string> - <string name="do_disclosure_generic" msgid="5615898451805157556">"This device is managed by your organization"</string> + <string name="do_disclosure_generic" msgid="5615898451805157556">"This device is managed by your organisation"</string> <string name="do_disclosure_with_name" msgid="5640615509915445501">"This device is managed by <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string> <string name="phone_hint" msgid="4872890986869209950">"Swipe from icon for phone"</string> <string name="voice_hint" msgid="8939888732119726665">"Swipe from icon for voice assist"</string> diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml index ca9e29aed15f..9903cc04e97a 100644 --- a/packages/SystemUI/res/values-en-rCA/strings.xml +++ b/packages/SystemUI/res/values-en-rCA/strings.xml @@ -401,7 +401,7 @@ <string name="speed_bump_explanation" msgid="1288875699658819755">"Less urgent notifications below"</string> <string name="notification_tap_again" msgid="7590196980943943842">"Tap again to open"</string> <string name="keyguard_unlock" msgid="6035822649218712063">"Swipe up to open"</string> - <string name="do_disclosure_generic" msgid="5615898451805157556">"This device is managed by your organization"</string> + <string name="do_disclosure_generic" msgid="5615898451805157556">"This device is managed by your organisation"</string> <string name="do_disclosure_with_name" msgid="5640615509915445501">"This device is managed by <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string> <string name="phone_hint" msgid="4872890986869209950">"Swipe from icon for phone"</string> <string name="voice_hint" msgid="8939888732119726665">"Swipe from icon for voice assist"</string> diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml index 82252ba862a2..dc661dd14a4e 100644 --- a/packages/SystemUI/res/values-en-rGB/strings.xml +++ b/packages/SystemUI/res/values-en-rGB/strings.xml @@ -401,7 +401,7 @@ <string name="speed_bump_explanation" msgid="1288875699658819755">"Less urgent notifications below"</string> <string name="notification_tap_again" msgid="7590196980943943842">"Tap again to open"</string> <string name="keyguard_unlock" msgid="6035822649218712063">"Swipe up to open"</string> - <string name="do_disclosure_generic" msgid="5615898451805157556">"This device is managed by your organization"</string> + <string name="do_disclosure_generic" msgid="5615898451805157556">"This device is managed by your organisation"</string> <string name="do_disclosure_with_name" msgid="5640615509915445501">"This device is managed by <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string> <string name="phone_hint" msgid="4872890986869209950">"Swipe from icon for phone"</string> <string name="voice_hint" msgid="8939888732119726665">"Swipe from icon for voice assist"</string> diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml index 82252ba862a2..dc661dd14a4e 100644 --- a/packages/SystemUI/res/values-en-rIN/strings.xml +++ b/packages/SystemUI/res/values-en-rIN/strings.xml @@ -401,7 +401,7 @@ <string name="speed_bump_explanation" msgid="1288875699658819755">"Less urgent notifications below"</string> <string name="notification_tap_again" msgid="7590196980943943842">"Tap again to open"</string> <string name="keyguard_unlock" msgid="6035822649218712063">"Swipe up to open"</string> - <string name="do_disclosure_generic" msgid="5615898451805157556">"This device is managed by your organization"</string> + <string name="do_disclosure_generic" msgid="5615898451805157556">"This device is managed by your organisation"</string> <string name="do_disclosure_with_name" msgid="5640615509915445501">"This device is managed by <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string> <string name="phone_hint" msgid="4872890986869209950">"Swipe from icon for phone"</string> <string name="voice_hint" msgid="8939888732119726665">"Swipe from icon for voice assist"</string> diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml index b25f2fcbdbdd..75a76ae66093 100644 --- a/packages/SystemUI/res/values-fr/strings.xml +++ b/packages/SystemUI/res/values-fr/strings.xml @@ -376,7 +376,7 @@ <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4453017157391574402">"Jusqu\'à l\'aube"</string> <string name="quick_settings_night_secondary_label_on_at" msgid="6256314040368487637">"À partir de <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_secondary_label_until" msgid="2749196569462600150">"Jusqu\'à <xliff:g id="TIME">%s</xliff:g>"</string> - <string name="quick_settings_ui_mode_night_label" msgid="3419947801072692538">"Thème foncé"</string> + <string name="quick_settings_ui_mode_night_label" msgid="3419947801072692538">"Thème sombre"</string> <string name="quick_settings_ui_mode_night_label_battery_saver" msgid="7438725724589758362">"Thème foncé\nÉconomiseur de batterie"</string> <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string> <string name="quick_settings_nfc_off" msgid="6883274004315134333">"La technologie NFC est désactivée"</string> diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml index 1a914e097655..3268ed06ff56 100644 --- a/packages/SystemUI/res/values-gl/strings.xml +++ b/packages/SystemUI/res/values-gl/strings.xml @@ -894,10 +894,10 @@ <string name="mobile_data_disable_message" msgid="4756541658791493506">"Non terás acceso aos datos nin a Internet a través de <xliff:g id="CARRIER">%s</xliff:g>. Internet só estará dispoñible mediante a wifi."</string> <string name="mobile_data_disable_message_default_carrier" msgid="6078110473451946831">"o teu operador"</string> <string name="touch_filtered_warning" msgid="8671693809204767551">"Dado que unha aplicación se superpón sobre unha solicitude de permiso, a configuración non pode verificar a túa resposta."</string> - <string name="slice_permission_title" msgid="7465009437851044444">"Queres permitir que a aplicación <xliff:g id="APP_0">%1$s</xliff:g> mostre partes de <xliff:g id="APP_2">%2$s</xliff:g>?"</string> + <string name="slice_permission_title" msgid="7465009437851044444">"Queres permitir que <xliff:g id="APP_0">%1$s</xliff:g> mostre fragmentos de aplicación de <xliff:g id="APP_2">%2$s</xliff:g>?"</string> <string name="slice_permission_text_1" msgid="3514586565609596523">"- Pode ler información da aplicación <xliff:g id="APP">%1$s</xliff:g>"</string> <string name="slice_permission_text_2" msgid="3146758297471143723">"- Pode levar a cabo accións dentro da aplicación <xliff:g id="APP">%1$s</xliff:g>"</string> - <string name="slice_permission_checkbox" msgid="7986504458640562900">"Permitir que a aplicación <xliff:g id="APP">%1$s</xliff:g> mostre partes de calquera aplicación"</string> + <string name="slice_permission_checkbox" msgid="7986504458640562900">"Permitir que <xliff:g id="APP">%1$s</xliff:g> mostre fragmentos de calquera aplicación"</string> <string name="slice_permission_allow" msgid="2340244901366722709">"Permitir"</string> <string name="slice_permission_deny" msgid="7683681514008048807">"Denegar"</string> <string name="auto_saver_title" msgid="1217959994732964228">"Tocar para programar a función Aforro de batería"</string> diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml index 08816c7d51e4..83a7aaa684c3 100644 --- a/packages/SystemUI/res/values-sq/strings.xml +++ b/packages/SystemUI/res/values-sq/strings.xml @@ -614,7 +614,7 @@ <string name="remove_from_settings_prompt" msgid="6069085993355887748">"Të hiqet Sintonizuesi i Sistemit të Ndërfaqes së Përdoruesit nga Cilësimet dhe të ndërpritet përdorimi i të gjitha funksioneve të tij?"</string> <string name="activity_not_found" msgid="348423244327799974">"Aplikacioni nuk është instaluar në pajisjen tënde."</string> <string name="clock_seconds" msgid="7689554147579179507">"Trego sekondat e orës"</string> - <string name="clock_seconds_desc" msgid="6282693067130470675">"Trego sekondat e orës në shiritin e statusit. Mund të ndikojë te jeta e baterisë."</string> + <string name="clock_seconds_desc" msgid="6282693067130470675">"Trego sekondat e orës në shiritin e statusit. Mund të ndikojë te kohëzgjatja e baterisë."</string> <string name="qs_rearrange" msgid="8060918697551068765">"Risistemo Cilësimet e shpejta"</string> <string name="show_brightness" msgid="6613930842805942519">"Shfaq ndriçimin te Cilësimet e shpejta"</string> <string name="experimental" msgid="6198182315536726162">"Eksperimentale"</string> diff --git a/packages/SystemUI/res/values/attrs_car.xml b/packages/SystemUI/res/values/attrs_car.xml index ced26c95dfd9..49b87f31d3ab 100644 --- a/packages/SystemUI/res/values/attrs_car.xml +++ b/packages/SystemUI/res/values/attrs_car.xml @@ -62,6 +62,8 @@ <attr name="unselectedAlpha" /> <!-- icon to be rendered when in selected state --> <attr name="selectedIcon" /> + <!-- icon to be rendered (drawable) --> + <attr name="icon"/> </declare-styleable> <!-- Custom attributes to configure hvac values --> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 7c2413039ae4..861187fded15 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -154,12 +154,8 @@ <!-- The number of milliseconds before the heads up notification accepts touches. --> <integer name="touch_acceptance_delay">700</integer> - <!-- The number of milliseconds before the ambient notification auto-dismisses. This will - override the default pulse length. --> - <integer name="heads_up_notification_decay_dozing">10000</integer> - <!-- The number of milliseconds to extend ambient pulse by when prompted (e.g. on touch) --> - <integer name="ambient_notification_extension_time">6000</integer> + <integer name="ambient_notification_extension_time">10000</integer> <!-- In multi-window, determines whether the stack where recents lives should grow from the smallest position when being launched. --> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index f4970d073741..be815e13e68e 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -112,7 +112,7 @@ <dimen name="status_bar_connected_device_bt_indicator_size">17dp</dimen> <!-- Height of a small notification in the status bar--> - <dimen name="notification_min_height">106dp</dimen> + <dimen name="notification_min_height">@*android:dimen/notification_min_height</dimen> <!-- Increased height of a small notification in the status bar --> <dimen name="notification_min_height_increased">146dp</dimen> @@ -183,6 +183,9 @@ <!-- The padding on the bottom of the notifications on the keyguard --> <dimen name="keyguard_indication_bottom_padding">12sp</dimen> + <!-- The padding at start and end of indication text shown on AOD --> + <dimen name="keyguard_indication_text_padding">16dp</dimen> + <!-- Shadows under the clock, date and other keyguard text fields --> <dimen name="keyguard_shadow_radius">5</dimen> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java index 98a8110209e6..98b7e2484478 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/ThumbnailData.java @@ -38,6 +38,7 @@ public class ThumbnailData { public int windowingMode; public int systemUiVisibility; public float scale; + public long snapshotId; public ThumbnailData() { thumbnail = null; @@ -49,6 +50,7 @@ public class ThumbnailData { isTranslucent = false; windowingMode = WINDOWING_MODE_UNDEFINED; systemUiVisibility = 0; + snapshotId = 0; } public ThumbnailData(TaskSnapshot snapshot) { @@ -61,5 +63,6 @@ public class ThumbnailData { isTranslucent = snapshot.isTranslucent(); windowingMode = snapshot.getWindowingMode(); systemUiVisibility = snapshot.getSystemUiVisibility(); + snapshotId = snapshot.getId(); } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java index 69630c4e90e8..4ad262fee520 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java @@ -237,6 +237,7 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe MIN_DRAG_SIZE, getResources().getDisplayMetrics()) && !mUpdateMonitor.isFaceDetectionRunning()) { mUpdateMonitor.requestFaceAuth(); + mCallback.userActivity(); showMessage(null, null); } } @@ -512,7 +513,7 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe case SimPuk: // Shortcut for SIM PIN/PUK to go to directly to user's security screen or home SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId); - if (securityMode == SecurityMode.None || mLockPatternUtils.isLockScreenDisabled( + if (securityMode == SecurityMode.None && mLockPatternUtils.isLockScreenDisabled( KeyguardUpdateMonitor.getCurrentUser())) { finish = true; eventSubtype = BOUNCER_DISMISS_SIM; diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 2eb93d3a6075..fd618b05f348 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -377,14 +377,15 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { } }; - private boolean mFaceSettingEnabledForUser; + private SparseBooleanArray mFaceSettingEnabledForUser = new SparseBooleanArray(); private BiometricManager mBiometricManager; private IBiometricEnabledOnKeyguardCallback mBiometricEnabledCallback = new IBiometricEnabledOnKeyguardCallback.Stub() { @Override - public void onChanged(BiometricSourceType type, boolean enabled) throws RemoteException { + public void onChanged(BiometricSourceType type, boolean enabled, int userId) + throws RemoteException { if (type == BiometricSourceType.FACE) { - mFaceSettingEnabledForUser = enabled; + mFaceSettingEnabledForUser.put(userId, enabled); updateFaceListeningState(); } } @@ -1711,7 +1712,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { // instance of KeyguardUpdateMonitor for each user but KeyguardUpdateMonitor is user-aware. return (mBouncer || mAuthInterruptActive || awakeKeyguard || shouldListenForFaceAssistant()) && !mSwitchingUser && !isFaceDisabled(user) && becauseCannotSkipBouncer - && !mKeyguardGoingAway && mFaceSettingEnabledForUser && !mLockIconPressed + && !mKeyguardGoingAway && mFaceSettingEnabledForUser.get(user) && !mLockIconPressed && strongAuthAllowsScanning && mIsPrimaryUser && !mSecureCameraLaunched; } @@ -1783,13 +1784,14 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { } /** - * If face hardware is available and user has enrolled. Not considering encryption or - * lockdown state. + * If face hardware is available, user has enrolled and enabled auth via setting. + * Not considering encryption or lock down state. */ public boolean isUnlockWithFacePossible(int userId) { return mFaceManager != null && mFaceManager.isHardwareDetected() && !isFaceDisabled(userId) - && mFaceManager.hasEnrolledTemplates(userId); + && mFaceManager.hasEnrolledTemplates(userId) + && mFaceSettingEnabledForUser.get(userId); } private void stopListeningForFingerprint() { @@ -2657,7 +2659,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { pw.println(" possible=" + isUnlockWithFacePossible(userId)); pw.println(" strongAuthFlags=" + Integer.toHexString(strongAuthFlags)); pw.println(" trustManaged=" + getUserTrustIsManaged(userId)); - pw.println(" enabledByUser=" + mFaceSettingEnabledForUser); + pw.println(" enabledByUser=" + mFaceSettingEnabledForUser.get(userId)); pw.println(" mSecureCameraLaunched=" + mSecureCameraLaunched); } } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java index 0041b0c9a469..ea175edf0be1 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java @@ -239,4 +239,8 @@ public class FalsingManagerFake implements FalsingManager { public void dump(PrintWriter pw) { } + + @Override + public void cleanup() { + } } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java index 67dfdcac4b22..fba0d5005db4 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java @@ -168,6 +168,7 @@ public class FalsingManagerImpl implements FalsingManager { .append("enabled=").append(isEnabled() ? 1 : 0) .append(" mScreenOn=").append(mScreenOn ? 1 : 0) .append(" mState=").append(StatusBarState.toShortString(mState)) + .append(" mShowingAod=").append(mShowingAod ? 1 : 0) .toString() ); } @@ -550,6 +551,14 @@ public class FalsingManagerImpl implements FalsingManager { pw.println(); } + @Override + public void cleanup() { + mSensorManager.unregisterListener(mSensorEventListener); + mContext.getContentResolver().unregisterContentObserver(mSettingsObserver); + Dependency.get(StatusBarStateController.class).removeCallback(mStatusBarStateListener); + KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mKeyguardUpdateCallback); + } + public Uri reportRejectedTouch() { if (mDataCollector.isEnabled()) { return mDataCollector.reportRejectedTouch(); diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java index 3cc8ec9afbb2..128cc614fd08 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java @@ -65,6 +65,7 @@ public class FalsingManagerProxy implements FalsingManager { public void onPluginConnected(FalsingPlugin plugin, Context context) { FalsingManager pluginFalsingManager = plugin.getFalsingManager(context); if (pluginFalsingManager != null) { + mInternalFalsingManager.cleanup(); mInternalFalsingManager = pluginFalsingManager; } } @@ -91,7 +92,11 @@ public class FalsingManagerProxy implements FalsingManager { @VisibleForTesting public void setupFalsingManager(Context context) { boolean brightlineEnabled = DeviceConfig.getBoolean( - DeviceConfig.NAMESPACE_SYSTEMUI, BRIGHTLINE_FALSING_MANAGER_ENABLED, false); + DeviceConfig.NAMESPACE_SYSTEMUI, BRIGHTLINE_FALSING_MANAGER_ENABLED, true); + + if (mInternalFalsingManager != null) { + mInternalFalsingManager.cleanup(); + } if (!brightlineEnabled) { mInternalFalsingManager = new FalsingManagerImpl(context); } else { @@ -290,4 +295,9 @@ public class FalsingManagerProxy implements FalsingManager { public void dump(PrintWriter pw) { mInternalFalsingManager.dump(pw); } + + @Override + public void cleanup() { + mInternalFalsingManager.cleanup(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java index 00f35aa4276c..cee01a4d3048 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java @@ -324,6 +324,11 @@ public class BrightLineFalsingManager implements FalsingManager { public void dump(PrintWriter printWriter) { } + @Override + public void cleanup() { + unregisterSensors(); + } + static void logDebug(String msg) { logDebug(msg, null); } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/DiagonalClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/DiagonalClassifier.java index 730907e1fa9c..cc6645415fd8 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/DiagonalClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/DiagonalClassifier.java @@ -16,9 +16,13 @@ package com.android.systemui.classifier.brightline; +import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DIAGONAL_HORIZONTAL_ANGLE_RANGE; +import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DIAGONAL_VERTICAL_ANGLE_RANGE; import static com.android.systemui.classifier.Classifier.LEFT_AFFORDANCE; import static com.android.systemui.classifier.Classifier.RIGHT_AFFORDANCE; +import android.provider.DeviceConfig; + /** * False on swipes that are too close to 45 degrees. * @@ -35,8 +39,20 @@ class DiagonalClassifier extends FalsingClassifier { private static final float ONE_HUNDRED_EIGHTY_DEG = (float) (Math.PI); private static final float THREE_HUNDRED_SIXTY_DEG = (float) (2 * Math.PI); + private final float mHorizontalAngleRange; + private final float mVerticalAngleRange; + DiagonalClassifier(FalsingDataProvider dataProvider) { super(dataProvider); + + mHorizontalAngleRange = DeviceConfig.getFloat( + DeviceConfig.NAMESPACE_SYSTEMUI, + BRIGHTLINE_FALSING_DIAGONAL_HORIZONTAL_ANGLE_RANGE, + HORIZONTAL_ANGLE_RANGE); + mVerticalAngleRange = DeviceConfig.getFloat( + DeviceConfig.NAMESPACE_SYSTEMUI, + BRIGHTLINE_FALSING_DIAGONAL_VERTICAL_ANGLE_RANGE, + VERTICAL_ANGLE_RANGE); } @Override @@ -52,11 +68,11 @@ class DiagonalClassifier extends FalsingClassifier { return false; } - float minAngle = DIAGONAL - HORIZONTAL_ANGLE_RANGE; - float maxAngle = DIAGONAL + HORIZONTAL_ANGLE_RANGE; + float minAngle = DIAGONAL - mHorizontalAngleRange; + float maxAngle = DIAGONAL + mHorizontalAngleRange; if (isVertical()) { - minAngle = DIAGONAL - VERTICAL_ANGLE_RANGE; - maxAngle = DIAGONAL + VERTICAL_ANGLE_RANGE; + minAngle = DIAGONAL - mVerticalAngleRange; + maxAngle = DIAGONAL + mVerticalAngleRange; } return angleBetween(angle, minAngle, maxAngle) diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/DistanceClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/DistanceClassifier.java index 005ee12c4f61..a6a617dc51de 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/DistanceClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/DistanceClassifier.java @@ -16,6 +16,14 @@ package com.android.systemui.classifier.brightline; +import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DISTANCE_HORIZONTAL_FLING_THRESHOLD_IN; +import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DISTANCE_HORIZONTAL_SWIPE_THRESHOLD_IN; +import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DISTANCE_SCREEN_FRACTION_MAX_DISTANCE; +import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DISTANCE_VELOCITY_TO_DISTANCE; +import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DISTANCE_VERTICAL_FLING_THRESHOLD_IN; +import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DISTANCE_VERTICAL_SWIPE_THRESHOLD_IN; + +import android.provider.DeviceConfig; import android.view.MotionEvent; import android.view.VelocityTracker; @@ -31,12 +39,13 @@ class DistanceClassifier extends FalsingClassifier { private static final float HORIZONTAL_SWIPE_THRESHOLD_DISTANCE_IN = 3; private static final float VERTICAL_SWIPE_THRESHOLD_DISTANCE_IN = 3; private static final float VELOCITY_TO_DISTANCE = 80f; - private static final float SCREEN_FRACTION_MIN_DISTANCE = 0.8f; + private static final float SCREEN_FRACTION_MAX_DISTANCE = 0.8f; private final float mVerticalFlingThresholdPx; private final float mHorizontalFlingThresholdPx; private final float mVerticalSwipeThresholdPx; private final float mHorizontalSwipeThresholdPx; + private final float mVelocityToDistanceMultiplier; private boolean mDistanceDirty; private DistanceVectors mCachedDistance; @@ -44,18 +53,48 @@ class DistanceClassifier extends FalsingClassifier { DistanceClassifier(FalsingDataProvider dataProvider) { super(dataProvider); + mVelocityToDistanceMultiplier = DeviceConfig.getFloat( + DeviceConfig.NAMESPACE_SYSTEMUI, + BRIGHTLINE_FALSING_DISTANCE_VELOCITY_TO_DISTANCE, + VELOCITY_TO_DISTANCE); + + float horizontalFlingThresholdIn = DeviceConfig.getFloat( + DeviceConfig.NAMESPACE_SYSTEMUI, + BRIGHTLINE_FALSING_DISTANCE_HORIZONTAL_FLING_THRESHOLD_IN, + HORIZONTAL_FLING_THRESHOLD_DISTANCE_IN); + + float verticalFlingThresholdIn = DeviceConfig.getFloat( + DeviceConfig.NAMESPACE_SYSTEMUI, + BRIGHTLINE_FALSING_DISTANCE_VERTICAL_FLING_THRESHOLD_IN, + VERTICAL_FLING_THRESHOLD_DISTANCE_IN); + + float horizontalSwipeThresholdIn = DeviceConfig.getFloat( + DeviceConfig.NAMESPACE_SYSTEMUI, + BRIGHTLINE_FALSING_DISTANCE_HORIZONTAL_SWIPE_THRESHOLD_IN, + HORIZONTAL_SWIPE_THRESHOLD_DISTANCE_IN); + + float verticalSwipeThresholdIn = DeviceConfig.getFloat( + DeviceConfig.NAMESPACE_SYSTEMUI, + BRIGHTLINE_FALSING_DISTANCE_VERTICAL_SWIPE_THRESHOLD_IN, + VERTICAL_SWIPE_THRESHOLD_DISTANCE_IN); + + float screenFractionMaxDistance = DeviceConfig.getFloat( + DeviceConfig.NAMESPACE_SYSTEMUI, + BRIGHTLINE_FALSING_DISTANCE_SCREEN_FRACTION_MAX_DISTANCE, + SCREEN_FRACTION_MAX_DISTANCE); + mHorizontalFlingThresholdPx = Math - .min(getWidthPixels() * SCREEN_FRACTION_MIN_DISTANCE, - HORIZONTAL_FLING_THRESHOLD_DISTANCE_IN * getXdpi()); + .min(getWidthPixels() * screenFractionMaxDistance, + horizontalFlingThresholdIn * getXdpi()); mVerticalFlingThresholdPx = Math - .min(getHeightPixels() * SCREEN_FRACTION_MIN_DISTANCE, - VERTICAL_FLING_THRESHOLD_DISTANCE_IN * getYdpi()); + .min(getHeightPixels() * screenFractionMaxDistance, + verticalFlingThresholdIn * getYdpi()); mHorizontalSwipeThresholdPx = Math - .min(getWidthPixels() * SCREEN_FRACTION_MIN_DISTANCE, - HORIZONTAL_SWIPE_THRESHOLD_DISTANCE_IN * getXdpi()); + .min(getWidthPixels() * screenFractionMaxDistance, + horizontalSwipeThresholdIn * getXdpi()); mVerticalSwipeThresholdPx = Math - .min(getHeightPixels() * SCREEN_FRACTION_MIN_DISTANCE, - VERTICAL_SWIPE_THRESHOLD_DISTANCE_IN * getYdpi()); + .min(getHeightPixels() * screenFractionMaxDistance, + verticalSwipeThresholdIn * getYdpi()); mDistanceDirty = true; } @@ -139,18 +178,18 @@ class DistanceClassifier extends FalsingClassifier { } boolean getPassedFlingThreshold() { - float dX = this.mDx + this.mVx * VELOCITY_TO_DISTANCE; - float dY = this.mDy + this.mVy * VELOCITY_TO_DISTANCE; + float dX = this.mDx + this.mVx * mVelocityToDistanceMultiplier; + float dY = this.mDy + this.mVy * mVelocityToDistanceMultiplier; if (isHorizontal()) { logDebug("Horizontal swipe and fling distance: " + this.mDx + ", " - + this.mVx * VELOCITY_TO_DISTANCE); + + this.mVx * mVelocityToDistanceMultiplier); logDebug("Threshold: " + mHorizontalFlingThresholdPx); return Math.abs(dX) >= mHorizontalFlingThresholdPx; } logDebug("Vertical swipe and fling distance: " + this.mDy + ", " - + this.mVy * VELOCITY_TO_DISTANCE); + + this.mVy * mVelocityToDistanceMultiplier); logDebug("Threshold: " + mVerticalFlingThresholdPx); return Math.abs(dY) >= mVerticalFlingThresholdPx; } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java index 94a8ac85b724..2644bf9f26ce 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java @@ -16,10 +16,12 @@ package com.android.systemui.classifier.brightline; +import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_PROXIMITY_PERCENT_COVERED_THRESHOLD; import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS; import android.hardware.Sensor; import android.hardware.SensorEvent; +import android.provider.DeviceConfig; import android.view.MotionEvent; @@ -31,8 +33,9 @@ import android.view.MotionEvent; */ class ProximityClassifier extends FalsingClassifier { - private static final double PERCENT_COVERED_THRESHOLD = 0.1; + private static final float PERCENT_COVERED_THRESHOLD = 0.1f; private final DistanceClassifier mDistanceClassifier; + private final float mPercentCoveredThreshold; private boolean mNear; private long mGestureStartTimeNs; @@ -44,6 +47,11 @@ class ProximityClassifier extends FalsingClassifier { FalsingDataProvider dataProvider) { super(dataProvider); this.mDistanceClassifier = distanceClassifier; + + mPercentCoveredThreshold = DeviceConfig.getFloat( + DeviceConfig.NAMESPACE_SYSTEMUI, + BRIGHTLINE_FALSING_PROXIMITY_PERCENT_COVERED_THRESHOLD, + PERCENT_COVERED_THRESHOLD); } @Override @@ -107,7 +115,7 @@ class ProximityClassifier extends FalsingClassifier { logInfo("Percent of gesture in proximity: " + mPercentNear); - if (mPercentNear > PERCENT_COVERED_THRESHOLD) { + if (mPercentNear > mPercentCoveredThreshold) { return !mDistanceClassifier.isLongSwipe(); } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/ZigZagClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/ZigZagClassifier.java index a62574f26399..c58b7db451b0 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/ZigZagClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/ZigZagClassifier.java @@ -16,7 +16,13 @@ package com.android.systemui.classifier.brightline; +import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_ZIGZAG_X_PRIMARY_DEVIANCE; +import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_ZIGZAG_X_SECONDARY_DEVIANCE; +import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_ZIGZAG_Y_PRIMARY_DEVIANCE; +import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_ZIGZAG_Y_SECONDARY_DEVIANCE; + import android.graphics.Point; +import android.provider.DeviceConfig; import android.view.MotionEvent; import java.util.ArrayList; @@ -37,8 +43,34 @@ class ZigZagClassifier extends FalsingClassifier { private static final float MAX_X_SECONDARY_DEVIANCE = .3f; private static final float MAX_Y_SECONDARY_DEVIANCE = .3f; + private final float mMaxXPrimaryDeviance; + private final float mMaxYPrimaryDeviance; + private final float mMaxXSecondaryDeviance; + private final float mMaxYSecondaryDeviance; + ZigZagClassifier(FalsingDataProvider dataProvider) { super(dataProvider); + + mMaxXPrimaryDeviance = DeviceConfig.getFloat( + DeviceConfig.NAMESPACE_SYSTEMUI, + BRIGHTLINE_FALSING_ZIGZAG_X_PRIMARY_DEVIANCE, + MAX_X_PRIMARY_DEVIANCE); + + mMaxYPrimaryDeviance = DeviceConfig.getFloat( + DeviceConfig.NAMESPACE_SYSTEMUI, + BRIGHTLINE_FALSING_ZIGZAG_Y_PRIMARY_DEVIANCE, + MAX_Y_PRIMARY_DEVIANCE); + + mMaxXSecondaryDeviance = DeviceConfig.getFloat( + DeviceConfig.NAMESPACE_SYSTEMUI, + BRIGHTLINE_FALSING_ZIGZAG_X_SECONDARY_DEVIANCE, + MAX_X_SECONDARY_DEVIANCE); + + mMaxYSecondaryDeviance = DeviceConfig.getFloat( + DeviceConfig.NAMESPACE_SYSTEMUI, + BRIGHTLINE_FALSING_ZIGZAG_Y_SECONDARY_DEVIANCE, + MAX_Y_SECONDARY_DEVIANCE); + } @Override @@ -98,11 +130,11 @@ class ZigZagClassifier extends FalsingClassifier { float maxXDeviance; float maxYDeviance; if (actualDx > actualDy) { - maxXDeviance = MAX_X_PRIMARY_DEVIANCE * totalDistanceIn * getXdpi(); - maxYDeviance = MAX_Y_SECONDARY_DEVIANCE * totalDistanceIn * getYdpi(); + maxXDeviance = mMaxXPrimaryDeviance * totalDistanceIn * getXdpi(); + maxYDeviance = mMaxYSecondaryDeviance * totalDistanceIn * getYdpi(); } else { - maxXDeviance = MAX_X_SECONDARY_DEVIANCE * totalDistanceIn * getXdpi(); - maxYDeviance = MAX_Y_PRIMARY_DEVIANCE * totalDistanceIn * getYdpi(); + maxXDeviance = mMaxXSecondaryDeviance * totalDistanceIn * getXdpi(); + maxYDeviance = mMaxYPrimaryDeviance * totalDistanceIn * getYdpi(); } logDebug("Straightness Deviance: (" + devianceX + "," + devianceY + ") vs " diff --git a/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java b/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java index 0e93f42a3554..d3e8b3d3236e 100644 --- a/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java +++ b/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java @@ -77,7 +77,7 @@ public class SysuiColorExtractor extends ColorExtractor implements Dumpable, protected void extractWallpaperColors() { super.extractWallpaperColors(); // mTonal is final but this method will be invoked by the base class during its ctor. - if (mTonal == null) { + if (mTonal == null || mNeutralColorsLock == null) { return; } mTonal.applyFallback(mLockColors == null ? mSystemColors : mLockColors, mNeutralColorsLock); diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java index 537c09ed3199..1bc7e635f9ed 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java @@ -30,6 +30,7 @@ import com.android.systemui.R; import com.android.systemui.SystemUIApplication; import com.android.systemui.dock.DockManager; import com.android.systemui.plugins.FalsingManager; +import com.android.systemui.statusbar.phone.BiometricUnlockController; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.util.AsyncSensorManager; import com.android.systemui.util.wakelock.DelayedWakeLock; @@ -70,7 +71,7 @@ public class DozeFactory { new DozeScreenState(wrappedService, handler, params, wakeLock), createDozeScreenBrightness(context, wrappedService, sensorManager, host, params, handler), - new DozeWallpaperState(context), + new DozeWallpaperState(context, getBiometricUnlockController(dozeService)), new DozeDockHandler(context, machine, host, config, handler, dockManager), new DozeAuthRemover(dozeService) }); @@ -108,4 +109,10 @@ public class DozeFactory { final SystemUIApplication app = (SystemUIApplication) appCandidate; return app.getComponent(DozeHost.class); } + + public static BiometricUnlockController getBiometricUnlockController(DozeService service) { + Application appCandidate = service.getApplication(); + final SystemUIApplication app = (SystemUIApplication) appCandidate; + return app.getComponent(BiometricUnlockController.class); + } } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java index 3c9d4a9704a0..ae6dac59b2f5 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java @@ -50,9 +50,23 @@ public interface DozeHost { */ void onSlpiTap(float x, float y); + /** + * Artificially dim down the the display by changing scrim opacities. + * @param scrimOpacity opacity from 0 to 1. + */ default void setAodDimmingScrim(float scrimOpacity) {} + + /** + * Sets the actual display brightness. + * @param value from 0 to 255. + */ void setDozeScreenBrightness(int value); + /** + * Makes scrims black and changes animation durations. + */ + default void prepareForGentleWakeUp() {} + 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 5be097c457dc..38ee2fed136d 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java @@ -82,7 +82,10 @@ public class DozeScreenState implements DozeMachine.Part { boolean messagePending = mHandler.hasCallbacks(mApplyPendingScreenState); boolean pulseEnding = oldState == DozeMachine.State.DOZE_PULSE_DONE && newState == DozeMachine.State.DOZE_AOD; - if (messagePending || oldState == DozeMachine.State.INITIALIZED || pulseEnding) { + boolean turningOn = (oldState == DozeMachine.State.DOZE_AOD_PAUSED + || oldState == DozeMachine.State.DOZE) && newState == DozeMachine.State.DOZE_AOD; + 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 // that the screen turns on again before the navigation bar is hidden. To work around @@ -91,7 +94,7 @@ public class DozeScreenState implements DozeMachine.Part { // Delay screen state transitions even longer while animations are running. boolean shouldDelayTransition = newState == DozeMachine.State.DOZE_AOD - && mParameters.shouldControlScreenOff(); + && mParameters.shouldControlScreenOff() && !turningOn; if (shouldDelayTransition) { mWakeLock.setAcquired(true); diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java index cf04b7f192e4..f6a921d6d127 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java @@ -43,6 +43,7 @@ import androidx.annotation.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; +import com.android.systemui.R; import com.android.systemui.plugins.SensorManagerPlugin; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.util.AlarmTimeout; @@ -198,7 +199,10 @@ public class DozeSensors { updateListening(); } - private void updateListening() { + /** + * Registers/unregisters sensors based on internal state. + */ + public void updateListening() { boolean anyListening = false; for (TriggerSensor s : mSensors) { // We don't want to be listening while we're PAUSED (prox sensor is covered) @@ -231,7 +235,7 @@ public class DozeSensors { public void onUserSwitched() { for (TriggerSensor s : mSensors) { - s.updateListener(); + s.updateListening(); } } @@ -246,7 +250,7 @@ public class DozeSensors { return; } for (TriggerSensor s : mSensors) { - s.updateListener(); + s.updateListening(); } } }; @@ -287,12 +291,21 @@ public class DozeSensors { long mLastNear; final AlarmTimeout mCooldownTimer; final AlwaysOnDisplayPolicy mPolicy; - + final Sensor mSensor; public ProxSensor(AlwaysOnDisplayPolicy policy) { mPolicy = policy; mCooldownTimer = new AlarmTimeout(mAlarmManager, this::updateRegistered, "prox_cooldown", mHandler); + + // The default prox sensor can be noisy, so let's use a prox gated brightness sensor + // if available. + Sensor sensor = DozeSensors.findSensorWithType(mSensorManager, + mContext.getString(R.string.doze_brightness_sensor_type)); + if (sensor == null) { + sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY); + } + mSensor = sensor; } void setRequested(boolean requested) { @@ -356,8 +369,9 @@ public class DozeSensors { @Override public String toString() { - return String.format("{registered=%s, requested=%s, coolingDown=%s, currentlyFar=%s}", - mRegistered, mRequested, mCooldownTimer.isScheduled(), mCurrentlyFar); + return String.format("{registered=%s, requested=%s, coolingDown=%s, currentlyFar=%s," + + " sensor=%s}", mRegistered, mRequested, mCooldownTimer.isScheduled(), + mCurrentlyFar, mSensor); } } @@ -409,22 +423,22 @@ public class DozeSensors { public void setListening(boolean listen) { if (mRequested == listen) return; mRequested = listen; - updateListener(); + updateListening(); } public void setDisabled(boolean disabled) { if (mDisabled == disabled) return; mDisabled = disabled; - updateListener(); + updateListening(); } public void ignoreSetting(boolean ignored) { if (mIgnoresSetting == ignored) return; mIgnoresSetting = ignored; - updateListener(); + updateListening(); } - public void updateListener() { + public void updateListening() { if (!mConfigured || mSensor == null) return; if (mRequested && !mDisabled && (enabledBySetting() || mIgnoresSetting) && !mRegistered) { @@ -480,7 +494,7 @@ public class DozeSensors { mCallback.onSensorPulse(mPulseReason, mSensorPerformsProxCheck, screenX, screenY, event.values); if (!mRegistered) { - updateListener(); // reregister, this sensor only fires once + updateListening(); // reregister, this sensor only fires once } })); } @@ -541,7 +555,7 @@ public class DozeSensors { } @Override - public void updateListener() { + public void updateListening() { if (!mConfigured) return; AsyncSensorManager asyncSensorManager = (AsyncSensorManager) mSensorManager; if (mRequested && !mDisabled && (enabledBySetting() || mIgnoresSetting) diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java index 97b08d5a12a6..8ef01e8d608e 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java @@ -201,7 +201,7 @@ public class DozeTriggers implements DozeMachine.Part { // Let's prepare the display to wake-up by drawing black. // This will cover the hardware wake-up sequence, where the display // becomes black for a few frames. - mDozeHost.setAodDimmingScrim(255f); + mDozeHost.setAodDimmingScrim(1f); } mMachine.wakeUp(); } @@ -314,6 +314,9 @@ public class DozeTriggers implements DozeMachine.Part { break; case DOZE_PULSE_DONE: mDozeSensors.requestTemporaryDisable(); + // A pulse will temporarily disable sensors that require a touch screen. + // Let's make sure that they are re-enabled when the pulse is over. + mDozeSensors.updateListening(); break; case FINISH: mBroadcastReceiver.unregister(mContext); diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java index 51e96d2eecad..1f33af8c3f55 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java @@ -16,6 +16,7 @@ package com.android.systemui.doze; +import static com.android.systemui.doze.DozeMachine.State.DOZE; import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSED; import android.app.AlarmManager; @@ -116,7 +117,7 @@ public class DozeUi implements DozeMachine.Part { public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) { switch (newState) { case DOZE_AOD: - if (oldState == DOZE_AOD_PAUSED) { + if (oldState == DOZE_AOD_PAUSED || oldState == DOZE) { // Whenever turning on the display, it's necessary to push a new frame. // The display buffers will be empty and need to be filled. mHost.dozeTimeTick(); @@ -130,6 +131,7 @@ public class DozeUi implements DozeMachine.Part { break; case DOZE: case DOZE_AOD_PAUSED: + mHost.prepareForGentleWakeUp(); unscheduleTimeTick(); break; case DOZE_REQUEST_PULSE: diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java index 1b3cd881b949..35c8b741381c 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeWallpaperState.java @@ -24,6 +24,7 @@ import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.statusbar.notification.stack.StackStateAnimator; +import com.android.systemui.statusbar.phone.BiometricUnlockController; import com.android.systemui.statusbar.phone.DozeParameters; import java.io.PrintWriter; @@ -38,18 +39,22 @@ public class DozeWallpaperState implements DozeMachine.Part { private final IWallpaperManager mWallpaperManagerService; private final DozeParameters mDozeParameters; + private final BiometricUnlockController mBiometricUnlockController; private boolean mIsAmbientMode; - public DozeWallpaperState(Context context) { + public DozeWallpaperState(Context context, + BiometricUnlockController biometricUnlockController) { this(IWallpaperManager.Stub.asInterface( ServiceManager.getService(Context.WALLPAPER_SERVICE)), + biometricUnlockController, DozeParameters.getInstance(context)); } @VisibleForTesting DozeWallpaperState(IWallpaperManager wallpaperManagerService, - DozeParameters parameters) { + BiometricUnlockController biometricUnlockController, DozeParameters parameters) { mWallpaperManagerService = wallpaperManagerService; + mBiometricUnlockController = biometricUnlockController; mDozeParameters = parameters; } @@ -76,7 +81,9 @@ public class DozeWallpaperState implements DozeMachine.Part { } else { boolean wakingUpFromPulse = oldState == DozeMachine.State.DOZE_PULSING && newState == DozeMachine.State.FINISH; - animated = !mDozeParameters.getDisplayNeedsBlanking() || wakingUpFromPulse; + boolean fastDisplay = !mDozeParameters.getDisplayNeedsBlanking(); + animated = (fastDisplay && !mBiometricUnlockController.unlockedByWakeAndUnlock()) + || wakingUpFromPulse; } if (isAmbientMode != mIsAmbientMode) { diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java index 46d53e410725..14459d6b8111 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java @@ -414,6 +414,7 @@ public class PipMenuActivityController { if (pinnedStackInfo != null && pinnedStackInfo.taskIds != null && pinnedStackInfo.taskIds.length > 0) { Intent intent = new Intent(mContext, PipMenuActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.putExtra(EXTRA_CONTROLLER_MESSENGER, mMessenger); intent.putExtra(EXTRA_ACTIONS, resolveMenuActions()); if (stackBounds != null) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java index cc1170f7409b..b444fa50a253 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java @@ -83,6 +83,11 @@ public class SectionHeaderView extends ActivatableNotificationView { bindContents(); } + @Override + public boolean isTransparent() { + return true; + } + /** Must be called whenever the UI mode changes (i.e. when we enter night mode). */ void onUiModeChanged() { updateBackgroundColors(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java index 01e2b28600db..7655056ea60b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java @@ -159,15 +159,13 @@ public class StackScrollAlgorithm { float drawStart = !ambientState.isOnKeyguard() ? ambientState.getTopPadding() + ambientState.getStackTranslation() + ambientState.getExpandAnimationTopChange() : 0; - float previousNotificationEnd = 0; - float previousNotificationStart = 0; + float clipStart = 0; int childCount = algorithmState.visibleChildren.size(); for (int i = 0; i < childCount; i++) { ExpandableView child = algorithmState.visibleChildren.get(i); ExpandableViewState state = child.getViewState(); if (!child.mustStayOnScreen() || state.headsUpIsVisible) { - previousNotificationEnd = Math.max(drawStart, previousNotificationEnd); - previousNotificationStart = Math.max(drawStart, previousNotificationStart); + clipStart = Math.max(drawStart, clipStart); } float newYTranslation = state.yTranslation; float newHeight = state.height; @@ -175,10 +173,10 @@ public class StackScrollAlgorithm { boolean isHeadsUp = (child instanceof ExpandableNotificationRow) && ((ExpandableNotificationRow) child).isPinned(); if (mClipNotificationScrollToTop - && !state.inShelf && newYTranslation < previousNotificationEnd - && (!isHeadsUp || ambientState.isShadeExpanded())) { + && (!state.inShelf || isHeadsUp) + && newYTranslation < clipStart) { // The previous view is overlapping on top, clip! - float overlapAmount = previousNotificationEnd - newYTranslation; + float overlapAmount = clipStart - newYTranslation; state.clipTopAmount = (int) overlapAmount; } else { state.clipTopAmount = 0; @@ -187,8 +185,7 @@ public class StackScrollAlgorithm { if (!child.isTransparent()) { // Only update the previous values if we are not transparent, // otherwise we would clip to a transparent view. - previousNotificationEnd = newNotificationEnd; - previousNotificationStart = newYTranslation; + clipStart = Math.max(clipStart, isHeadsUp ? newYTranslation : newNotificationEnd); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java index bc7174d33b13..94cd2cdaa9d3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java @@ -141,6 +141,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback { private BiometricSourceType mPendingAuthenticatedBioSourceType = null; private boolean mPendingShowBouncer; private boolean mHasScreenTurnedOnSinceAuthenticating; + private boolean mFadedAwayAfterWakeAndUnlock; private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class); @@ -260,6 +261,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback { boolean unlockAllowed = mKeyguardBypassController.onBiometricAuthenticated( biometricSourceType); if (unlockAllowed) { + mKeyguardViewMediator.userActivity(); startWakeAndUnlock(biometricSourceType); } else { Log.d(TAG, "onBiometricAuthenticated aborted by bypass controller"); @@ -367,6 +369,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback { @Override public void onStartedGoingToSleep(int why) { resetMode(); + mFadedAwayAfterWakeAndUnlock = false; mPendingAuthenticatedUserId = -1; mPendingAuthenticatedBioSourceType = null; } @@ -466,8 +469,11 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback { } if (mStatusBarKeyguardViewManager.isShowing()) { if (mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing() && unlockingAllowed) { - return bypass && !mKeyguardBypassController.canPlaySubtleWindowAnimations() - ? MODE_UNLOCK_COLLAPSING : MODE_UNLOCK_FADING; + if (bypass && mKeyguardBypassController.canPlaySubtleWindowAnimations()) { + return MODE_UNLOCK_FADING; + } else { + return MODE_DISMISS_BOUNCER; + } } else if (unlockingAllowed) { return bypass ? MODE_UNLOCK_FADING : MODE_NONE; } else { @@ -509,6 +515,9 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback { } public void finishKeyguardFadingAway() { + if (isWakeAndUnlock()) { + mFadedAwayAfterWakeAndUnlock = true; + } resetMode(); } @@ -559,6 +568,14 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback { } /** + * Successful authentication with fingerprint, face, or iris that wakes up the device. + * This will return {@code true} even after the keyguard fades away. + */ + public boolean unlockedByWakeAndUnlock() { + return isWakeAndUnlock() || mFadedAwayAfterWakeAndUnlock; + } + + /** * Successful authentication with fingerprint, face, or iris when the screen was either * on or off. */ @@ -567,6 +584,13 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback { } /** + * Successful authentication with fingerprint, face, or iris when the lockscreen fades away + */ + public boolean isUnlockFading() { + return mMode == MODE_UNLOCK_FADING; + } + + /** * Translates biometric source type for logging purpose. */ private int toSubtype(BiometricSourceType biometricSourceType) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java index cbaf85c511dd..a7e7f085ffd7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java @@ -72,8 +72,6 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, private NotificationGroupManager mGroupManager; private VisualStabilityManager mVisualStabilityManager; private StatusBarTouchableRegionManager mStatusBarTouchableRegionManager; - @VisibleForTesting - int mAutoDismissNotificationDecayDozing; private boolean mReleaseOnExpandFinish; private int mStatusBarHeight; @@ -120,8 +118,6 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, KeyguardBypassController bypassController) { super(context); Resources resources = mContext.getResources(); - mAutoDismissNotificationDecayDozing = resources.getInteger( - R.integer.heads_up_notification_decay_dozing); mExtensionTime = resources.getInteger(R.integer.ambient_notification_extension_time); mAutoHeadsUpNotificationDecay = resources.getInteger( R.integer.auto_heads_up_notification_decay); @@ -367,12 +363,15 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, } public Region calculateTouchableRegion() { - if (!hasPinnedHeadsUp()) { + NotificationEntry topEntry = getTopEntry(); + // This call could be made in an inconsistent state while the pinnedMode hasn't been + // updated yet, but callbacks leading out of the headsUp manager, querying it. Let's + // therefore also check if the topEntry is null. + if (!hasPinnedHeadsUp() || topEntry == null) { mTouchableRegion.set(0, 0, mStatusBarWindowView.getWidth(), mStatusBarHeight); updateRegionForNotch(mTouchableRegion); } else { - NotificationEntry topEntry = getTopEntry(); if (topEntry.isChildInGroup()) { final NotificationEntry groupSummary = mGroupManager.getGroupSummary(topEntry.notification); @@ -612,9 +611,7 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, } private int getDecayDuration() { - if (mStatusBarStateController.isDozing()) { - return mAutoDismissNotificationDecayDozing; - } else if (isAutoHeadsUp()) { + if (isAutoHeadsUp()) { return getRecommendedHeadsUpTimeoutMs(mAutoHeadsUpNotificationDecay); } else { return getRecommendedHeadsUpTimeoutMs(mAutoDismissNotificationDecay); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt index c88b22bbbced..0aec2b12fa92 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt @@ -167,4 +167,8 @@ class KeyguardBypassController { pw.print(" qSExpanded: "); pw.println(qSExpanded) pw.print(" bouncerShowing: "); pw.println(bouncerShowing) } + + companion object { + const val BYPASS_PANEL_FADE_DURATION = 67 + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java index 49afae7415ae..1360a084207b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java @@ -102,6 +102,7 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange private boolean mWakeAndUnlockRunning; private boolean mKeyguardShowing; private boolean mShowingLaunchAffordance; + private boolean mKeyguardJustShown; private boolean mUpdatePending; private final KeyguardMonitor.Callback mKeyguardMonitorCallback = @@ -115,6 +116,9 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange mBlockUpdates = false; force = true; } + if (!wasShowing && mKeyguardShowing) { + mKeyguardJustShown = true; + } update(force); } @@ -273,8 +277,10 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange int state = getState(); int lastState = mLastState; + boolean keyguardJustShown = mKeyguardJustShown; mIsFaceUnlockState = state == STATE_SCANNING_FACE; mLastState = state; + mKeyguardJustShown = false; boolean shouldUpdate = lastState != state || mForceUpdate; if (mBlockUpdates && canBlockUpdates()) { @@ -283,7 +289,7 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange if (shouldUpdate) { mForceUpdate = false; @LockAnimIndex final int lockAnimIndex = getAnimationIndexForTransition(lastState, - state, mPulsing, mDozing); + state, mPulsing, mDozing, keyguardJustShown); boolean isAnim = lockAnimIndex != -1; int iconRes = isAnim ? getThemedAnimationResId(lockAnimIndex) : getIconForState(state); @@ -412,7 +418,7 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange } private static int getAnimationIndexForTransition(int oldState, int newState, boolean pulsing, - boolean dozing) { + boolean dozing, boolean keyguardJustShown) { // Never animate when screen is off if (dozing && !pulsing) { @@ -423,7 +429,7 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange return ERROR; } else if (oldState != STATE_LOCK_OPEN && newState == STATE_LOCK_OPEN) { return UNLOCK; - } else if (oldState == STATE_LOCK_OPEN && newState == STATE_LOCKED) { + } else if (oldState == STATE_LOCK_OPEN && newState == STATE_LOCKED && !keyguardJustShown) { return LOCK; } else if (newState == STATE_SCANNING_FACE) { return SCANNING; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java index 0bbfbefb7883..195870bde25a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java @@ -464,7 +464,7 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State if (!sbn.isGroup() || sbn.getNotification().isGroupSummary()) { return false; } - if (!mHeadsUpManager.isAlerting(entry.key)) { + if (mHeadsUpManager != null && !mHeadsUpManager.isAlerting(entry.key)) { return false; } return (sbn.getNotification().fullScreenIntent != null diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java index d2159ca15b24..21de8a59836e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java @@ -4,7 +4,6 @@ import android.content.Context; import android.content.res.Resources; import android.graphics.Color; import android.graphics.Rect; -import android.provider.Settings; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -32,7 +31,6 @@ import com.android.systemui.statusbar.notification.NotificationUtils; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; -import com.android.systemui.tuner.TunerService; import java.util.ArrayList; import java.util.Objects; @@ -58,7 +56,6 @@ public class NotificationIconAreaController implements DarkReceiver, private final KeyguardBypassController mBypassController; private final DozeParameters mDozeParameters; - private boolean mShowSilentOnLockscreen = true; private int mIconSize; private int mIconHPadding; private int mIconTint = Color.WHITE; @@ -101,11 +98,6 @@ public class NotificationIconAreaController implements DarkReceiver, initializeNotificationAreaViews(context); reloadAodColor(); - - TunerService tunerService = Dependency.get(TunerService.class); - tunerService.addTunable((key, newValue) -> { - mShowSilentOnLockscreen = "1".equals(newValue); - }, Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS); } protected View inflateIconArea(LayoutInflater inflater) { @@ -238,7 +230,7 @@ public class NotificationIconAreaController implements DarkReceiver, } protected boolean shouldShowNotificationIcon(NotificationEntry entry, - boolean showAmbient, boolean showLowPriority, boolean hideDismissed, + boolean showAmbient, boolean hideDismissed, boolean hideRepliedMessages, boolean hideCurrentMedia, boolean hideCenteredIcon, boolean hidePulsing, boolean onlyShowCenteredIcon) { @@ -257,9 +249,6 @@ public class NotificationIconAreaController implements DarkReceiver, if (hideCurrentMedia && entry.key.equals(mMediaManager.getMediaNotificationKey())) { return false; } - if (!showLowPriority && !entry.isHighPriority()) { - return false; - } if (!entry.isTopLevelChild()) { return false; } @@ -297,7 +286,6 @@ public class NotificationIconAreaController implements DarkReceiver, private void updateShelfIcons() { updateIconsForLayout(entry -> entry.expandedIcon, mShelfIcons, true /* showAmbient */, - true /* showLowPriority */, false /* hideDismissed */, false /* hideRepliedMessages */, false /* hideCurrentMedia */, @@ -309,7 +297,6 @@ public class NotificationIconAreaController implements DarkReceiver, public void updateStatusBarIcons() { updateIconsForLayout(entry -> entry.icon, mNotificationIcons, false /* showAmbient */, - true /* showLowPriority */, true /* hideDismissed */, true /* hideRepliedMessages */, false /* hideCurrentMedia */, @@ -321,7 +308,6 @@ public class NotificationIconAreaController implements DarkReceiver, private void updateCenterIcon() { updateIconsForLayout(entry -> entry.centeredIcon, mCenteredIcon, false /* showAmbient */, - true /* showLowPriority */, false /* hideDismissed */, false /* hideRepliedMessages */, false /* hideCurrentMedia */, @@ -333,7 +319,6 @@ public class NotificationIconAreaController implements DarkReceiver, public void updateAodNotificationIcons() { updateIconsForLayout(entry -> entry.aodIcon, mAodIcons, false /* showAmbient */, - mShowSilentOnLockscreen /* showLowPriority */, true /* hideDismissed */, true /* hideRepliedMessages */, true /* hideCurrentMedia */, @@ -353,7 +338,7 @@ public class NotificationIconAreaController implements DarkReceiver, * @param hidePulsing should pulsing notifications be hidden */ private void updateIconsForLayout(Function<NotificationEntry, StatusBarIconView> function, - NotificationIconContainer hostLayout, boolean showAmbient, boolean showLowPriority, + NotificationIconContainer hostLayout, boolean showAmbient, boolean hideDismissed, boolean hideRepliedMessages, boolean hideCurrentMedia, boolean hideCenteredIcon, boolean hidePulsing, boolean onlyShowCenteredIcon) { ArrayList<StatusBarIconView> toShow = new ArrayList<>( @@ -364,7 +349,7 @@ public class NotificationIconAreaController implements DarkReceiver, View view = mNotificationScrollLayout.getChildAt(i); if (view instanceof ExpandableNotificationRow) { NotificationEntry ent = ((ExpandableNotificationRow) view).getEntry(); - if (shouldShowNotificationIcon(ent, showAmbient, showLowPriority, hideDismissed, + if (shouldShowNotificationIcon(ent, showAmbient, hideDismissed, hideRepliedMessages, hideCurrentMedia, hideCenteredIcon, hidePulsing, onlyShowCenteredIcon)) { StatusBarIconView iconView = function.apply(ent); 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 3f38c049a977..1aec5e4d0b1d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -487,6 +487,20 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo } /** + * 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()) { + mCurrentInFrontAlpha = 1f; + mAnimateChange = false; + updateScrims(); + mAnimateChange = true; + mAnimationDuration = ANIMATION_DURATION_LONG; + } + } + + /** * If the lock screen sensor is active. */ public void setWakeLockScreenSensorActive(boolean active) { @@ -932,6 +946,12 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo } } + public void setUnlockIsFading(boolean unlockFading) { + for (ScrimState state : ScrimState.values()) { + state.setUnlockIsFading(unlockFading); + } + } + public void setLaunchingAffordanceWithPreview(boolean launchingAffordanceWithPreview) { for (ScrimState state : ScrimState.values()) { state.setLaunchingAffordanceWithPreview(launchingAffordanceWithPreview); 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 d152ecd8b930..763e0d70469b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java @@ -147,7 +147,9 @@ public enum ScrimState { public void prepare(ScrimState previousState) { mCurrentBehindAlpha = 0; mCurrentInFrontAlpha = 0; - mAnimationDuration = StatusBar.FADE_KEYGUARD_DURATION; + mAnimationDuration = mUnlockIsFading + ? KeyguardBypassController.BYPASS_PANEL_FADE_DURATION + : StatusBar.FADE_KEYGUARD_DURATION; mAnimateChange = !mLaunchingAffordanceWithPreview; if (previousState == ScrimState.AOD) { @@ -198,6 +200,7 @@ public enum ScrimState { boolean mHasBackdrop; boolean mLaunchingAffordanceWithPreview; boolean mWakeLockScreenSensorActive; + boolean mUnlockIsFading; ScrimState(int index) { mIndex = index; @@ -285,4 +288,8 @@ public enum ScrimState { public void setWakeLockScreenSensorActive(boolean active) { mWakeLockScreenSensorActive = active; } + + public void setUnlockIsFading(boolean unlockIsFading) { + mUnlockIsFading = unlockIsFading; + } }
\ No newline at end of file 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 40ebe583b021..f8e6aa3b844f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -1229,6 +1229,7 @@ public class StatusBar extends SystemUI implements DemoMode, mDozeScrimController, keyguardViewMediator, mScrimController, this, UnlockMethodCache.getInstance(mContext), new Handler(), mKeyguardUpdateMonitor, mKeyguardBypassController); + putComponent(BiometricUnlockController.class, mBiometricUnlockController); mStatusBarKeyguardViewManager = keyguardViewMediator.registerStatusBar(this, getBouncerContainer(), mNotificationPanel, mBiometricUnlockController, mStatusBarWindow.findViewById(R.id.lock_icon_container), mStackScroller, @@ -3839,6 +3840,7 @@ public class StatusBar extends SystemUI implements DemoMode, public void notifyBiometricAuthModeChanged() { updateDozing(); + mScrimController.setUnlockIsFading(mBiometricUnlockController.isUnlockFading()); updateScrimController(); mStatusBarWindow.onBiometricAuthModeChanged(mBiometricUnlockController.isWakeAndUnlock(), mBiometricUnlockController.isBiometricUnlock()); @@ -3850,7 +3852,8 @@ public class StatusBar extends SystemUI implements DemoMode, // We don't want to end up in KEYGUARD state when we're unlocking with // fingerprint from doze. We should cross fade directly from black. - boolean wakeAndUnlocking = mBiometricUnlockController.isWakeAndUnlock(); + boolean unlocking = mBiometricUnlockController.isWakeAndUnlock() + || mKeyguardMonitor.isKeyguardFadingAway(); // Do not animate the scrim expansion when triggered by the fingerprint sensor. mScrimController.setExpansionAffectsAlpha( @@ -3875,9 +3878,9 @@ public class StatusBar extends SystemUI implements DemoMode, } else if (isPulsing()) { mScrimController.transitionTo(ScrimState.PULSING, mDozeScrimController.getScrimCallback()); - } else if (mDozing && !wakeAndUnlocking) { + } else if (mDozing && !unlocking) { mScrimController.transitionTo(ScrimState.AOD); - } else if (mIsKeyguard && !wakeAndUnlocking) { + } else if (mIsKeyguard && !unlocking) { mScrimController.transitionTo(ScrimState.KEYGUARD); } else if (mBubbleController.isStackExpanded()) { mScrimController.transitionTo(ScrimState.BUBBLE_EXPANDED); @@ -4118,6 +4121,11 @@ public class StatusBar extends SystemUI implements DemoMode, mScrimController.setAodFrontScrimAlpha(scrimOpacity); } + @Override + public void prepareForGentleWakeUp() { + mScrimController.prepareForGentleWakeUp(); + } + private void dispatchTap(View view, float x, float y) { long now = SystemClock.elapsedRealtime(); dispatchTouchEvent(view, x, y, now, MotionEvent.ACTION_DOWN); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index 65be7089ff08..5ce1329d5a89 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -85,7 +85,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb // make everything a bit slower to bridge a gap until the user is unlocked and home screen has // dranw its first frame. private static final long KEYGUARD_DISMISS_DURATION_LOCKED = 2000; - private static final long BYPASS_PANEL_FADE_DURATION = 67; private static String TAG = "StatusBarKeyguardViewManager"; @@ -270,7 +269,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb boolean keyguardWithoutQs = mStatusBarStateController.getState() == StatusBarState.KEYGUARD && !mNotificationPanelView.isQsExpanded(); boolean lockVisible = (mBouncer.isShowing() || keyguardWithoutQs) - && !mBouncer.isAnimatingAway(); + && !mBouncer.isAnimatingAway() && !mKeyguardMonitor.isKeyguardFadingAway(); if (mLastLockVisible != lockVisible) { mLastLockVisible = lockVisible; @@ -279,8 +278,14 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb AppearAnimationUtils.DEFAULT_APPEAR_DURATION /* duration */, 0 /* delay */); } else { + final long duration; + if (needsBypassFading()) { + duration = KeyguardBypassController.BYPASS_PANEL_FADE_DURATION; + } else { + duration = AppearAnimationUtils.DEFAULT_APPEAR_DURATION / 2; + } CrossFadeHelper.fadeOut(mLockIconContainer, - AppearAnimationUtils.DEFAULT_APPEAR_DURATION / 2 /* duration */, + duration /* duration */, 0 /* delay */, null /* runnable */); } } @@ -567,7 +572,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb if (needsBypassFading()) { ViewGroupFadeHelper.fadeOutAllChildrenExcept(mNotificationPanelView, mNotificationContainer, - BYPASS_PANEL_FADE_DURATION, + KeyguardBypassController.BYPASS_PANEL_FADE_DURATION, () -> { mStatusBar.hideKeyguard(); onKeyguardFadedAway(); @@ -583,7 +588,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb if (needsBypassFading()) { ViewGroupFadeHelper.fadeOutAllChildrenExcept(mNotificationPanelView, mNotificationContainer, - BYPASS_PANEL_FADE_DURATION, + KeyguardBypassController.BYPASS_PANEL_FADE_DURATION, () -> { mStatusBar.hideKeyguard(); }); @@ -602,6 +607,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mBiometricUnlockController.finishKeyguardFadingAway(); } } + updateLockIcon(); updateStates(); mStatusBarWindowController.setKeyguardShowing(false); mViewMediatorCallback.keyguardGone(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java index f36c56f60965..a71fcdbd9914 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java @@ -107,6 +107,9 @@ public class UnlockMethodCache { mListeners.remove(listener); } + /** + * If there are faces enrolled and user enabled face auth on keyguard. + */ public boolean isUnlockingWithFacePossible() { return mIsUnlockingWithFacePossible; } @@ -119,15 +122,16 @@ public class UnlockMethodCache { || (Build.IS_DEBUGGABLE && DEBUG_AUTH_WITH_ADB && mDebugUnlocked); boolean trustManaged = mKeyguardUpdateMonitor.getUserTrustIsManaged(user); boolean trusted = mKeyguardUpdateMonitor.getUserHasTrust(user); - boolean hasEnrolledFaces = mKeyguardUpdateMonitor.isUnlockWithFacePossible(user); - boolean changed = secure != mSecure || canSkipBouncer != mCanSkipBouncer || - trustManaged != mTrustManaged || mIsUnlockingWithFacePossible != hasEnrolledFaces; + boolean isUnlockingWithFacePossible = mKeyguardUpdateMonitor.isUnlockWithFacePossible(user); + boolean changed = secure != mSecure || canSkipBouncer != mCanSkipBouncer + || trustManaged != mTrustManaged + || mIsUnlockingWithFacePossible != isUnlockingWithFacePossible; if (changed || updateAlways) { mSecure = secure; mCanSkipBouncer = canSkipBouncer; mTrusted = trusted; mTrustManaged = trustManaged; - mIsUnlockingWithFacePossible = hasEnrolledFaces; + mIsUnlockingWithFacePossible = isUnlockingWithFacePossible; notifyListeners(); } Trace.endSection(); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java index bdc6341bde91..db6177a63f96 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java @@ -103,7 +103,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { when(context.getPackageManager()).thenReturn(mPackageManager); doAnswer(invocation -> { IBiometricEnabledOnKeyguardCallback callback = invocation.getArgument(0); - callback.onChanged(BiometricSourceType.FACE, true /* enabled */); + callback.onChanged(BiometricSourceType.FACE, true /* enabled */, + KeyguardUpdateMonitor.getCurrentUser()); return null; }).when(mBiometricManager).registerEnabledOnKeyguardCallback(any()); when(mFaceManager.isHardwareDetected()).thenReturn(true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java index 329ef1c19a2b..7ea6493da83d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java @@ -56,6 +56,9 @@ public class FalsingManagerProxyTest extends SysuiTestCase { mHandler = new Handler(mTestableLooper.getLooper()); mDefaultConfigValue = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI, BRIGHTLINE_FALSING_MANAGER_ENABLED, false); + // In case it runs on a device where it's been set to true, set it to false by hand. + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, + BRIGHTLINE_FALSING_MANAGER_ENABLED, "false", false); } @After diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java index 2d6ae2629ce4..2ed0970ce44b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java @@ -24,6 +24,7 @@ import static org.mockito.Mockito.when; import static org.mockito.Mockito.withSettings; import com.android.systemui.statusbar.phone.DozeParameters; +import com.android.systemui.utils.hardware.FakeSensorManager; import org.mockito.Answers; import org.mockito.MockSettings; @@ -39,6 +40,7 @@ public class DozeConfigurationUtil { when(params.getPickupPerformsProxCheck()).thenReturn(true); when(params.getPolicy()).thenReturn(mock(AlwaysOnDisplayPolicy.class)); when(params.doubleTapReportsTouchCoordinates()).thenReturn(false); + when(params.getDisplayNeedsBlanking()).thenReturn(false); doneHolder[0] = true; return params; @@ -52,11 +54,17 @@ public class DozeConfigurationUtil { when(config.pickupGestureEnabled(anyInt())).thenReturn(false); when(config.pulseOnNotificationEnabled(anyInt())).thenReturn(true); when(config.alwaysOnEnabled(anyInt())).thenReturn(false); + when(config.enabled(anyInt())).thenReturn(true); + when(config.getWakeLockScreenDebounce()).thenReturn(0L); when(config.doubleTapSensorType()).thenReturn(null); when(config.tapSensorType()).thenReturn(null); when(config.longPressSensorType()).thenReturn(null); + when(config.tapGestureEnabled(anyInt())).thenReturn(true); + when(config.tapSensorAvailable()).thenReturn(true); + when(config.tapSensorType()).thenReturn(FakeSensorManager.TAP_SENSOR_TYPE); + when(config.dozePickupSensorAvailable()).thenReturn(false); when(config.wakeScreenGestureAvailable()).thenReturn(false); 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 6979fd811817..eb8ef09d0635 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java @@ -19,6 +19,7 @@ package com.android.systemui.doze; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -27,18 +28,17 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.AlarmManager; -import android.app.Instrumentation; +import android.hardware.Sensor; import android.hardware.display.AmbientDisplayConfiguration; import android.os.Handler; import android.os.Looper; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper.RunWithLooper; -import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; -import com.android.systemui.dock.DockManagerFake; +import com.android.systemui.dock.DockManager; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.util.wakelock.WakeLock; import com.android.systemui.util.wakelock.WakeLockFake; @@ -46,14 +46,12 @@ import com.android.systemui.utils.hardware.FakeSensorManager; import org.junit.Before; import org.junit.BeforeClass; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; @SmallTest -@Ignore("failing") @RunWith(AndroidTestingRunner.class) -@RunWithLooper +@RunWithLooper(setAsMainLooper = true) public class DozeTriggersTest extends SysuiTestCase { private DozeTriggers mTriggers; private DozeMachine mMachine; @@ -61,10 +59,10 @@ public class DozeTriggersTest extends SysuiTestCase { private AmbientDisplayConfiguration mConfig; private DozeParameters mParameters; private FakeSensorManager mSensors; + private Sensor mTapSensor; private WakeLock mWakeLock; - private Instrumentation mInstrumentation; private AlarmManager mAlarmManager; - private DockManagerFake mDockManagerFake; + private DockManager mDockManagerFake; @BeforeClass public static void setupSuite() { @@ -74,15 +72,15 @@ public class DozeTriggersTest extends SysuiTestCase { @Before public void setUp() throws Exception { - mInstrumentation = InstrumentationRegistry.getInstrumentation(); mMachine = mock(DozeMachine.class); mAlarmManager = mock(AlarmManager.class); - mHost = new DozeHostFake(); + mHost = spy(new DozeHostFake()); mConfig = DozeConfigurationUtil.createMockConfig(); mParameters = DozeConfigurationUtil.createMockParameters(); - mSensors = new FakeSensorManager(mContext); + mSensors = spy(new FakeSensorManager(mContext)); + mTapSensor = mSensors.getFakeTapSensor().getSensor(); mWakeLock = new WakeLockFake(); - mDockManagerFake = spy(new DockManagerFake()); + mDockManagerFake = mock(DockManager.class); mTriggers = new DozeTriggers(mContext, mMachine, mHost, mAlarmManager, mConfig, mParameters, mSensors, Handler.createAsync(Looper.myLooper()), mWakeLock, true, @@ -95,29 +93,45 @@ public class DozeTriggersTest extends SysuiTestCase { mTriggers.transitionTo(DozeMachine.State.UNINITIALIZED, DozeMachine.State.INITIALIZED); mTriggers.transitionTo(DozeMachine.State.INITIALIZED, DozeMachine.State.DOZE); + clearInvocations(mMachine); mHost.callback.onNotificationAlerted(); - mSensors.getMockProximitySensor().sendProximityResult(false); /* Near */ verify(mMachine, never()).requestState(any()); verify(mMachine, never()).requestPulse(anyInt()); mHost.callback.onNotificationAlerted(); - mSensors.getMockProximitySensor().sendProximityResult(true); /* Far */ verify(mMachine).requestPulse(anyInt()); } @Test + public void testTransitionTo_disablesAndEnablesTouchSensors() { + when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE); + + mTriggers.transitionTo(DozeMachine.State.INITIALIZED, DozeMachine.State.DOZE); + verify(mSensors).requestTriggerSensor(any(), eq(mTapSensor)); + + clearInvocations(mSensors); + mTriggers.transitionTo(DozeMachine.State.DOZE, + DozeMachine.State.DOZE_REQUEST_PULSE); + mTriggers.transitionTo(DozeMachine.State.DOZE_REQUEST_PULSE, + DozeMachine.State.DOZE_PULSING); + verify(mSensors).cancelTriggerSensor(any(), eq(mTapSensor)); + + clearInvocations(mSensors); + mTriggers.transitionTo(DozeMachine.State.DOZE_PULSING, DozeMachine.State.DOZE_PULSE_DONE); + verify(mSensors).requestTriggerSensor(any(), eq(mTapSensor)); + } + + @Test public void testDockEventListener_registerAndUnregister() { mTriggers.transitionTo(DozeMachine.State.UNINITIALIZED, DozeMachine.State.INITIALIZED); - verify(mDockManagerFake).addListener(any()); mTriggers.transitionTo(DozeMachine.State.DOZE, DozeMachine.State.FINISH); - verify(mDockManagerFake).removeListener(any()); } @@ -128,7 +142,6 @@ public class DozeTriggersTest extends SysuiTestCase { mTriggers.onSensor(DozeLog.REASON_SENSOR_DOUBLE_TAP, false /* sensorPerformedProxCheck */, 50 /* screenX */, 50 /* screenY */, null /* rawValues */); - verify(mMachine, never()).wakeUp(); } @@ -142,7 +155,7 @@ public class DozeTriggersTest extends SysuiTestCase { false /* sensorPerformedProxCheck */, 50 /* screenX */, 50 /* screenY */, null /* rawValues */); - verify(mHost).setAodDimmingScrim(eq(255)); + verify(mHost).setAodDimmingScrim(eq(1f)); verify(mMachine).wakeUp(); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java index 87ae85f6d302..f07edf331f13 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeWallpaperStateTest.java @@ -18,6 +18,7 @@ package com.android.systemui.doze; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -29,6 +30,7 @@ import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.notification.stack.StackStateAnimator; +import com.android.systemui.statusbar.phone.BiometricUnlockController; import com.android.systemui.statusbar.phone.DozeParameters; import org.junit.Before; @@ -44,12 +46,14 @@ public class DozeWallpaperStateTest extends SysuiTestCase { private DozeWallpaperState mDozeWallpaperState; @Mock IWallpaperManager mIWallpaperManager; + @Mock BiometricUnlockController mBiometricUnlockController; @Mock DozeParameters mDozeParameters; @Before public void setUp() { MockitoAnnotations.initMocks(this); - mDozeWallpaperState = new DozeWallpaperState(mIWallpaperManager, mDozeParameters); + mDozeWallpaperState = new DozeWallpaperState(mIWallpaperManager, mBiometricUnlockController, + mDozeParameters); } @Test @@ -102,6 +106,20 @@ public class DozeWallpaperStateTest extends SysuiTestCase { } @Test + public void testDoesNotAnimate_whenWakeAndUnlock() throws RemoteException { + // Pre-conditions + when(mDozeParameters.getAlwaysOn()).thenReturn(true); + when(mBiometricUnlockController.unlockedByWakeAndUnlock()).thenReturn(true); + + mDozeWallpaperState.transitionTo(DozeMachine.State.UNINITIALIZED, + DozeMachine.State.DOZE_AOD); + clearInvocations(mIWallpaperManager); + + mDozeWallpaperState.transitionTo(DozeMachine.State.DOZE_AOD, DozeMachine.State.FINISH); + verify(mIWallpaperManager).setInAmbientMode(eq(false), eq(0L)); + } + + @Test public void testTransitionTo_requestPulseIsAmbientMode() throws RemoteException { mDozeWallpaperState.transitionTo(DozeMachine.State.DOZE, DozeMachine.State.DOZE_REQUEST_PULSE); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java index 5c1f473360b2..881cc3915880 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java @@ -64,7 +64,7 @@ public class AlertingNotificationManagerTest extends SysuiTestCase { protected static final int TEST_AUTO_DISMISS_TIME = 500; // Number of notifications to use in tests requiring multiple notifications private static final int TEST_NUM_NOTIFICATIONS = 4; - protected static final int TEST_TIMEOUT_TIME = 10000; + protected static final int TEST_TIMEOUT_TIME = 15000; protected final Runnable TEST_TIMEOUT_RUNNABLE = () -> mTimedOut = true; private AlertingNotificationManager mAlertingNotificationManager; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java index a99dc7fb6924..7d9920db36a4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.phone; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyFloat; @@ -192,6 +194,34 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { } @Test + public void onBiometricAuthenticated_whenBypassOnBouncer_dismissBouncer() { + reset(mKeyguardBypassController); + when(mUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(true); + when(mKeyguardBypassController.getBypassEnabled()).thenReturn(true); + when(mKeyguardBypassController.onBiometricAuthenticated(any())).thenReturn(true); + when(mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing()).thenReturn(true); + mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT, + BiometricSourceType.FACE); + + verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(eq(false)); + assertThat(mBiometricUnlockController.getMode()) + .isEqualTo(BiometricUnlockController.MODE_DISMISS_BOUNCER); + } + + @Test + public void onBiometricAuthenticated_whenBypassOnBouncer_respectsCanPlaySubtleAnim() { + when(mUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(true); + when(mKeyguardBypassController.getBypassEnabled()).thenReturn(true); + when(mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing()).thenReturn(true); + mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT, + BiometricSourceType.FACE); + + verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(eq(false)); + assertThat(mBiometricUnlockController.getMode()) + .isEqualTo(BiometricUnlockController.MODE_UNLOCK_FADING); + } + + @Test public void onBiometricAuthenticated_whenFaceAndPulsing_dontDismissKeyguard() { reset(mUpdateMonitor); reset(mStatusBarKeyguardViewManager); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java index f8b9e6879dcf..48934da9fb37 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java @@ -70,7 +70,6 @@ public class HeadsUpManagerPhoneTest extends AlertingNotificationManagerTest { setUp(statusBarWindowView, groupManager, bar, vsManager); mMinimumDisplayTime = TEST_MINIMUM_DISPLAY_TIME; mAutoDismissNotificationDecay = TEST_AUTO_DISMISS_TIME; - mAutoDismissNotificationDecayDozing = TEST_AUTO_DISMISS_TIME; } } @@ -134,13 +133,11 @@ public class HeadsUpManagerPhoneTest extends AlertingNotificationManagerTest { @Test public void testExtendHeadsUp() { - when(mStatusBarStateController.isDozing()).thenReturn(true); mHeadsUpManager.showNotification(mEntry); Runnable pastNormalTimeRunnable = () -> mLivesPastNormalTime = mHeadsUpManager.isAlerting(mEntry.key); mTestHandler.postDelayed(pastNormalTimeRunnable, - mHeadsUpManager.mAutoDismissNotificationDecayDozing + - mHeadsUpManager.mExtensionTime / 2); + TEST_AUTO_DISMISS_TIME + mHeadsUpManager.mExtensionTime / 2); mTestHandler.postDelayed(TEST_TIMEOUT_RUNNABLE, TEST_TIMEOUT_TIME); mHeadsUpManager.extendHeadsUp(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/hardware/FakeSensorManager.java b/packages/SystemUI/tests/src/com/android/systemui/utils/hardware/FakeSensorManager.java index a4ae166d0675..29b8ab600caf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/utils/hardware/FakeSensorManager.java +++ b/packages/SystemUI/tests/src/com/android/systemui/utils/hardware/FakeSensorManager.java @@ -40,18 +40,23 @@ import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; +import javax.annotation.Nullable; + /** * Rudimentary fake for SensorManager * - * Currently only supports the proximity sensor. + * Currently only supports proximity, light and tap sensors. * * Note that this class ignores the "Handler" argument, so the test is responsible for calling the * listener on the right thread. */ public class FakeSensorManager extends SensorManager { + public static final String TAP_SENSOR_TYPE = "tapSensorType"; + private final MockProximitySensor mMockProximitySensor; private final FakeGenericSensor mFakeLightSensor; + private final FakeGenericSensor mFakeTapSensor; private final FakeGenericSensor[] mSensors; public FakeSensorManager(Context context) throws Exception { @@ -59,12 +64,13 @@ public class FakeSensorManager extends SensorManager { .getDefaultSensor(Sensor.TYPE_PROXIMITY); if (proxSensor == null) { // No prox? Let's create a fake one! - proxSensor = createSensor(Sensor.TYPE_PROXIMITY); + proxSensor = createSensor(Sensor.TYPE_PROXIMITY, null); } mSensors = new FakeGenericSensor[]{ mMockProximitySensor = new MockProximitySensor(proxSensor), - mFakeLightSensor = new FakeGenericSensor(createSensor(Sensor.TYPE_LIGHT)), + mFakeLightSensor = new FakeGenericSensor(createSensor(Sensor.TYPE_LIGHT, null)), + mFakeTapSensor = new FakeGenericSensor(createSensor(99, TAP_SENSOR_TYPE)) }; } @@ -76,6 +82,10 @@ public class FakeSensorManager extends SensorManager { return mFakeLightSensor; } + public FakeGenericSensor getFakeTapSensor() { + return mFakeTapSensor; + } + @Override public Sensor getDefaultSensor(int type) { Sensor s = super.getDefaultSensor(type); @@ -160,13 +170,13 @@ public class FakeSensorManager extends SensorManager { @Override protected boolean requestTriggerSensorImpl(TriggerEventListener listener, Sensor sensor) { - return false; + return true; } @Override protected boolean cancelTriggerSensorImpl(TriggerEventListener listener, Sensor sensor, boolean disable) { - return false; + return true; } @Override @@ -185,12 +195,15 @@ public class FakeSensorManager extends SensorManager { return false; } - private Sensor createSensor(int type) throws Exception { + private Sensor createSensor(int type, @Nullable String stringType) throws Exception { Constructor<Sensor> constr = Sensor.class.getDeclaredConstructor(); constr.setAccessible(true); Sensor sensor = constr.newInstance(); setSensorType(sensor, type); + if (stringType != null) { + setSensorField(sensor, "mStringType", stringType); + } setSensorField(sensor, "mName", "Mock " + sensor.getStringType() + "/" + type); setSensorField(sensor, "mVendor", "Mock Vendor"); setSensorField(sensor, "mVersion", 1); diff --git a/proto/src/task_snapshot.proto b/proto/src/task_snapshot.proto index 381d9834fa87..821db860dd96 100644 --- a/proto/src/task_snapshot.proto +++ b/proto/src/task_snapshot.proto @@ -33,4 +33,5 @@ bool is_translucent = 9; string top_activity_component = 10; float scale = 11; - }
\ No newline at end of file + int64 id = 12; + } diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java index 1873de53867e..2db7f0e108b4 100644 --- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java @@ -17,7 +17,6 @@ package com.android.server.accessibility; import static android.accessibilityservice.AccessibilityServiceInfo.DEFAULT; -import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY; import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS; import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS; @@ -37,6 +36,7 @@ import android.content.ServiceConnection; import android.content.pm.PackageManager; import android.content.pm.ParceledListSlice; import android.graphics.Region; +import android.hardware.display.DisplayManager; import android.os.Binder; import android.os.Build; import android.os.Bundle; @@ -49,6 +49,7 @@ import android.os.RemoteException; import android.os.SystemClock; import android.util.Slog; import android.util.SparseArray; +import android.view.Display; import android.view.KeyEvent; import android.view.MagnificationSpec; import android.view.View; @@ -91,6 +92,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ private final WindowManagerInternal mWindowManagerService; private final GlobalActionPerformer mGlobalActionPerformer; private final AccessibilityWindowManager mA11yWindowManager; + private final DisplayManager mDisplayManager; private final PowerManager mPowerManager; // Handler for scheduling method invocations on the main thread. @@ -149,7 +151,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ // types as message types allowing us to remove messages per event type. public Handler mEventDispatchHandler; - final IBinder mOverlayWindowToken = new Binder(); + final SparseArray<IBinder> mOverlayWindowTokens = new SparseArray(); public interface SystemSupport { @@ -222,6 +224,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ mSystemSupport = systemSupport; mInvocationHandler = new InvocationHandler(mainHandler.getLooper()); mA11yWindowManager = a11yWindowManager; + mDisplayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE); mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); mEventDispatchHandler = new Handler(mainHandler.getLooper()) { @Override @@ -928,24 +931,73 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ } public void onAdded() { + final Display[] displays = mDisplayManager.getDisplays(); + for (int i = 0; i < displays.length; i++) { + final int displayId = displays[i].getDisplayId(); + onDisplayAdded(displayId); + } + } + + /** + * Called whenever a logical display has been added to the system. Add a window token for adding + * an accessibility overlay. + * + * @param displayId The id of the logical display that was added. + */ + public void onDisplayAdded(int displayId) { final long identity = Binder.clearCallingIdentity(); try { - mWindowManagerService.addWindowToken(mOverlayWindowToken, - TYPE_ACCESSIBILITY_OVERLAY, DEFAULT_DISPLAY); + final IBinder overlayWindowToken = new Binder(); + mWindowManagerService.addWindowToken(overlayWindowToken, TYPE_ACCESSIBILITY_OVERLAY, + displayId); + synchronized (mLock) { + mOverlayWindowTokens.put(displayId, overlayWindowToken); + } } finally { Binder.restoreCallingIdentity(identity); } } public void onRemoved() { + final Display[] displays = mDisplayManager.getDisplays(); + for (int i = 0; i < displays.length; i++) { + final int displayId = displays[i].getDisplayId(); + onDisplayRemoved(displayId); + } + } + + /** + * Called whenever a logical display has been removed from the system. Remove a window token for + * removing an accessibility overlay. + * + * @param displayId The id of the logical display that was added. + */ + public void onDisplayRemoved(int displayId) { final long identity = Binder.clearCallingIdentity(); try { - mWindowManagerService.removeWindowToken(mOverlayWindowToken, true, DEFAULT_DISPLAY); + mWindowManagerService.removeWindowToken(mOverlayWindowTokens.get(displayId), true, + displayId); + synchronized (mLock) { + mOverlayWindowTokens.remove(displayId); + } } finally { Binder.restoreCallingIdentity(identity); } } + /** + * Gets overlay window token by the display Id. + * + * @param displayId The id of the logical display that was added. + * @return window token. + */ + @Override + public IBinder getOverlayWindowToken(int displayId) { + synchronized (mLock) { + return mOverlayWindowTokens.get(displayId); + } + } + public void resetLocked() { mSystemSupport.getKeyEventDispatcher().flush(this); try { diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 47f137c63efa..a2204518e762 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -579,10 +579,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub // Make sure clients receiving this event will be able to get the // current state of the windows as the window manager may be delaying // the computation for performance reasons. + // TODO [Multi-Display] : using correct display Id to replace DEFAULT_DISPLAY if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED && mA11yWindowManager.isTrackingWindowsLocked()) { WindowManagerInternal wm = LocalServices.getService(WindowManagerInternal.class); - wm.computeWindowsForAccessibility(); + wm.computeWindowsForAccessibility(Display.DEFAULT_DISPLAY); } synchronized (mLock) { notifyAccessibilityServicesDelayedLocked(event, false); @@ -2550,6 +2551,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mInputFilter.onDisplayChanged(); } UserState userState = getCurrentUserStateLocked(); + if (displayId != Display.DEFAULT_DISPLAY) { + final List<AccessibilityServiceConnection> services = userState.mBoundServices; + for (int i = 0; i < services.size(); i++) { + AccessibilityServiceConnection boundClient = services.get(i); + boundClient.onDisplayAdded(displayId); + } + } updateMagnificationLocked(userState); } } @@ -2566,6 +2574,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub if (mInputFilter != null) { mInputFilter.onDisplayChanged(); } + UserState userState = getCurrentUserStateLocked(); + if (displayId != Display.DEFAULT_DISPLAY) { + final List<AccessibilityServiceConnection> services = userState.mBoundServices; + for (int i = 0; i < services.size(); i++) { + AccessibilityServiceConnection boundClient = services.get(i); + boundClient.onDisplayRemoved(displayId); + } + } } if (mMagnificationController != null) { mMagnificationController.onDisplayRemoved(displayId); @@ -2601,8 +2617,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub public final RemoteCallbackList<IAccessibilityManagerClient> mUserClients = new RemoteCallbackList<>(); - public final SparseArray<IBinder> mWindowTokens = new SparseArray<>(); - // Transient state. public final ArrayList<AccessibilityServiceConnection> mBoundServices = new ArrayList<>(); diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java index 547f7d3f5070..02306c02fa6f 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java @@ -31,6 +31,7 @@ import android.os.RemoteException; import android.os.UserHandle; import android.provider.Settings; import android.util.Slog; +import android.view.Display; import com.android.server.accessibility.AccessibilityManagerService.UserState; import com.android.server.wm.WindowManagerInternal; @@ -196,7 +197,7 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect return; } try { - serviceInterface.init(this, mId, mOverlayWindowToken); + serviceInterface.init(this, mId, mOverlayWindowTokens.get(Display.DEFAULT_DISPLAY)); } catch (RemoteException re) { Slog.w(LOG_TAG, "Error while setting connection for service: " + serviceInterface, re); diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java index 841527261e29..c12929153781 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java @@ -23,7 +23,6 @@ import static com.android.internal.util.function.pooled.PooledLambda.obtainMessa import android.annotation.NonNull; import android.annotation.Nullable; -import android.graphics.Rect; import android.graphics.Region; import android.os.Binder; import android.os.Handler; @@ -34,6 +33,7 @@ import android.os.UserHandle; import android.text.TextUtils; import android.util.Slog; import android.util.SparseArray; +import android.view.Display; import android.view.IWindow; import android.view.WindowInfo; import android.view.WindowManager; @@ -277,7 +277,7 @@ public class AccessibilityWindowManager } else if (!oldWindow.activityToken.equals(newWindow.activityToken)) { return true; } - if (!oldWindow.boundsInScreen.equals(newWindow.boundsInScreen)) { + if (!oldWindow.regionInScreen.equals(newWindow.regionInScreen)) { return true; } if (oldWindow.childTokens != null && newWindow.childTokens != null @@ -312,7 +312,9 @@ public class AccessibilityWindowManager // In some cases, onWindowsForAccessibilityChanged will be called immediately in // setWindowsForAccessibilityCallback. We'll lost windows if flag is false. mTrackingWindows = true; - mWindowManagerInternal.setWindowsForAccessibilityCallback(this); + // TODO [Multi-Display] : using correct display Id to replace DEFAULT_DISPLAY + mWindowManagerInternal.setWindowsForAccessibilityCallback(Display.DEFAULT_DISPLAY, + this); } } } @@ -323,7 +325,9 @@ public class AccessibilityWindowManager public void stopTrackingWindows() { synchronized (mLock) { if (mTrackingWindows) { - mWindowManagerInternal.setWindowsForAccessibilityCallback(null); + // TODO [Multi-Display] : using correct display Id to replace DEFAULT_DISPLAY + mWindowManagerInternal.setWindowsForAccessibilityCallback(Display.DEFAULT_DISPLAY, + null); mTrackingWindows = false; clearWindowsLocked(); } @@ -522,7 +526,8 @@ public class AccessibilityWindowManager } } } - mWindowManagerInternal.computeWindowsForAccessibility(); + // TODO [Multi-Display] : using correct display Id to replace DEFAULT_DISPLAY + mWindowManagerInternal.computeWindowsForAccessibility(Display.DEFAULT_DISPLAY); return windowId; } @@ -755,20 +760,20 @@ public class AccessibilityWindowManager boolean windowInteractiveRegionChanged = false; final int windowCount = mWindows.size(); - final Rect currentWindowBounds = new Rect(); + final Region currentWindowRegions = new Region(); for (int i = windowCount - 1; i >= 0; i--) { AccessibilityWindowInfo currentWindow = mWindows.get(i); if (windowInteractiveRegion == null) { if (currentWindow.getId() == windowId) { - currentWindow.getBoundsInScreen(currentWindowBounds); - outRegion.set(currentWindowBounds); + currentWindow.getRegionInScreen(currentWindowRegions); + outRegion.set(currentWindowRegions); windowInteractiveRegion = outRegion; continue; } } else if (currentWindow.getType() != AccessibilityWindowInfo.TYPE_ACCESSIBILITY_OVERLAY) { - currentWindow.getBoundsInScreen(currentWindowBounds); - if (windowInteractiveRegion.op(currentWindowBounds, Region.Op.DIFFERENCE)) { + currentWindow.getRegionInScreen(currentWindowRegions); + if (windowInteractiveRegion.op(currentWindowRegions, Region.Op.DIFFERENCE)) { windowInteractiveRegionChanged = true; } } @@ -1115,7 +1120,7 @@ public class AccessibilityWindowManager reportedWindow.setType(getTypeForWindowManagerWindowType(window.type)); reportedWindow.setLayer(window.layer); reportedWindow.setFocused(window.focused); - reportedWindow.setBoundsInScreen(window.boundsInScreen); + reportedWindow.setRegionInScreen(window.regionInScreen); reportedWindow.setTitle(window.title); reportedWindow.setAnchorId(window.accessibilityIdOfAnchor); reportedWindow.setPictureInPicture(window.inPictureInPicture); diff --git a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java index 285bd09d599c..2698b72fdb36 100644 --- a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java +++ b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java @@ -27,6 +27,7 @@ import android.os.IBinder; import android.os.IBinder.DeathRecipient; import android.os.RemoteException; import android.util.Slog; +import android.view.Display; import android.view.accessibility.AccessibilityEvent; import com.android.internal.util.DumpUtils; @@ -246,7 +247,8 @@ class UiAutomationManager { // another thread. if (serviceInterface != null) { service.linkToDeath(this, 0); - serviceInterface.init(this, mId, mOverlayWindowToken); + serviceInterface.init(this, mId, + mOverlayWindowTokens.get(Display.DEFAULT_DISPLAY)); } } catch (RemoteException re) { Slog.w(LOG_TAG, "Error initialized connection", re); diff --git a/services/art-profile b/services/art-profile index a9d5982d0d90..cbc4627b2129 100644 --- a/services/art-profile +++ b/services/art-profile @@ -16071,7 +16071,7 @@ PLcom/android/server/wm/DisplayPolicy;->notifyDisplayReady()V PLcom/android/server/wm/DisplayPolicy;->offsetInputMethodWindowLw(Lcom/android/server/wm/WindowState;Lcom/android/server/wm/DisplayFrames;)V HSPLcom/android/server/wm/DisplayPolicy;->onConfigurationChanged()V PLcom/android/server/wm/DisplayPolicy;->onPowerKeyDown(Z)V -HSPLcom/android/server/wm/DisplayPolicy;->prepareAddWindowLw(Lcom/android/server/wm/WindowState;Landroid/view/WindowManager$LayoutParams;)I +HSPLcom/android/server/wm/DisplayPolicy;->validateAddingWindowLw(Landroid/view/WindowManager$LayoutParams;)I HSPLcom/android/server/wm/DisplayPolicy;->removeWindowLw(Lcom/android/server/wm/WindowState;)V HSPLcom/android/server/wm/DisplayPolicy;->requestTransientBars(Lcom/android/server/wm/WindowState;)V HSPLcom/android/server/wm/DisplayPolicy;->resetSystemUiVisibilityLw()V diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java index aada96771324..1e1e07d32588 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java @@ -769,6 +769,19 @@ final class AutofillManagerServiceImpl } /** + * Updates the last fill response when a dataset is shown. + */ + void logDatasetShown(int sessionId, @Nullable Bundle clientState) { + synchronized (mLock) { + if (isValidEventLocked("logDatasetShown", sessionId)) { + mEventHistory.addEvent( + new Event(Event.TYPE_DATASETS_SHOWN, null, clientState, null, null, null, + null, null, null, null, null)); + } + } + } + + /** * Updates the last fill response when an autofill context is committed. */ @GuardedBy("mLock") diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 988db6a87100..46c2a85d0691 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -2497,6 +2497,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mService.getServicePackageName(), mComponentName, serviceLabel, serviceIcon, this, id, mCompatMode); + mService.logDatasetShown(id, mClientState); + synchronized (mLock) { if (mUiShownTime == 0) { // Log first time UI is shown. diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java index 8df25b5a6465..d5a7c818bfb2 100644 --- a/services/backup/java/com/android/server/backup/BackupManagerService.java +++ b/services/backup/java/com/android/server/backup/BackupManagerService.java @@ -194,6 +194,11 @@ public class BackupManagerService { } } + boolean isAbleToServeUser(int userId) { + return getServiceUsers().get(UserHandle.USER_SYSTEM) != null + && getServiceUsers().get(userId) != null; + } + /** * Returns a lst of users currently unlocked that have a * {@link UserBackupManagerService} registered. diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java index 53bbac4d41fe..f4b66456c27b 100644 --- a/services/backup/java/com/android/server/backup/Trampoline.java +++ b/services/backup/java/com/android/server/backup/Trampoline.java @@ -202,11 +202,10 @@ public class Trampoline extends IBackupManager.Stub { } } - // A user is ready for a backup if it's unlocked and is not suppressed by a device - // admin (device owner or profile owner). + // This method should not perform any I/O (e.g. do not call isBackupActivatedForUser), + // it's used in multiple places where I/O waits would cause system lock-ups. private boolean isUserReadyForBackup(int userId) { - return mService != null && mService.getServiceUsers().get(userId) != null - && isBackupActivatedForUser(userId); + return mService != null && mService.isAbleToServeUser(userId); } /** diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java index 18ee7a488998..18c38dc9efdb 100644 --- a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java +++ b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java @@ -128,7 +128,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba private static final String TAG = "PFTBT"; - private UserBackupManagerService backupManagerService; + private UserBackupManagerService mUserBackupManagerService; private final Object mCancelLock = new Object(); ArrayList<PackageInfo> mPackages; @@ -159,7 +159,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba @Nullable IBackupManagerMonitor monitor, @Nullable OnTaskFinishedListener listener, boolean userInitiated) { super(observer); - this.backupManagerService = backupManagerService; + this.mUserBackupManagerService = backupManagerService; mTransportClient = transportClient; mUpdateSchedule = updateSchedule; mLatch = latch; @@ -252,16 +252,16 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba } private void registerTask() { - synchronized (backupManagerService.getCurrentOpLock()) { + synchronized (mUserBackupManagerService.getCurrentOpLock()) { Slog.d(TAG, "backupmanager pftbt token=" + Integer.toHexString(mCurrentOpToken)); - backupManagerService.getCurrentOperations().put( + mUserBackupManagerService.getCurrentOperations().put( mCurrentOpToken, new Operation(OP_PENDING, this, OP_TYPE_BACKUP)); } } public void unregisterTask() { - backupManagerService.removeOperation(mCurrentOpToken); + mUserBackupManagerService.removeOperation(mCurrentOpToken); } @Override @@ -288,7 +288,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba mCancelAll = true; if (mIsDoingBackup) { - backupManagerService.handleCancel(mBackupRunnerOpToken, cancelAll); + mUserBackupManagerService.handleCancel(mBackupRunnerOpToken, cancelAll); try { // If we're running a backup we should be connected to a transport IBackupTransport transport = @@ -320,16 +320,17 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba int backupRunStatus = BackupManager.SUCCESS; try { - if (!backupManagerService.isEnabled() || !backupManagerService.isSetupComplete()) { + if (!mUserBackupManagerService.isEnabled() + || !mUserBackupManagerService.isSetupComplete()) { // Backups are globally disabled, so don't proceed. if (DEBUG) { - Slog.i(TAG, "full backup requested but enabled=" + backupManagerService + Slog.i(TAG, "full backup requested but enabled=" + mUserBackupManagerService .isEnabled() - + " setupComplete=" + backupManagerService.isSetupComplete() + + " setupComplete=" + mUserBackupManagerService.isSetupComplete() + "; ignoring"); } int monitoringEvent; - if (backupManagerService.isSetupComplete()) { + if (mUserBackupManagerService.isSetupComplete()) { monitoringEvent = BackupManagerMonitor.LOG_EVENT_ID_BACKUP_DISABLED; } else { monitoringEvent = BackupManagerMonitor.LOG_EVENT_ID_DEVICE_NOT_PROVISIONED; @@ -532,7 +533,8 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba // Roll this package to the end of the backup queue if we're // in a queue-driven mode (regardless of success/failure) if (mUpdateSchedule) { - backupManagerService.enqueueFullBackup(packageName, System.currentTimeMillis()); + mUserBackupManagerService.enqueueFullBackup( + packageName, System.currentTimeMillis()); } if (backupPackageStatus == BackupTransport.TRANSPORT_PACKAGE_REJECTED) { @@ -549,7 +551,8 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba // from the preflight pass. If we got as far as preflight, we now need // to tear down the target process. if (mBackupRunner != null) { - backupManagerService.tearDownAgentAndKill(currentPackage.applicationInfo); + mUserBackupManagerService.tearDownAgentAndKill( + currentPackage.applicationInfo); } // ... and continue looping. } else if (backupPackageStatus == BackupTransport.TRANSPORT_QUOTA_EXCEEDED) { @@ -561,7 +564,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba EventLog.writeEvent(EventLogTags.FULL_BACKUP_QUOTA_EXCEEDED, packageName); } - backupManagerService.tearDownAgentAndKill(currentPackage.applicationInfo); + mUserBackupManagerService.tearDownAgentAndKill(currentPackage.applicationInfo); // Do nothing, clean up, and continue looping. } else if (backupPackageStatus == BackupTransport.AGENT_ERROR) { BackupObserverUtils @@ -569,7 +572,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba BackupManager.ERROR_AGENT_FAILURE); Slog.w(TAG, "Application failure for package: " + packageName); EventLog.writeEvent(EventLogTags.BACKUP_AGENT_FAILURE, packageName); - backupManagerService.tearDownAgentAndKill(currentPackage.applicationInfo); + mUserBackupManagerService.tearDownAgentAndKill(currentPackage.applicationInfo); // Do nothing, clean up, and continue looping. } else if (backupPackageStatus == BackupManager.ERROR_BACKUP_CANCELLED) { BackupObserverUtils @@ -578,7 +581,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba Slog.w(TAG, "Backup cancelled. package=" + packageName + ", cancelAll=" + mCancelAll); EventLog.writeEvent(EventLogTags.FULL_BACKUP_CANCELLED, packageName); - backupManagerService.tearDownAgentAndKill(currentPackage.applicationInfo); + mUserBackupManagerService.tearDownAgentAndKill(currentPackage.applicationInfo); // Do nothing, clean up, and continue looping. } else if (backupPackageStatus != BackupTransport.TRANSPORT_OK) { BackupObserverUtils @@ -588,7 +591,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba EventLog.writeEvent(EventLogTags.FULL_BACKUP_TRANSPORT_FAILURE); // Abort entire backup pass. backupRunStatus = BackupManager.ERROR_TRANSPORT_ABORTED; - backupManagerService.tearDownAgentAndKill(currentPackage.applicationInfo); + mUserBackupManagerService.tearDownAgentAndKill(currentPackage.applicationInfo); return; } else { // Success! @@ -596,14 +599,14 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba .sendBackupOnPackageResult(mBackupObserver, packageName, BackupManager.SUCCESS); EventLog.writeEvent(EventLogTags.FULL_BACKUP_SUCCESS, packageName); - backupManagerService.logBackupComplete(packageName); + mUserBackupManagerService.logBackupComplete(packageName); } cleanUpPipes(transportPipes); cleanUpPipes(enginePipes); if (currentPackage.applicationInfo != null) { Slog.i(TAG, "Unbinding agent in " + packageName); try { - backupManagerService.getActivityManager().unbindBackupAgent( + mUserBackupManagerService.getActivityManager().unbindBackupAgent( currentPackage.applicationInfo); } catch (RemoteException e) { /* can't happen; activity manager is local */ } } @@ -639,8 +642,8 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba mJob.finishBackupPass(mUserId); } - synchronized (backupManagerService.getQueueLock()) { - backupManagerService.setRunningFullBackupTask(null); + synchronized (mUserBackupManagerService.getQueueLock()) { + mUserBackupManagerService.setRunningFullBackupTask(null); } mListener.onFinished("PFTBT.run()"); @@ -650,11 +653,11 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba // Now that we're actually done with schedule-driven work, reschedule // the next pass based on the new queue state. if (mUpdateSchedule) { - backupManagerService.scheduleNextFullBackupJob(backoff); + mUserBackupManagerService.scheduleNextFullBackupJob(backoff); } Slog.i(TAG, "Full data backup pass finished."); - backupManagerService.getWakelock().release(); + mUserBackupManagerService.getWakelock().release(); } } @@ -709,13 +712,13 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba long fullBackupAgentTimeoutMillis = mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis(); try { - backupManagerService.prepareOperationTimeout( + mUserBackupManagerService.prepareOperationTimeout( mCurrentOpToken, fullBackupAgentTimeoutMillis, this, OP_TYPE_BACKUP_WAIT); if (MORE_DEBUG) { Slog.d(TAG, "Preflighting full payload of " + pkg.packageName); } agent.doMeasureFullBackup(mQuota, mCurrentOpToken, - backupManagerService.getBackupManagerBinder(), mTransportFlags); + mUserBackupManagerService.getBackupManagerBinder(), mTransportFlags); // Now wait to get our result back. If this backstop timeout is reached without // the latch being thrown, flow will continue as though a result or "normal" @@ -765,7 +768,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba } mResult.set(result); mLatch.countDown(); - backupManagerService.removeOperation(mCurrentOpToken); + mUserBackupManagerService.removeOperation(mCurrentOpToken); } @Override @@ -775,7 +778,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba } mResult.set(BackupTransport.AGENT_ERROR); mLatch.countDown(); - backupManagerService.removeOperation(mCurrentOpToken); + mUserBackupManagerService.removeOperation(mCurrentOpToken); } @Override @@ -812,7 +815,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba mOutput = ParcelFileDescriptor.dup(output.getFileDescriptor()); mTarget = target; mCurrentOpToken = currentOpToken; - mEphemeralToken = backupManagerService.generateRandomIntegerToken(); + mEphemeralToken = mUserBackupManagerService.generateRandomIntegerToken(); mPreflight = new SinglePackageBackupPreflight( transportClient, quota, mEphemeralToken, transportFlags); mPreflightLatch = new CountDownLatch(1); @@ -825,23 +828,32 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba } void registerTask() { - synchronized (backupManagerService.getCurrentOpLock()) { - backupManagerService.getCurrentOperations().put( + synchronized (mUserBackupManagerService.getCurrentOpLock()) { + mUserBackupManagerService.getCurrentOperations().put( mCurrentOpToken, new Operation(OP_PENDING, this, OP_TYPE_BACKUP_WAIT)); } } void unregisterTask() { - synchronized (backupManagerService.getCurrentOpLock()) { - backupManagerService.getCurrentOperations().remove(mCurrentOpToken); + synchronized (mUserBackupManagerService.getCurrentOpLock()) { + mUserBackupManagerService.getCurrentOperations().remove(mCurrentOpToken); } } @Override public void run() { FileOutputStream out = new FileOutputStream(mOutput.getFileDescriptor()); - mEngine = new FullBackupEngine(backupManagerService, out, mPreflight, mTarget, false, - this, mQuota, mCurrentOpToken, mTransportFlags); + mEngine = + new FullBackupEngine( + mUserBackupManagerService, + out, + mPreflight, + mTarget, + false, + this, + mQuota, + mCurrentOpToken, + mTransportFlags); try { try { if (!mIsCancelled) { @@ -928,13 +940,13 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba mCurrentPackage, BackupManagerMonitor.LOG_EVENT_CATEGORY_AGENT, null); mIsCancelled = true; // Cancel tasks spun off by this task. - backupManagerService.handleCancel(mEphemeralToken, cancelAll); - backupManagerService.tearDownAgentAndKill(mTarget.applicationInfo); + mUserBackupManagerService.handleCancel(mEphemeralToken, cancelAll); + mUserBackupManagerService.tearDownAgentAndKill(mTarget.applicationInfo); // Free up everyone waiting on this task and its children. mPreflightLatch.countDown(); mBackupLatch.countDown(); // We are done with this operation. - backupManagerService.removeOperation(mCurrentOpToken); + mUserBackupManagerService.removeOperation(mCurrentOpToken); } } } diff --git a/services/core/java/com/android/server/BluetoothService.java b/services/core/java/com/android/server/BluetoothService.java index 5c5b477352b1..112cf0853722 100644 --- a/services/core/java/com/android/server/BluetoothService.java +++ b/services/core/java/com/android/server/BluetoothService.java @@ -18,8 +18,7 @@ package com.android.server; import android.bluetooth.BluetoothAdapter; import android.content.Context; - -import com.android.internal.os.RoSystemProperties; +import android.os.UserManager; class BluetoothService extends SystemService { private BluetoothManagerService mBluetoothManagerService; @@ -47,7 +46,7 @@ class BluetoothService extends SystemService { publishBinderService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE, mBluetoothManagerService); } else if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY && - !RoSystemProperties.MULTIUSER_HEADLESS_SYSTEM_USER) { + !UserManager.isHeadlessSystemUserMode()) { initialize(); } } diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java index 930cf9efdb1a..5089ee0ace57 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -42,7 +42,6 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; -import android.content.pm.PackageManagerInternal; import android.content.pm.ResolveInfo; import android.content.pm.Signature; import android.content.res.Resources; @@ -121,6 +120,7 @@ import com.android.server.location.LocationRequestStatistics.PackageStatistics; import com.android.server.location.MockProvider; import com.android.server.location.PassiveProvider; import com.android.server.location.RemoteListenerHelper; +import com.android.server.pm.permission.PermissionManagerServiceInternal; import java.io.ByteArrayOutputStream; import java.io.FileDescriptor; @@ -282,12 +282,12 @@ public class LocationManagerService extends ILocationManager.Stub { // Let the package manager query which are the default location // providers as they get certain permissions granted by default. - PackageManagerInternal packageManagerInternal = LocalServices.getService( - PackageManagerInternal.class); - packageManagerInternal.setLocationPackagesProvider( + PermissionManagerServiceInternal permissionManagerInternal = LocalServices.getService( + PermissionManagerServiceInternal.class); + permissionManagerInternal.setLocationPackagesProvider( userId -> mContext.getResources().getStringArray( com.android.internal.R.array.config_locationProviderPackageNames)); - packageManagerInternal.setLocationExtraPackagesProvider( + permissionManagerInternal.setLocationExtraPackagesProvider( userId -> mContext.getResources().getStringArray( com.android.internal.R.array.config_locationExtraPackageNames)); diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java index 80d7ac931111..df5005eed66b 100644 --- a/services/core/java/com/android/server/NetworkScoreService.java +++ b/services/core/java/com/android/server/NetworkScoreService.java @@ -26,7 +26,6 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; import android.content.pm.PackageManager; -import android.content.pm.PackageManagerInternal; import android.database.ContentObserver; import android.location.LocationManager; import android.net.INetworkRecommendationProvider; @@ -54,15 +53,14 @@ import android.provider.Settings.Global; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; -import android.util.IntArray; import android.util.Log; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.content.PackageMonitor; import com.android.internal.os.TransferPipe; -import com.android.internal.telephony.SmsApplication; import com.android.internal.util.DumpUtils; +import com.android.server.pm.permission.PermissionManagerServiceInternal; import java.io.FileDescriptor; import java.io.IOException; @@ -294,7 +292,7 @@ public class NetworkScoreService extends INetworkScoreService.Stub { String useOpenWifiPackage = Global.getString(mContext.getContentResolver(), Global.USE_OPEN_WIFI_PACKAGE); if (!TextUtils.isEmpty(useOpenWifiPackage)) { - LocalServices.getService(PackageManagerInternal.class) + LocalServices.getService(PermissionManagerServiceInternal.class) .grantDefaultPermissionsToDefaultUseOpenWifiApp(useOpenWifiPackage, userId); } @@ -306,17 +304,14 @@ public class NetworkScoreService extends INetworkScoreService.Stub { false /*notifyForDescendants*/, mUseOpenWifiPackageObserver); // Set a callback for the package manager to query the use open wifi app. - LocalServices.getService(PackageManagerInternal.class).setUseOpenWifiAppPackagesProvider( - new PackageManagerInternal.PackagesProvider() { - @Override - public String[] getPackages(int userId) { - String useOpenWifiPackage = Global.getString(mContext.getContentResolver(), - Global.USE_OPEN_WIFI_PACKAGE); - if (!TextUtils.isEmpty(useOpenWifiPackage)) { - return new String[]{useOpenWifiPackage}; - } - return null; + LocalServices.getService(PermissionManagerServiceInternal.class) + .setUseOpenWifiAppPackagesProvider((userId) -> { + String useOpenWifiPackage = Global.getString(mContext.getContentResolver(), + Global.USE_OPEN_WIFI_PACKAGE); + if (!TextUtils.isEmpty(useOpenWifiPackage)) { + return new String[]{useOpenWifiPackage}; } + return null; }); } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index adb0909f46b8..ff8c3e954460 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -347,6 +347,7 @@ import com.android.server.ThreadPriorityBooster; import com.android.server.Watchdog; import com.android.server.am.ActivityManagerServiceDumpProcessesProto.UidObserverRegistrationProto; import com.android.server.appop.AppOpsService; +import com.android.server.compat.CompatConfig; import com.android.server.contentcapture.ContentCaptureManagerInternal; import com.android.server.firewall.IntentFirewall; import com.android.server.job.JobSchedulerInternal; @@ -392,7 +393,9 @@ import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; @@ -5027,6 +5030,7 @@ public class ActivityManagerService extends IActivityManager.Stub bindApplicationTimeMillis = SystemClock.elapsedRealtime(); mAtmInternal.preBindApplication(app.getWindowProcessController()); final ActiveInstrumentation instr2 = app.getActiveInstrumentation(); + long[] disabledCompatChanges = CompatConfig.get().getDisabledChanges(app.info); if (app.isolatedEntryPoint != null) { // This is an isolated process which should just call an entry point instead of // being bound to an application. @@ -5042,7 +5046,8 @@ public class ActivityManagerService extends IActivityManager.Stub new Configuration(app.getWindowProcessController().getConfiguration()), app.compat, getCommonServicesLocked(app.isolated), mCoreSettingsObserver.getCoreSettingsLocked(), - buildSerial, autofillOptions, contentCaptureOptions); + buildSerial, autofillOptions, contentCaptureOptions, + disabledCompatChanges); } else { thread.bindApplication(processName, appInfo, providers, null, profilerInfo, null, null, null, testMode, @@ -5051,7 +5056,8 @@ public class ActivityManagerService extends IActivityManager.Stub new Configuration(app.getWindowProcessController().getConfiguration()), app.compat, getCommonServicesLocked(app.isolated), mCoreSettingsObserver.getCoreSettingsLocked(), - buildSerial, autofillOptions, contentCaptureOptions); + buildSerial, autofillOptions, contentCaptureOptions, + disabledCompatChanges); } if (profilerInfo != null) { profilerInfo.closeFd(); @@ -7706,6 +7712,34 @@ public class ActivityManagerService extends IActivityManager.Stub return null; } + int checkContentProviderUriPermission(Uri uri, int userId, int callingUid, int modeFlags) { + final String name = uri.getAuthority(); + final long ident = Binder.clearCallingIdentity(); + ContentProviderHolder holder = null; + try { + holder = getContentProviderExternalUnchecked(name, null, callingUid, + "*checkContentProviderUriPermission*", userId); + if (holder != null) { + return holder.provider.checkUriPermission(null, uri, callingUid, modeFlags); + } + } catch (RemoteException e) { + Log.w(TAG, "Content provider dead retrieving " + uri, e); + return PackageManager.PERMISSION_DENIED; + } catch (Exception e) { + Log.w(TAG, "Exception while determining type of " + uri, e); + return PackageManager.PERMISSION_DENIED; + } finally { + try { + if (holder != null) { + removeContentProviderExternalUnchecked(name, null, userId); + } + } finally { + Binder.restoreCallingIdentity(ident); + } + } + return PackageManager.PERMISSION_DENIED; + } + private boolean canClearIdentity(int callingPid, int callingUid, int userId) { if (UserHandle.getUserId(callingUid) == userId) { return true; @@ -17826,6 +17860,35 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override + public int checkContentProviderUriPermission(Uri uri, int userId, + int callingUid, int modeFlags) { + // We can find ourselves needing to check Uri permissions while + // already holding the WM lock, which means reaching back here for + // the AM lock would cause an inversion. The WM team has requested + // that we use the strategy below instead of shifting where Uri + // grants are calculated. + + // Since we could also arrive here while holding the AM lock, we + // can't always delegate the call through the handler, and we need + // to delicately dance between the deadlocks. + if (Thread.currentThread().holdsLock(ActivityManagerService.this)) { + return ActivityManagerService.this.checkContentProviderUriPermission(uri, + userId, callingUid, modeFlags); + } else { + final CompletableFuture<Integer> res = new CompletableFuture<>(); + mHandler.post(() -> { + res.complete(ActivityManagerService.this.checkContentProviderUriPermission(uri, + userId, callingUid, modeFlags)); + }); + try { + return res.get(); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } + } + } + + @Override public void onWakefulnessChanged(int wakefulness) { ActivityManagerService.this.onWakefulnessChanged(wakefulness); } diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index f7512f9d25e7..33070dc6b757 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -89,6 +89,7 @@ import android.view.Display; import com.android.internal.util.HexDump; import com.android.internal.util.MemInfoReader; import com.android.internal.util.Preconditions; +import com.android.server.compat.CompatConfig; import java.io.BufferedReader; import java.io.File; @@ -290,6 +291,8 @@ final class ActivityManagerShellCommand extends ShellCommand { return runNoHomeScreen(pw); case "wait-for-broadcast-idle": return runWaitForBroadcastIdle(pw); + case "compat": + return runCompat(pw); default: return handleDefaultCommands(cmd); } @@ -2874,6 +2877,50 @@ final class ActivityManagerShellCommand extends ShellCommand { return 0; } + private int runCompat(PrintWriter pw) { + final CompatConfig config = CompatConfig.get(); + String toggleValue = getNextArgRequired(); + long changeId; + String changeIdString = getNextArgRequired(); + try { + changeId = Long.parseLong(changeIdString); + } catch (NumberFormatException e) { + changeId = config.lookupChangeId(changeIdString); + } + if (changeId == -1) { + pw.println("Unknown or invalid change: '" + changeIdString + "'."); + } + String packageName = getNextArgRequired(); + switch(toggleValue) { + case "enable": + if (!config.addOverride(changeId, packageName, true)) { + pw.println("Warning! Change " + changeId + " is not known yet. Enabling it" + + " could have no effect."); + } + pw.println("Enabled change " + changeId + " for " + packageName + "."); + return 0; + case "disable": + if (!config.addOverride(changeId, packageName, false)) { + pw.println("Warning! Change " + changeId + " is not known yet. Disabling it" + + " could have no effect."); + } + pw.println("Disabled change " + changeId + " for " + packageName + "."); + return 0; + case "reset": + if (config.removeOverride(changeId, packageName)) { + pw.println("Reset change " + changeId + " for " + packageName + + " to default value."); + } else { + pw.println("No override exists for changeId " + changeId + "."); + } + return 0; + default: + pw.println("Invalid toggle value: '" + toggleValue + "'."); + } + return -1; + } + + private Resources getResources(PrintWriter pw) throws RemoteException { // system resources does not contain all the device configuration, construct it manually. Configuration config = mInterface.getConfiguration(); @@ -3181,6 +3228,8 @@ final class ActivityManagerShellCommand extends ShellCommand { pw.println(" without restarting any processes."); pw.println(" write"); pw.println(" Write all pending state to storage."); + pw.println(" compat enable|disable|reset <CHANGE_ID|CHANGE_NAME> <PACKAGE_NAME>"); + pw.println(" Toggles a change either by id or by name for <PACKAGE_NAME>."); pw.println(); Intent.printIntentArgsHelp(pw, ""); } diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags index 421b75586224..cf0de061185f 100644 --- a/services/core/java/com/android/server/am/EventLogTags.logtags +++ b/services/core/java/com/android/server/am/EventLogTags.logtags @@ -146,4 +146,7 @@ option java_package com.android.server.am 30065 am_on_top_resumed_lost_called (Token|1|5),(Component Name|3),(Reason|3) # An activity been add into stopping list -30066 am_add_to_stopping (User|1|5),(Token|1|5),(Component Name|3),(Reason|3)
\ No newline at end of file +30066 am_add_to_stopping (User|1|5),(Token|1|5),(Component Name|3),(Reason|3) + +# Keyguard status changed ++30067 am_set_keyguard_shown (keyguardShowing|1),(aodShowing|1),(keyguardGoingAway|1),(Reason|3)
\ No newline at end of file diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index ffbc6b314776..5465309f7826 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -1526,6 +1526,9 @@ public final class ProcessList { if ("1".equals(SystemProperties.get("debug.assert"))) { runtimeFlags |= Zygote.DEBUG_ENABLE_ASSERT; } + if ("1".equals(SystemProperties.get("debug.ignoreappsignalhandler"))) { + runtimeFlags |= Zygote.DEBUG_IGNORE_APP_SIGNAL_HANDLER; + } if (mService.mNativeDebuggingApp != null && mService.mNativeDebuggingApp.equals(app.processName)) { // Enable all debug flags required by the native debugger. diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index 2fe5bbe8c1ae..30070160e682 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -198,6 +198,7 @@ public class AppOpsService extends IAppOpsService.Stub { } }; + @GuardedBy("this") @VisibleForTesting final SparseArray<UidState> mUidStates = new SparseArray<>(); @@ -1187,7 +1188,7 @@ public class AppOpsService extends IAppOpsService.Stub { } } - private void pruneOp(Op op, int uid, String packageName) { + private void pruneOpLocked(Op op, int uid, String packageName) { if (!op.hasAnyTime()) { Ops ops = getOpsRawLocked(uid, packageName, false /* isPrivileged */, false /* edit */); if (ops != null) { @@ -1396,7 +1397,7 @@ public class AppOpsService extends IAppOpsService.Stub { if (mode == AppOpsManager.opToDefaultMode(op.op)) { // If going into the default mode, prune this op // if there is nothing else interesting in it. - pruneOp(op, uid, packageName); + pruneOpLocked(op, uid, packageName); } scheduleFastWriteLocked(); } @@ -2979,23 +2980,25 @@ public class AppOpsService extends IAppOpsService.Stub { out.startTag(null, "app-ops"); out.attribute(null, "v", String.valueOf(CURRENT_VERSION)); - final int uidStateCount = mUidStates.size(); - for (int i = 0; i < uidStateCount; i++) { - UidState uidState = mUidStates.valueAt(i); - if (uidState.opModes != null && uidState.opModes.size() > 0) { - out.startTag(null, "uid"); - out.attribute(null, "n", Integer.toString(uidState.uid)); - SparseIntArray uidOpModes = uidState.opModes; - final int opCount = uidOpModes.size(); - for (int j = 0; j < opCount; j++) { - final int op = uidOpModes.keyAt(j); - final int mode = uidOpModes.valueAt(j); - out.startTag(null, "op"); - out.attribute(null, "n", Integer.toString(op)); - out.attribute(null, "m", Integer.toString(mode)); - out.endTag(null, "op"); + synchronized (this) { + final int uidStateCount = mUidStates.size(); + for (int i = 0; i < uidStateCount; i++) { + UidState uidState = mUidStates.valueAt(i); + if (uidState.opModes != null && uidState.opModes.size() > 0) { + out.startTag(null, "uid"); + out.attribute(null, "n", Integer.toString(uidState.uid)); + SparseIntArray uidOpModes = uidState.opModes; + final int opCount = uidOpModes.size(); + for (int j = 0; j < opCount; j++) { + final int op = uidOpModes.keyAt(j); + final int mode = uidOpModes.valueAt(j); + out.startTag(null, "op"); + out.attribute(null, "n", Integer.toString(op)); + out.attribute(null, "m", Integer.toString(mode)); + out.endTag(null, "op"); + } + out.endTag(null, "uid"); } - out.endTag(null, "uid"); } } diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index 4d09c3b73a9d..cb6cf74d4f52 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -125,6 +125,8 @@ import java.io.PrintWriter; /*package*/ void onAudioServerDied() { // Restore forced usage for communications and record synchronized (mDeviceStateLock) { + AudioSystem.setParameters( + "BT_SCO=" + (mForcedUseForComm == AudioSystem.FORCE_BT_SCO ? "on" : "off")); onSetForceUse(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, "onAudioServerDied"); onSetForceUse(AudioSystem.FOR_RECORD, mForcedUseForComm, "onAudioServerDied"); } diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java index df6f73b914a8..bd3cd5439b32 100644 --- a/services/core/java/com/android/server/biometrics/BiometricService.java +++ b/services/core/java/com/android/server/biometrics/BiometricService.java @@ -521,8 +521,8 @@ public class BiometricService extends SystemService { List<EnabledOnKeyguardCallback> callbacks = mEnabledOnKeyguardCallbacks; for (int i = 0; i < callbacks.size(); i++) { callbacks.get(i).notify(BiometricSourceType.FACE, - mFaceEnabledOnKeyguard.getOrDefault(userId, - DEFAULT_KEYGUARD_ENABLED)); + mFaceEnabledOnKeyguard.getOrDefault(userId, DEFAULT_KEYGUARD_ENABLED), + userId); } } } @@ -540,9 +540,9 @@ public class BiometricService extends SystemService { } } - void notify(BiometricSourceType sourceType, boolean enabled) { + void notify(BiometricSourceType sourceType, boolean enabled, int userId) { try { - mCallback.onChanged(sourceType, enabled); + mCallback.onChanged(sourceType, enabled, userId); } catch (DeadObjectException e) { Slog.w(TAG, "Death while invoking notify", e); mEnabledOnKeyguardCallbacks.remove(this); @@ -796,7 +796,8 @@ public class BiometricService extends SystemService { mEnabledOnKeyguardCallbacks.add(new EnabledOnKeyguardCallback(callback)); try { callback.onChanged(BiometricSourceType.FACE, - mSettingObserver.getFaceEnabledOnKeyguard()); + mSettingObserver.getFaceEnabledOnKeyguard(), + UserHandle.getCallingUserId()); } catch (RemoteException e) { Slog.w(TAG, "Remote exception", e); } diff --git a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java index f08423eb2016..20eb6180832c 100644 --- a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java +++ b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java @@ -665,8 +665,12 @@ public abstract class BiometricServiceBase extends SystemService mMetricsLogger.count(getConstants().tagHalDied(), 1); mHALDeathCount++; mCurrentUserId = UserHandle.USER_NULL; - handleError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE, - 0 /*vendorCode */); + + // All client lifecycle must be managed on the handler. + mHandler.post(() -> { + handleError(getHalDeviceId(), BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE, + 0 /*vendorCode */); + }); StatsLog.write(StatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED, statsModality(), BiometricsProtoEnums.ISSUE_HAL_DEATH); diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java index bb3b9be2bd2f..2a866f390283 100644 --- a/services/core/java/com/android/server/compat/CompatChange.java +++ b/services/core/java/com/android/server/compat/CompatChange.java @@ -106,6 +106,12 @@ public final class CompatChange { * @return {@code true} if the change should be enabled for the package. */ boolean isEnabled(ApplicationInfo app) { + if (app.isSystemApp()) { + // All changes are enabled for system apps, and we do not support overrides. + // Compatibility issues for system apps should be addressed in the app itself when + // the compatibility change is made. + return true; + } if (mPackageOverrides != null && mPackageOverrides.containsKey(app.packageName)) { return mPackageOverrides.get(app.packageName); } diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java index fea5d836ac25..bcf1d80781a6 100644 --- a/services/core/java/com/android/server/compat/CompatConfig.java +++ b/services/core/java/com/android/server/compat/CompatConfig.java @@ -128,20 +128,24 @@ public final class CompatConfig { * <p>Note, package overrides are not persistent and will be lost on system or runtime restart. * * @param changeId The ID of the change to be overridden. Note, this call will succeed even if - * this change is not known; it will only have any affect if any code in the + * this change is not known; it will only have any effect if any code in the * platform is gated on the ID given. * @param packageName The app package name to override the change for. * @param enabled If the change should be enabled or disabled. + * @return {@code true} if the change existed before adding the override. */ - public void addOverride(long changeId, String packageName, boolean enabled) { + public boolean addOverride(long changeId, String packageName, boolean enabled) { + boolean alreadyKnown = true; synchronized (mChanges) { CompatChange c = mChanges.get(changeId); if (c == null) { + alreadyKnown = false; c = new CompatChange(changeId); addChange(c); } c.addPackageOverride(packageName, enabled); } + return alreadyKnown; } /** @@ -151,14 +155,18 @@ public final class CompatConfig { * * @param changeId The ID of the change that was overridden. * @param packageName The app package name that was overridden. + * @return {@code true} if an override existed; */ - public void removeOverride(long changeId, String packageName) { + public boolean removeOverride(long changeId, String packageName) { + boolean overrideExists = false; synchronized (mChanges) { CompatChange c = mChanges.get(changeId); if (c != null) { + overrideExists = true; c.removePackageOverride(packageName); } } + return overrideExists; } } diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java index 7824a0ac3194..3e1817bd14ff 100644 --- a/services/core/java/com/android/server/content/ContentService.java +++ b/services/core/java/com/android/server/content/ContentService.java @@ -40,7 +40,6 @@ import android.content.SyncInfo; import android.content.SyncRequest; import android.content.SyncStatusInfo; import android.content.pm.PackageManager; -import android.content.pm.PackageManagerInternal; import android.content.pm.ProviderInfo; import android.database.IContentObserver; import android.database.sqlite.SQLiteException; @@ -71,6 +70,7 @@ import com.android.internal.util.DumpUtils; import com.android.internal.util.IndentingPrintWriter; import com.android.server.LocalServices; import com.android.server.SystemService; +import com.android.server.pm.permission.PermissionManagerServiceInternal; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -280,15 +280,11 @@ public final class ContentService extends IContentService.Stub { // Let the package manager query for the sync adapters for a given authority // as we grant default permissions to sync adapters for specific authorities. - PackageManagerInternal packageManagerInternal = LocalServices.getService( - PackageManagerInternal.class); - packageManagerInternal.setSyncAdapterPackagesprovider( - new PackageManagerInternal.SyncAdapterPackagesProvider() { - @Override - public String[] getPackages(String authority, int userId) { - return getSyncAdapterPackagesForAuthorityAsUser(authority, userId); - } - }); + final PermissionManagerServiceInternal permissionManagerInternal = + LocalServices.getService(PermissionManagerServiceInternal.class); + permissionManagerInternal.setSyncAdapterPackagesProvider((authority, userId) -> { + return getSyncAdapterPackagesForAuthorityAsUser(authority, userId); + }); final IntentFilter packageFilter = new IntentFilter(); packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED); diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 99341d1c96ae..4f33ebb035da 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -303,6 +303,8 @@ public final class DisplayManagerService extends SystemService { private final Spline mMinimumBrightnessSpline; private final ColorSpace mWideColorSpace; + private SensorManager mSensorManager; + public DisplayManagerService(Context context) { this(context, new Injector()); } @@ -430,7 +432,7 @@ public final class DisplayManagerService extends SystemService { } mDisplayModeDirector.setListener(new AllowedDisplayModeObserver()); - mDisplayModeDirector.start(); + mDisplayModeDirector.start(mSensorManager); mHandler.sendEmptyMessage(MSG_REGISTER_ADDITIONAL_DISPLAY_ADAPTERS); } @@ -2358,6 +2360,7 @@ public final class DisplayManagerService extends SystemService { }; mDisplayPowerController = new DisplayPowerController( mContext, callbacks, handler, sensorManager, blanker); + mSensorManager = sensorManager; } mHandler.sendEmptyMessage(MSG_LOAD_BRIGHTNESS_CONFIGURATION); diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java index 14bd2d8150da..78a48dac6fb5 100644 --- a/services/core/java/com/android/server/display/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/DisplayModeDirector.java @@ -18,26 +18,41 @@ package com.android.server.display; import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.res.Resources; import android.database.ContentObserver; import android.hardware.display.DisplayManager; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; + import android.net.Uri; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.UserHandle; +import android.os.PowerManager; +import android.os.SystemClock; import android.provider.Settings; +import android.text.TextUtils; import android.util.Slog; import android.util.SparseArray; import android.view.Display; import android.view.DisplayInfo; import com.android.internal.R; +import com.android.server.display.whitebalance.DisplayWhiteBalanceFactory; +import com.android.server.display.whitebalance.AmbientFilter; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.Objects; /** @@ -74,7 +89,7 @@ public class DisplayModeDirector { private final AppRequestObserver mAppRequestObserver; private final SettingsObserver mSettingsObserver; private final DisplayObserver mDisplayObserver; - + private final BrightnessObserver mBrightnessObserver; private Listener mListener; @@ -87,6 +102,8 @@ public class DisplayModeDirector { mAppRequestObserver = new AppRequestObserver(); mSettingsObserver = new SettingsObserver(context, handler); mDisplayObserver = new DisplayObserver(context, handler); + mBrightnessObserver = new BrightnessObserver(context, handler); + } /** @@ -96,15 +113,17 @@ public class DisplayModeDirector { * This has to be deferred because the object may be constructed before the rest of the system * is ready. */ - public void start() { + public void start(SensorManager sensorManager) { mSettingsObserver.observe(); mDisplayObserver.observe(); mSettingsObserver.observe(); + mBrightnessObserver.observe(sensorManager); synchronized (mLock) { // We may have a listener already registered before the call to start, so go ahead and // notify them to pick up our newly initialized state. notifyAllowedModesChangedLocked(); } + } /** @@ -315,6 +334,7 @@ public class DisplayModeDirector { } mSettingsObserver.dumpLocked(pw); mAppRequestObserver.dumpLocked(pw); + mBrightnessObserver.dumpLocked(pw); } } @@ -486,20 +506,15 @@ public class DisplayModeDirector { Settings.System.getUriFor(Settings.System.PEAK_REFRESH_RATE); private final Uri mLowPowerModeSetting = Settings.Global.getUriFor(Settings.Global.LOW_POWER_MODE); - private final Uri mBrightnessSetting = - Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS); private final Context mContext; private final float mDefaultPeakRefreshRate; - private final int mBrightnessThreshold; SettingsObserver(@NonNull Context context, @NonNull Handler handler) { super(handler); mContext = context; mDefaultPeakRefreshRate = (float) context.getResources().getInteger( R.integer.config_defaultPeakRefreshRate); - mBrightnessThreshold = context.getResources().getInteger( - R.integer.config_brightnessThresholdOfPeakRefreshRate); } public void observe() { @@ -508,14 +523,9 @@ public class DisplayModeDirector { UserHandle.USER_SYSTEM); cr.registerContentObserver(mLowPowerModeSetting, false /*notifyDescendants*/, this, UserHandle.USER_SYSTEM); - if (mBrightnessThreshold >= 0) { - cr.registerContentObserver(mBrightnessSetting, false /*notifyDescendants*/, this, - UserHandle.USER_SYSTEM); - } synchronized (mLock) { updateRefreshRateSettingLocked(); updateLowPowerModeSettingLocked(); - updateBrightnessSettingLocked(); } } @@ -526,8 +536,6 @@ public class DisplayModeDirector { updateRefreshRateSettingLocked(); } else if (mLowPowerModeSetting.equals(uri)) { updateLowPowerModeSettingLocked(); - } else if (mBrightnessThreshold >=0 && mBrightnessSetting.equals(uri)) { - updateBrightnessSettingLocked(); } } } @@ -542,6 +550,7 @@ public class DisplayModeDirector { vote = null; } updateVoteLocked(Vote.PRIORITY_LOW_POWER_MODE, vote); + mBrightnessObserver.onLowPowerModeEnabled(inLowPowerMode); } private void updateRefreshRateSettingLocked() { @@ -549,23 +558,7 @@ public class DisplayModeDirector { Settings.System.PEAK_REFRESH_RATE, mDefaultPeakRefreshRate); Vote vote = Vote.forRefreshRates(0f, peakRefreshRate); updateVoteLocked(Vote.PRIORITY_USER_SETTING_REFRESH_RATE, vote); - } - - private void updateBrightnessSettingLocked() { - int brightness = Settings.System.getInt(mContext.getContentResolver(), - Settings.System.SCREEN_BRIGHTNESS, -1); - - if (brightness < 0) { - return; - } - - final Vote vote; - if (brightness <= mBrightnessThreshold) { - vote = Vote.forRefreshRates(0f, 60f); - } else { - vote = null; - } - updateVoteLocked(Vote.PRIORITY_LOW_BRIGHTNESS, vote); + mBrightnessObserver.onPeakRefreshRateEnabled(peakRefreshRate > 60f); } public void dumpLocked(PrintWriter pw) { @@ -715,4 +708,240 @@ public class DisplayModeDirector { } } } + + /** + * This class manages brightness threshold for switching between 60 hz and higher refresh rate. + * See more information at the definition of + * {@link R.array#config_brightnessThresholdsOfPeakRefreshRate} and + * {@link R.array#config_ambientThresholdsOfPeakRefreshRate}. + */ + private class BrightnessObserver extends ContentObserver { + private final Uri mDisplayBrightnessSetting = + Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS); + + private final static int LIGHT_SENSOR_RATE_MS = 250; + private final int[] mDisplayBrightnessThresholds; + private final int[] mAmbientBrightnessThresholds; + // valid threshold if any item from the array >= 0 + private boolean mShouldObserveDisplayChange; + private boolean mShouldObserveAmbientChange; + + private SensorManager mSensorManager; + private Sensor mLightSensor; + // Take it as low brightness before valid sensor data comes + private float mAmbientLux = -1.0f; + private AmbientFilter mAmbientFilter; + + private final Context mContext; + private ScreenStateReceiver mScreenStateReceiver; + + // Enable light sensor only when screen is on, peak refresh rate enabled and low power mode + // off. After initialization, these states will be updated from the same handler thread. + private boolean mScreenOn = false; + private boolean mPeakRefreshRateEnabled = false; + private boolean mLowPowerModeEnabled = false; + + BrightnessObserver(Context context, Handler handler) { + super(handler); + mContext = context; + mDisplayBrightnessThresholds = context.getResources().getIntArray( + R.array.config_brightnessThresholdsOfPeakRefreshRate); + mAmbientBrightnessThresholds = context.getResources().getIntArray( + R.array.config_ambientThresholdsOfPeakRefreshRate); + if (mDisplayBrightnessThresholds.length != mAmbientBrightnessThresholds.length) { + throw new RuntimeException("display brightness threshold array and ambient " + + "brightness threshold array have different length"); + } + + mShouldObserveDisplayChange = checkShouldObserve(mDisplayBrightnessThresholds); + mShouldObserveAmbientChange = checkShouldObserve(mAmbientBrightnessThresholds); + } + + public void observe(SensorManager sensorManager) { + if (mShouldObserveDisplayChange) { + final ContentResolver cr = mContext.getContentResolver(); + cr.registerContentObserver(mDisplayBrightnessSetting, + false /*notifyDescendants*/, this, UserHandle.USER_SYSTEM); + } + + if (mShouldObserveAmbientChange) { + Resources resources = mContext.getResources(); + String lightSensorType = resources.getString( + com.android.internal.R.string.config_displayLightSensorType); + + Sensor lightSensor = null; + if (!TextUtils.isEmpty(lightSensorType)) { + List<Sensor> sensors = sensorManager.getSensorList(Sensor.TYPE_ALL); + for (int i = 0; i < sensors.size(); i++) { + Sensor sensor = sensors.get(i); + if (lightSensorType.equals(sensor.getStringType())) { + lightSensor = sensor; + break; + } + } + } + + if (lightSensor == null) { + lightSensor = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT); + } + + if (lightSensor != null) { + final Resources res = mContext.getResources(); + + mAmbientFilter = DisplayWhiteBalanceFactory.createBrightnessFilter(res); + mSensorManager = sensorManager; + mLightSensor = lightSensor; + + // Intent.ACTION_SCREEN_ON is not sticky. Check current screen status. + if (mContext.getSystemService(PowerManager.class).isInteractive()) { + onScreenOn(true); + } + mScreenStateReceiver = new ScreenStateReceiver(mContext); + } + } + + if (mShouldObserveDisplayChange || mShouldObserveAmbientChange) { + synchronized (mLock) { + onBrightnessChangedLocked(); + } + } + } + + public void onPeakRefreshRateEnabled(boolean b) { + if (mShouldObserveAmbientChange && mPeakRefreshRateEnabled != b) { + mPeakRefreshRateEnabled = b; + updateSensorStatus(); + } + } + + public void onLowPowerModeEnabled(boolean b) { + if (mShouldObserveAmbientChange && mLowPowerModeEnabled != b) { + mLowPowerModeEnabled = b; + updateSensorStatus(); + } + } + + public void dumpLocked(PrintWriter pw) { + pw.println(" BrightnessObserver"); + + for (int d: mDisplayBrightnessThresholds) { + pw.println(" mDisplayBrightnessThreshold: " + d); + } + + for (int d: mAmbientBrightnessThresholds) { + pw.println(" mAmbientBrightnessThreshold: " + d); + } + } + + @Override + public void onChange(boolean selfChange, Uri uri, int userId) { + synchronized (mLock) { + onBrightnessChangedLocked(); + } + } + + /** + * Checks to see if at least one value is positive, in which case it is necessary to listen + * to value changes. + */ + private boolean checkShouldObserve(int[] a) { + for (int d: a) { + if (d >= 0) { + return true; + } + } + + return false; + } + + private void onBrightnessChangedLocked() { + int brightness = Settings.System.getInt(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS, -1); + + Vote vote = null; + for (int i = 0; i < mDisplayBrightnessThresholds.length; i++) { + int disp = mDisplayBrightnessThresholds[i]; + int ambi = mAmbientBrightnessThresholds[i]; + + if (disp >= 0 && ambi >= 0) { + if (brightness <= disp && mAmbientLux <= ambi) { + vote = Vote.forRefreshRates(0f, 60f); + } + } else if (disp >= 0) { + if (brightness <= disp) { + vote = Vote.forRefreshRates(0f, 60f); + } + } else if (ambi >= 0) { + if (mAmbientLux <= ambi) { + vote = Vote.forRefreshRates(0f, 60f); + } + } + + if (vote != null) { + break; + } + } + + if (DEBUG) { + Slog.d(TAG, "Display brightness " + brightness + ", ambient lux " + mAmbientLux + + (vote != null ? " 60hz only" : " no refresh rate limit")); + } + updateVoteLocked(Vote.PRIORITY_LOW_BRIGHTNESS, vote); + } + + private void onScreenOn(boolean on) { + // Not check mShouldObserveAmbientChange because Screen status receiver is registered + // only when it is true. + if (mScreenOn != on) { + mScreenOn = on; + updateSensorStatus(); + } + } + + private void updateSensorStatus() { + if (mSensorManager == null || mLightSensorListener == null) { + return; + } + + if (mScreenOn && !mLowPowerModeEnabled && mPeakRefreshRateEnabled) { + mSensorManager.registerListener(mLightSensorListener, + mLightSensor, LIGHT_SENSOR_RATE_MS * 1000, mHandler); + } else { + mSensorManager.unregisterListener(mLightSensorListener); + } + } + + private final SensorEventListener mLightSensorListener = new SensorEventListener() { + @Override + public void onSensorChanged(SensorEvent event) { + long now = SystemClock.uptimeMillis(); + mAmbientFilter.addValue(now, event.values[0]); + mAmbientLux = mAmbientFilter.getEstimate(now); + + synchronized (mLock) { + onBrightnessChangedLocked(); + } + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + // Not used. + } + }; + + private final class ScreenStateReceiver extends BroadcastReceiver { + public ScreenStateReceiver(Context context) { + IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_SCREEN_OFF); + filter.addAction(Intent.ACTION_SCREEN_ON); + filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); + context.registerReceiver(this, filter, null, mHandler); + } + + @Override + public void onReceive(Context context, Intent intent) { + onScreenOn(Intent.ACTION_SCREEN_ON.equals(intent.getAction())); + } + } + } } diff --git a/services/core/java/com/android/server/display/color/ColorDisplayService.java b/services/core/java/com/android/server/display/color/ColorDisplayService.java index 2e5aafe0da72..7fb5b191a9b0 100644 --- a/services/core/java/com/android/server/display/color/ColorDisplayService.java +++ b/services/core/java/com/android/server/display/color/ColorDisplayService.java @@ -63,6 +63,8 @@ import android.provider.Settings.Secure; import android.provider.Settings.System; import android.util.MathUtils; import android.util.Slog; +import android.util.SparseIntArray; +import android.view.Display; import android.view.SurfaceControl; import android.view.accessibility.AccessibilityManager; import android.view.animation.AnimationUtils; @@ -171,6 +173,11 @@ public final class ColorDisplayService extends SystemService { private NightDisplayAutoMode mNightDisplayAutoMode; + /** + * Map of color modes -> display composition colorspace + */ + private SparseIntArray mColorModeCompositionColorSpaces = null; + public ColorDisplayService(Context context) { super(context); mHandler = new TintHandler(DisplayThread.get().getLooper()); @@ -267,6 +274,30 @@ public final class ColorDisplayService extends SystemService { return Secure.getIntForUser(cr, Secure.USER_SETUP_COMPLETE, 0, userHandle) == 1; } + private void setUpDisplayCompositionColorSpaces(Resources res) { + mColorModeCompositionColorSpaces = null; + + final int[] colorModes = res.getIntArray(R.array.config_displayCompositionColorModes); + if (colorModes == null) { + return; + } + + final int[] compSpaces = res.getIntArray(R.array.config_displayCompositionColorSpaces); + if (compSpaces == null) { + return; + } + + if (colorModes.length != compSpaces.length) { + Slog.e(TAG, "Number of composition color spaces doesn't match specified color modes"); + return; + } + + mColorModeCompositionColorSpaces = new SparseIntArray(colorModes.length); + for (int i = 0; i < colorModes.length; i++) { + mColorModeCompositionColorSpaces.put(colorModes[i], compSpaces[i]); + } + } + private void setUp() { Slog.d(TAG, "setUp: currentUser=" + mCurrentUser); @@ -359,6 +390,8 @@ public final class ColorDisplayService extends SystemService { onAccessibilityInversionChanged(); onAccessibilityDaltonizerChanged(); + setUpDisplayCompositionColorSpaces(getContext().getResources()); + // Set the color mode, if valid, and immediately apply the updated tint matrix based on the // existing activated state. This ensures consistency of tint across the color mode change. onDisplayColorModeChanged(getColorModeInternal()); @@ -450,6 +483,14 @@ public final class ColorDisplayService extends SystemService { } } + private int getCompositionColorSpace(int mode) { + if (mColorModeCompositionColorSpaces == null) { + return Display.COLOR_MODE_INVALID; + } + + return mColorModeCompositionColorSpaces.get(mode, Display.COLOR_MODE_INVALID); + } + private void onDisplayColorModeChanged(int mode) { if (mode == NOT_SET) { return; @@ -470,7 +511,8 @@ public final class ColorDisplayService extends SystemService { // DisplayTransformManager.needsLinearColorMatrix(), therefore it is dependent // on the state of DisplayTransformManager. final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class); - dtm.setColorMode(mode, mNightDisplayTintController.getMatrix()); + dtm.setColorMode(mode, mNightDisplayTintController.getMatrix(), + getCompositionColorSpace(mode)); if (mDisplayWhiteBalanceTintController.isAvailable(getContext())) { updateDisplayWhiteBalanceStatus(); diff --git a/services/core/java/com/android/server/display/color/DisplayTransformManager.java b/services/core/java/com/android/server/display/color/DisplayTransformManager.java index 5ff45a97706e..d5706a59b987 100644 --- a/services/core/java/com/android/server/display/color/DisplayTransformManager.java +++ b/services/core/java/com/android/server/display/color/DisplayTransformManager.java @@ -26,6 +26,7 @@ import android.os.ServiceManager; import android.os.SystemProperties; import android.util.Slog; import android.util.SparseArray; +import android.view.Display; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; @@ -77,6 +78,8 @@ public class DisplayTransformManager { @VisibleForTesting static final String PERSISTENT_PROPERTY_SATURATION = "persist.sys.sf.color_saturation"; @VisibleForTesting + static final String PERSISTENT_PROPERTY_COMPOSITION_COLOR_MODE = "persist.sys.sf.color_mode"; + @VisibleForTesting static final String PERSISTENT_PROPERTY_DISPLAY_COLOR = "persist.sys.sf.native_mode"; private static final float COLOR_SATURATION_NATURAL = 1.0f; @@ -251,23 +254,24 @@ public class DisplayTransformManager { /** * Sets color mode and updates night display transform values. */ - public boolean setColorMode(int colorMode, float[] nightDisplayMatrix) { + public boolean setColorMode(int colorMode, float[] nightDisplayMatrix, + int compositionColorMode) { if (colorMode == ColorDisplayManager.COLOR_MODE_NATURAL) { applySaturation(COLOR_SATURATION_NATURAL); - setDisplayColor(DISPLAY_COLOR_MANAGED); + setDisplayColor(DISPLAY_COLOR_MANAGED, compositionColorMode); } else if (colorMode == ColorDisplayManager.COLOR_MODE_BOOSTED) { applySaturation(COLOR_SATURATION_BOOSTED); - setDisplayColor(DISPLAY_COLOR_MANAGED); + setDisplayColor(DISPLAY_COLOR_MANAGED, compositionColorMode); } else if (colorMode == ColorDisplayManager.COLOR_MODE_SATURATED) { applySaturation(COLOR_SATURATION_NATURAL); - setDisplayColor(DISPLAY_COLOR_UNMANAGED); + setDisplayColor(DISPLAY_COLOR_UNMANAGED, compositionColorMode); } else if (colorMode == ColorDisplayManager.COLOR_MODE_AUTOMATIC) { applySaturation(COLOR_SATURATION_NATURAL); - setDisplayColor(DISPLAY_COLOR_ENHANCED); + setDisplayColor(DISPLAY_COLOR_ENHANCED, compositionColorMode); } else if (colorMode >= ColorDisplayManager.VENDOR_COLOR_MODE_RANGE_MIN && colorMode <= ColorDisplayManager.VENDOR_COLOR_MODE_RANGE_MAX) { applySaturation(COLOR_SATURATION_NATURAL); - setDisplayColor(colorMode); + setDisplayColor(colorMode, compositionColorMode); } setColorMatrix(LEVEL_COLOR_MATRIX_NIGHT_DISPLAY, nightDisplayMatrix); @@ -323,13 +327,21 @@ public class DisplayTransformManager { /** * Toggles native mode on/off in SurfaceFlinger. */ - private void setDisplayColor(int color) { + private void setDisplayColor(int color, int compositionColorMode) { SystemProperties.set(PERSISTENT_PROPERTY_DISPLAY_COLOR, Integer.toString(color)); + if (compositionColorMode != Display.COLOR_MODE_INVALID) { + SystemProperties.set(PERSISTENT_PROPERTY_COMPOSITION_COLOR_MODE, + Integer.toString(compositionColorMode)); + } + final IBinder flinger = ServiceManager.getService(SURFACE_FLINGER); if (flinger != null) { final Parcel data = Parcel.obtain(); data.writeInterfaceToken("android.ui.ISurfaceComposer"); data.writeInt(color); + if (compositionColorMode != Display.COLOR_MODE_INVALID) { + data.writeInt(compositionColorMode); + } try { flinger.transact(SURFACE_FLINGER_TRANSACTION_DISPLAY_COLOR, data, null, 0); } catch (RemoteException ex) { diff --git a/services/core/java/com/android/server/display/whitebalance/AmbientFilter.java b/services/core/java/com/android/server/display/whitebalance/AmbientFilter.java index 123cd73a3ff3..35808974b9e4 100644 --- a/services/core/java/com/android/server/display/whitebalance/AmbientFilter.java +++ b/services/core/java/com/android/server/display/whitebalance/AmbientFilter.java @@ -36,7 +36,7 @@ import java.util.Arrays; * - {@link WeightedMovingAverageAmbientFilter} * A weighted average prioritising recent changes. */ -abstract class AmbientFilter { +abstract public class AmbientFilter { protected static final boolean DEBUG = false; // Enable for verbose logs. @@ -156,8 +156,7 @@ abstract class AmbientFilter { /** * A weighted average prioritising recent changes. */ - @VisibleForTesting - public static class WeightedMovingAverageAmbientFilter extends AmbientFilter { + static class WeightedMovingAverageAmbientFilter extends AmbientFilter { // How long the latest ambient value change is predicted to last. private static final int PREDICTION_TIME = 100; // Milliseconds diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java index 4df7d6b14f25..bf0a1d16219d 100644 --- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java +++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceFactory.java @@ -115,8 +115,7 @@ public class DisplayWhiteBalanceFactory { * Creates a BrightnessFilter which functions as a weighted moving average buffer for recent * brightness values. */ - @VisibleForTesting - static AmbientFilter createBrightnessFilter(Resources resources) { + public static AmbientFilter createBrightnessFilter(Resources resources) { final int horizon = resources.getInteger( com.android.internal.R.integer.config_displayWhiteBalanceBrightnessFilterHorizon); final float intercept = getFloat(resources, @@ -129,7 +128,6 @@ public class DisplayWhiteBalanceFactory { + "expected config_displayWhiteBalanceBrightnessFilterIntercept"); } - /** * Creates an ambient color sensor instance to redirect sensor data to callbacks. */ diff --git a/services/core/java/com/android/server/job/controllers/TimeController.java b/services/core/java/com/android/server/job/controllers/TimeController.java index ababad961c2d..4c11947212f9 100644 --- a/services/core/java/com/android/server/job/controllers/TimeController.java +++ b/services/core/java/com/android/server/job/controllers/TimeController.java @@ -18,20 +18,13 @@ package com.android.server.job.controllers; import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; -import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AlarmManager; import android.app.AlarmManager.OnAlarmListener; -import android.content.ContentResolver; import android.content.Context; -import android.database.ContentObserver; -import android.net.Uri; -import android.os.Handler; import android.os.Process; import android.os.UserHandle; import android.os.WorkSource; -import android.provider.Settings; -import android.util.KeyValueListParser; import android.util.Log; import android.util.Slog; import android.util.TimeUtils; @@ -39,7 +32,6 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.IndentingPrintWriter; -import com.android.server.job.ConstantsProto; import com.android.server.job.JobSchedulerService; import com.android.server.job.StateControllerProto; @@ -63,9 +55,6 @@ public final class TimeController extends StateController { /** Delay alarm tag for logging purposes */ private final String DELAY_TAG = "*job.delay*"; - private final Handler mHandler; - private final TcConstants mTcConstants; - private long mNextJobExpiredElapsedMillis; private long mNextDelayExpiredElapsedMillis; @@ -81,14 +70,6 @@ public final class TimeController extends StateController { mNextJobExpiredElapsedMillis = Long.MAX_VALUE; mNextDelayExpiredElapsedMillis = Long.MAX_VALUE; mChainedAttributionEnabled = mService.isChainedAttributionEnabled(); - - mHandler = new Handler(mContext.getMainLooper()); - mTcConstants = new TcConstants(mHandler); - } - - @Override - public void onSystemServicesReady() { - mTcConstants.start(mContext.getContentResolver()); } /** @@ -133,20 +114,16 @@ public final class TimeController extends StateController { job.setTrackingController(JobStatus.TRACKING_TIME); WorkSource ws = deriveWorkSource(job.getSourceUid(), job.getSourcePackageName()); - final long deadlineExpiredElapsed = - job.hasDeadlineConstraint() ? job.getLatestRunTimeElapsed() : Long.MAX_VALUE; - final long delayExpiredElapsed = - job.hasTimingDelayConstraint() ? job.getEarliestRunTime() : Long.MAX_VALUE; - if (mTcConstants.SKIP_NOT_READY_JOBS) { - if (wouldBeReadyWithConstraintLocked(job, JobStatus.CONSTRAINT_TIMING_DELAY)) { - maybeUpdateDelayAlarmLocked(delayExpiredElapsed, ws); - } - if (wouldBeReadyWithConstraintLocked(job, JobStatus.CONSTRAINT_DEADLINE)) { - maybeUpdateDeadlineAlarmLocked(deadlineExpiredElapsed, ws); - } - } else { - maybeUpdateDelayAlarmLocked(delayExpiredElapsed, ws); - maybeUpdateDeadlineAlarmLocked(deadlineExpiredElapsed, ws); + + // Only update alarms if the job would be ready with the relevant timing constraint + // satisfied. + if (job.hasTimingDelayConstraint() + && wouldBeReadyWithConstraintLocked(job, JobStatus.CONSTRAINT_TIMING_DELAY)) { + maybeUpdateDelayAlarmLocked(job.getEarliestRunTime(), ws); + } + if (job.hasDeadlineConstraint() + && wouldBeReadyWithConstraintLocked(job, JobStatus.CONSTRAINT_DEADLINE)) { + maybeUpdateDeadlineAlarmLocked(job.getLatestRunTimeElapsed(), ws); } } } @@ -168,10 +145,6 @@ public final class TimeController extends StateController { @Override public void evaluateStateLocked(JobStatus job) { - if (!mTcConstants.SKIP_NOT_READY_JOBS) { - return; - } - final long nowElapsedMillis = sElapsedRealtimeClock.millis(); // Check deadline constraint first because if it's satisfied, we avoid a little bit of @@ -261,9 +234,7 @@ public final class TimeController extends StateController { } it.remove(); } else { // Sorted by expiry time, so take the next one and stop. - if (mTcConstants.SKIP_NOT_READY_JOBS - && !wouldBeReadyWithConstraintLocked( - job, JobStatus.CONSTRAINT_DEADLINE)) { + if (!wouldBeReadyWithConstraintLocked(job, JobStatus.CONSTRAINT_DEADLINE)) { if (DEBUG) { Slog.i(TAG, "Skipping " + job + " because deadline won't make it ready."); @@ -321,9 +292,7 @@ public final class TimeController extends StateController { ready = true; } } else { - if (mTcConstants.SKIP_NOT_READY_JOBS - && !wouldBeReadyWithConstraintLocked( - job, JobStatus.CONSTRAINT_TIMING_DELAY)) { + if (!wouldBeReadyWithConstraintLocked(job, JobStatus.CONSTRAINT_TIMING_DELAY)) { if (DEBUG) { Slog.i(TAG, "Skipping " + job + " because delay won't make it ready."); @@ -458,81 +427,6 @@ public final class TimeController extends StateController { checkExpiredDelaysAndResetAlarm(); } - @VisibleForTesting - class TcConstants extends ContentObserver { - private ContentResolver mResolver; - private final KeyValueListParser mParser = new KeyValueListParser(','); - - private static final String KEY_SKIP_NOT_READY_JOBS = "skip_not_ready_jobs"; - - private static final boolean DEFAULT_SKIP_NOT_READY_JOBS = true; - - /** - * Whether or not TimeController should skip setting wakeup alarms for jobs that aren't - * ready now. - */ - public boolean SKIP_NOT_READY_JOBS = DEFAULT_SKIP_NOT_READY_JOBS; - - /** - * Creates a content observer. - * - * @param handler The handler to run {@link #onChange} on, or null if none. - */ - TcConstants(Handler handler) { - super(handler); - } - - private void start(ContentResolver resolver) { - mResolver = resolver; - mResolver.registerContentObserver(Settings.Global.getUriFor( - Settings.Global.JOB_SCHEDULER_TIME_CONTROLLER_CONSTANTS), false, this); - onChange(true, null); - } - - @Override - public void onChange(boolean selfChange, Uri uri) { - final String constants = Settings.Global.getString( - mResolver, Settings.Global.JOB_SCHEDULER_TIME_CONTROLLER_CONSTANTS); - - try { - mParser.setString(constants); - } catch (Exception e) { - // Failed to parse the settings string, log this and move on with defaults. - Slog.e(TAG, "Bad jobscheduler time controller settings", e); - } - - final boolean oldVal = SKIP_NOT_READY_JOBS; - SKIP_NOT_READY_JOBS = mParser.getBoolean( - KEY_SKIP_NOT_READY_JOBS, DEFAULT_SKIP_NOT_READY_JOBS); - - if (oldVal != SKIP_NOT_READY_JOBS) { - synchronized (mLock) { - recheckAlarmsLocked(); - } - } - } - - private void dump(IndentingPrintWriter pw) { - pw.println(); - pw.println("TimeController:"); - pw.increaseIndent(); - pw.printPair(KEY_SKIP_NOT_READY_JOBS, SKIP_NOT_READY_JOBS).println(); - pw.decreaseIndent(); - } - - private void dump(ProtoOutputStream proto) { - final long tcToken = proto.start(ConstantsProto.TIME_CONTROLLER); - proto.write(ConstantsProto.TimeController.SKIP_NOT_READY_JOBS, SKIP_NOT_READY_JOBS); - proto.end(tcToken); - } - } - - @VisibleForTesting - @NonNull - TcConstants getTcConstants() { - return mTcConstants; - } - @Override public void dumpControllerStateLocked(IndentingPrintWriter pw, Predicate<JobStatus> predicate) { @@ -607,14 +501,4 @@ public final class TimeController extends StateController { proto.end(mToken); proto.end(token); } - - @Override - public void dumpConstants(IndentingPrintWriter pw) { - mTcConstants.dump(pw); - } - - @Override - public void dumpConstants(ProtoOutputStream proto) { - mTcConstants.dump(proto); - } } diff --git a/services/core/java/com/android/server/locksettings/PasswordSlotManager.java b/services/core/java/com/android/server/locksettings/PasswordSlotManager.java index 5cbd237a0722..4ef63c05325c 100644 --- a/services/core/java/com/android/server/locksettings/PasswordSlotManager.java +++ b/services/core/java/com/android/server/locksettings/PasswordSlotManager.java @@ -122,7 +122,7 @@ public class PasswordSlotManager { */ public void markSlotDeleted(int slot) throws RuntimeException { ensureSlotMapLoaded(); - if (mSlotMap.containsKey(slot) && mSlotMap.get(slot) != getMode()) { + if (mSlotMap.containsKey(slot) && !mSlotMap.get(slot).equals(getMode())) { throw new RuntimeException("password slot " + slot + " cannot be deleted"); } mSlotMap.remove(slot); diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java index 01936c9159dd..12137fe8d841 100644 --- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java @@ -32,16 +32,15 @@ import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.os.Looper; -import android.os.Message; import android.os.RemoteException; import android.text.TextUtils; import android.util.ArrayMap; import android.util.Log; -import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.function.pooled.PooledLambda; import java.io.PrintWriter; import java.lang.ref.WeakReference; @@ -204,13 +203,15 @@ class MediaRouter2ServiceImpl { UserRecord oldUser = mUserRecords.get(oldUserId); if (oldUser != null) { - oldUser.mHandler.sendEmptyMessage(MediaRouterService.UserHandler.MSG_STOP); + oldUser.mHandler.sendMessage( + obtainMessage(UserHandler::stop, oldUser.mHandler)); disposeUserIfNeededLocked(oldUser); // since no longer current user } UserRecord newUser = mUserRecords.get(userId); if (newUser != null) { - newUser.mHandler.sendEmptyMessage(MediaRouterService.UserHandler.MSG_START); + newUser.mHandler.sendMessage( + obtainMessage(UserHandler::start, newUser.mHandler)); } } } @@ -296,8 +297,9 @@ class MediaRouter2ServiceImpl { if (clientRecord != null) { clientRecord.mControlCategories = categories; - clientRecord.mUserRecord.mHandler.obtainMessage( - UserHandler.MSG_UPDATE_CLIENT_USAGE, clientRecord).sendToTarget(); + clientRecord.mUserRecord.mHandler.sendMessage( + obtainMessage(UserHandler::updateClientUsage, + clientRecord.mUserRecord.mHandler, clientRecord)); } } @@ -307,9 +309,9 @@ class MediaRouter2ServiceImpl { ClientRecord clientRecord = mAllClientRecords.get(binder); if (clientRecord != null) { - Pair<MediaRoute2Info, Intent> obj = new Pair<>(route, request); - clientRecord.mUserRecord.mHandler.obtainMessage( - UserHandler.MSG_SEND_CONTROL_REQUEST, obj).sendToTarget(); + clientRecord.mUserRecord.mHandler.sendMessage( + obtainMessage(UserHandler::sendControlRequest, + clientRecord.mUserRecord.mHandler, route, request)); } } @@ -346,8 +348,9 @@ class MediaRouter2ServiceImpl { final int count = userRecord.mClientRecords.size(); for (int i = 0; i < count; i++) { ClientRecord clientRecord = userRecord.mClientRecords.get(i); - clientRecord.mUserRecord.mHandler.obtainMessage( - UserHandler.MSG_UPDATE_CLIENT_USAGE, clientRecord).sendToTarget(); + clientRecord.mUserRecord.mHandler.sendMessage( + obtainMessage(UserHandler::updateClientUsage, + clientRecord.mUserRecord.mHandler, clientRecord)); } } } @@ -381,7 +384,8 @@ class MediaRouter2ServiceImpl { Slog.d(TAG, userRecord + ": Initialized"); } if (userRecord.mUserId == mCurrentUserId) { - userRecord.mHandler.sendEmptyMessage(UserHandler.MSG_START); + userRecord.mHandler.sendMessage( + obtainMessage(UserHandler::start, userRecord.mHandler)); } } @@ -497,13 +501,6 @@ class MediaRouter2ServiceImpl { MediaRoute2ProviderWatcher.Callback, MediaRoute2ProviderProxy.Callback { - //TODO: Use PooledLambda instead. - static final int MSG_START = 1; - static final int MSG_STOP = 2; - - static final int MSG_UPDATE_CLIENT_USAGE = 11; - static final int MSG_SEND_CONTROL_REQUEST = 12; - private final WeakReference<MediaRouter2ServiceImpl> mServiceRef; private final UserRecord mUserRecord; private final MediaRoute2ProviderWatcher mWatcher; @@ -524,28 +521,6 @@ class MediaRouter2ServiceImpl { this, mUserRecord.mUserId); } - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MSG_START: { - start(); - break; - } - case MSG_STOP: { - stop(); - break; - } - case MSG_UPDATE_CLIENT_USAGE: { - updateClientUsage((ClientRecord) msg.obj); - break; - } - case MSG_SEND_CONTROL_REQUEST: { - Pair<MediaRoute2Info, Intent> obj = (Pair<MediaRoute2Info, Intent>) msg.obj; - sendControlRequest(obj.first, obj.second); - } - } - } - private void start() { if (!mRunning) { mRunning = true; @@ -613,7 +588,7 @@ class MediaRouter2ServiceImpl { private void scheduleUpdateManagerState() { if (!mManagerStateUpdateScheduled) { mManagerStateUpdateScheduled = true; - post(this::updateManagerState); + sendMessage(PooledLambda.obtainMessage(UserHandler::updateManagerState, this)); } } diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index d3346cddf3d0..b4a80994cc65 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.GuardedBy; 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.RoSystemProperties; import com.android.internal.telephony.PhoneConstants; import com.android.internal.util.ArrayUtils; import com.android.internal.util.ConcurrentUtils; @@ -403,8 +402,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private IConnectivityManager mConnManager; private PowerManagerInternal mPowerManagerInternal; private IDeviceIdleController mDeviceIdleController; + + /** Current cached value of the current Battery Saver mode's setting for restrict background. */ @GuardedBy("mUidRulesFirstLock") - private PowerSaveState mRestrictBackgroundPowerState; + private boolean mRestrictBackgroundLowPowerMode; // Store the status of restrict background before turning on battery saver. // Used to restore mRestrictBackground when battery saver is turned off. @@ -771,11 +772,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // Update the restrictBackground if battery saver is turned on mRestrictBackgroundBeforeBsm = mLoadedRestrictBackground; - mRestrictBackgroundPowerState = mPowerManagerInternal - .getLowPowerState(ServiceType.DATA_SAVER); - final boolean localRestrictBackground = - mRestrictBackgroundPowerState.batterySaverEnabled; - if (localRestrictBackground && !mLoadedRestrictBackground) { + mRestrictBackgroundLowPowerMode = mPowerManagerInternal + .getLowPowerState(ServiceType.DATA_SAVER).batterySaverEnabled; + if (mRestrictBackgroundLowPowerMode && !mLoadedRestrictBackground) { mLoadedRestrictBackground = true; } mPowerManagerInternal.registerLowPowerModeObserver( @@ -1354,7 +1353,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final Intent viewIntent = buildViewDataUsageIntent(res, policy.template); // TODO: Resolve to single code path. - if (isHeadlessSystemUserBuild()) { + if (UserManager.isHeadlessSystemUserMode()) { builder.setContentIntent(PendingIntent.getActivityAsUser( mContext, 0, viewIntent, PendingIntent.FLAG_UPDATE_CURRENT, /* options= */ null, UserHandle.CURRENT)); @@ -1382,7 +1381,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final Intent intent = buildNetworkOverLimitIntent(res, policy.template); // TODO: Resolve to single code path. - if (isHeadlessSystemUserBuild()) { + if (UserManager.isHeadlessSystemUserMode()) { builder.setContentIntent(PendingIntent.getActivityAsUser( mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT, /* options= */ null, UserHandle.CURRENT)); @@ -1413,7 +1412,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final Intent intent = buildViewDataUsageIntent(res, policy.template); // TODO: Resolve to single code path. - if (isHeadlessSystemUserBuild()) { + if (UserManager.isHeadlessSystemUserMode()) { builder.setContentIntent(PendingIntent.getActivityAsUser( mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT, /* options= */ null, UserHandle.CURRENT)); @@ -1440,7 +1439,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final Intent viewIntent = buildViewDataUsageIntent(res, policy.template); // TODO: Resolve to single code path. - if (isHeadlessSystemUserBuild()) { + if (UserManager.isHeadlessSystemUserMode()) { builder.setContentIntent(PendingIntent.getActivityAsUser( mContext, 0, viewIntent, PendingIntent.FLAG_UPDATE_CURRENT, /* options= */ null, UserHandle.CURRENT)); @@ -2900,7 +2899,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { sendRestrictBackgroundChangedMsg(); mLogger.restrictBackgroundChanged(oldRestrictBackground, mRestrictBackground); - if (mRestrictBackgroundPowerState.globalBatterySaverEnabled) { + if (mRestrictBackgroundLowPowerMode) { mRestrictBackgroundChangedInBsm = true; } synchronized (mNetworkPoliciesSecondLock) { @@ -4907,17 +4906,21 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { @GuardedBy("mUidRulesFirstLock") @VisibleForTesting void updateRestrictBackgroundByLowPowerModeUL(final PowerSaveState result) { - mRestrictBackgroundPowerState = result; + if (mRestrictBackgroundLowPowerMode == result.batterySaverEnabled) { + // Nothing changed. Nothing to do. + return; + } + mRestrictBackgroundLowPowerMode = result.batterySaverEnabled; - boolean restrictBackground = result.batterySaverEnabled; + boolean restrictBackground = mRestrictBackgroundLowPowerMode; boolean shouldInvokeRestrictBackground; - // store the temporary mRestrictBackgroundChangedInBsm and update it at last + // store the temporary mRestrictBackgroundChangedInBsm and update it at the end. boolean localRestrictBgChangedInBsm = mRestrictBackgroundChangedInBsm; - if (result.globalBatterySaverEnabled) { + if (mRestrictBackgroundLowPowerMode) { // Try to turn on restrictBackground if (1) it is off and (2) batter saver need to // turn it on. - shouldInvokeRestrictBackground = !mRestrictBackground && result.batterySaverEnabled; + shouldInvokeRestrictBackground = !mRestrictBackground; mRestrictBackgroundBeforeBsm = mRestrictBackground; localRestrictBgChangedInBsm = false; } else { @@ -5297,10 +5300,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { return (bundle != null) ? bundle.getBoolean(key, defaultValue) : defaultValue; } - private static boolean isHeadlessSystemUserBuild() { - return RoSystemProperties.MULTIUSER_HEADLESS_SYSTEM_USER; - } - private class NotificationId { private final String mTag; private final int mId; diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 976931c5e86b..d30895efbdc3 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -25,9 +25,12 @@ 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; import static android.app.NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED; +import static android.app.NotificationManager.ACTION_AUTOMATIC_ZEN_RULE_STATUS_CHANGED; import static android.app.NotificationManager.ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED; import static android.app.NotificationManager.ACTION_NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED; import static android.app.NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED; +import static android.app.NotificationManager.EXTRA_AUTOMATIC_ZEN_RULE_ID; +import static android.app.NotificationManager.EXTRA_AUTOMATIC_ZEN_RULE_STATUS; import static android.app.NotificationManager.IMPORTANCE_LOW; import static android.app.NotificationManager.IMPORTANCE_MIN; import static android.app.NotificationManager.IMPORTANCE_NONE; @@ -398,7 +401,7 @@ public class NotificationManagerService extends SystemService { // for enabling and disabling notification pulse behavior boolean mScreenOn = true; - protected boolean mInCall = false; + protected boolean mInCallStateOffHook = false; boolean mNotificationPulseEnabled; private Uri mInCallNotificationUri; @@ -1296,7 +1299,7 @@ public class NotificationManagerService extends SystemService { mScreenOn = false; updateNotificationPulse(); } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) { - mInCall = TelephonyManager.EXTRA_STATE_OFFHOOK + mInCallStateOffHook = TelephonyManager.EXTRA_STATE_OFFHOOK .equals(intent.getStringExtra(TelephonyManager.EXTRA_STATE)); updateNotificationPulse(); } else if (action.equals(Intent.ACTION_USER_STOPPED)) { @@ -1645,6 +1648,15 @@ public class NotificationManagerService extends SystemService { sendRegisteredOnlyBroadcast(NotificationManager.ACTION_NOTIFICATION_POLICY_CHANGED); mRankingHandler.requestSort(); } + + @Override + void onAutomaticRuleStatusChanged(int userId, String pkg, String id, int status) { + Intent intent = new Intent(ACTION_AUTOMATIC_ZEN_RULE_STATUS_CHANGED); + intent.setPackage(pkg); + intent.putExtra(EXTRA_AUTOMATIC_ZEN_RULE_ID, id); + intent.putExtra(EXTRA_AUTOMATIC_ZEN_RULE_STATUS, status); + getContext().sendBroadcastAsUser(intent, UserHandle.of(userId)); + } }); mPreferencesHelper = new PreferencesHelper(getContext(), mPackageManagerClient, @@ -5812,7 +5824,7 @@ public class NotificationManagerService extends SystemService { } if (DBG) Slog.v(TAG, "Interrupting!"); if (hasValidSound) { - if (mInCall) { + if (isInCall()) { playInCallNotification(); beep = true; } else { @@ -5826,7 +5838,7 @@ public class NotificationManagerService extends SystemService { final boolean ringerModeSilent = mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_SILENT; - if (!mInCall && hasValidVibrate && !ringerModeSilent) { + if (!isInCall() && hasValidVibrate && !ringerModeSilent) { buzz = playVibration(record, vibration, hasValidSound); if(buzz) { mVibrateNotificationKey = key; @@ -5914,7 +5926,7 @@ public class NotificationManagerService extends SystemService { return false; } // not if in call or the screen's on - if (mInCall || mScreenOn) { + if (isInCall() || mScreenOn) { return false; } @@ -7015,7 +7027,7 @@ public class NotificationManagerService extends SystemService { } // Don't flash while we are in a call or screen is on - if (ledNotification == null || mInCall || mScreenOn) { + if (ledNotification == null || isInCall() || mScreenOn) { mNotificationLight.turnOff(); } else { NotificationRecord.Light light = ledNotification.getLight(); @@ -7483,6 +7495,18 @@ public class NotificationManagerService extends SystemService { } } + private boolean isInCall() { + if (mInCallStateOffHook) { + return true; + } + int audioMode = mAudioManager.getMode(); + if (audioMode == AudioManager.MODE_IN_CALL + || audioMode == AudioManager.MODE_IN_COMMUNICATION) { + return true; + } + return false; + } + public class NotificationAssistants extends ManagedServices { static final String TAG_ENABLED_NOTIFICATION_ASSISTANTS = "enabled_assistants"; diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index f81015dae468..ee948b28e546 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -16,6 +16,10 @@ package com.android.server.notification; +import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_DISABLED; +import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_ENABLED; +import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_REMOVED; + import android.app.AppOpsManager; import android.app.AutomaticZenRule; import android.app.Notification; @@ -351,6 +355,12 @@ public class ZenModeHelper { "Cannot update rules not owned by your condition provider"); } } + if (rule.enabled != automaticZenRule.isEnabled()) { + dispatchOnAutomaticRuleStatusChanged(mConfig.user, rule.pkg, ruleId, + automaticZenRule.isEnabled() + ? AUTOMATIC_RULE_STATUS_ENABLED : AUTOMATIC_RULE_STATUS_DISABLED); + } + populateZenRule(automaticZenRule, rule, false); return setConfigLocked(newConfig, reason, rule.component, true); } @@ -370,6 +380,8 @@ public class ZenModeHelper { throw new SecurityException( "Cannot delete rules not owned by your condition provider"); } + dispatchOnAutomaticRuleStatusChanged( + mConfig.user, rule.pkg, id, AUTOMATIC_RULE_STATUS_REMOVED); return setConfigLocked(newConfig, reason, null, true); } } @@ -1120,6 +1132,13 @@ public class ZenModeHelper { } } + private void dispatchOnAutomaticRuleStatusChanged(int userId, String pkg, String id, + int status) { + for (Callback callback : mCallbacks) { + callback.onAutomaticRuleStatusChanged(userId, pkg, id, status); + } + } + private ZenModeConfig readDefaultConfig(Resources resources) { XmlResourceParser parser = null; try { @@ -1509,5 +1528,6 @@ public class ZenModeHelper { void onZenModeChanged() {} void onPolicyChanged() {} void onConsolidatedPolicyChanged() {} + void onAutomaticRuleStatusChanged(int userId, String pkg, String id, int status) {} } } diff --git a/services/core/java/com/android/server/om/IdmapManager.java b/services/core/java/com/android/server/om/IdmapManager.java index b604aa87f8f3..1f20968ca0c8 100644 --- a/services/core/java/com/android/server/om/IdmapManager.java +++ b/services/core/java/com/android/server/om/IdmapManager.java @@ -237,14 +237,9 @@ class IdmapManager { return fulfilledPolicies | IIdmap2.POLICY_OEM_PARTITION; } - // Check partitions for which there exists no policy so overlays on these partitions will - // not fulfill the system policy. - if (ai.isProductServices()) { - return fulfilledPolicies; - } - + // System_ext partition (/system_ext) is considered as system // Check this last since every partition except for data is scanned as system in the PMS. - if (ai.isSystemApp()) { + if (ai.isSystemApp() || ai.isSystemExt()) { return fulfilledPolicies | IIdmap2.POLICY_SYSTEM_PARTITION; } diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java index 9094e1bf4c5a..e5a2e777a796 100644 --- a/services/core/java/com/android/server/pm/OtaDexoptService.java +++ b/services/core/java/com/android/server/pm/OtaDexoptService.java @@ -376,12 +376,12 @@ public class OtaDexoptService extends IOtaDexopt.Stub { continue; } - // If the path is in /system, /vendor, /product or /product_services, ignore. It will + // If the path is in /system, /vendor, /product or /system_ext, ignore. It will // have been ota-dexopted into /data/ota and moved into the dalvik-cache already. if (pkg.codePath.startsWith("/system") || pkg.codePath.startsWith("/vendor") || pkg.codePath.startsWith("/product") - || pkg.codePath.startsWith("/product_services")) { + || pkg.codePath.startsWith("/system_ext")) { continue; } diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 6f9a918d105c..4eddb9301a69 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -23,6 +23,7 @@ import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STOR import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR; import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK; import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SPLIT; +import static android.content.pm.PackageParser.APEX_FILE_EXTENSION; import static android.content.pm.PackageParser.APK_FILE_EXTENSION; import static android.system.OsConstants.O_CREAT; import static android.system.OsConstants.O_RDONLY; @@ -1484,7 +1485,29 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { "Too many files for apex install"); } - mResolvedBaseFile = addedFiles[0]; + try { + resolveStageDirLocked(); + } catch (IOException e) { + throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR, + "Failed to resolve stage location", e); + } + + File addedFile = addedFiles[0]; // there is only one file + + // Ensure file name has proper suffix + final String sourceName = addedFile.getName(); + final String targetName = sourceName.endsWith(APEX_FILE_EXTENSION) + ? sourceName + : sourceName + APEX_FILE_EXTENSION; + if (!FileUtils.isValidExtFilename(targetName)) { + throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, + "Invalid filename: " + targetName); + } + + final File targetFile = new File(mResolvedStageDir, targetName); + resolveAndStageFile(addedFile, targetFile); + + mResolvedBaseFile = targetFile; } /** diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 8660d191960c..c72b38aa0919 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -311,7 +311,6 @@ import com.android.server.pm.dex.DexoptOptions; import com.android.server.pm.dex.PackageDexUsage; import com.android.server.pm.dex.ViewCompiler; import com.android.server.pm.permission.BasePermission; -import com.android.server.pm.permission.DefaultPermissionGrantPolicy; import com.android.server.pm.permission.PermissionManagerService; import com.android.server.pm.permission.PermissionManagerServiceInternal; import com.android.server.pm.permission.PermissionsState; @@ -472,7 +471,7 @@ public class PackageManagerService extends IPackageManager.Stub static final int SCAN_AS_OEM = 1 << 19; static final int SCAN_AS_VENDOR = 1 << 20; static final int SCAN_AS_PRODUCT = 1 << 21; - static final int SCAN_AS_PRODUCT_SERVICES = 1 << 22; + static final int SCAN_AS_SYSTEM_EXT = 1 << 22; static final int SCAN_AS_ODM = 1 << 23; @IntDef(flag = true, prefix = { "SCAN_" }, value = { @@ -584,7 +583,7 @@ public class PackageManagerService extends IPackageManager.Stub private static final String PRODUCT_OVERLAY_DIR = "/product/overlay"; - private static final String PRODUCT_SERVICES_OVERLAY_DIR = "/product_services/overlay"; + private static final String SYSTEM_EXT_OVERLAY_DIR = "/system_ext/overlay"; private static final String ODM_OVERLAY_DIR = "/odm/overlay"; @@ -948,8 +947,6 @@ public class PackageManagerService extends IPackageManager.Stub final SparseArray<IntentFilterVerificationState> mIntentFilterVerificationStates = new SparseArray<>(); - // TODO remove this and go through mPermissonManager directly - final DefaultPermissionGrantPolicy mDefaultPermissionPolicy; // Internal interface for permission manager private final PermissionManagerServiceInternal mPermissionManager; // Public interface for permission manager @@ -992,15 +989,6 @@ public class PackageManagerService extends IPackageManager.Stub void receiveVerificationResponse(int verificationId); } - @GuardedBy("mPackages") - private PackageManagerInternal.DefaultBrowserProvider mDefaultBrowserProvider; - - @GuardedBy("mPackages") - private PackageManagerInternal.DefaultDialerProvider mDefaultDialerProvider; - - @GuardedBy("mPackages") - private PackageManagerInternal.DefaultHomeProvider mDefaultHomeProvider; - private class IntentVerifierProxy implements IntentFilterVerifier<ActivityIntentInfo> { private Context mContext; private ComponentName mIntentFilterVerifierComponent; @@ -1966,7 +1954,7 @@ public class PackageManagerService extends IPackageManager.Stub final PackageSetting pkgSetting = mSettings.mPackages.get(packageName); if (pkgSetting.getInstallReason(userId) != PackageManager.INSTALL_REASON_DEVICE_RESTORE) { - setDefaultBrowserAsyncLPw(null, userId); + mPermissionManager.setDefaultBrowser(null, true, true, userId); } } } @@ -2348,7 +2336,6 @@ public class PackageManagerService extends IPackageManager.Stub mPackages /*externalLock*/); mPermissionManagerService = (IPermissionManager) ServiceManager.getService("permissionmgr"); - mDefaultPermissionPolicy = mPermissionManager.getDefaultPermissionGrantPolicy(); mSettings = new Settings(Environment.getDataDirectory(), mPermissionManager.getPermissionSettings(), mPackages); } @@ -2540,7 +2527,7 @@ public class PackageManagerService extends IPackageManager.Stub scanFlags = scanFlags | SCAN_FIRST_BOOT_OR_UPGRADE; } - // Collect vendor/product/product_services overlay packages. (Do this before scanning + // Collect vendor/product/system_ext overlay packages. (Do this before scanning // any apps.) // For security and version matching reason, only consider overlay packages if they // reside in the right directory. @@ -2558,12 +2545,12 @@ public class PackageManagerService extends IPackageManager.Stub | SCAN_AS_SYSTEM | SCAN_AS_PRODUCT, 0); - scanDirTracedLI(new File(PRODUCT_SERVICES_OVERLAY_DIR), + scanDirTracedLI(new File(SYSTEM_EXT_OVERLAY_DIR), mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_AS_SYSTEM - | SCAN_AS_PRODUCT_SERVICES, + | SCAN_AS_SYSTEM_EXT, 0); scanDirTracedLI(new File(ODM_OVERLAY_DIR), mDefParseFlags @@ -2721,37 +2708,37 @@ public class PackageManagerService extends IPackageManager.Stub | SCAN_AS_PRODUCT, 0); - // Collected privileged /product_services packages. - File privilegedProductServicesAppDir = - new File(Environment.getProductServicesDirectory(), "priv-app"); + // Collected privileged /system_ext packages. + File privilegedSystemExtAppDir = + new File(Environment.getSystemExtDirectory(), "priv-app"); try { - privilegedProductServicesAppDir = - privilegedProductServicesAppDir.getCanonicalFile(); + privilegedSystemExtAppDir = + privilegedSystemExtAppDir.getCanonicalFile(); } catch (IOException e) { // failed to look up canonical path, continue with original one } - scanDirTracedLI(privilegedProductServicesAppDir, + scanDirTracedLI(privilegedSystemExtAppDir, mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_AS_SYSTEM - | SCAN_AS_PRODUCT_SERVICES + | SCAN_AS_SYSTEM_EXT | SCAN_AS_PRIVILEGED, 0); - // Collect ordinary /product_services packages. - File productServicesAppDir = new File(Environment.getProductServicesDirectory(), "app"); + // Collect ordinary /system_ext packages. + File systemExtAppDir = new File(Environment.getSystemExtDirectory(), "app"); try { - productServicesAppDir = productServicesAppDir.getCanonicalFile(); + systemExtAppDir = systemExtAppDir.getCanonicalFile(); } catch (IOException e) { // failed to look up canonical path, continue with original one } - scanDirTracedLI(productServicesAppDir, + scanDirTracedLI(systemExtAppDir, mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_AS_SYSTEM - | SCAN_AS_PRODUCT_SERVICES, + | SCAN_AS_SYSTEM_EXT, 0); // Prune any system packages that no longer exist. @@ -2981,23 +2968,23 @@ public class PackageManagerService extends IPackageManager.Stub scanFlags | SCAN_AS_SYSTEM | SCAN_AS_PRODUCT; - } else if (FileUtils.contains(privilegedProductServicesAppDir, scanFile)) { + } else if (FileUtils.contains(privilegedSystemExtAppDir, scanFile)) { reparseFlags = mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR; rescanFlags = scanFlags | SCAN_AS_SYSTEM - | SCAN_AS_PRODUCT_SERVICES + | SCAN_AS_SYSTEM_EXT | SCAN_AS_PRIVILEGED; - } else if (FileUtils.contains(productServicesAppDir, scanFile)) { + } else if (FileUtils.contains(systemExtAppDir, scanFile)) { reparseFlags = mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR; rescanFlags = scanFlags | SCAN_AS_SYSTEM - | SCAN_AS_PRODUCT_SERVICES; + | SCAN_AS_SYSTEM_EXT; } else { Slog.e(TAG, "Ignoring unexpected fallback path " + scanFile); continue; @@ -5499,40 +5486,6 @@ public class PackageManagerService extends IPackageManager.Stub } @Override - public boolean isPermissionRevokedByPolicy(String permission, String packageName, int userId) { - if (UserHandle.getCallingUserId() != userId) { - mContext.enforceCallingPermission( - android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, - "isPermissionRevokedByPolicy for user " + userId); - } - - if (checkPermission(permission, packageName, userId) - == PackageManager.PERMISSION_GRANTED) { - return false; - } - - final int callingUid = Binder.getCallingUid(); - if (getInstantAppPackageName(callingUid) != null) { - if (!isCallerSameApp(packageName, callingUid)) { - return false; - } - } else { - if (isInstantApp(packageName, userId)) { - return false; - } - } - - final long identity = Binder.clearCallingIdentity(); - try { - final int flags = mPermissionManager - .getPermissionFlags(permission, packageName, Binder.getCallingUid(), userId); - return (flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0; - } finally { - Binder.restoreCallingIdentity(identity); - } - } - - @Override public String getPermissionControllerPackageName() { synchronized (mPackages) { return mRequiredPermissionControllerPackage; @@ -5588,46 +5541,6 @@ public class PackageManagerService extends IPackageManager.Stub } @Override - public boolean shouldShowRequestPermissionRationale(String permName, - String packageName, int userId) { - if (UserHandle.getCallingUserId() != userId) { - mContext.enforceCallingPermission( - android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, - "canShowRequestPermissionRationale for user " + userId); - } - - final int uid = getPackageUid(packageName, MATCH_DEBUG_TRIAGED_MISSING, userId); - if (UserHandle.getAppId(getCallingUid()) != UserHandle.getAppId(uid)) { - return false; - } - - if (checkPermission(permName, packageName, userId) - == PackageManager.PERMISSION_GRANTED) { - return false; - } - - final int flags; - - final long identity = Binder.clearCallingIdentity(); - try { - flags = mPermissionManager - .getPermissionFlags(permName, packageName, Binder.getCallingUid(), userId); - } finally { - Binder.restoreCallingIdentity(identity); - } - - final int fixedFlags = PackageManager.FLAG_PERMISSION_SYSTEM_FIXED - | PackageManager.FLAG_PERMISSION_POLICY_FIXED - | PackageManager.FLAG_PERMISSION_USER_FIXED; - - if ((flags & fixedFlags) != 0) { - return false; - } - - return (flags & PackageManager.FLAG_PERMISSION_USER_SET) != 0; - } - - @Override public boolean isProtectedBroadcast(String actionName) { // allow instant applications synchronized (mProtectedBroadcasts) { @@ -7286,7 +7199,8 @@ public class PackageManagerService extends IPackageManager.Stub } else { // Browser/generic handling case. If there's a default browser, go straight // to that (but only if there is no other higher-priority match). - final String defaultBrowserPackageName = getDefaultBrowserPackageName(userId); + final String defaultBrowserPackageName = + mPermissionManager.getDefaultBrowser(userId); int maxMatchPrio = 0; ResolveInfo defaultBrowserMatch = null; final int numCandidates = matchAllList.size(); @@ -10340,7 +10254,7 @@ public class PackageManagerService extends IPackageManager.Stub * <li>{@link #SCAN_AS_OEM}</li> * <li>{@link #SCAN_AS_VENDOR}</li> * <li>{@link #SCAN_AS_PRODUCT}</li> - * <li>{@link #SCAN_AS_PRODUCT_SERVICES}</li> + * <li>{@link #SCAN_AS_SYSTEM_EXT}</li> * <li>{@link #SCAN_AS_INSTANT_APP}</li> * <li>{@link #SCAN_AS_VIRTUAL_PRELOAD}</li> * <li>{@link #SCAN_AS_ODM}</li> @@ -10377,8 +10291,8 @@ public class PackageManagerService extends IPackageManager.Stub scanFlags |= SCAN_AS_PRODUCT; } if ((systemPkgSetting.pkgPrivateFlags - & ApplicationInfo.PRIVATE_FLAG_PRODUCT_SERVICES) != 0) { - scanFlags |= SCAN_AS_PRODUCT_SERVICES; + & ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT) != 0) { + scanFlags |= SCAN_AS_SYSTEM_EXT; } if ((systemPkgSetting.pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_ODM) != 0) { @@ -11156,8 +11070,8 @@ public class PackageManagerService extends IPackageManager.Stub pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PRODUCT; } - if ((scanFlags & SCAN_AS_PRODUCT_SERVICES) != 0) { - pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PRODUCT_SERVICES; + if ((scanFlags & SCAN_AS_SYSTEM_EXT) != 0) { + pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT; } if ((scanFlags & SCAN_AS_ODM) != 0) { @@ -12121,8 +12035,8 @@ public class PackageManagerService extends IPackageManager.Stub codeRoot = Environment.getOdmDirectory(); } else if (FileUtils.contains(Environment.getProductDirectory(), codePath)) { codeRoot = Environment.getProductDirectory(); - } else if (FileUtils.contains(Environment.getProductServicesDirectory(), codePath)) { - codeRoot = Environment.getProductServicesDirectory(); + } else if (FileUtils.contains(Environment.getSystemExtDirectory(), codePath)) { + codeRoot = Environment.getSystemExtDirectory(); } else if (FileUtils.contains(Environment.getOdmDirectory(), codePath)) { codeRoot = Environment.getOdmDirectory(); } else { @@ -12826,7 +12740,9 @@ public class PackageManagerService extends IPackageManager.Stub @Override public void setSystemAppHiddenUntilInstalled(String packageName, boolean hidden) { - enforceSystemOrPhoneCaller("setSystemAppHiddenUntilInstalled"); + final int callingUid = Binder.getCallingUid(); + PackageManagerServiceUtils + .enforceSystemOrPhoneCaller("setSystemAppHiddenUntilInstalled", callingUid); synchronized (mPackages) { final PackageSetting pkgSetting = mSettings.mPackages.get(packageName); if (pkgSetting == null || !pkgSetting.isSystem()) { @@ -12849,7 +12765,9 @@ public class PackageManagerService extends IPackageManager.Stub @Override public boolean setSystemAppInstallState(String packageName, boolean installed, int userId) { - enforceSystemOrPhoneCaller("setSystemAppInstallState"); + final int callingUid = Binder.getCallingUid(); + PackageManagerServiceUtils + .enforceSystemOrPhoneCaller("setSystemAppInstallState", callingUid); synchronized (mPackages) { final PackageSetting pkgSetting = mSettings.mPackages.get(packageName); // The target app should always be in system @@ -13421,7 +13339,7 @@ public class PackageManagerService extends IPackageManager.Stub final long callingId = Binder.clearCallingIdentity(); try { final String activeLauncherPackageName = getActiveLauncherPackageName(userId); - final String dialerPackageName = getDefaultDialerPackageName(userId); + final String dialerPackageName = mPermissionManager.getDefaultDialer(userId); for (int i = 0; i < packageNames.length; i++) { canSuspend[i] = false; final String packageName = packageNames[i]; @@ -13503,19 +13421,6 @@ public class PackageManagerService extends IPackageManager.Stub return resolveInfo == null ? null : resolveInfo.activityInfo.packageName; } - @Nullable - private String getDefaultDialerPackageName(@UserIdInt int userId) { - PackageManagerInternal.DefaultDialerProvider provider; - synchronized (mPackages) { - provider = mDefaultDialerProvider; - } - if (provider == null) { - Slog.e(TAG, "mDefaultDialerProvider is null"); - return null; - } - return provider.getDefaultDialer(userId); - } - @Override public void verifyPendingInstall(int id, int verificationCode) throws RemoteException { mContext.enforceCallingOrSelfPermission( @@ -13874,75 +13779,6 @@ public class PackageManagerService extends IPackageManager.Stub } } - @Override - public boolean setDefaultBrowserPackageName(String packageName, int userId) { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null); - if (UserHandle.getCallingUserId() != userId) { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null); - } - if (userId == UserHandle.USER_ALL) { - return false; - } - PackageManagerInternal.DefaultBrowserProvider provider; - synchronized (mPackages) { - provider = mDefaultBrowserProvider; - } - if (provider == null) { - Slog.e(TAG, "mDefaultBrowserProvider is null"); - return false; - } - boolean successful = provider.setDefaultBrowser(packageName, userId); - if (!successful) { - return false; - } - if (packageName != null) { - synchronized (mPackages) { - mDefaultPermissionPolicy.grantDefaultPermissionsToDefaultBrowser(packageName, - userId); - } - } - return true; - } - - private void setDefaultBrowserAsyncLPw(@Nullable String packageName, @UserIdInt int userId) { - if (userId == UserHandle.USER_ALL) { - return; - } - if (mDefaultBrowserProvider == null) { - Slog.e(TAG, "mDefaultBrowserProvider is null"); - return; - } - mDefaultBrowserProvider.setDefaultBrowserAsync(packageName, userId); - if (packageName != null) { - synchronized (mPackages) { - mDefaultPermissionPolicy.grantDefaultPermissionsToDefaultBrowser(packageName, - userId); - } - } - } - - @Override - public String getDefaultBrowserPackageName(int userId) { - if (UserHandle.getCallingUserId() != userId) { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null); - } - if (getInstantAppPackageName(Binder.getCallingUid()) != null) { - return null; - } - PackageManagerInternal.DefaultBrowserProvider provider; - synchronized (mPackages) { - provider = mDefaultBrowserProvider; - } - if (provider == null) { - Slog.e(TAG, "mDefaultBrowserProvider is null"); - return null; - } - return provider.getDefaultBrowser(userId); - } - /** * Get the "allow unknown sources" setting. * @@ -16082,13 +15918,13 @@ public class PackageManagerService extends IPackageManager.Stub && compareSignatures(sharedUserSignatures, pkg.mSigningDetails.signatures) != PackageManager.SIGNATURE_MATCH) { - if (SystemProperties.getInt("ro.product.first_api_level", 0) <= 28) { + if (SystemProperties.getInt("ro.product.first_api_level", 0) <= 29) { // Mismatched signatures is an error and silently skipping system // packages will likely break the device in unforeseen ways. - // However, - // we allow the device to boot anyway because, prior to P, - // vendors were - // not expecting the platform to crash in this situation. + // However, we allow the device to boot anyway because, prior to Q, + // vendors were not expecting the platform to crash in this + // situation. + // This WILL be a hard failure on any new API levels after Q. throw new ReconcileFailure( INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES, "Signature mismatch for shared user: " @@ -17678,9 +17514,9 @@ public class PackageManagerService extends IPackageManager.Stub return (pkg.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0; } - private static boolean isProductServicesApp(PackageParser.Package pkg) { + private static boolean isSystemExtApp(PackageParser.Package pkg) { return (pkg.applicationInfo.privateFlags - & ApplicationInfo.PRIVATE_FLAG_PRODUCT_SERVICES) != 0; + & ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT) != 0; } private static boolean isOdmApp(PackageParser.Package pkg) { @@ -18445,13 +18281,13 @@ public class PackageManagerService extends IPackageManager.Stub final File privilegedVendorAppDir = new File(Environment.getVendorDirectory(), "priv-app"); final File privilegedOdmAppDir = new File(Environment.getOdmDirectory(), "priv-app"); final File privilegedProductAppDir = new File(Environment.getProductDirectory(), "priv-app"); - final File privilegedProductServicesAppDir = - new File(Environment.getProductServicesDirectory(), "priv-app"); + final File privilegedSystemExtAppDir = + new File(Environment.getSystemExtDirectory(), "priv-app"); return path.startsWith(privilegedAppDir.getCanonicalPath() + "/") || path.startsWith(privilegedVendorAppDir.getCanonicalPath() + "/") || path.startsWith(privilegedOdmAppDir.getCanonicalPath() + "/") || path.startsWith(privilegedProductAppDir.getCanonicalPath() + "/") - || path.startsWith(privilegedProductServicesAppDir.getCanonicalPath() + "/"); + || path.startsWith(privilegedSystemExtAppDir.getCanonicalPath() + "/"); } catch (IOException e) { Slog.e(TAG, "Unable to access code path " + path); } @@ -18486,10 +18322,10 @@ public class PackageManagerService extends IPackageManager.Stub return false; } - static boolean locationIsProductServices(String path) { + static boolean locationIsSystemExt(String path) { try { return path.startsWith( - Environment.getProductServicesDirectory().getCanonicalPath() + "/"); + Environment.getSystemExtDirectory().getCanonicalPath() + "/"); } catch (IOException e) { Slog.e(TAG, "Unable to access code path " + path); } @@ -18622,8 +18458,8 @@ public class PackageManagerService extends IPackageManager.Stub if (locationIsProduct(codePathString)) { scanFlags |= SCAN_AS_PRODUCT; } - if (locationIsProductServices(codePathString)) { - scanFlags |= SCAN_AS_PRODUCT_SERVICES; + if (locationIsSystemExt(codePathString)) { + scanFlags |= SCAN_AS_SYSTEM_EXT; } if (locationIsOdm(codePathString)) { scanFlags |= SCAN_AS_ODM; @@ -19688,10 +19524,10 @@ public class PackageManagerService extends IPackageManager.Stub } private void clearDefaultBrowserIfNeededForUser(String packageName, int userId) { - final String defaultBrowserPackageName = getDefaultBrowserPackageName(userId); + final String defaultBrowserPackageName = mPermissionManager.getDefaultBrowser(userId); if (!TextUtils.isEmpty(defaultBrowserPackageName)) { if (packageName.equals(defaultBrowserPackageName)) { - setDefaultBrowserPackageName(null, userId); + mPermissionManager.setDefaultBrowser(null, true, true, userId); } } } @@ -19719,7 +19555,7 @@ public class PackageManagerService extends IPackageManager.Stub // significant refactoring to keep all default apps in the package // manager (cleaner but more work) or have the services provide // callbacks to the package manager to request a default app reset. - setDefaultBrowserPackageName(null, userId); + mPermissionManager.setDefaultBrowser(null, true, true, userId); resetNetworkPolicies(userId); synchronized (mPackages) { scheduleWritePackageRestrictionsLocked(userId); @@ -19972,17 +19808,14 @@ public class PackageManagerService extends IPackageManager.Stub parser.setInput(new ByteArrayInputStream(backup), StandardCharsets.UTF_8.name()); restoreFromXml(parser, userId, TAG_DEFAULT_APPS, (parser1, userId1) -> { - String defaultBrowser; + final String defaultBrowser; synchronized (mPackages) { mSettings.readDefaultAppsLPw(parser1, userId1); defaultBrowser = mSettings.removeDefaultBrowserPackageNameLPw(userId1); } if (defaultBrowser != null) { - PackageManagerInternal.DefaultBrowserProvider provider; - synchronized (mPackages) { - provider = mDefaultBrowserProvider; - } - provider.setDefaultBrowser(defaultBrowser, userId1); + mPermissionManager + .setDefaultBrowser(defaultBrowser, false, false, userId1); } }); } catch (Exception e) { @@ -20219,15 +20052,7 @@ public class PackageManagerService extends IPackageManager.Stub } allHomeCandidates.addAll(resolveInfos); - PackageManagerInternal.DefaultHomeProvider provider; - synchronized (mPackages) { - provider = mDefaultHomeProvider; - } - if (provider == null) { - Slog.e(TAG, "mDefaultHomeProvider is null"); - return null; - } - String packageName = provider.getDefaultHome(userId); + final String packageName = mPermissionManager.getDefaultHome(userId); if (packageName == null) { return null; } @@ -20280,15 +20105,7 @@ public class PackageManagerService extends IPackageManager.Stub final String packageName = preferredResolveInfo != null && preferredResolveInfo.activityInfo != null ? preferredResolveInfo.activityInfo.packageName : null; - final PackageManagerInternal.DefaultHomeProvider provider; - synchronized (mPackages) { - provider = mDefaultHomeProvider; - } - if (provider == null) { - Slog.e(TAG, "Default home provider has not been set"); - return false; - } - final String currentPackageName = provider.getDefaultHome(userId); + final String currentPackageName = mPermissionManager.getDefaultHome(userId); if (TextUtils.equals(currentPackageName, packageName)) { return false; } @@ -20298,7 +20115,7 @@ public class PackageManagerService extends IPackageManager.Stub // PermissionController manages default home directly. return false; } - provider.setDefaultHomeAsync(packageName, userId, (successful) -> { + mPermissionManager.setDefaultHome(currentPackageName, userId, (successful) -> { if (successful) { postPreferredActivityChangedBroadcast(userId); } @@ -20854,7 +20671,8 @@ public class PackageManagerService extends IPackageManager.Stub // Disable any carrier apps. We do this very early in boot to prevent the apps from being // disabled after already being started. CarrierAppUtils.disableCarrierAppsUntilPrivileged(mContext.getOpPackageName(), this, - mContext.getContentResolver(), UserHandle.USER_SYSTEM); + mPermissionManagerService, mContext.getContentResolver(), + UserHandle.USER_SYSTEM); disableSkuSpecificApps(); @@ -20868,8 +20686,6 @@ public class PackageManagerService extends IPackageManager.Stub Log.d(TAG, "compatibility mode:" + compatibilityModeEnabled); } - int[] grantPermissionsUserIds = EMPTY_INT_ARRAY; - synchronized (mPackages) { // Verify that all of the preferred activity components actually // exist. It is possible for applications to be updated and at @@ -20899,27 +20715,9 @@ public class PackageManagerService extends IPackageManager.Stub mSettings.mPreferredActivities.keyAt(i)); } } - - for (int userId : UserManagerService.getInstance().getUserIds()) { - if (!mSettings.areDefaultRuntimePermissionsGrantedLPr(userId)) { - grantPermissionsUserIds = ArrayUtils.appendInt( - grantPermissionsUserIds, userId); - } - } } sUserManager.systemReady(); - // If we upgraded grant all default permissions before kicking off. - for (int userId : grantPermissionsUserIds) { - mDefaultPermissionPolicy.grantDefaultPermissions(userId); - } - - if (grantPermissionsUserIds == EMPTY_INT_ARRAY) { - // If we did not grant default permissions, we preload from this the - // default permission exceptions lazily to ensure we don't hit the - // disk on a new user creation. - mDefaultPermissionPolicy.scheduleReadDefaultPermissionExceptions(); - } // Now that we've scanned all packages, and granted any default // permissions, ensure permissions are updated. Beware of dragons if you @@ -22943,11 +22741,7 @@ public class PackageManagerService extends IPackageManager.Stub } void onNewUserCreated(final int userId) { - mDefaultPermissionPolicy.grantDefaultPermissions(userId); - synchronized(mPackages) { - // NOTE: This adds UPDATE_PERMISSIONS_REPLACE_PKG - mPermissionManager.updateAllPermissions(StorageManager.UUID_PRIVATE_INTERNAL, true); - } + mPermissionManager.onNewUserCreated(userId); } @Override @@ -22962,44 +22756,6 @@ public class PackageManagerService extends IPackageManager.Stub } @Override - public void setPermissionEnforced(String permission, boolean enforced) { - // TODO: Now that we no longer change GID for storage, this should to away. - mContext.enforceCallingOrSelfPermission(Manifest.permission.GRANT_RUNTIME_PERMISSIONS, - "setPermissionEnforced"); - if (READ_EXTERNAL_STORAGE.equals(permission)) { - synchronized (mPackages) { - if (mSettings.mReadExternalStorageEnforced == null - || mSettings.mReadExternalStorageEnforced != enforced) { - mSettings.mReadExternalStorageEnforced = - enforced ? Boolean.TRUE : Boolean.FALSE; - mSettings.writeLPr(); - } - } - // kill any non-foreground processes so we restart them and - // grant/revoke the GID. - final IActivityManager am = ActivityManager.getService(); - if (am != null) { - final long token = Binder.clearCallingIdentity(); - try { - am.killProcessesBelowForeground("setPermissionEnforcement"); - } catch (RemoteException e) { - } finally { - Binder.restoreCallingIdentity(token); - } - } - } else { - throw new IllegalArgumentException("No selective enforcement for " + permission); - } - } - - @Override - @Deprecated - public boolean isPermissionEnforced(String permission) { - // allow instant applications - return true; - } - - @Override public boolean isStorageLow() { // allow instant applications final long token = Binder.clearCallingIdentity(); @@ -23463,8 +23219,20 @@ public class PackageManagerService extends IPackageManager.Stub @Override public boolean filterAppAccess(PackageParser.Package pkg, int callingUid, int userId) { synchronized (mPackages) { - return PackageManagerService.this.filterAppAccessLPr( - (PackageSetting) pkg.mExtras, callingUid, userId); + return PackageManagerService.this + .filterAppAccessLPr((PackageSetting) pkg.mExtras, callingUid, userId); + } + } + + @Override + public boolean filterAppAccess(String packageName, int callingUid, int userId) { + synchronized (mPackages) { + final PackageParser.Package pkg = mPackages.get(packageName); + if (pkg == null) { + return false; + } + return PackageManagerService.this + .filterAppAccessLPr((PackageSetting) pkg.mExtras, callingUid, userId); } } @@ -23531,7 +23299,7 @@ public class PackageManagerService extends IPackageManager.Stub public String getKnownPackageName(int knownPackage, int userId) { switch(knownPackage) { case PackageManagerInternal.PACKAGE_BROWSER: - return getDefaultBrowserPackageName(userId); + return mPermissionManager.getDefaultBrowser(userId); case PackageManagerInternal.PACKAGE_INSTALLER: return mRequiredInstallerPackage; case PackageManagerInternal.PACKAGE_SETUP_WIZARD: @@ -23565,37 +23333,6 @@ public class PackageManagerService extends IPackageManager.Stub } @Override - public void setLocationPackagesProvider(PackagesProvider provider) { - mDefaultPermissionPolicy.setLocationPackagesProvider(provider); - } - - @Override - public void setLocationExtraPackagesProvider(PackagesProvider provider) { - mDefaultPermissionPolicy.setLocationExtraPackagesProvider(provider); - } - - @Override - public void setVoiceInteractionPackagesProvider(PackagesProvider provider) { - mDefaultPermissionPolicy.setVoiceInteractionPackagesProvider(provider); - } - - @Override - public void setUseOpenWifiAppPackagesProvider(PackagesProvider provider) { - mDefaultPermissionPolicy.setUseOpenWifiAppPackagesProvider(provider); - } - - @Override - public void setSyncAdapterPackagesprovider(SyncAdapterPackagesProvider provider) { - mDefaultPermissionPolicy.setSyncAdapterPackagesProvider(provider); - } - - @Override - public void grantDefaultPermissionsToDefaultUseOpenWifiApp(String packageName, int userId) { - mDefaultPermissionPolicy.grantDefaultPermissionsToDefaultUseOpenWifiApp( - packageName, userId); - } - - @Override public void setKeepUninstalledPackages(final List<String> packageList) { Preconditions.checkNotNull(packageList); List<String> removedFromList = null; @@ -24108,27 +23845,6 @@ public class PackageManagerService extends IPackageManager.Stub } @Override - public void setDefaultBrowserProvider(@NonNull DefaultBrowserProvider provider) { - synchronized (mPackages) { - mDefaultBrowserProvider = provider; - } - } - - @Override - public void setDefaultDialerProvider(@NonNull DefaultDialerProvider provider) { - synchronized (mPackages) { - mDefaultDialerProvider = provider; - } - } - - @Override - public void setDefaultHomeProvider(@NonNull DefaultHomeProvider provider) { - synchronized (mPackages) { - mDefaultHomeProvider = provider; - } - } - - @Override public boolean isApexPackage(String packageName) { return PackageManagerService.this.mApexManager.isApexPackage(packageName); } @@ -24174,13 +23890,6 @@ public class PackageManagerService extends IPackageManager.Stub } @Override - public boolean wereDefaultPermissionsGrantedSinceBoot(int userId) { - synchronized (mPackages) { - return mDefaultPermissionPolicy.wereDefaultPermissionsGrantedSinceBoot(userId); - } - } - - @Override public void setRuntimePermissionsFingerPrint(@NonNull String fingerPrint, @UserIdInt int userId) { synchronized (mPackages) { @@ -24266,6 +23975,25 @@ public class PackageManagerService extends IPackageManager.Stub && UserHandle.isSameApp(installerPackageSetting.appId, callingUid); } } + + @Override + public boolean areDefaultRuntimePermissionsGranted(int userId) { + synchronized (mPackages) { + return mSettings.areDefaultRuntimePermissionsGrantedLPr(userId); + } + } + + @Override + public void setReadExternalStorageEnforced(boolean enforced) { + synchronized (mPackages) { + if (mSettings.mReadExternalStorageEnforced != null + && mSettings.mReadExternalStorageEnforced == enforced) { + return; + } + mSettings.mReadExternalStorageEnforced = enforced ? Boolean.TRUE : Boolean.FALSE; + mSettings.writeLPr(); + } + } } @GuardedBy("mPackages") @@ -24334,83 +24062,6 @@ public class PackageManagerService extends IPackageManager.Stub } } - @Override - public void grantDefaultPermissionsToEnabledCarrierApps(String[] packageNames, int userId) { - enforceSystemOrPhoneCaller("grantPermissionsToEnabledCarrierApps"); - synchronized (mPackages) { - final long identity = Binder.clearCallingIdentity(); - try { - mDefaultPermissionPolicy.grantDefaultPermissionsToEnabledCarrierApps( - packageNames, userId); - } finally { - Binder.restoreCallingIdentity(identity); - } - } - } - - @Override - public void grantDefaultPermissionsToEnabledImsServices(String[] packageNames, int userId) { - enforceSystemOrPhoneCaller("grantDefaultPermissionsToEnabledImsServices"); - synchronized (mPackages) { - final long identity = Binder.clearCallingIdentity(); - try { - mDefaultPermissionPolicy.grantDefaultPermissionsToEnabledImsServices( - packageNames, userId); - } finally { - Binder.restoreCallingIdentity(identity); - } - } - } - - @Override - public void grantDefaultPermissionsToEnabledTelephonyDataServices( - String[] packageNames, int userId) { - enforceSystemOrPhoneCaller("grantDefaultPermissionsToEnabledTelephonyDataServices"); - synchronized (mPackages) { - Binder.withCleanCallingIdentity( () -> mDefaultPermissionPolicy. - grantDefaultPermissionsToEnabledTelephonyDataServices( - packageNames, userId)); - } - } - - @Override - public void revokeDefaultPermissionsFromDisabledTelephonyDataServices( - String[] packageNames, int userId) { - enforceSystemOrPhoneCaller("revokeDefaultPermissionsFromDisabledTelephonyDataServices"); - synchronized (mPackages) { - Binder.withCleanCallingIdentity( () -> mDefaultPermissionPolicy. - revokeDefaultPermissionsFromDisabledTelephonyDataServices( - packageNames, userId)); - } - } - - @Override - public void grantDefaultPermissionsToActiveLuiApp(String packageName, int userId) { - enforceSystemOrPhoneCaller("grantDefaultPermissionsToActiveLuiApp"); - synchronized (mPackages) { - final long identity = Binder.clearCallingIdentity(); - try { - mDefaultPermissionPolicy.grantDefaultPermissionsToActiveLuiApp( - packageName, userId); - } finally { - Binder.restoreCallingIdentity(identity); - } - } - } - - @Override - public void revokeDefaultPermissionsFromLuiApps(String[] packageNames, int userId) { - enforceSystemOrPhoneCaller("revokeDefaultPermissionsFromLuiApps"); - synchronized (mPackages) { - final long identity = Binder.clearCallingIdentity(); - try { - mDefaultPermissionPolicy.revokeDefaultPermissionsFromLuiApps(packageNames, userId); - } finally { - Binder.restoreCallingIdentity(identity); - } - } - } - void forEachPackage(Consumer<PackageParser.Package> actionLocked) { synchronized (mPackages) { int numPackages = mPackages.size(); @@ -24435,14 +24086,6 @@ public class PackageManagerService extends IPackageManager.Stub } } - private static void enforceSystemOrPhoneCaller(String tag) { - int callingUid = Binder.getCallingUid(); - if (callingUid != Process.PHONE_UID && callingUid != Process.SYSTEM_UID) { - throw new SecurityException( - "Cannot call " + tag + " from UID " + callingUid); - } - } - boolean isHistoricalPackageUsageAvailable() { return mPackageUsage.isHistoricalPackageUsageAvailable(); } diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java index f56e1ef0de09..4c7db9afee0a 100644 --- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java +++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java @@ -390,6 +390,17 @@ public class PackageManagerServiceUtils { } /** + * Enforces that the caller must be either the system process or the phone process. + * If not, throws a {@link SecurityException}. + */ + public static void enforceSystemOrPhoneCaller(String methodName, int callingUid) { + if (callingUid != Process.PHONE_UID && callingUid != Process.SYSTEM_UID) { + throw new SecurityException( + "Cannot call " + methodName + " from UID " + callingUid); + } + } + + /** * Derive the value of the {@code cpuAbiOverride} based on the provided * value and an optional stored value from the package settings. */ diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index 53c1c1f91beb..85bc9f3d56d6 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -2020,7 +2020,7 @@ class PackageManagerShellCommand extends ShellCommand { getErrPrintWriter().println("Error: no enforcement specified"); return 1; } - mInterface.setPermissionEnforced(permission, Boolean.parseBoolean(enforcedRaw)); + mPermissionManager.setPermissionEnforced(permission, Boolean.parseBoolean(enforcedRaw)); return 0; } @@ -2042,10 +2042,10 @@ class PackageManagerShellCommand extends ShellCommand { } } - private boolean isProductServicesApp(String pkg) { + private boolean isSystemExtApp(String pkg) { try { final PackageInfo info = mInterface.getPackageInfo(pkg, 0, UserHandle.USER_SYSTEM); - return info != null && info.applicationInfo.isProductServices(); + return info != null && info.applicationInfo.isSystemExt(); } catch (RemoteException e) { return false; } @@ -2063,9 +2063,9 @@ class PackageManagerShellCommand extends ShellCommand { privAppPermissions = SystemConfig.getInstance().getVendorPrivAppPermissions(pkg); } else if (isProductApp(pkg)) { privAppPermissions = SystemConfig.getInstance().getProductPrivAppPermissions(pkg); - } else if (isProductServicesApp(pkg)) { + } else if (isSystemExtApp(pkg)) { privAppPermissions = SystemConfig.getInstance() - .getProductServicesPrivAppPermissions(pkg); + .getSystemExtPrivAppPermissions(pkg); } else { privAppPermissions = SystemConfig.getInstance().getPrivAppPermissions(pkg); } @@ -2087,9 +2087,9 @@ class PackageManagerShellCommand extends ShellCommand { privAppPermissions = SystemConfig.getInstance().getVendorPrivAppDenyPermissions(pkg); } else if (isProductApp(pkg)) { privAppPermissions = SystemConfig.getInstance().getProductPrivAppDenyPermissions(pkg); - } else if (isProductServicesApp(pkg)) { + } else if (isSystemExtApp(pkg)) { privAppPermissions = SystemConfig.getInstance() - .getProductServicesPrivAppDenyPermissions(pkg); + .getSystemExtPrivAppDenyPermissions(pkg); } else { privAppPermissions = SystemConfig.getInstance().getPrivAppDenyPermissions(pkg); } diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java index e85989315417..4ea8a30fa206 100644 --- a/services/core/java/com/android/server/pm/PackageSetting.java +++ b/services/core/java/com/android/server/pm/PackageSetting.java @@ -148,8 +148,8 @@ public final class PackageSetting extends PackageSettingBase { return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0; } - public boolean isProductServices() { - return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRODUCT_SERVICES) != 0; + public boolean isSystemExt() { + return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT) != 0; } public boolean isOdm() { diff --git a/services/core/java/com/android/server/pm/SettingBase.java b/services/core/java/com/android/server/pm/SettingBase.java index a24818f04f52..ec9746dabceb 100644 --- a/services/core/java/com/android/server/pm/SettingBase.java +++ b/services/core/java/com/android/server/pm/SettingBase.java @@ -63,7 +63,7 @@ abstract class SettingBase { | ApplicationInfo.PRIVATE_FLAG_OEM | ApplicationInfo.PRIVATE_FLAG_VENDOR | ApplicationInfo.PRIVATE_FLAG_PRODUCT - | ApplicationInfo.PRIVATE_FLAG_PRODUCT_SERVICES + | ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT | ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER | ApplicationInfo.PRIVATE_FLAG_ODM); } diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 11a8f4b895f5..3bc2236a8221 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -774,7 +774,7 @@ public final class Settings { | ApplicationInfo.PRIVATE_FLAG_OEM | ApplicationInfo.PRIVATE_FLAG_VENDOR | ApplicationInfo.PRIVATE_FLAG_PRODUCT - | ApplicationInfo.PRIVATE_FLAG_PRODUCT_SERVICES + | ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT | ApplicationInfo.PRIVATE_FLAG_ODM); pkgSetting.pkgFlags |= pkgFlags & ApplicationInfo.FLAG_SYSTEM; pkgSetting.pkgPrivateFlags |= @@ -786,7 +786,7 @@ public final class Settings { pkgSetting.pkgPrivateFlags |= pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRODUCT; pkgSetting.pkgPrivateFlags |= - pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRODUCT_SERVICES; + pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT; pkgSetting.pkgPrivateFlags |= pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_ODM; pkgSetting.primaryCpuAbiString = primaryCpuAbi; @@ -4413,7 +4413,7 @@ public final class Settings { ApplicationInfo.PRIVATE_FLAG_STATIC_SHARED_LIBRARY, "STATIC_SHARED_LIBRARY", ApplicationInfo.PRIVATE_FLAG_VENDOR, "VENDOR", ApplicationInfo.PRIVATE_FLAG_PRODUCT, "PRODUCT", - ApplicationInfo.PRIVATE_FLAG_PRODUCT_SERVICES, "PRODUCT_SERVICES", + ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT, "SYSTEM_EXT", ApplicationInfo.PRIVATE_FLAG_VIRTUAL_PRELOAD, "VIRTUAL_PRELOAD", ApplicationInfo.PRIVATE_FLAG_ODM, "ODM", }; diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java index 42b65c357959..bf141a0f139b 100644 --- a/services/core/java/com/android/server/pm/StagingManager.java +++ b/services/core/java/com/android/server/pm/StagingManager.java @@ -242,11 +242,11 @@ public class StagingManager { } if (sessionContainsApk(session)) { - if (!installApksInSession(session, /* preReboot */ true)) { - session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, - "APK verification failed. Check logcat messages for " - + "more information."); + try { + installApksInSession(session, /* preReboot */ true); // TODO(b/118865310): abort the session on apexd. + } catch (PackageManagerException e) { + session.setStagedSessionFailed(e.error, e.getMessage()); return; } } @@ -315,7 +315,7 @@ public class StagingManager { } private void resumeSession(@NonNull PackageInstallerSession session) { - boolean hasApex = sessionContainsApex(session); + final boolean hasApex = sessionContainsApex(session); if (hasApex) { // Check with apexservice whether the apex packages have been activated. ApexSessionInfo apexSessionInfo = mApexManager.getStagedSessionInfo(session.sessionId); @@ -350,10 +350,10 @@ public class StagingManager { } } // The APEX part of the session is activated, proceed with the installation of APKs. - if (!installApksInSession(session, /* preReboot */ false)) { - session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, - "Staged installation of APKs failed. Check logcat messages for" - + "more information."); + try { + installApksInSession(session, /* preReboot */ false); + } catch (PackageManagerException e) { + session.setStagedSessionFailed(e.error, e.getMessage()); if (!hasApex) { return; @@ -388,16 +388,22 @@ public class StagingManager { return ret; } + @NonNull private PackageInstallerSession createAndWriteApkSession( - @NonNull PackageInstallerSession originalSession, boolean preReboot) { + @NonNull PackageInstallerSession originalSession, boolean preReboot) + throws PackageManagerException { + final int errorCode = preReboot ? SessionInfo.STAGED_SESSION_VERIFICATION_FAILED + : SessionInfo.STAGED_SESSION_ACTIVATION_FAILED; if (originalSession.stageDir == null) { Slog.wtf(TAG, "Attempting to install a staged APK session with no staging dir"); - return null; + throw new PackageManagerException(errorCode, + "Attempting to install a staged APK session with no staging dir"); } List<String> apkFilePaths = findAPKsInDir(originalSession.stageDir); if (apkFilePaths.isEmpty()) { Slog.w(TAG, "Can't find staged APK in " + originalSession.stageDir.getAbsolutePath()); - return null; + throw new PackageManagerException(errorCode, + "Can't find staged APK in " + originalSession.stageDir.getAbsolutePath()); } PackageInstaller.SessionParams params = originalSession.params.copy(); @@ -424,20 +430,22 @@ public class StagingManager { long sizeBytes = pfd.getStatSize(); if (sizeBytes < 0) { Slog.e(TAG, "Unable to get size of: " + apkFilePath); - return null; + throw new PackageManagerException(errorCode, + "Unable to get size of: " + apkFilePath); } apkSession.write(apkFile.getName(), 0, sizeBytes, pfd); } } catch (IOException e) { Slog.e(TAG, "Failure to install APK staged session " + originalSession.sessionId, e); - return null; + throw new PackageManagerException(errorCode, "Failed to write APK session", e); } return apkSession; } - private boolean commitApkSession(@NonNull PackageInstallerSession apkSession, - int originalSessionId, boolean preReboot) { - + private void commitApkSession(@NonNull PackageInstallerSession apkSession, + int originalSessionId, boolean preReboot) throws PackageManagerException { + final int errorCode = preReboot ? SessionInfo.STAGED_SESSION_VERIFICATION_FAILED + : SessionInfo.STAGED_SESSION_ACTIVATION_FAILED; if (!preReboot) { if ((apkSession.params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) { // If rollback is available for this session, notify the rollback @@ -457,23 +465,24 @@ public class StagingManager { final Intent result = receiver.getResult(); final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE); - if (status == PackageInstaller.STATUS_SUCCESS) { - return true; + if (status != PackageInstaller.STATUS_SUCCESS) { + + final String errorMessage = result.getStringExtra( + PackageInstaller.EXTRA_STATUS_MESSAGE); + Slog.e(TAG, "Failure to install APK staged session " + originalSessionId + " [" + + errorMessage + "]"); + throw new PackageManagerException(errorCode, errorMessage); } - Slog.e(TAG, "Failure to install APK staged session " + originalSessionId + " [" - + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]"); - return false; } - private boolean installApksInSession(@NonNull PackageInstallerSession session, - boolean preReboot) { + private void installApksInSession(@NonNull PackageInstallerSession session, + boolean preReboot) throws PackageManagerException { + final int errorCode = preReboot ? SessionInfo.STAGED_SESSION_VERIFICATION_FAILED + : SessionInfo.STAGED_SESSION_ACTIVATION_FAILED; if (!session.isMultiPackage() && !isApexSession(session)) { // APK single-packaged staged session. Do a regular install. PackageInstallerSession apkSession = createAndWriteApkSession(session, preReboot); - if (apkSession == null) { - return false; - } - return commitApkSession(apkSession, session.sessionId, preReboot); + commitApkSession(apkSession, session.sessionId, preReboot); } else if (session.isMultiPackage()) { // For multi-package staged sessions containing APKs, we identify which child sessions // contain an APK, and with those then create a new multi-package group of sessions, @@ -491,7 +500,7 @@ public class StagingManager { } if (childSessions.isEmpty()) { // APEX-only multi-package staged session, nothing to do. - return true; + return; } PackageInstaller.SessionParams params = session.params.copy(); params.isStaged = false; @@ -508,26 +517,24 @@ public class StagingManager { } catch (IOException e) { Slog.e(TAG, "Unable to prepare multi-package session for staged session " + session.sessionId); - return false; + throw new PackageManagerException(errorCode, + "Unable to prepare multi-package session for staged session"); } for (PackageInstallerSession sessionToClone : childSessions) { PackageInstallerSession apkChildSession = createAndWriteApkSession(sessionToClone, preReboot); - if (apkChildSession == null) { - return false; - } try { apkParentSession.addChildSessionId(apkChildSession.sessionId); } catch (IllegalStateException e) { Slog.e(TAG, "Failed to add a child session for installing the APK files", e); - return false; + throw new PackageManagerException(errorCode, + "Failed to add a child session " + apkChildSession.sessionId); } } - return commitApkSession(apkParentSession, session.sessionId, preReboot); + commitApkSession(apkParentSession, session.sessionId, preReboot); } // APEX single-package staged session, nothing to do. - return true; } void commitSession(@NonNull PackageInstallerSession session) { diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING index e375fa44cb3f..b829f0b53086 100644 --- a/services/core/java/com/android/server/pm/TEST_MAPPING +++ b/services/core/java/com/android/server/pm/TEST_MAPPING @@ -8,7 +8,9 @@ }, { "name": "CtsCompilationTestCases" - }, + } + ], + "postsubmit": [ { "name": "CtsPermissionTestCases", "options": [ 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 db01d7754d06..fe0b3a3966f9 100644 --- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java @@ -34,8 +34,6 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManagerInternal; -import android.content.pm.PackageManagerInternal.PackagesProvider; -import android.content.pm.PackageManagerInternal.SyncAdapterPackagesProvider; import android.content.pm.PermissionInfo; import android.content.pm.ProviderInfo; import android.content.pm.ResolveInfo; @@ -69,6 +67,8 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.util.ArrayUtils; import com.android.internal.util.XmlUtils; import com.android.server.LocalServices; +import com.android.server.pm.permission.PermissionManagerServiceInternal.PackagesProvider; +import com.android.server.pm.permission.PermissionManagerServiceInternal.SyncAdapterPackagesProvider; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -1388,8 +1388,7 @@ public final class DefaultPermissionGrantPolicy { if (dir.isDirectory() && dir.canRead()) { Collections.addAll(ret, dir.listFiles()); } - dir = new File(Environment.getProductServicesDirectory(), - "etc/default-permissions"); + dir = new File(Environment.getSystemExtDirectory(), "etc/default-permissions"); if (dir.isDirectory() && dir.canRead()) { Collections.addAll(ret, dir.listFiles()); } 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 9d0b42716e7a..673ab6c5f095 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -33,6 +33,7 @@ import static android.content.pm.PackageManager.FLAG_PERMISSION_WHITELIST_INSTAL import static android.content.pm.PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM; import static android.content.pm.PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE; import static android.content.pm.PackageManager.MASK_PERMISSION_FLAGS_ALL; +import static android.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING; import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; import static android.permission.PermissionManager.KILL_APP_REASON_GIDS_CHANGED; import static android.permission.PermissionManager.KILL_APP_REASON_PERMISSIONS_REVOKED; @@ -118,6 +119,9 @@ import com.android.server.pm.PackageManagerServiceUtils; import com.android.server.pm.PackageSetting; import com.android.server.pm.SharedUserSetting; import com.android.server.pm.UserManagerService; +import com.android.server.pm.permission.PermissionManagerServiceInternal.DefaultBrowserProvider; +import com.android.server.pm.permission.PermissionManagerServiceInternal.DefaultDialerProvider; +import com.android.server.pm.permission.PermissionManagerServiceInternal.DefaultHomeProvider; import com.android.server.pm.permission.PermissionManagerServiceInternal.PermissionCallback; import com.android.server.pm.permission.PermissionsState.PermissionState; import com.android.server.policy.SoftRestrictedPermissionPolicy; @@ -250,6 +254,15 @@ public class PermissionManagerService extends IPermissionManager.Stub { @GuardedBy("mLock") private final OnPermissionChangeListeners mOnPermissionChangeListeners; + @GuardedBy("mLock") + private DefaultBrowserProvider mDefaultBrowserProvider; + + @GuardedBy("mLock") + private DefaultDialerProvider mDefaultDialerProvider; + + @GuardedBy("mLock") + private DefaultHomeProvider mDefaultHomeProvider; + // TODO: Take a look at the methods defined in the callback. // The callback was initially created to support the split between permission // manager and the package manager. However, it's started to be used for other @@ -1609,6 +1622,226 @@ public class PermissionManagerService extends IPermissionManager.Stub { mPackageManagerInt.writePermissionSettings(asyncUpdatedUsers.toArray(), true); } + @Override + public String getDefaultBrowser(int userId) { + final int callingUid = Binder.getCallingUid(); + if (UserHandle.getUserId(callingUid) != userId) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null); + } + if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) { + return null; + } + synchronized (mLock) { + return mDefaultBrowserProvider == null + ? null : mDefaultBrowserProvider.getDefaultBrowser(userId); + } + } + + @Override + public boolean setDefaultBrowser(String packageName, int userId) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null); + if (UserHandle.getCallingUserId() != userId) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null); + } + return setDefaultBrowserInternal(packageName, false, true, userId); + } + + private boolean setDefaultBrowserInternal(String packageName, boolean async, + boolean doGrant, int userId) { + synchronized (mLock) { + if (userId == UserHandle.USER_ALL) { + return false; + } + if (mDefaultBrowserProvider == null) { + return false; + } + if (async) { + mDefaultBrowserProvider.setDefaultBrowserAsync(packageName, userId); + } else { + if (!mDefaultBrowserProvider.setDefaultBrowser(packageName, userId)) { + return false; + } + } + if (doGrant && packageName != null) { + mDefaultPermissionGrantPolicy + .grantDefaultPermissionsToDefaultBrowser(packageName, userId); + } + } + return true; + } + + @Override + public void grantDefaultPermissionsToEnabledCarrierApps(String[] packageNames, int userId) { + final int callingUid = Binder.getCallingUid(); + PackageManagerServiceUtils + .enforceSystemOrPhoneCaller("grantPermissionsToEnabledCarrierApps", callingUid); + synchronized (mLock) { + Binder.withCleanCallingIdentity(() -> mDefaultPermissionGrantPolicy + .grantDefaultPermissionsToEnabledCarrierApps(packageNames, userId)); + } + } + + @Override + public void grantDefaultPermissionsToEnabledImsServices(String[] packageNames, int userId) { + final int callingUid = Binder.getCallingUid(); + PackageManagerServiceUtils.enforceSystemOrPhoneCaller( + "grantDefaultPermissionsToEnabledImsServices", callingUid); + synchronized (mLock) { + Binder.withCleanCallingIdentity(() -> mDefaultPermissionGrantPolicy + .grantDefaultPermissionsToEnabledImsServices(packageNames, userId)); + } + } + + @Override + public void grantDefaultPermissionsToEnabledTelephonyDataServices( + String[] packageNames, int userId) { + final int callingUid = Binder.getCallingUid(); + PackageManagerServiceUtils.enforceSystemOrPhoneCaller( + "grantDefaultPermissionsToEnabledTelephonyDataServices", callingUid); + synchronized (mLock) { + Binder.withCleanCallingIdentity(() -> mDefaultPermissionGrantPolicy + .grantDefaultPermissionsToEnabledTelephonyDataServices( + packageNames, userId)); + } + } + + @Override + public void revokeDefaultPermissionsFromDisabledTelephonyDataServices( + String[] packageNames, int userId) { + final int callingUid = Binder.getCallingUid(); + PackageManagerServiceUtils.enforceSystemOrPhoneCaller( + "revokeDefaultPermissionsFromDisabledTelephonyDataServices", callingUid); + synchronized (mLock) { + Binder.withCleanCallingIdentity(() -> mDefaultPermissionGrantPolicy + .revokeDefaultPermissionsFromDisabledTelephonyDataServices( + packageNames, userId)); + } + } + + @Override + public void grantDefaultPermissionsToActiveLuiApp(String packageName, int userId) { + final int callingUid = Binder.getCallingUid(); + PackageManagerServiceUtils + .enforceSystemOrPhoneCaller("grantDefaultPermissionsToActiveLuiApp", callingUid); + synchronized (mLock) { + Binder.withCleanCallingIdentity(() -> mDefaultPermissionGrantPolicy + .grantDefaultPermissionsToActiveLuiApp(packageName, userId)); + } + } + + @Override + public void revokeDefaultPermissionsFromLuiApps(String[] packageNames, int userId) { + final int callingUid = Binder.getCallingUid(); + PackageManagerServiceUtils + .enforceSystemOrPhoneCaller("revokeDefaultPermissionsFromLuiApps", callingUid); + synchronized (mLock) { + Binder.withCleanCallingIdentity(() -> mDefaultPermissionGrantPolicy + .revokeDefaultPermissionsFromLuiApps(packageNames, userId)); + } + } + + @Override + public void setPermissionEnforced(String permName, boolean enforced) { + // TODO: Now that we no longer change GID for storage, this should to away. + mContext.enforceCallingOrSelfPermission(Manifest.permission.GRANT_RUNTIME_PERMISSIONS, + "setPermissionEnforced"); + if (READ_EXTERNAL_STORAGE.equals(permName)) { + mPackageManagerInt.setReadExternalStorageEnforced(enforced); + // kill any non-foreground processes so we restart them and + // grant/revoke the GID. + final IActivityManager am = ActivityManager.getService(); + if (am != null) { + final long token = Binder.clearCallingIdentity(); + try { + am.killProcessesBelowForeground("setPermissionEnforcement"); + } catch (RemoteException e) { + } finally { + Binder.restoreCallingIdentity(token); + } + } + } else { + throw new IllegalArgumentException("No selective enforcement for " + permName); + } + } + + /** @deprecated */ + @Override + @Deprecated + public boolean isPermissionEnforced(String permName) { + // allow instant applications + return true; + } + + @Override + public boolean shouldShowRequestPermissionRationale(String permName, + String packageName, int userId) { + final int callingUid = Binder.getCallingUid(); + if (UserHandle.getCallingUserId() != userId) { + mContext.enforceCallingPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, + "canShowRequestPermissionRationale for user " + userId); + } + + final int uid = + mPackageManagerInt.getPackageUid(packageName, MATCH_DEBUG_TRIAGED_MISSING, userId); + if (UserHandle.getAppId(callingUid) != UserHandle.getAppId(uid)) { + return false; + } + + if (checkPermission(permName, packageName, userId) + == PackageManager.PERMISSION_GRANTED) { + return false; + } + + final int flags; + + final long identity = Binder.clearCallingIdentity(); + try { + flags = getPermissionFlagsInternal(permName, packageName, callingUid, userId); + } finally { + Binder.restoreCallingIdentity(identity); + } + + final int fixedFlags = PackageManager.FLAG_PERMISSION_SYSTEM_FIXED + | PackageManager.FLAG_PERMISSION_POLICY_FIXED + | PackageManager.FLAG_PERMISSION_USER_FIXED; + + if ((flags & fixedFlags) != 0) { + return false; + } + + return (flags & PackageManager.FLAG_PERMISSION_USER_SET) != 0; + } + + @Override + public boolean isPermissionRevokedByPolicy(String permName, String packageName, int userId) { + if (UserHandle.getCallingUserId() != userId) { + mContext.enforceCallingPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, + "isPermissionRevokedByPolicy for user " + userId); + } + + if (checkPermission(permName, packageName, userId) == PackageManager.PERMISSION_GRANTED) { + return false; + } + + final int callingUid = Binder.getCallingUid(); + if (mPackageManagerInt.filterAppAccess(packageName, callingUid, userId)) { + return false; + } + + final long identity = Binder.clearCallingIdentity(); + try { + final int flags = getPermissionFlagsInternal(permName, packageName, callingUid, userId); + return (flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0; + } finally { + Binder.restoreCallingIdentity(identity); + } + } + /** * Get the state of the runtime permissions as xml file. * @@ -2746,9 +2979,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { } else if (pkg.isProduct()) { wlPermissions = SystemConfig.getInstance().getProductPrivAppPermissions(pkg.packageName); - } else if (pkg.isProductServices()) { + } else if (pkg.isSystemExt()) { wlPermissions = - SystemConfig.getInstance().getProductServicesPrivAppPermissions( + SystemConfig.getInstance().getSystemExtPrivAppPermissions( pkg.packageName); } else { wlPermissions = SystemConfig.getInstance().getPrivAppPermissions(pkg.packageName); @@ -2782,9 +3015,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { } else if (pkg.isProduct()) { deniedPermissions = SystemConfig.getInstance() .getProductPrivAppDenyPermissions(pkg.packageName); - } else if (pkg.isProductServices()) { + } else if (pkg.isSystemExt()) { deniedPermissions = SystemConfig.getInstance() - .getProductServicesPrivAppDenyPermissions(pkg.packageName); + .getSystemExtPrivAppDenyPermissions(pkg.packageName); } else { deniedPermissions = SystemConfig.getInstance() .getPrivAppDenyPermissions(pkg.packageName); @@ -2793,13 +3026,15 @@ public class PermissionManagerService extends IPermissionManager.Stub { deniedPermissions == null || !deniedPermissions.contains(perm); if (permissionViolation) { Slog.w(TAG, "Privileged permission " + perm + " for package " - + pkg.packageName + " - not in privapp-permissions whitelist"); + + pkg.packageName + " (" + pkg.codePath + + ") not in privapp-permissions whitelist"); if (RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_ENFORCE) { if (mPrivappPermissionsViolations == null) { mPrivappPermissionsViolations = new ArraySet<>(); } - mPrivappPermissionsViolations.add(pkg.packageName + ": " + perm); + mPrivappPermissionsViolations.add( + pkg.packageName + " (" + pkg.codePath + "): " + perm); } } else { return false; @@ -3714,6 +3949,24 @@ public class PermissionManagerService extends IPermissionManager.Stub { } mPermissionControllerManager = mContext.getSystemService(PermissionControllerManager.class); + + int[] grantPermissionsUserIds = EMPTY_INT_ARRAY; + for (int userId : UserManagerService.getInstance().getUserIds()) { + if (!mPackageManagerInt.areDefaultRuntimePermissionsGranted(userId)) { + grantPermissionsUserIds = ArrayUtils.appendInt( + grantPermissionsUserIds, userId); + } + } + // If we upgraded grant all default permissions before kicking off. + for (int userId : grantPermissionsUserIds) { + mDefaultPermissionGrantPolicy.grantDefaultPermissions(userId); + } + if (grantPermissionsUserIds == EMPTY_INT_ARRAY) { + // If we did not grant default permissions, we preload from this the + // default permission exceptions lazily to ensure we don't hit the + // disk on a new user creation. + mDefaultPermissionGrantPolicy.scheduleReadDefaultPermissionExceptions(); + } } private static String getVolumeUuidForPackage(PackageParser.Package pkg) { @@ -3847,20 +4100,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { .getAppOpPermissionPackagesInternal(permName, callingUid); } @Override - public int getPermissionFlags(String permName, String packageName, int callingUid, - int userId) { - return PermissionManagerService.this - .getPermissionFlagsInternal(permName, packageName, callingUid, userId); - } - @Override - public void updatePermissionFlags(String permName, String packageName, int flagMask, - int flagValues, int callingUid, int userId, boolean overridePolicy, - PermissionCallback callback) { - PermissionManagerService.this.updatePermissionFlagsInternal( - permName, packageName, flagMask, flagValues, callingUid, userId, - overridePolicy, callback); - } - @Override public void enforceCrossUserPermission(int callingUid, int userId, boolean requireFullPermission, boolean checkShell, String message) { PermissionManagerService.this.enforceCrossUserPermission(callingUid, userId, @@ -3882,10 +4121,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { return mSettings; } @Override - public DefaultPermissionGrantPolicy getDefaultPermissionGrantPolicy() { - return mDefaultPermissionGrantPolicy; - } - @Override public BasePermission getPermissionTEMP(String permName) { synchronized (PermissionManagerService.this.mLock) { return mSettings.getPermissionLocked(permName); @@ -3956,9 +4191,166 @@ public class PermissionManagerService extends IPermissionManager.Stub { mCheckPermissionDelegate = delegate; } } + @Override - public void notifyPermissionsChangedTEMP(int uid) { - mOnPermissionChangeListeners.onPermissionsChanged(uid); + public void setDefaultBrowserProvider(@NonNull DefaultBrowserProvider provider) { + synchronized (mLock) { + mDefaultBrowserProvider = provider; + } + } + + @Override + public void setDefaultBrowser(String packageName, boolean async, boolean doGrant, + int userId) { + setDefaultBrowserInternal(packageName, async, doGrant, userId); + } + + @Override + public void setDefaultDialerProvider(@NonNull DefaultDialerProvider provider) { + synchronized (mLock) { + mDefaultDialerProvider = provider; + } + } + + @Override + public void setDefaultHomeProvider(@NonNull DefaultHomeProvider provider) { + synchronized (mLock) { + mDefaultHomeProvider = provider; + } + } + + @Override + public void setDefaultHome(String packageName, int userId, Consumer<Boolean> callback) { + synchronized (mLock) { + if (userId == UserHandle.USER_ALL) { + return; + } + if (mDefaultHomeProvider == null) { + return; + } + mDefaultHomeProvider.setDefaultHomeAsync(packageName, userId, callback); + } + } + + @Override + public void setDialerAppPackagesProvider(PackagesProvider provider) { + synchronized (mLock) { + mDefaultPermissionGrantPolicy.setDialerAppPackagesProvider(provider); + } + } + + @Override + public void setLocationExtraPackagesProvider(PackagesProvider provider) { + synchronized (mLock) { + mDefaultPermissionGrantPolicy.setLocationExtraPackagesProvider(provider); + } + } + + @Override + public void setLocationPackagesProvider(PackagesProvider provider) { + synchronized (mLock) { + mDefaultPermissionGrantPolicy.setLocationPackagesProvider(provider); + } + } + + @Override + public void setSimCallManagerPackagesProvider(PackagesProvider provider) { + synchronized (mLock) { + mDefaultPermissionGrantPolicy.setSimCallManagerPackagesProvider(provider); + } + } + + @Override + public void setSmsAppPackagesProvider(PackagesProvider provider) { + synchronized (mLock) { + mDefaultPermissionGrantPolicy.setSmsAppPackagesProvider(provider); + } + } + + @Override + public void setSyncAdapterPackagesProvider(SyncAdapterPackagesProvider provider) { + synchronized (mLock) { + mDefaultPermissionGrantPolicy.setSyncAdapterPackagesProvider(provider); + } + } + + @Override + public void setUseOpenWifiAppPackagesProvider(PackagesProvider provider) { + synchronized (mLock) { + mDefaultPermissionGrantPolicy.setUseOpenWifiAppPackagesProvider(provider); + } + } + + @Override + public void setVoiceInteractionPackagesProvider(PackagesProvider provider) { + synchronized (mLock) { + mDefaultPermissionGrantPolicy.setVoiceInteractionPackagesProvider(provider); + } + } + + @Override + public String getDefaultBrowser(int userId) { + synchronized (mLock) { + return mDefaultBrowserProvider == null + ? null : mDefaultBrowserProvider.getDefaultBrowser(userId); + } + } + + @Override + public String getDefaultDialer(int userId) { + synchronized (mLock) { + return mDefaultDialerProvider == null + ? null : mDefaultDialerProvider.getDefaultDialer(userId); + } + } + + @Override + public String getDefaultHome(int userId) { + synchronized (mLock) { + return mDefaultHomeProvider == null + ? null : mDefaultHomeProvider.getDefaultHome(userId); + } + } + + @Override + public void grantDefaultPermissionsToDefaultSimCallManager(String packageName, int userId) { + synchronized (mLock) { + mDefaultPermissionGrantPolicy + .grantDefaultPermissionsToDefaultSimCallManager(packageName, userId); + } + } + + @Override + public void grantDefaultPermissionsToDefaultUseOpenWifiApp(String packageName, int userId) { + synchronized (mLock) { + mDefaultPermissionGrantPolicy + .grantDefaultPermissionsToDefaultUseOpenWifiApp(packageName, userId); + } + } + + @Override + public void grantDefaultPermissionsToDefaultBrowser(String packageName, int userId) { + synchronized (mLock) { + mDefaultPermissionGrantPolicy + .grantDefaultPermissionsToDefaultBrowser(packageName, userId); + } + } + + @Override + public boolean wereDefaultPermissionsGrantedSinceBoot(int userId) { + synchronized (mLock) { + return mDefaultPermissionGrantPolicy.wereDefaultPermissionsGrantedSinceBoot(userId); + } + } + + @Override + public void onNewUserCreated(int userId) { + synchronized (mLock) { + mDefaultPermissionGrantPolicy.grantDefaultPermissions(userId); + // NOTE: This adds UPDATE_PERMISSIONS_REPLACE_PKG + PermissionManagerService.this.updateAllPermissions( + StorageManager.UUID_PRIVATE_INTERNAL, true, mDefaultPermissionCallback); + } } } diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java index 2fdab4d4d125..04ec5ba04bb6 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java @@ -27,6 +27,7 @@ import android.permission.PermissionManagerInternal; import java.util.ArrayList; import java.util.List; +import java.util.function.Consumer; /** * Internal interfaces services. @@ -35,6 +36,109 @@ import java.util.List; */ public abstract class PermissionManagerServiceInternal extends PermissionManagerInternal { /** + * Provider for package names. + */ + public interface PackagesProvider { + + /** + * Gets the packages for a given user. + * @param userId The user id. + * @return The package names. + */ + String[] getPackages(int userId); + } + + /** + * Provider for package names. + */ + public interface SyncAdapterPackagesProvider { + + /** + * Gets the sync adapter packages for given authority and user. + * @param authority The authority. + * @param userId The user id. + * @return The package names. + */ + String[] getPackages(String authority, int userId); + } + + /** + * Provider for default browser + */ + public interface DefaultBrowserProvider { + + /** + * Get the package name of the default browser. + * + * @param userId the user id + * + * @return the package name of the default browser, or {@code null} if none + */ + @Nullable + String getDefaultBrowser(@UserIdInt int userId); + + /** + * Set the package name of the default browser. + * + * @param packageName package name of the default browser, or {@code null} to remove + * @param userId the user id + * + * @return whether the default browser was successfully set. + */ + boolean setDefaultBrowser(@Nullable String packageName, @UserIdInt int userId); + + /** + * Set the package name of the default browser asynchronously. + * + * @param packageName package name of the default browser, or {@code null} to remove + * @param userId the user id + */ + void setDefaultBrowserAsync(@Nullable String packageName, @UserIdInt int userId); + } + + /** + * Provider for default dialer + */ + public interface DefaultDialerProvider { + + /** + * Get the package name of the default dialer. + * + * @param userId the user id + * + * @return the package name of the default dialer, or {@code null} if none + */ + @Nullable + String getDefaultDialer(@UserIdInt int userId); + } + + /** + * Provider for default home + */ + public interface DefaultHomeProvider { + + /** + * Get the package name of the default home. + * + * @param userId the user id + * + * @return the package name of the default home, or {@code null} if none + */ + @Nullable + String getDefaultHome(@UserIdInt int userId); + + /** + * Set the package name of the default home. + * + * @param packageName package name of the default home, or {@code null} to remove + * @param userId the user id + * @param callback the callback made after the default home as been updated + */ + void setDefaultHomeAsync(@Nullable String packageName, @UserIdInt int userId, + @NonNull Consumer<Boolean> callback); + } + + /** * Callbacks invoked when interesting actions have been taken on a permission. * <p> * NOTE: The current arguments are merely to support the existing use cases. This @@ -159,17 +263,6 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager public abstract @Nullable String[] getAppOpPermissionPackages( @NonNull String permName, int callingUid); - public abstract int getPermissionFlags(@NonNull String permName, - @NonNull String packageName, int callingUid, int userId); - - /** - * Updates the flags associated with a permission by replacing the flags in - * the specified mask with the provided flag values. - */ - public abstract void updatePermissionFlags(@NonNull String permName, - @NonNull String packageName, int flagMask, int flagValues, int callingUid, int userId, - boolean overridePolicy, @Nullable PermissionCallback callback); - /** * Enforces the request is from the system or an app that has INTERACT_ACROSS_USERS * or INTERACT_ACROSS_USERS_FULL permissions, if the {@code userid} is not for the caller. @@ -189,13 +282,13 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager public abstract void enforceGrantRevokeRuntimePermissionPermissions(@NonNull String message); public abstract @NonNull PermissionSettings getPermissionSettings(); - public abstract @NonNull DefaultPermissionGrantPolicy getDefaultPermissionGrantPolicy(); + + /** Grants default browser permissions to the given package */ + public abstract void grantDefaultPermissionsToDefaultBrowser( + @NonNull String packageName, @UserIdInt int userId); /** HACK HACK methods to allow for partial migration of data to the PermissionManager class */ public abstract @Nullable BasePermission getPermissionTEMP(@NonNull String permName); - /** HACK HACK notify the permission listener; this shouldn't be needed after permissions - * are fully removed from the package manager */ - public abstract void notifyPermissionsChangedTEMP(int uid); /** Get all permission that have a certain protection level */ public abstract @NonNull ArrayList<PermissionInfo> getAllPermissionWithProtectionLevel( @@ -214,4 +307,142 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager * @param delegate A delegate instance or {@code null} to clear. */ public abstract void setCheckPermissionDelegate(@Nullable CheckPermissionDelegate delegate); + + /** + * Sets the dialer application packages provider. + * @param provider The provider. + */ + public abstract void setDialerAppPackagesProvider(PackagesProvider provider); + + /** + * Set the location extra packages provider. + * @param provider The packages provider. + */ + public abstract void setLocationExtraPackagesProvider(PackagesProvider provider); + + /** + * Sets the location provider packages provider. + * @param provider The packages provider. + */ + public abstract void setLocationPackagesProvider(PackagesProvider provider); + + /** + * Sets the SIM call manager packages provider. + * @param provider The provider. + */ + public abstract void setSimCallManagerPackagesProvider(PackagesProvider provider); + + /** + * Sets the SMS application packages provider. + * @param provider The provider. + */ + public abstract void setSmsAppPackagesProvider(PackagesProvider provider); + + /** + * Sets the sync adapter packages provider. + * @param provider The provider. + */ + public abstract void setSyncAdapterPackagesProvider(SyncAdapterPackagesProvider provider); + + /** + * Sets the Use Open Wifi packages provider. + * @param provider The packages provider. + */ + public abstract void setUseOpenWifiAppPackagesProvider(PackagesProvider provider); + + /** + * Sets the voice interaction packages provider. + * @param provider The packages provider. + */ + public abstract void setVoiceInteractionPackagesProvider(PackagesProvider provider); + + /** + * Sets the default browser provider. + * + * @param provider the provider + */ + public abstract void setDefaultBrowserProvider(@NonNull DefaultBrowserProvider provider); + + /** + * Sets the package name of the default browser provider for the given user. + * + * @param packageName The package name of the default browser or {@code null} + * to clear the default browser + * @param async If {@code true}, set the default browser asynchronously, + * otherwise set it synchronously + * @param doGrant If {@code true} and if {@code packageName} is not {@code null}, + * perform default permission grants on the browser, otherwise skip the + * default permission grants. + * @param userId The user to set the default browser for. + */ + public abstract void setDefaultBrowser(@Nullable String packageName, boolean async, + boolean doGrant, @UserIdInt int userId); + + /** + * Sets the default dialer provider. + * + * @param provider the provider + */ + public abstract void setDefaultDialerProvider(@NonNull DefaultDialerProvider provider); + + /** + * Sets the default home provider. + * + * @param provider the provider + */ + public abstract void setDefaultHomeProvider(@NonNull DefaultHomeProvider provider); + + /** + * Asynchronously sets the package name of the default home provider for the given user. + * + * @param packageName The package name of the default home or {@code null} + * to clear the default browser + * @param userId The user to set the default browser for + * @param callback Invoked after the default home has been set + */ + public abstract void setDefaultHome(@Nullable String packageName, @UserIdInt int userId, + @NonNull Consumer<Boolean> callback); + + /** + * Returns the default browser package name for the given user. + */ + @Nullable + public abstract String getDefaultBrowser(@UserIdInt int userId); + + /** + * Returns the default dialer package name for the given user. + */ + @Nullable + public abstract String getDefaultDialer(@UserIdInt int userId); + + /** + * Returns the default home package name for the given user. + */ + @Nullable + public abstract String getDefaultHome(@UserIdInt int userId); + + /** + * Requests granting of the default permissions to the current default Use Open Wifi app. + * @param packageName The default use open wifi package name. + * @param userId The user for which to grant the permissions. + */ + public abstract void grantDefaultPermissionsToDefaultSimCallManager( + @NonNull String packageName, @UserIdInt int userId); + + /** + * Requests granting of the default permissions to the current default Use Open Wifi app. + * @param packageName The default use open wifi package name. + * @param userId The user for which to grant the permissions. + */ + public abstract void grantDefaultPermissionsToDefaultUseOpenWifiApp( + @NonNull String packageName, @UserIdInt int userId); + + /** + * Returns whether or not default permission grants have been performed for the given + * user since the device booted. + */ + public abstract boolean wereDefaultPermissionsGrantedSinceBoot(@UserIdInt int userId); + + /** Called when a new user has been created. */ + public abstract void onNewUserCreated(@UserIdInt int userId); } diff --git a/services/core/java/com/android/server/pm/permission/TEST_MAPPING b/services/core/java/com/android/server/pm/permission/TEST_MAPPING index ee7a098b167b..af94e441f3c0 100644 --- a/services/core/java/com/android/server/pm/permission/TEST_MAPPING +++ b/services/core/java/com/android/server/pm/permission/TEST_MAPPING @@ -14,9 +14,6 @@ }, { "include-filter": "android.permission.cts.SharedUidPermissionsTest" - }, - { - "include-filter": "android.permission.cts.PermissionUpdateListenerTest" } ] }, @@ -56,6 +53,14 @@ "include-filter": "android.appsecurity.cts.AppSecurityTests#testPermissionDiffCert" } ] + }, + { + "name": "CtsPermissionTestCases", + "options": [ + { + "include-filter": "android.permission.cts.PermissionUpdateListenerTest" + } + ] } ] } diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java index a569bffef141..8da7f7bb6898 100644 --- a/services/core/java/com/android/server/policy/PermissionPolicyService.java +++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java @@ -260,9 +260,11 @@ public final class PermissionPolicyService extends SystemService { private void grantOrUpgradeDefaultRuntimePermissionsIfNeeded(@UserIdInt int userId) { if (DEBUG) Slog.i(LOG_TAG, "grantOrUpgradeDefaultPermsIfNeeded(" + userId + ")"); - final PackageManagerInternal packageManagerInternal = LocalServices.getService( - PackageManagerInternal.class); - if (packageManagerInternal.wereDefaultPermissionsGrantedSinceBoot(userId)) { + final PackageManagerInternal packageManagerInternal = + LocalServices.getService(PackageManagerInternal.class); + final PermissionManagerServiceInternal permissionManagerInternal = + LocalServices.getService(PermissionManagerServiceInternal.class); + if (permissionManagerInternal.wereDefaultPermissionsGrantedSinceBoot(userId)) { if (DEBUG) Slog.i(LOG_TAG, "defaultPermsWereGrantedSinceBoot(" + userId + ")"); // Now call into the permission controller to apply policy around permissions diff --git a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java index d53f6854dfdf..c1a6dbd8ae14 100644 --- a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java +++ b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java @@ -28,11 +28,14 @@ import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_INST import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT; import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT; +import static java.lang.Integer.min; + import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AppOpsManager; import android.content.Context; import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; import android.os.Build; import android.os.UserHandle; @@ -73,6 +76,41 @@ public abstract class SoftRestrictedPermissionPolicy { }; /** + * TargetSDK is per package. To make sure two apps int the same shared UID do not fight over + * what to set, always compute the combined targetSDK. + * + * @param context A context + * @param appInfo The app that is changed + * @param user The user the app belongs to + * + * @return The minimum targetSDK of all apps sharing the uid of the app + */ + private static int getMinimumTargetSDK(@NonNull Context context, + @NonNull ApplicationInfo appInfo, @NonNull UserHandle user) { + PackageManager pm = context.getPackageManager(); + + int minimumTargetSDK = appInfo.targetSdkVersion; + + String[] uidPkgs = pm.getPackagesForUid(appInfo.uid); + if (uidPkgs != null) { + for (String uidPkg : uidPkgs) { + if (!uidPkg.equals(appInfo.packageName)) { + ApplicationInfo uidPkgInfo; + try { + uidPkgInfo = pm.getApplicationInfoAsUser(uidPkg, 0, user); + } catch (PackageManager.NameNotFoundException e) { + continue; + } + + minimumTargetSDK = min(minimumTargetSDK, uidPkgInfo.targetSdkVersion); + } + } + } + + return minimumTargetSDK; + } + + /** * Get the policy for a soft restricted permission. * * @param context A context to use @@ -99,12 +137,36 @@ public abstract class SoftRestrictedPermissionPolicy { final int targetSDK; if (appInfo != null) { - flags = context.getPackageManager().getPermissionFlags(permission, - appInfo.packageName, user); + PackageManager pm = context.getPackageManager(); + flags = pm.getPermissionFlags(permission, appInfo.packageName, user); applyRestriction = (flags & FLAG_PERMISSION_APPLY_RESTRICTION) != 0; isWhiteListed = (flags & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0; - hasRequestedLegacyExternalStorage = appInfo.hasRequestedLegacyExternalStorage(); - targetSDK = appInfo.targetSdkVersion; + targetSDK = getMinimumTargetSDK(context, appInfo, user); + + boolean hasAnyRequestedLegacyExternalStorage = + appInfo.hasRequestedLegacyExternalStorage(); + + // hasRequestedLegacyExternalStorage is per package. To make sure two apps in + // the same shared UID do not fight over what to set, always compute the + // combined hasRequestedLegacyExternalStorage + String[] uidPkgs = pm.getPackagesForUid(appInfo.uid); + if (uidPkgs != null) { + for (String uidPkg : uidPkgs) { + if (!uidPkg.equals(appInfo.packageName)) { + ApplicationInfo uidPkgInfo; + try { + uidPkgInfo = pm.getApplicationInfoAsUser(uidPkg, 0, user); + } catch (PackageManager.NameNotFoundException e) { + continue; + } + + hasAnyRequestedLegacyExternalStorage |= + uidPkgInfo.hasRequestedLegacyExternalStorage(); + } + } + } + + hasRequestedLegacyExternalStorage = hasAnyRequestedLegacyExternalStorage; } else { flags = 0; applyRestriction = false; @@ -155,7 +217,7 @@ public abstract class SoftRestrictedPermissionPolicy { final int flags = context.getPackageManager().getPermissionFlags(permission, appInfo.packageName, user); isWhiteListed = (flags & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0; - targetSDK = appInfo.targetSdkVersion; + targetSDK = getMinimumTargetSDK(context, appInfo, user); } else { isWhiteListed = false; targetSDK = 0; diff --git a/services/core/java/com/android/server/policy/TEST_MAPPING b/services/core/java/com/android/server/policy/TEST_MAPPING index 9f64039e01a1..484017bf4ef4 100644 --- a/services/core/java/com/android/server/policy/TEST_MAPPING +++ b/services/core/java/com/android/server/policy/TEST_MAPPING @@ -33,6 +33,9 @@ "options": [ { "include-filter": "android.permission2.cts.RestrictedPermissionsTest" + }, + { + "include-filter": "android.permission2.cts.RestrictedStoragePermissionSharedUidTest" } ] }, diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java index b503ce87bae2..1948b202fd63 100644 --- a/services/core/java/com/android/server/role/RoleManagerService.java +++ b/services/core/java/com/android/server/role/RoleManagerService.java @@ -76,6 +76,7 @@ import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.FgThread; import com.android.server.LocalServices; import com.android.server.SystemService; +import com.android.server.pm.permission.PermissionManagerServiceInternal; import java.io.ByteArrayOutputStream; import java.io.FileDescriptor; @@ -166,11 +167,11 @@ public class RoleManagerService extends SystemService implements RoleUserState.C LocalServices.addService(RoleManagerInternal.class, new Internal()); - PackageManagerInternal packageManagerInternal = LocalServices.getService( - PackageManagerInternal.class); - packageManagerInternal.setDefaultBrowserProvider(new DefaultBrowserProvider()); - packageManagerInternal.setDefaultDialerProvider(new DefaultDialerProvider()); - packageManagerInternal.setDefaultHomeProvider(new DefaultHomeProvider()); + PermissionManagerServiceInternal permissionManagerInternal = + LocalServices.getService(PermissionManagerServiceInternal.class); + permissionManagerInternal.setDefaultBrowserProvider(new DefaultBrowserProvider()); + permissionManagerInternal.setDefaultDialerProvider(new DefaultDialerProvider()); + permissionManagerInternal.setDefaultHomeProvider(new DefaultHomeProvider()); registerUserRemovedReceiver(); } @@ -755,7 +756,8 @@ public class RoleManagerService extends SystemService implements RoleUserState.C } } - private class DefaultBrowserProvider implements PackageManagerInternal.DefaultBrowserProvider { + private class DefaultBrowserProvider implements + PermissionManagerServiceInternal.DefaultBrowserProvider { @Nullable @Override @@ -809,7 +811,8 @@ public class RoleManagerService extends SystemService implements RoleUserState.C } } - private class DefaultDialerProvider implements PackageManagerInternal.DefaultDialerProvider { + private class DefaultDialerProvider implements + PermissionManagerServiceInternal.DefaultDialerProvider { @Nullable @Override @@ -819,7 +822,8 @@ public class RoleManagerService extends SystemService implements RoleUserState.C } } - private class DefaultHomeProvider implements PackageManagerInternal.DefaultHomeProvider { + private class DefaultHomeProvider implements + PermissionManagerServiceInternal.DefaultHomeProvider { @Nullable @Override diff --git a/services/core/java/com/android/server/telecom/TelecomLoaderService.java b/services/core/java/com/android/server/telecom/TelecomLoaderService.java index 54369ca5c367..a853529f49e4 100644 --- a/services/core/java/com/android/server/telecom/TelecomLoaderService.java +++ b/services/core/java/com/android/server/telecom/TelecomLoaderService.java @@ -39,7 +39,6 @@ import com.android.internal.telephony.SmsApplication; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.pm.UserManagerService; -import com.android.server.pm.permission.DefaultPermissionGrantPolicy; import com.android.server.pm.permission.PermissionManagerServiceInternal; /** @@ -66,9 +65,9 @@ public class TelecomLoaderService extends SystemService { ServiceManager.addService(Context.TELECOM_SERVICE, service); synchronized (mLock) { + final PermissionManagerServiceInternal permissionManager = + LocalServices.getService(PermissionManagerServiceInternal.class); if (mDefaultSimCallManagerRequests != null) { - final DefaultPermissionGrantPolicy permissionPolicy = - getDefaultPermissionGrantPolicy(); if (mDefaultSimCallManagerRequests != null) { TelecomManager telecomManager = (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE); @@ -80,7 +79,7 @@ public class TelecomLoaderService extends SystemService { for (int i = requestCount - 1; i >= 0; i--) { final int userId = mDefaultSimCallManagerRequests.get(i); mDefaultSimCallManagerRequests.remove(i); - permissionPolicy + permissionManager .grantDefaultPermissionsToDefaultSimCallManager( packageName, userId); } @@ -99,11 +98,6 @@ public class TelecomLoaderService extends SystemService { } } - private DefaultPermissionGrantPolicy getDefaultPermissionGrantPolicy() { - return LocalServices.getService(PermissionManagerServiceInternal.class) - .getDefaultPermissionGrantPolicy(); - } - private static final ComponentName SERVICE_COMPONENT = new ComponentName( "com.android.server.telecom", "com.android.server.telecom.components.TelecomService"); @@ -162,10 +156,11 @@ public class TelecomLoaderService extends SystemService { private void registerDefaultAppProviders() { - final DefaultPermissionGrantPolicy permissionPolicy = getDefaultPermissionGrantPolicy(); + final PermissionManagerServiceInternal permissionManager = + LocalServices.getService(PermissionManagerServiceInternal.class); // Set a callback for the permission grant policy to query the default sms app. - permissionPolicy.setSmsAppPackagesProvider(userId -> { + permissionManager.setSmsAppPackagesProvider(userId -> { synchronized (mLock) { if (mServiceConnection == null) { return null; @@ -180,7 +175,7 @@ public class TelecomLoaderService extends SystemService { }); // Set a callback for the permission grant policy to query the default dialer app. - permissionPolicy.setDialerAppPackagesProvider(userId -> { + permissionManager.setDialerAppPackagesProvider(userId -> { synchronized (mLock) { if (mServiceConnection == null) { return null; @@ -194,7 +189,7 @@ public class TelecomLoaderService extends SystemService { }); // Set a callback for the permission grant policy to query the default sim call manager. - permissionPolicy.setSimCallManagerPackagesProvider(userId -> { + permissionManager.setSimCallManagerPackagesProvider(userId -> { synchronized (mLock) { if (mServiceConnection == null) { if (mDefaultSimCallManagerRequests == null) { @@ -215,12 +210,11 @@ public class TelecomLoaderService extends SystemService { } private void registerDefaultAppNotifier() { - final DefaultPermissionGrantPolicy permissionPolicy = getDefaultPermissionGrantPolicy(); // Notify the package manager on default app changes final RoleManager roleManager = mContext.getSystemService(RoleManager.class); roleManager.addOnRoleHoldersChangedListenerAsUser(mContext.getMainExecutor(), - (roleName, user) -> updateSimCallManagerPermissions(permissionPolicy, - user.getIdentifier()), UserHandle.ALL); + (roleName, user) -> updateSimCallManagerPermissions(user.getIdentifier()), + UserHandle.ALL); } @@ -230,7 +224,7 @@ public class TelecomLoaderService extends SystemService { public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) { for (int userId : UserManagerService.getInstance().getUserIds()) { - updateSimCallManagerPermissions(getDefaultPermissionGrantPolicy(), userId); + updateSimCallManagerPermissions(userId); } } } @@ -240,16 +234,16 @@ public class TelecomLoaderService extends SystemService { new IntentFilter(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED), null, null); } - private void updateSimCallManagerPermissions( - DefaultPermissionGrantPolicy permissionGrantPolicy, int userId) { + private void updateSimCallManagerPermissions(int userId) { + final PermissionManagerServiceInternal permissionManager = + LocalServices.getService(PermissionManagerServiceInternal.class); TelecomManager telecomManager = (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE); PhoneAccountHandle phoneAccount = telecomManager.getSimCallManager(userId); if (phoneAccount != null) { Slog.i(TAG, "updating sim call manager permissions for userId:" + userId); String packageName = phoneAccount.getComponentName().getPackageName(); - permissionGrantPolicy.grantDefaultPermissionsToDefaultSimCallManager( - packageName, userId); + permissionManager.grantDefaultPermissionsToDefaultSimCallManager(packageName, userId); } } } diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java index 55f062bca2d1..04839e1bd6db 100644 --- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java +++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java @@ -110,6 +110,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub { private static final String TAG = "UriGrantsManagerService"; // Maximum number of persisted Uri grants a package is allowed private static final int MAX_PERSISTED_URI_GRANTS = 128; + private static final boolean ENABLE_DYNAMIC_PERMISSIONS = false; private final Object mLock = new Object(); private final Context mContext; @@ -949,7 +950,25 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub { return false; } - return readMet && writeMet; + // If this provider says that grants are always required, we need to + // consult it directly to determine if the UID has permission + final boolean forceMet; + if (ENABLE_DYNAMIC_PERMISSIONS && pi.forceUriPermissions) { + final int providerUserId = UserHandle.getUserId(pi.applicationInfo.uid); + final int clientUserId = UserHandle.getUserId(uid); + if (providerUserId == clientUserId) { + forceMet = (mAmInternal.checkContentProviderUriPermission(grantUri.uri, + providerUserId, uid, modeFlags) == PackageManager.PERMISSION_GRANTED); + } else { + // The provider can't track cross-user permissions, so we have + // to assume they're always denied + forceMet = false; + } + } else { + forceMet = true; + } + + return readMet && writeMet && forceMet; } private void removeUriPermissionIfNeeded(UriPermission perm) { diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java index bace7e3c6dab..26ca975bfc8f 100644 --- a/services/core/java/com/android/server/wm/AccessibilityController.java +++ b/services/core/java/com/android/server/wm/AccessibilityController.java @@ -22,6 +22,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; +import static com.android.server.wm.utils.RegionUtils.forEachRect; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; @@ -84,7 +85,8 @@ final class AccessibilityController { private SparseArray<DisplayMagnifier> mDisplayMagnifiers = new SparseArray<>(); - private WindowsForAccessibilityObserver mWindowsForAccessibilityObserver; + private SparseArray<WindowsForAccessibilityObserver> mWindowsForAccessibilityObserver = + new SparseArray<>(); public boolean setMagnificationCallbacksLocked(int displayId, MagnificationCallbacks callbacks) { @@ -114,27 +116,52 @@ final class AccessibilityController { return result; } - public void setWindowsForAccessibilityCallback(WindowsForAccessibilityCallback callback) { + public boolean setWindowsForAccessibilityCallbackLocked(int displayId, + WindowsForAccessibilityCallback callback) { if (callback != null) { - if (mWindowsForAccessibilityObserver != null) { - throw new IllegalStateException( - "Windows for accessibility callback already set!"); + final DisplayContent dc = mService.mRoot.getDisplayContent(displayId); + if (dc == null) { + return false; + } + + final Display display = dc.getDisplay(); + if (mWindowsForAccessibilityObserver.get(displayId) != null) { + if (display.getType() == Display.TYPE_VIRTUAL && dc.getParentWindow() != null) { + // The window observer of this embedded display had been set from + // window manager after setting its parent window + return true; + } else { + throw new IllegalStateException( + "Windows for accessibility callback of display " + + displayId + " already set!"); + } } - mWindowsForAccessibilityObserver = new WindowsForAccessibilityObserver( - mService, callback); + if (display.getType() == Display.TYPE_OVERLAY) { + return false; + } + mWindowsForAccessibilityObserver.put(displayId, + new WindowsForAccessibilityObserver(mService, displayId, callback)); } else { - if (mWindowsForAccessibilityObserver == null) { + final WindowsForAccessibilityObserver windowsForA11yObserver = + mWindowsForAccessibilityObserver.get(displayId); + if (windowsForA11yObserver == null) { throw new IllegalStateException( - "Windows for accessibility callback already cleared!"); + "Windows for accessibility callback of display " + displayId + + " already cleared!"); } - mWindowsForAccessibilityObserver = null; + mWindowsForAccessibilityObserver.remove(displayId); } + return true; } - public void performComputeChangedWindowsNotLocked(boolean forceSend) { + public void performComputeChangedWindowsNotLocked(int displayId, boolean forceSend) { WindowsForAccessibilityObserver observer = null; synchronized (mService) { - observer = mWindowsForAccessibilityObserver; + final WindowsForAccessibilityObserver windowsForA11yObserver = + mWindowsForAccessibilityObserver.get(displayId); + if (windowsForA11yObserver != null) { + observer = windowsForA11yObserver; + } } if (observer != null) { observer.performComputeChangedWindowsNotLocked(forceSend); @@ -146,9 +173,10 @@ final class AccessibilityController { if (displayMagnifier != null) { displayMagnifier.setMagnificationSpecLocked(spec); } - // TODO: support multi-display for windows observer - if (mWindowsForAccessibilityObserver != null && displayId == Display.DEFAULT_DISPLAY) { - mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked(); + final WindowsForAccessibilityObserver windowsForA11yObserver = + mWindowsForAccessibilityObserver.get(displayId); + if (windowsForA11yObserver != null) { + windowsForA11yObserver.scheduleComputeChangedWindowsLocked(); } } @@ -172,9 +200,10 @@ final class AccessibilityController { if (displayMagnifier != null) { displayMagnifier.onWindowLayersChangedLocked(); } - // TODO: support multi-display for windows observer - if (mWindowsForAccessibilityObserver != null && displayId == Display.DEFAULT_DISPLAY) { - mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked(); + final WindowsForAccessibilityObserver windowsForA11yObserver = + mWindowsForAccessibilityObserver.get(displayId); + if (windowsForA11yObserver != null) { + windowsForA11yObserver.scheduleComputeChangedWindowsLocked(); } } @@ -184,9 +213,10 @@ final class AccessibilityController { if (displayMagnifier != null) { displayMagnifier.onRotationChangedLocked(displayContent); } - // TODO: support multi-display for windows observer - if (mWindowsForAccessibilityObserver != null && displayId == Display.DEFAULT_DISPLAY) { - mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked(); + final WindowsForAccessibilityObserver windowsForA11yObserver = + mWindowsForAccessibilityObserver.get(displayId); + if (windowsForA11yObserver != null) { + windowsForA11yObserver.scheduleComputeChangedWindowsLocked(); } } @@ -205,29 +235,36 @@ final class AccessibilityController { if (displayMagnifier != null) { displayMagnifier.onWindowTransitionLocked(windowState, transition); } - // TODO: support multi-display for windows observer - if (mWindowsForAccessibilityObserver != null && displayId == Display.DEFAULT_DISPLAY) { - mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked(); + final WindowsForAccessibilityObserver windowsForA11yObserver = + mWindowsForAccessibilityObserver.get(displayId); + if (windowsForA11yObserver != null) { + windowsForA11yObserver.scheduleComputeChangedWindowsLocked(); } } - public void onWindowFocusChangedNotLocked() { + public void onWindowFocusChangedNotLocked(int displayId) { // Not relevant for the display magnifier. WindowsForAccessibilityObserver observer = null; synchronized (mService) { - observer = mWindowsForAccessibilityObserver; + final WindowsForAccessibilityObserver windowsForA11yObserver = + mWindowsForAccessibilityObserver.get(displayId); + if (windowsForA11yObserver != null) { + observer = windowsForA11yObserver; + } } if (observer != null) { observer.performComputeChangedWindowsNotLocked(false); } } - public void onSomeWindowResizedOrMovedLocked() { + public void onSomeWindowResizedOrMovedLocked(int displayId) { // Not relevant for the display magnifier. - if (mWindowsForAccessibilityObserver != null) { - mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked(); + final WindowsForAccessibilityObserver windowsForA11yObserver = + mWindowsForAccessibilityObserver.get(displayId); + if (windowsForA11yObserver != null) { + windowsForA11yObserver.scheduleComputeChangedWindowsLocked(); } } @@ -263,6 +300,29 @@ final class AccessibilityController { } } + public void handleWindowObserverOfEmbeddedDisplayLocked(int embeddedDisplayId, + WindowState parentWindow) { + if (embeddedDisplayId == Display.DEFAULT_DISPLAY || parentWindow == null) { + return; + } + // Finds the parent display of this embedded display + final int parentDisplayId; + WindowState candidate = parentWindow; + while (candidate != null) { + parentWindow = candidate; + candidate = parentWindow.getDisplayContent().getParentWindow(); + } + parentDisplayId = parentWindow.getDisplayId(); + // Uses the observer of parent display + final WindowsForAccessibilityObserver windowsForA11yObserver = + mWindowsForAccessibilityObserver.get(parentDisplayId); + + if (windowsForA11yObserver != null) { + // Replaces the observer of embedded display to the one of parent display + mWindowsForAccessibilityObserver.put(embeddedDisplayId, windowsForA11yObserver); + } + } + private static void populateTransformationMatrixLocked(WindowState windowState, Matrix outMatrix) { windowState.getTransformationMatrix(sTempFloats, outMatrix); @@ -1058,13 +1118,17 @@ final class AccessibilityController { private final WindowsForAccessibilityCallback mCallback; + private final int mDisplayId; + private final long mRecurringAccessibilityEventsIntervalMillis; public WindowsForAccessibilityObserver(WindowManagerService windowManagerService, + int displayId, WindowsForAccessibilityCallback callback) { mContext = windowManagerService.mContext; mService = windowManagerService; mCallback = callback; + mDisplayId = displayId; mHandler = new MyHandler(mService.mH.getLooper()); mRecurringAccessibilityEventsIntervalMillis = ViewConfiguration .getSendRecurringAccessibilityEventsInterval(); @@ -1099,14 +1163,17 @@ final class AccessibilityController { // Do not send the windows if there is no current focus as // the window manager is still looking for where to put it. // We will do the work when we get a focus change callback. - // TODO(b/112273690): Support multiple displays + // TODO [Multi-Display] : only checks top focused window if (!isCurrentFocusWindowOnDefaultDisplay()) { return; } - WindowManager windowManager = (WindowManager) - mContext.getSystemService(Context.WINDOW_SERVICE); - windowManager.getDefaultDisplay().getRealSize(mTempPoint); + final DisplayContent dc = mService.mRoot.getDisplayContent(mDisplayId); + if (dc == null) { + return; + } + final Display display = dc.getDisplay(); + display.getRealSize(mTempPoint); final int screenWidth = mTempPoint.x; final int screenHeight = mTempPoint.y; @@ -1126,14 +1193,13 @@ final class AccessibilityController { // Iterate until we figure out what is touchable for the entire screen. for (int i = visibleWindowCount - 1; i >= 0; i--) { final WindowState windowState = visibleWindows.valueAt(i); + final Region regionInScreen = new Region(); + computeWindowRegionInScreen(windowState, regionInScreen); - final Rect boundsInScreen = mTempRect; - computeWindowBoundsInScreen(windowState, boundsInScreen); - - if (windowMattersToAccessibility(windowState, boundsInScreen, unaccountedSpace, + if (windowMattersToAccessibility(windowState, regionInScreen, unaccountedSpace, skipRemainingWindowsForTasks)) { - addPopulatedWindowInfo(windowState, boundsInScreen, windows, addedWindows); - updateUnaccountedSpace(windowState, boundsInScreen, unaccountedSpace, + addPopulatedWindowInfo(windowState, regionInScreen, windows, addedWindows); + updateUnaccountedSpace(windowState, regionInScreen, unaccountedSpace, skipRemainingWindowsForTasks); focusedWindowAdded |= windowState.isFocused(); } @@ -1171,8 +1237,9 @@ final class AccessibilityController { clearAndRecycleWindows(windows); } - private boolean windowMattersToAccessibility(WindowState windowState, Rect boundsInScreen, - Region unaccountedSpace, HashSet<Integer> skipRemainingWindowsForTasks) { + private boolean windowMattersToAccessibility(WindowState windowState, + Region regionInScreen, Region unaccountedSpace, + HashSet<Integer> skipRemainingWindowsForTasks) { if (windowState.isFocused()) { return true; } @@ -1192,7 +1259,7 @@ final class AccessibilityController { } // If the window is completely covered by other windows - ignore. - if (unaccountedSpace.quickReject(boundsInScreen)) { + if (unaccountedSpace.quickReject(regionInScreen)) { return false; } @@ -1204,7 +1271,7 @@ final class AccessibilityController { return false; } - private void updateUnaccountedSpace(WindowState windowState, Rect boundsInScreen, + private void updateUnaccountedSpace(WindowState windowState, Region regionInScreen, Region unaccountedSpace, HashSet<Integer> skipRemainingWindowsForTasks) { if (windowState.mAttrs.type != WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY) { @@ -1212,59 +1279,71 @@ final class AccessibilityController { // Account for the space this window takes if the window // is not an accessibility overlay which does not change // the reported windows. - unaccountedSpace.op(boundsInScreen, unaccountedSpace, + unaccountedSpace.op(regionInScreen, unaccountedSpace, Region.Op.REVERSE_DIFFERENCE); // If a window is modal it prevents other windows from being touched if ((windowState.mAttrs.flags & (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL)) == 0) { - // Account for all space in the task, whether the windows in it are - // touchable or not. The modal window blocks all touches from the task's - // area. - unaccountedSpace.op(windowState.getDisplayFrameLw(), unaccountedSpace, - Region.Op.REVERSE_DIFFERENCE); + if (!windowState.hasTapExcludeRegion()) { + // Account for all space in the task, whether the windows in it are + // touchable or not. The modal window blocks all touches from the task's + // area. + unaccountedSpace.op(windowState.getDisplayFrameLw(), unaccountedSpace, + Region.Op.REVERSE_DIFFERENCE); + } else { + // If a window has tap exclude region, we need to account it. + final Region displayRegion = new Region(windowState.getDisplayFrameLw()); + final Region tapExcludeRegion = new Region(); + windowState.amendTapExcludeRegion(tapExcludeRegion); + displayRegion.op(tapExcludeRegion, displayRegion, + Region.Op.REVERSE_DIFFERENCE); + unaccountedSpace.op(displayRegion, unaccountedSpace, + Region.Op.REVERSE_DIFFERENCE); + } final Task task = windowState.getTask(); if (task != null) { // If the window is associated with a particular task, we can skip the // rest of the windows for that task. skipRemainingWindowsForTasks.add(task.mTaskId); - } else { + } else if (!windowState.hasTapExcludeRegion()) { // If the window is not associated with a particular task, then it is - // globally modal. In this case we can skip all remaining windows. + // globally modal. In this case we can skip all remaining windows when + // it doesn't has tap exclude region. unaccountedSpace.setEmpty(); } } } } - private void computeWindowBoundsInScreen(WindowState windowState, Rect outBounds) { + private void computeWindowRegionInScreen(WindowState windowState, Region outRegion) { // Get the touchable frame. Region touchableRegion = mTempRegion1; windowState.getTouchableRegion(touchableRegion); - Rect touchableFrame = mTempRect; - touchableRegion.getBounds(touchableFrame); - - // Move to origin as all transforms are captured by the matrix. - RectF windowFrame = mTempRectF; - windowFrame.set(touchableFrame); - windowFrame.offset(-windowState.getFrameLw().left, -windowState.getFrameLw().top); // Map the frame to get what appears on the screen. Matrix matrix = mTempMatrix; populateTransformationMatrixLocked(windowState, matrix); - matrix.mapRect(windowFrame); - // Got the bounds. - outBounds.set((int) windowFrame.left, (int) windowFrame.top, - (int) windowFrame.right, (int) windowFrame.bottom); + forEachRect(touchableRegion, rect -> { + // Move to origin as all transforms are captured by the matrix. + RectF windowFrame = mTempRectF; + windowFrame.set(rect); + windowFrame.offset(-windowState.getFrameLw().left, -windowState.getFrameLw().top); + + matrix.mapRect(windowFrame); + + // Union all rects. + outRegion.union(new Rect((int) windowFrame.left, (int) windowFrame.top, + (int) windowFrame.right, (int) windowFrame.bottom)); + }); } - private static void addPopulatedWindowInfo( - WindowState windowState, Rect boundsInScreen, + private static void addPopulatedWindowInfo(WindowState windowState, Region regionInScreen, List<WindowInfo> out, Set<IBinder> tokenOut) { final WindowInfo window = windowState.getWindowInfo(); - window.boundsInScreen.set(boundsInScreen); + window.regionInScreen.set(regionInScreen); window.layer = tokenOut.size(); out.add(window); tokenOut.add(window.token); @@ -1292,8 +1371,12 @@ final class AccessibilityController { private void populateVisibleWindowsOnScreenLocked(SparseArray<WindowState> outWindows) { final List<WindowState> tempWindowStatesList = new ArrayList<>(); - final DisplayContent dc = mService.getDefaultDisplayContentLocked(); - dc.forAllWindows((w) -> { + final DisplayContent dc = mService.mRoot.getDisplayContent(mDisplayId); + if (dc == null) { + return; + } + + dc.forAllWindows(w -> { if (w.isVisibleLw()) { tempWindowStatesList.add(w); } @@ -1306,17 +1389,10 @@ final class AccessibilityController { return; } - // TODO: Use Region instead to get rid of this complicated logic. - // Check the tap exclude region of the parent window. If the tap exclude region - // is empty, it means there is another can-receive-pointer-event view on top of - // the region. Hence, we don't count the window as visible. - if (w.isVisibleLw() && parentWindow.getDisplayContent().isDefaultDisplay - && parentWindow.hasTapExcludeRegion() - && tempWindowStatesList.contains(parentWindow)) { - tempWindowStatesList.add( - tempWindowStatesList.lastIndexOf(parentWindow) + 1, w); + if (w.isVisibleLw() && tempWindowStatesList.contains(parentWindow)) { + tempWindowStatesList.add(tempWindowStatesList.lastIndexOf(parentWindow), w); } - }, true /* traverseTopToBottom */); + }, false /* traverseTopToBottom */); for (int i = 0; i < tempWindowStatesList.size(); i++) { outWindows.put(i, tempWindowStatesList.get(i)); } @@ -1334,7 +1410,7 @@ final class AccessibilityController { } return displayParentWindow; } - + // TODO [Multi-Display] : only checks top focused window private boolean isCurrentFocusWindowOnDefaultDisplay() { final WindowState focusedWindow = mService.mRoot.getTopFocusedDisplayContent().mCurrentFocus; diff --git a/services/core/java/com/android/server/wm/ActivityDisplay.java b/services/core/java/com/android/server/wm/ActivityDisplay.java index fe4811dce274..f1cd721cfd8c 100644 --- a/services/core/java/com/android/server/wm/ActivityDisplay.java +++ b/services/core/java/com/android/server/wm/ActivityDisplay.java @@ -174,6 +174,7 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> mDisplayId = display.getDisplayId(); mDisplay = display; mDisplayContent = createDisplayContent(); + mDisplayContent.reconfigureDisplayLocked(); updateBounds(); } @@ -1229,7 +1230,7 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack> final ActivityStack stack = mStacks.get(stackNdx); // Always finish non-standard type stacks. if (destroyContentOnRemoval || !stack.isActivityTypeStandardOrUndefined()) { - stack.finishAllActivitiesLocked(true /* immediately */); + stack.finishAllActivitiesImmediately(); } else { // If default display is in split-window mode, set windowing mode of the stack // to split-screen secondary. Otherwise, set the windowing mode to undefined by diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 9c87ce8e1744..26b1f7b49aae 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -92,6 +92,8 @@ import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; import static android.view.Surface.ROTATION_270; import static android.view.Surface.ROTATION_90; +import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE; +import static android.view.WindowManager.TRANSIT_TASK_CLOSE; import static com.android.server.am.ActivityRecordProto.CONFIGURATION_CONTAINER; import static com.android.server.am.ActivityRecordProto.FRONT_OF_TASK; @@ -103,6 +105,7 @@ import static com.android.server.am.ActivityRecordProto.VISIBLE; import static com.android.server.am.EventLogTags.AM_RELAUNCH_ACTIVITY; import static com.android.server.am.EventLogTags.AM_RELAUNCH_RESUME_ACTIVITY; import static com.android.server.wm.ActivityStack.ActivityState.DESTROYED; +import static com.android.server.wm.ActivityStack.ActivityState.FINISHING; import static com.android.server.wm.ActivityStack.ActivityState.INITIALIZING; import static com.android.server.wm.ActivityStack.ActivityState.PAUSED; import static com.android.server.wm.ActivityStack.ActivityState.PAUSING; @@ -116,18 +119,29 @@ import static com.android.server.wm.ActivityStack.LAUNCH_TICK_MSG; import static com.android.server.wm.ActivityStack.PAUSE_TIMEOUT_MSG; import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_VISIBLE; import static com.android.server.wm.ActivityStack.STOP_TIMEOUT_MSG; +import static com.android.server.wm.ActivityStackSupervisor.PAUSE_IMMEDIATELY; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONTAINERS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_FOCUS; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_PAUSE; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SAVED_STATE; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STATES; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TRANSITION; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_USER_LEAVING; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_VISIBILITY; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CONTAINERS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_FOCUS; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_PAUSE; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RESULTS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_SAVED_STATE; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STATES; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_TRANSITION; +import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_USER_LEAVING; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_VISIBILITY; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; @@ -151,6 +165,7 @@ import static org.xmlpull.v1.XmlPullParser.START_TAG; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.Activity; import android.app.ActivityManager.TaskDescription; import android.app.ActivityOptions; import android.app.PendingIntent; @@ -194,6 +209,7 @@ import android.os.SystemClock; import android.os.UserHandle; import android.os.storage.StorageManager; import android.service.voice.IVoiceInteractionSession; +import android.util.ArraySet; import android.util.EventLog; import android.util.Log; import android.util.MergedConfiguration; @@ -215,11 +231,14 @@ import com.android.internal.util.XmlUtils; import com.android.server.AttributeCache; import com.android.server.AttributeCache.Entry; import com.android.server.am.AppTimeTracker; +import com.android.server.am.EventLogTags; import com.android.server.am.PendingIntentRecord; import com.android.server.uri.UriPermissionOwner; import com.android.server.wm.ActivityMetricsLogger.WindowingModeTransitionInfoSnapshot; import com.android.server.wm.ActivityStack.ActivityState; +import com.google.android.collect.Sets; + import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; @@ -240,11 +259,16 @@ import java.util.Objects; final class ActivityRecord extends ConfigurationContainer { private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityRecord" : TAG_ATM; private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION; + private static final String TAG_CONTAINERS = TAG + POSTFIX_CONTAINERS; + private static final String TAG_FOCUS = TAG + POSTFIX_FOCUS; + private static final String TAG_PAUSE = TAG + POSTFIX_PAUSE; + private static final String TAG_RESULTS = TAG + POSTFIX_RESULTS; private static final String TAG_SAVED_STATE = TAG + POSTFIX_SAVED_STATE; private static final String TAG_STATES = TAG + POSTFIX_STATES; private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH; + private static final String TAG_TRANSITION = TAG + POSTFIX_TRANSITION; + private static final String TAG_USER_LEAVING = TAG + POSTFIX_USER_LEAVING; private static final String TAG_VISIBILITY = TAG + POSTFIX_VISIBILITY; - private static final String TAG_FOCUS = TAG + POSTFIX_FOCUS; private static final String ATTR_ID = "id"; private static final String TAG_INTENT = "intent"; @@ -317,12 +341,15 @@ final class ActivityRecord extends ConfigurationContainer { UriPermissionOwner uriPermissions; // current special URI access perms. WindowProcessController app; // if non-null, hosting application private ActivityState mState; // current state we are in - Bundle icicle; // last saved activity state - PersistableBundle persistentState; // last persistently saved activity state - // TODO: See if this is still needed. - boolean frontOfTask; // is this the root activity of its task? + private Bundle mIcicle; // last saved activity state + private PersistableBundle mPersistentState; // last persistently saved activity state + private boolean mHaveState = true; // Indicates whether the last saved state of activity is + // preserved. This starts out 'true', since the initial state + // of an activity is that we have everything, and we should + // never consider it lacking in state to be removed if it + // dies. After an activity is launched it follows the value + // of #mIcicle. boolean launchFailed; // set if a launched failed, to abort on 2nd try - boolean haveState; // have we gotten the last activity state? boolean stopped; // is activity pause finished? boolean delayedResume; // not yet resumed because of stopped app switches? boolean finishing; // activity in pending finish list? @@ -439,7 +466,7 @@ final class ActivityRecord extends ConfigurationContainer { pw.print(" userId="); pw.println(mUserId); pw.print(prefix); pw.print("app="); pw.println(app); pw.print(prefix); pw.println(intent.toInsecureStringWithClip()); - pw.print(prefix); pw.print("frontOfTask="); pw.print(frontOfTask); + pw.print(prefix); pw.print("rootOfTask="); pw.print(isRootOfTask()); pw.print(" task="); pw.println(task); pw.print(prefix); pw.print("taskAffinity="); pw.println(taskAffinity); pw.print(prefix); pw.print("mActivityComponent="); @@ -552,8 +579,8 @@ final class ActivityRecord extends ConfigurationContainer { if (lastLaunchTime == 0) pw.print("0"); else TimeUtils.formatDuration(lastLaunchTime, now, pw); pw.println(); - pw.print(prefix); pw.print("haveState="); pw.print(haveState); - pw.print(" icicle="); pw.println(icicle); + pw.print(prefix); pw.print("mHaveState="); pw.print(mHaveState); + pw.print(" mIcicle="); pw.println(mIcicle); pw.print(prefix); pw.print("state="); pw.print(mState); pw.print(" stopped="); pw.print(stopped); pw.print(" delayedResume="); pw.print(delayedResume); @@ -614,6 +641,34 @@ final class ActivityRecord extends ConfigurationContainer { } } + /** Update the saved state of an activity. */ + void setSavedState(@Nullable Bundle savedState) { + mIcicle = savedState; + mHaveState = mIcicle != null; + } + + /** + * Get the actual Bundle instance of the saved state. + * @see #hasSavedState() for checking if the record has saved state. + */ + @Nullable Bundle getSavedState() { + return mIcicle; + } + + /** + * Check if the activity has saved state. + * @return {@code true} if the client reported a non-empty saved state from last onStop(), or + * if this record was just created and the client is yet to be launched and resumed. + */ + boolean hasSavedState() { + return mHaveState; + } + + /** @return The actual PersistableBundle instance of the saved persistent state. */ + @Nullable PersistableBundle getPersistentSavedState() { + return mPersistentState; + } + void updateApplicationInfo(ApplicationInfo aInfo) { info.applicationInfo = aInfo; } @@ -916,8 +971,9 @@ final class ActivityRecord extends ConfigurationContainer { return ResolverActivity.class.getName().equals(className); } - boolean isResolverActivity() { - return isResolverActivity(mActivityComponent.getClassName()); + boolean isResolverOrDelegateActivity() { + return isResolverActivity(mActivityComponent.getClassName()) || Objects.equals( + mActivityComponent, mAtmService.mStackSupervisor.getSystemChooserActivity()); } boolean isResolverOrChildActivity() { @@ -956,7 +1012,6 @@ final class ActivityRecord extends ConfigurationContainer { resultWho = _resultWho; requestCode = _reqCode; setState(INITIALIZING, "ActivityRecord ctor"); - frontOfTask = false; launchFailed = false; stopped = false; delayedResume = false; @@ -971,10 +1026,6 @@ final class ActivityRecord extends ConfigurationContainer { hasBeenLaunched = false; mStackSupervisor = supervisor; - // This starts out true, since the initial state of an activity is that we have everything, - // and we shouldn't never consider it lacking in state to be removed if it dies. - haveState = true; - // If the class name in the intent doesn't match that of the target, this is // probably an alias. We have to create a new ComponentName object to keep track // of the real activity name, so that FLAG_ACTIVITY_CLEAR_TOP is handled properly. @@ -1247,7 +1298,8 @@ final class ActivityRecord extends ConfigurationContainer { && intent.getType() == null; } - private boolean canLaunchHomeActivity(int uid, ActivityRecord sourceRecord) { + @VisibleForTesting + boolean canLaunchHomeActivity(int uid, ActivityRecord sourceRecord) { if (uid == Process.myUid() || uid == 0) { // System process can launch home activity. return true; @@ -1257,8 +1309,8 @@ final class ActivityRecord extends ConfigurationContainer { if (recentTasks != null && recentTasks.isCallerRecents(uid)) { return true; } - // Resolver activity can launch home activity. - return sourceRecord != null && sourceRecord.isResolverActivity(); + // Resolver or system chooser activity can launch home activity. + return sourceRecord != null && sourceRecord.isResolverOrDelegateActivity(); } /** @@ -1277,7 +1329,7 @@ final class ActivityRecord extends ConfigurationContainer { ActivityOptions options, ActivityRecord sourceRecord) { int activityType = ACTIVITY_TYPE_UNDEFINED; if ((!componentSpecified || canLaunchHomeActivity(launchedFromUid, sourceRecord)) - && isHomeIntent(intent) && !isResolverActivity()) { + && isHomeIntent(intent) && !isResolverOrDelegateActivity()) { // This sure looks like a home activity! activityType = ACTIVITY_TYPE_HOME; @@ -1523,12 +1575,283 @@ final class ActivityRecord extends ConfigurationContainer { stack.moveToFront(reason, task); // Report top activity change to tracking services and WM if (mRootActivityContainer.getTopResumedActivity() == this) { - // TODO(b/111361570): Support multiple focused apps in WM mAtmService.setResumedActivityUncheckLocked(this, reason); } return true; } + /** Finish all activities in the task with the same affinity as this one. */ + void finishActivityAffinity() { + final ArrayList<ActivityRecord> activities = getTaskRecord().mActivities; + for (int index = activities.indexOf(this); index >= 0; --index) { + final ActivityRecord cur = activities.get(index); + if (!Objects.equals(cur.taskAffinity, taskAffinity)) { + break; + } + cur.finishActivityLocked(Activity.RESULT_CANCELED, null /* resultData */, + "request-affinity", true /* oomAdj */); + } + } + + /** + * Sets the result for activity that started this one, clears the references to activities + * started for result from this one, and clears new intents. + */ + void finishActivityResults(int resultCode, Intent resultData) { + // Send the result if needed + if (resultTo != null) { + if (DEBUG_RESULTS) { + Slog.v(TAG_RESULTS, "Adding result to " + resultTo + + " who=" + resultWho + " req=" + requestCode + + " res=" + resultCode + " data=" + resultData); + } + if (resultTo.mUserId != mUserId) { + if (resultData != null) { + resultData.prepareToLeaveUser(mUserId); + } + } + if (info.applicationInfo.uid > 0) { + mAtmService.mUgmInternal.grantUriPermissionFromIntent(info.applicationInfo.uid, + resultTo.packageName, resultData, + resultTo.getUriPermissionsLocked(), resultTo.mUserId); + } + resultTo.addResultLocked(this, resultWho, requestCode, resultCode, resultData); + resultTo = null; + } else if (DEBUG_RESULTS) { + Slog.v(TAG_RESULTS, "No result destination from " + this); + } + + // Make sure this HistoryRecord is not holding on to other resources, + // because clients have remote IPC references to this object so we + // can't assume that will go away and want to avoid circular IPC refs. + results = null; + pendingResults = null; + newIntents = null; + setSavedState(null /* savedState */); + } + + /** + * See {@link #finishActivityLocked(int, Intent, String, boolean, boolean)} + */ + boolean finishActivityLocked(int resultCode, Intent resultData, String reason, boolean oomAdj) { + return finishActivityLocked(resultCode, resultData, reason, oomAdj, !PAUSE_IMMEDIATELY); + } + + /** + * @return Returns true if this activity has been removed from the history + * list, or false if it is still in the list and will be removed later. + */ + boolean finishActivityLocked(int resultCode, Intent resultData, String reason, boolean oomAdj, + boolean pauseImmediately) { + if (finishing) { + Slog.w(TAG, "Duplicate finish request for " + this); + return false; + } + + mAtmService.mWindowManager.deferSurfaceLayout(); + try { + makeFinishingLocked(); + final TaskRecord task = getTaskRecord(); + EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY, + mUserId, System.identityHashCode(this), + task.taskId, shortComponentName, reason); + final ArrayList<ActivityRecord> activities = task.mActivities; + final int index = activities.indexOf(this); + if (index < (activities.size() - 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); + next.intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); + } + } + + pauseKeyDispatchingLocked(); + + final ActivityStack stack = getActivityStack(); + stack.adjustFocusedActivityStack(this, "finishActivity"); + + finishActivityResults(resultCode, resultData); + + final boolean endTask = index <= 0 && !task.isClearingToReuseTask(); + final int transit = endTask ? TRANSIT_TASK_CLOSE : TRANSIT_ACTIVITY_CLOSE; + if (stack.getResumedActivity() == this) { + if (DEBUG_VISIBILITY || DEBUG_TRANSITION) { + Slog.v(TAG_TRANSITION, "Prepare close transition: finishing " + this); + } + if (endTask) { + mAtmService.getTaskChangeNotificationController().notifyTaskRemovalStarted( + task.getTaskInfo()); + } + getDisplay().mDisplayContent.prepareAppTransition(transit, false); + + // When finishing the activity preemptively take the snapshot before the app window + // is marked as hidden and any configuration changes take place + if (mAtmService.mWindowManager.mTaskSnapshotController != null) { + final ArraySet<Task> tasks = Sets.newArraySet(task.mTask); + mAtmService.mWindowManager.mTaskSnapshotController.snapshotTasks(tasks); + mAtmService.mWindowManager.mTaskSnapshotController + .addSkipClosingAppSnapshotTasks(tasks); + } + + // Tell window manager to prepare for this one to be removed. + setVisibility(false); + + if (stack.mPausingActivity == null) { + if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Finish needs to pause: " + this); + if (DEBUG_USER_LEAVING) { + Slog.v(TAG_USER_LEAVING, "finish() => pause with userLeaving=false"); + } + stack.startPausingLocked(false, false, null, pauseImmediately); + } + + if (endTask) { + mAtmService.getLockTaskController().clearLockedTask(task); + } + } else if (!isState(PAUSING)) { + // If the activity is PAUSING, we will complete the finish once + // it is done pausing; else we can just directly finish it here. + if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Finish not pausing: " + this); + if (visible) { + prepareActivityHideTransitionAnimation(transit); + } + + final int finishMode = (visible || nowVisible) ? FINISH_AFTER_VISIBLE + : FINISH_AFTER_PAUSE; + final boolean removedActivity = finishCurrentActivityLocked(finishMode, oomAdj, + "finishActivityLocked") == null; + + // The following code is an optimization. When the last non-task overlay activity + // is removed from the task, we remove the entire task from the stack. However, + // since that is done after the scheduled destroy callback from the activity, that + // call to change the visibility of the task overlay activities would be out of + // sync with the activity visibility being set for this finishing activity above. + // In this case, we can set the visibility of all the task overlay activities when + // we detect the last one is finishing to keep them in sync. + if (task.onlyHasTaskOverlayActivities(true /* excludeFinishing */)) { + for (ActivityRecord taskOverlay : task.mActivities) { + if (!taskOverlay.mTaskOverlay) { + continue; + } + taskOverlay.prepareActivityHideTransitionAnimation(transit); + } + } + return removedActivity; + } else { + if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Finish waiting for pause of: " + this); + } + + return false; + } finally { + mAtmService.mWindowManager.continueSurfaceLayout(); + } + } + + private void prepareActivityHideTransitionAnimation(int transit) { + final DisplayContent dc = getDisplay().mDisplayContent; + dc.prepareAppTransition(transit, false); + setVisibility(false); + dc.executeAppTransition(); + } + + static final int FINISH_IMMEDIATELY = 0; + private static final int FINISH_AFTER_PAUSE = 1; + static final int FINISH_AFTER_VISIBLE = 2; + + ActivityRecord finishCurrentActivityLocked(int mode, boolean oomAdj, String reason) { + // First things first: if this activity is currently visible, + // and the resumed activity is not yet visible, then hold off on + // finishing until the resumed one becomes visible. + + // The activity that we are finishing may be over the lock screen. In this case, we do not + // want to consider activities that cannot be shown on the lock screen as running and should + // proceed with finishing the activity if there is no valid next top running activity. + // Note that if this finishing activity is floating task, we don't need to wait the + // next activity resume and can destroy it directly. + final ActivityStack stack = getActivityStack(); + final ActivityDisplay display = getDisplay(); + final ActivityRecord next = display.topRunningActivity(true /* considerKeyguardState */); + final boolean isFloating = getConfiguration().windowConfiguration.tasksAreFloating(); + + if (mode == FINISH_AFTER_VISIBLE && (visible || nowVisible) + && next != null && !next.nowVisible && !isFloating) { + if (!mStackSupervisor.mStoppingActivities.contains(this)) { + stack.addToStopping(this, false /* scheduleIdle */, false /* idleDelayed */, + "finishCurrentActivityLocked"); + } + if (DEBUG_STATES) { + Slog.v(TAG_STATES, "Moving to STOPPING: " + this + " (finish requested)"); + } + setState(STOPPING, "finishCurrentActivityLocked"); + if (oomAdj) { + mAtmService.updateOomAdj(); + } + return this; + } + + // make sure the record is cleaned out of other places. + mStackSupervisor.mStoppingActivities.remove(this); + mStackSupervisor.mGoingToSleepActivities.remove(this); + final ActivityState prevState = getState(); + if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to FINISHING: " + this); + + setState(FINISHING, "finishCurrentActivityLocked"); + + // Don't destroy activity immediately if the display contains home stack, although there is + // no next activity at the moment but another home activity should be started later. Keep + // this activity alive until next home activity is resumed then user won't see a temporary + // black screen. + final boolean noRunningStack = next == null && display.topRunningActivity() == null + && display.getHomeStack() == null; + final boolean noFocusedStack = stack != display.getFocusedStack(); + final boolean finishingInNonFocusedStackOrNoRunning = mode == FINISH_AFTER_VISIBLE + && prevState == PAUSED && (noFocusedStack || noRunningStack); + + if (mode == FINISH_IMMEDIATELY + || (prevState == PAUSED && (mode == FINISH_AFTER_PAUSE || inPinnedWindowingMode())) + || finishingInNonFocusedStackOrNoRunning + || prevState == STARTED + || prevState == STOPPING + || prevState == STOPPED + || prevState == ActivityState.INITIALIZING) { + makeFinishingLocked(); + boolean activityRemoved = stack.destroyActivityLocked(this, true /* removeFromApp */, + "finish-imm:" + reason); + + if (finishingInNonFocusedStackOrNoRunning) { + // Finishing activity that was in paused state and it was in not currently focused + // stack, need to make something visible in its place. Also if the display does not + // have running activity, the configuration may need to be updated for restoring + // original orientation of the display. + mRootActivityContainer.ensureVisibilityAndConfig(next, getDisplayId(), + false /* markFrozenIfConfigChanged */, true /* deferResume */); + } + if (activityRemoved) { + mRootActivityContainer.resumeFocusedStacksTopActivities(); + } + if (DEBUG_CONTAINERS) { + Slog.d(TAG_CONTAINERS, "destroyActivityLocked: finishCurrentActivityLocked r=" + + this + " destroy returned removed=" + activityRemoved); + } + return activityRemoved ? null : this; + } + + // Need to go through the full pause cycle to get this + // activity into the stopped state and then finish it. + if (DEBUG_ALL) Slog.v(TAG, "Enqueueing pending finish: " + this); + mStackSupervisor.mFinishingActivities.add(this); + resumeKeyDispatchingLocked(); + mRootActivityContainer.resumeFocusedStacksTopActivities(); + // If activity was not paused at this point - explicitly pause it to start finishing + // process. Finishing will be completed once it reports pause back. + if (isState(RESUMED) && stack.mPausingActivity != null) { + stack.startPausingLocked(false /* userLeaving */, false /* uiSleeping */, + next /* resuming */, false /* dontWait */); + } + return this; + } + void makeFinishingLocked() { if (finishing) { return; @@ -2184,8 +2507,7 @@ final class ActivityRecord extends ConfigurationContainer { // been removed (e.g. destroy timeout), so the token could be null. return; } - r.icicle = null; - r.haveState = false; + r.setSavedState(null /* savedState */); final ActivityDisplay display = r.getDisplay(); if (display != null) { @@ -2258,19 +2580,18 @@ final class ActivityRecord extends ConfigurationContainer { return; } if (newPersistentState != null) { - persistentState = newPersistentState; + mPersistentState = newPersistentState; mAtmService.notifyTaskPersisterLocked(task, false); } - if (DEBUG_SAVED_STATE) Slog.i(TAG_SAVED_STATE, "Saving icicle of " + this + ": " + icicle); if (newIcicle != null) { // If icicle is null, this is happening due to a timeout, so we haven't really saved // the state. - icicle = newIcicle; - haveState = true; + setSavedState(newIcicle); launchCount = 0; updateTaskDescription(description); } + if (DEBUG_SAVED_STATE) Slog.i(TAG_SAVED_STATE, "Saving icicle of " + this + ": " + mIcicle); if (!stopped) { if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to STOPPED: " + this + " (stop complete)"); stack.mHandler.removeMessages(STOP_TIMEOUT_MSG, this); @@ -2551,7 +2872,8 @@ final class ActivityRecord extends ConfigurationContainer { } final TaskRecord task = r.task; final int activityNdx = task.mActivities.indexOf(r); - if (activityNdx < 0 || (onlyRoot && activityNdx > task.findEffectiveRootIndex())) { + if (activityNdx < 0 + || (onlyRoot && activityNdx > task.findRootIndex(true /* effectiveRoot */))) { return INVALID_TASK_ID; } return task.taskId; @@ -2589,7 +2911,7 @@ final class ActivityRecord extends ConfigurationContainer { } final ActivityStack stack = getActivityStack(); if (stack == null || this == stack.getResumedActivity() || this == stack.mPausingActivity - || !haveState || !stopped) { + || !mHaveState || !stopped) { // We're not ready for this kind of thing. return false; } @@ -3524,7 +3846,7 @@ final class ActivityRecord extends ConfigurationContainer { // The restarting state avoids removing this record when process is died. setState(RESTARTING_PROCESS, "restartActivityProcess"); - if (!visible || haveState) { + if (!visible || mHaveState) { // Kill its process immediately because the activity should be in background. // The activity state will be update to {@link #DESTROYED} in // {@link ActivityStack#cleanUpActivityLocked} when handling process died. @@ -3613,9 +3935,9 @@ final class ActivityRecord extends ConfigurationContainer { intent.saveToXml(out); out.endTag(null, TAG_INTENT); - if (isPersistable() && persistentState != null) { + if (isPersistable() && mPersistentState != null) { out.startTag(null, TAG_PERSISTABLEBUNDLE); - persistentState.saveToXml(out); + mPersistentState.saveToXml(out); out.endTag(null, TAG_PERSISTABLEBUNDLE); } } @@ -3696,7 +4018,7 @@ final class ActivityRecord extends ConfigurationContainer { 0 /* reqCode */, componentSpecified, false /* rootVoiceInteraction */, stackSupervisor, null /* options */, null /* sourceRecord */); - r.persistentState = persistentState; + r.mPersistentState = persistentState; r.taskDescription = taskDescription; r.createTime = createTime; @@ -3802,6 +4124,27 @@ final class ActivityRecord extends ConfigurationContainer { return display != null && this == display.getResumedActivity(); } + + /** + * Check if this is the root of the task - first activity that is not finishing, starting from + * the bottom of the task. If all activities are finishing - then this method will return + * {@code true} if the activity is at the bottom. + * + * NOTE: This is different from 'effective root' - an activity that defines the task identity. + */ + boolean isRootOfTask() { + if (task == null) { + return false; + } + final ActivityRecord rootActivity = task.getRootActivity(); + if (rootActivity != null) { + return this == rootActivity; + } + // No non-finishing activity found. In this case the bottom-most activity is considered to + // be the root. + return task.getChildAt(0) == this; + } + void registerRemoteAnimations(RemoteAnimationDefinition definition) { if (mAppWindowToken == null) { Slog.w(TAG_WM, "Attempted to register remote animations with non-existing app" @@ -3845,7 +4188,7 @@ final class ActivityRecord extends ConfigurationContainer { writeIdentifierToProto(proto, IDENTIFIER); proto.write(STATE, mState.toString()); proto.write(VISIBLE, visible); - proto.write(FRONT_OF_TASK, frontOfTask); + proto.write(FRONT_OF_TASK, isRootOfTask()); if (hasProcess()) { proto.write(PROC_ID, app.getPid()); } diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java index 18a57ae17fda..81d8fa9554bc 100644 --- a/services/core/java/com/android/server/wm/ActivityStack.java +++ b/services/core/java/com/android/server/wm/ActivityStack.java @@ -56,9 +56,10 @@ import static com.android.server.am.ActivityStackProto.RESUMED_ACTIVITY; import static com.android.server.am.ActivityStackProto.TASKS; import static com.android.server.wm.ActivityDisplay.POSITION_BOTTOM; import static com.android.server.wm.ActivityDisplay.POSITION_TOP; +import static com.android.server.wm.ActivityRecord.FINISH_AFTER_VISIBLE; +import static com.android.server.wm.ActivityRecord.FINISH_IMMEDIATELY; import static com.android.server.wm.ActivityStack.ActivityState.DESTROYED; import static com.android.server.wm.ActivityStack.ActivityState.DESTROYING; -import static com.android.server.wm.ActivityStack.ActivityState.FINISHING; import static com.android.server.wm.ActivityStack.ActivityState.PAUSED; import static com.android.server.wm.ActivityStack.ActivityState.PAUSING; import static com.android.server.wm.ActivityStack.ActivityState.RESUMED; @@ -78,7 +79,6 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONTAIN import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_PAUSE; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RELEASE; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS; -import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SAVED_STATE; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STACK; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STATES; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH; @@ -93,7 +93,6 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CONTA import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_PAUSE; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RELEASE; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RESULTS; -import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_SAVED_STATE; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STACK; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STATES; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH; @@ -165,8 +164,6 @@ import com.android.server.am.AppTimeTracker; import com.android.server.am.EventLogTags; import com.android.server.am.PendingIntentRecord; -import com.google.android.collect.Sets; - import java.io.FileDescriptor; import java.io.PrintWriter; import java.lang.ref.WeakReference; @@ -187,7 +184,6 @@ class ActivityStack extends ConfigurationContainer { private static final String TAG_PAUSE = TAG + POSTFIX_PAUSE; private static final String TAG_RELEASE = TAG + POSTFIX_RELEASE; private static final String TAG_RESULTS = TAG + POSTFIX_RESULTS; - private static final String TAG_SAVED_STATE = TAG + POSTFIX_SAVED_STATE; private static final String TAG_STACK = TAG + POSTFIX_STACK; private static final String TAG_STATES = TAG + POSTFIX_STATES; private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH; @@ -565,7 +561,6 @@ class ActivityStack extends ConfigurationContainer { + reason); setResumedActivity(record, reason + " - onActivityStateChanged"); if (record == mRootActivityContainer.getTopResumedActivity()) { - // TODO(b/111361570): Support multiple focused apps in WM mService.setResumedActivityUncheckLocked(record, reason); } mStackSupervisor.mRecentTasks.add(record.getTaskRecord()); @@ -1500,8 +1495,6 @@ class ActivityStack extends ConfigurationContainer { + " callers=" + Debug.getCallers(5)); r.setState(RESUMED, "minimalResumeActivityLocked"); r.completeResumeLocked(); - if (DEBUG_SAVED_STATE) Slog.i(TAG_SAVED_STATE, - "Launch completed; removing icicle of " + r.icicle); } private void clearLaunchTime(ActivityRecord r) { @@ -1783,7 +1776,7 @@ class ActivityStack extends ConfigurationContainer { if (r.finishing) { if (DEBUG_PAUSE) Slog.v(TAG, "Executing finish of failed to pause activity: " + r); - finishCurrentActivityLocked(r, FINISH_AFTER_VISIBLE, false, + r.finishCurrentActivityLocked(FINISH_AFTER_VISIBLE, false, "activityPausedLocked"); } } @@ -1802,7 +1795,7 @@ class ActivityStack extends ConfigurationContainer { prev.setState(PAUSED, "completePausedLocked"); if (prev.finishing) { if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Executing finish of activity: " + prev); - prev = finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE, false, + prev = prev.finishCurrentActivityLocked(FINISH_AFTER_VISIBLE, false /* oomAdj */, "completePausedLocked"); } else if (prev.hasProcess()) { if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueue pending stop if needed: " + prev @@ -1896,7 +1889,7 @@ class ActivityStack extends ConfigurationContainer { // last of activity of the last task the stack will be empty and must // be cleared immediately. boolean forceIdle = mStackSupervisor.mStoppingActivities.size() > MAX_STOPPING_TO_FORCE - || (r.frontOfTask && mTaskHistory.size() <= 1); + || (r.isRootOfTask() && mTaskHistory.size() <= 1); if (scheduleIdle || forceIdle) { if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Scheduling idle now: forceIdle=" + forceIdle + "immediate=" + !idleDelayed); @@ -3210,8 +3203,6 @@ class ActivityStack extends ConfigurationContainer { r.createAppWindowToken(); } - task.setFrontOfTask(); - // The transition animation and starting window are not needed if {@code allowMoveToFront} // is false, because the activity won't be visible. if ((!isHomeOrRecentsStack() || numActivities() > 0) && allowMoveToFront) { @@ -3323,20 +3314,17 @@ class ActivityStack extends ConfigurationContainer { } /** - * Perform a reset of the given task, if needed as part of launching it. - * Returns the new HistoryRecord at the top of the task. - */ - /** - * Helper method for #resetTaskIfNeededLocked. - * We are inside of the task being reset... we'll either finish this activity, push it out - * for another task, or leave it as-is. + * Helper method for {@link #resetTaskIfNeededLocked(ActivityRecord, ActivityRecord)}. + * Performs a reset of the given task, if needed for new activity start. * @param task The task containing the Activity (taskTop) that might be reset. - * @param forceReset + * @param forceReset Flag indicating if clear task was requested * @return An ActivityOptions that needs to be processed. */ private ActivityOptions resetTargetTaskIfNeededLocked(TaskRecord task, boolean forceReset) { ActivityOptions topOptions = null; + // Tracker of the end of currently handled reply chain (sublist) of activities. What happens + // to activities in the same chain will depend on what the end activity of the chain needs. int replyChainEnd = -1; boolean canMoveOptions = true; @@ -3344,11 +3332,14 @@ class ActivityStack extends ConfigurationContainer { // the root, we may no longer have the task!). final ArrayList<ActivityRecord> activities = task.mActivities; final int numActivities = activities.size(); - final int rootActivityNdx = task.findEffectiveRootIndex(); - for (int i = numActivities - 1; i > rootActivityNdx; --i ) { + int lastActivityNdx = task.findRootIndex(true /* effectiveRoot */); + if (lastActivityNdx == -1) { + lastActivityNdx = 0; + } + for (int i = numActivities - 1; i > lastActivityNdx; --i) { ActivityRecord target = activities.get(i); - if (target.frontOfTask) - break; + // TODO: Why is this needed? Looks like we're breaking the loop before we reach the root + if (target.isRootOfTask()) break; final int flags = target.info.flags; final boolean finishOnTaskLaunch = @@ -3381,12 +3372,13 @@ class ActivityStack extends ConfigurationContainer { // bottom of the activity stack. This also keeps it // correctly ordered with any activities we previously // moved. + // TODO: We should probably look for other stacks also, since corresponding task + // with the same affinity is unlikely to be in the same stack. final TaskRecord targetTask; final ActivityRecord bottom = !mTaskHistory.isEmpty() && !mTaskHistory.get(0).mActivities.isEmpty() ? mTaskHistory.get(0).mActivities.get(0) : null; - if (bottom != null && target.taskAffinity != null - && target.taskAffinity.equals(bottom.getTaskRecord().affinity)) { + 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, // then merge it into the same task. @@ -3396,7 +3388,8 @@ class ActivityStack extends ConfigurationContainer { } else { targetTask = createTaskRecord( mStackSupervisor.getNextTaskIdForUserLocked(target.mUserId), - target.info, null, null, null, false); + target.info, null /* intent */, null /* voiceSession */, + null /* voiceInteractor */, false /* toTop */); targetTask.affinityIntent = target.intent; if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity " + target + " out to new task " + targetTask); @@ -3458,8 +3451,8 @@ class ActivityStack extends ConfigurationContainer { } if (DEBUG_TASKS) Slog.w(TAG_TASKS, "resetTaskIntendedTask: calling finishActivity on " + p); - if (finishActivityLocked( - p, Activity.RESULT_CANCELED, null, "reset-task", false)) { + if (p.finishActivityLocked(Activity.RESULT_CANCELED, null /* resultData */, + "reset-task", false /* oomAdj */)) { end--; srcPos--; } @@ -3477,28 +3470,34 @@ class ActivityStack extends ConfigurationContainer { } /** - * Helper method for #resetTaskIfNeededLocked. Processes all of the activities in a given - * TaskRecord looking for an affinity with the task of resetTaskIfNeededLocked.taskTop. + * Helper method for {@link #resetTaskIfNeededLocked(ActivityRecord, ActivityRecord)}. + * Processes all of the activities in a given TaskRecord looking for an affinity with the task + * of resetTaskIfNeededLocked.taskTop. * @param affinityTask The task we are looking for an affinity to. * @param task Task that resetTaskIfNeededLocked.taskTop belongs to. * @param topTaskIsHigher True if #task has already been processed by resetTaskIfNeededLocked. - * @param forceReset Flag passed in to resetTaskIfNeededLocked. + * @param forceReset Flag indicating if clear task was requested */ + // TODO: Consider merging with #resetTargetTaskIfNeededLocked() above private int resetAffinityTaskIfNeededLocked(TaskRecord affinityTask, TaskRecord task, boolean topTaskIsHigher, boolean forceReset, int taskInsertionPoint) { + // Tracker of the end of currently handled reply chain (sublist) of activities. What happens + // to activities in the same chain will depend on what the end activity of the chain needs. int replyChainEnd = -1; - final int taskId = task.taskId; final String taskAffinity = task.affinity; final ArrayList<ActivityRecord> activities = affinityTask.mActivities; final int numActivities = activities.size(); - final int rootActivityNdx = affinityTask.findEffectiveRootIndex(); // Do not operate on or below the effective root Activity. - for (int i = numActivities - 1; i > rootActivityNdx; --i) { + int lastActivityNdx = affinityTask.findRootIndex(true /* effectiveRoot */); + if (lastActivityNdx == -1) { + lastActivityNdx = 0; + } + for (int i = numActivities - 1; i > lastActivityNdx; --i) { ActivityRecord target = activities.get(i); - if (target.frontOfTask) - break; + // TODO: Why is this needed? Looks like we're breaking the loop before we reach the root + if (target.isRootOfTask()) break; final int flags = target.info.flags; boolean finishOnTaskLaunch = (flags & ActivityInfo.FLAG_FINISH_ON_TASK_LAUNCH) != 0; @@ -3537,8 +3536,8 @@ class ActivityStack extends ConfigurationContainer { if (p.finishing) { continue; } - finishActivityLocked( - p, Activity.RESULT_CANCELED, null, "move-affinity", false); + p.finishActivityLocked(Activity.RESULT_CANCELED, null /* resultData */, + "move-affinity", false /* oomAdj */); } } else { if (taskInsertionPoint < 0) { @@ -3567,13 +3566,13 @@ class ActivityStack extends ConfigurationContainer { // instance of the same activity? Then we drop the instance // below so it remains singleTop. if (target.info.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP) { - ArrayList<ActivityRecord> taskActivities = task.mActivities; - int targetNdx = taskActivities.indexOf(target); + final ArrayList<ActivityRecord> taskActivities = task.mActivities; + final int targetNdx = taskActivities.indexOf(target); if (targetNdx > 0) { - ActivityRecord p = taskActivities.get(targetNdx - 1); + final ActivityRecord p = taskActivities.get(targetNdx - 1); if (p.intent.getComponent().equals(target.intent.getComponent())) { - finishActivityLocked(p, Activity.RESULT_CANCELED, null, "replace", - false); + p.finishActivityLocked(Activity.RESULT_CANCELED, + null /* resultData */, "replace", false /* oomAdj */); } } } @@ -3591,11 +3590,11 @@ class ActivityStack extends ConfigurationContainer { (newActivity.info.flags & ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0; final TaskRecord task = taskTop.getTaskRecord(); - /** False until we evaluate the TaskRecord associated with taskTop. Switches to true - * for remaining tasks. Used for later tasks to reparent to task. */ + // False until we evaluate the TaskRecord associated with taskTop. Switches to true + // for remaining tasks. Used for later tasks to reparent to task. boolean taskFound = false; - /** If ActivityOptions are moved out and need to be aborted or moved to taskTop. */ + // If ActivityOptions are moved out and need to be aborted or moved to taskTop. ActivityOptions topOptions = null; // Preserve the location for reparenting in the new task. @@ -3676,7 +3675,7 @@ class ActivityStack extends ConfigurationContainer { return false; } - private void adjustFocusedActivityStack(ActivityRecord r, String reason) { + void adjustFocusedActivityStack(ActivityRecord r, String reason) { if (!mRootActivityContainer.isTopDisplayFocusedStack(this) || ((mResumedActivity != r) && (mResumedActivity != null))) { return; @@ -3711,9 +3710,6 @@ class ActivityStack extends ConfigurationContainer { if (nextFocusableStack != null) { final ActivityRecord top = nextFocusableStack.topRunningActivityLocked(); if (top != null && top == mRootActivityContainer.getTopResumedActivity()) { - // TODO(b/111361570): Remove this and update focused app per-display in - // WindowManager every time an activity becomes resumed in - // ActivityTaskManagerService#setResumedActivityUncheckLocked(). mService.setResumedActivityUncheckLocked(top, reason); } return; @@ -3831,7 +3827,7 @@ class ActivityStack extends ConfigurationContainer { return false; } - finishActivityLocked(r, resultCode, resultData, reason, oomAdj); + r.finishActivityLocked(resultCode, resultData, reason, oomAdj); return true; } @@ -3843,8 +3839,8 @@ class ActivityStack extends ConfigurationContainer { if (r.resultTo == self && r.requestCode == requestCode) { if ((r.resultWho == null && resultWho == null) || (r.resultWho != null && r.resultWho.equals(resultWho))) { - finishActivityLocked(r, Activity.RESULT_CANCELED, null, "request-sub", - false); + r.finishActivityLocked(Activity.RESULT_CANCELED, null /* resultData */, + "request-sub", false /* oomAdj */); } } } @@ -3874,7 +3870,8 @@ class ActivityStack extends ConfigurationContainer { int activityNdx = task.mActivities.indexOf(r); getDisplay().mDisplayContent.prepareAppTransition( TRANSIT_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */); - finishActivityLocked(r, Activity.RESULT_CANCELED, null, reason, false); + r.finishActivityLocked(Activity.RESULT_CANCELED, null /* resultData */, reason, + false /* oomAdj */); finishedTask = task; // Also terminate any activities below it that aren't yet // stopped, to avoid a situation where one will get @@ -3895,7 +3892,8 @@ class ActivityStack extends ConfigurationContainer { if (!r.isActivityTypeHome() || mService.mHomeProcess != r.app) { Slog.w(TAG, " Force finishing activity " + r.intent.getComponent().flattenToShortString()); - finishActivityLocked(r, Activity.RESULT_CANCELED, null, reason, false); + r.finishActivityLocked(Activity.RESULT_CANCELED, null /* resultData */, reason, + false /* oomAdj */); } } } @@ -3911,8 +3909,8 @@ class ActivityStack extends ConfigurationContainer { for (int activityNdx = tr.mActivities.size() - 1; activityNdx >= 0; --activityNdx) { ActivityRecord r = tr.mActivities.get(activityNdx); if (!r.finishing) { - finishActivityLocked(r, Activity.RESULT_CANCELED, null, "finish-voice", - false); + r.finishActivityLocked(Activity.RESULT_CANCELED, null /* resultData */, + "finish-voice", false /* oomAdj */); didOne = true; } } @@ -3941,280 +3939,17 @@ class ActivityStack extends ConfigurationContainer { } } - final boolean finishActivityAffinityLocked(ActivityRecord r) { - ArrayList<ActivityRecord> activities = r.getTaskRecord().mActivities; - for (int index = activities.indexOf(r); index >= 0; --index) { - ActivityRecord cur = activities.get(index); - if (!Objects.equals(cur.taskAffinity, r.taskAffinity)) { - break; - } - finishActivityLocked(cur, Activity.RESULT_CANCELED, null, "request-affinity", true); - } - return true; - } - - private void finishActivityResultsLocked(ActivityRecord r, int resultCode, Intent resultData) { - // send the result - ActivityRecord resultTo = r.resultTo; - if (resultTo != null) { - if (DEBUG_RESULTS) Slog.v(TAG_RESULTS, "Adding result to " + resultTo - + " who=" + r.resultWho + " req=" + r.requestCode - + " res=" + resultCode + " data=" + resultData); - if (resultTo.mUserId != r.mUserId) { - if (resultData != null) { - resultData.prepareToLeaveUser(r.mUserId); - } - } - if (r.info.applicationInfo.uid > 0) { - mService.mUgmInternal.grantUriPermissionFromIntent(r.info.applicationInfo.uid, - resultTo.packageName, resultData, - resultTo.getUriPermissionsLocked(), resultTo.mUserId); - } - resultTo.addResultLocked(r, r.resultWho, r.requestCode, resultCode, resultData); - r.resultTo = null; - } - else if (DEBUG_RESULTS) Slog.v(TAG_RESULTS, "No result destination from " + r); - - // Make sure this HistoryRecord is not holding on to other resources, - // because clients have remote IPC references to this object so we - // can't assume that will go away and want to avoid circular IPC refs. - r.results = null; - r.pendingResults = null; - r.newIntents = null; - r.icicle = null; - } - - /** - * See {@link #finishActivityLocked(ActivityRecord, int, Intent, String, boolean, boolean)} - */ - final boolean finishActivityLocked(ActivityRecord r, int resultCode, Intent resultData, - String reason, boolean oomAdj) { - return finishActivityLocked(r, resultCode, resultData, reason, oomAdj, !PAUSE_IMMEDIATELY); - } - - /** - * @return Returns true if this activity has been removed from the history - * list, or false if it is still in the list and will be removed later. - */ - final boolean finishActivityLocked(ActivityRecord r, int resultCode, Intent resultData, - String reason, boolean oomAdj, boolean pauseImmediately) { - if (r.finishing) { - Slog.w(TAG, "Duplicate finish request for " + r); - return false; - } - - mWindowManager.deferSurfaceLayout(); - try { - r.makeFinishingLocked(); - final TaskRecord task = r.getTaskRecord(); - EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY, - r.mUserId, System.identityHashCode(r), - task.taskId, r.shortComponentName, reason); - final ArrayList<ActivityRecord> activities = task.mActivities; - final int index = activities.indexOf(r); - if (index < (activities.size() - 1)) { - task.setFrontOfTask(); - if ((r.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. - ActivityRecord next = activities.get(index+1); - next.intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); - } - } - - r.pauseKeyDispatchingLocked(); - - adjustFocusedActivityStack(r, "finishActivity"); - - finishActivityResultsLocked(r, resultCode, resultData); - - final boolean endTask = index <= 0 && !task.isClearingToReuseTask(); - final int transit = endTask ? TRANSIT_TASK_CLOSE : TRANSIT_ACTIVITY_CLOSE; - if (mResumedActivity == r) { - if (DEBUG_VISIBILITY || DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, - "Prepare close transition: finishing " + r); - if (endTask) { - mService.getTaskChangeNotificationController().notifyTaskRemovalStarted( - task.getTaskInfo()); - } - getDisplay().mDisplayContent.prepareAppTransition(transit, false); - - // When finishing the activity pre-emptively take the snapshot before the app window - // is marked as hidden and any configuration changes take place - if (mWindowManager.mTaskSnapshotController != null) { - final ArraySet<Task> tasks = Sets.newArraySet(task.mTask); - mWindowManager.mTaskSnapshotController.snapshotTasks(tasks); - mWindowManager.mTaskSnapshotController.addSkipClosingAppSnapshotTasks(tasks); - } - - // Tell window manager to prepare for this one to be removed. - r.setVisibility(false); - - if (mPausingActivity == null) { - if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Finish needs to pause: " + r); - if (DEBUG_USER_LEAVING) Slog.v(TAG_USER_LEAVING, - "finish() => pause with userLeaving=false"); - startPausingLocked(false, false, null, pauseImmediately); - } - - if (endTask) { - mService.getLockTaskController().clearLockedTask(task); - } - } else if (!r.isState(PAUSING)) { - // If the activity is PAUSING, we will complete the finish once - // it is done pausing; else we can just directly finish it here. - if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Finish not pausing: " + r); - if (r.visible) { - prepareActivityHideTransitionAnimation(r, transit); - } - - final int finishMode = (r.visible || r.nowVisible) ? FINISH_AFTER_VISIBLE - : FINISH_AFTER_PAUSE; - final boolean removedActivity = finishCurrentActivityLocked(r, finishMode, oomAdj, - "finishActivityLocked") == null; - - // The following code is an optimization. When the last non-task overlay activity - // is removed from the task, we remove the entire task from the stack. However, - // since that is done after the scheduled destroy callback from the activity, that - // call to change the visibility of the task overlay activities would be out of - // sync with the activitiy visibility being set for this finishing activity above. - // In this case, we can set the visibility of all the task overlay activities when - // we detect the last one is finishing to keep them in sync. - if (task.onlyHasTaskOverlayActivities(true /* excludeFinishing */)) { - for (ActivityRecord taskOverlay : task.mActivities) { - if (!taskOverlay.mTaskOverlay) { - continue; - } - prepareActivityHideTransitionAnimation(taskOverlay, transit); - } - } - return removedActivity; - } else { - if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Finish waiting for pause of: " + r); - } - - return false; - } finally { - mWindowManager.continueSurfaceLayout(); - } - } - - private void prepareActivityHideTransitionAnimation(ActivityRecord r, int transit) { - final DisplayContent dc = getDisplay().mDisplayContent; - dc.prepareAppTransition(transit, false); - r.setVisibility(false); - dc.executeAppTransition(); - } - - static final int FINISH_IMMEDIATELY = 0; - static final int FINISH_AFTER_PAUSE = 1; - static final int FINISH_AFTER_VISIBLE = 2; - - final ActivityRecord finishCurrentActivityLocked(ActivityRecord r, int mode, boolean oomAdj, - String reason) { - // First things first: if this activity is currently visible, - // and the resumed activity is not yet visible, then hold off on - // finishing until the resumed one becomes visible. - - // The activity that we are finishing may be over the lock screen. In this case, we do not - // want to consider activities that cannot be shown on the lock screen as running and should - // proceed with finishing the activity if there is no valid next top running activity. - // Note that if this finishing activity is floating task, we don't need to wait the - // next activity resume and can destroy it directly. - final ActivityDisplay display = getDisplay(); - final ActivityRecord next = display.topRunningActivity(true /* considerKeyguardState */); - final boolean isFloating = r.getConfiguration().windowConfiguration.tasksAreFloating(); - - if (mode == FINISH_AFTER_VISIBLE && (r.visible || r.nowVisible) - && next != null && !next.nowVisible && !isFloating) { - if (!mStackSupervisor.mStoppingActivities.contains(r)) { - addToStopping(r, false /* scheduleIdle */, false /* idleDelayed */, - "finishCurrentActivityLocked"); - } - if (DEBUG_STATES) Slog.v(TAG_STATES, - "Moving to STOPPING: "+ r + " (finish requested)"); - r.setState(STOPPING, "finishCurrentActivityLocked"); - if (oomAdj) { - mService.updateOomAdj(); - } - return r; - } - - // make sure the record is cleaned out of other places. - mStackSupervisor.mStoppingActivities.remove(r); - mStackSupervisor.mGoingToSleepActivities.remove(r); - final ActivityState prevState = r.getState(); - if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to FINISHING: " + r); - - r.setState(FINISHING, "finishCurrentActivityLocked"); - - // Don't destroy activity immediately if the display contains home stack, although there is - // no next activity at the moment but another home activity should be started later. Keep - // this activity alive until next home activity is resumed then user won't see a temporary - // black screen. - final boolean noRunningStack = next == null && display.topRunningActivity() == null - && display.getHomeStack() == null; - final boolean noFocusedStack = r.getActivityStack() != display.getFocusedStack(); - final boolean finishingInNonFocusedStackOrNoRunning = mode == FINISH_AFTER_VISIBLE - && prevState == PAUSED && (noFocusedStack || noRunningStack); - - if (mode == FINISH_IMMEDIATELY - || (prevState == PAUSED - && (mode == FINISH_AFTER_PAUSE || inPinnedWindowingMode())) - || finishingInNonFocusedStackOrNoRunning - || prevState == STARTED - || prevState == STOPPING - || prevState == STOPPED - || prevState == ActivityState.INITIALIZING) { - r.makeFinishingLocked(); - boolean activityRemoved = destroyActivityLocked(r, true, "finish-imm:" + reason); - - if (finishingInNonFocusedStackOrNoRunning) { - // Finishing activity that was in paused state and it was in not currently focused - // stack, need to make something visible in its place. Also if the display does not - // have running activity, the configuration may need to be updated for restoring - // original orientation of the display. - mRootActivityContainer.ensureVisibilityAndConfig(next, mDisplayId, - false /* markFrozenIfConfigChanged */, true /* deferResume */); - } - if (activityRemoved) { - mRootActivityContainer.resumeFocusedStacksTopActivities(); - } - if (DEBUG_CONTAINERS) Slog.d(TAG_CONTAINERS, - "destroyActivityLocked: finishCurrentActivityLocked r=" + r + - " destroy returned removed=" + activityRemoved); - return activityRemoved ? null : r; - } - - // Need to go through the full pause cycle to get this - // activity into the stopped state and then finish it. - if (DEBUG_ALL) Slog.v(TAG, "Enqueueing pending finish: " + r); - mStackSupervisor.mFinishingActivities.add(r); - r.resumeKeyDispatchingLocked(); - mRootActivityContainer.resumeFocusedStacksTopActivities(); - // If activity was not paused at this point - explicitly pause it to start finishing - // process. Finishing will be completed once it reports pause back. - if (r.isState(RESUMED) && mPausingActivity != null) { - startPausingLocked(false /* userLeaving */, false /* uiSleeping */, next /* resuming */, - false /* dontWait */); - } - return r; - } - - void finishAllActivitiesLocked(boolean immediately) { + /** Finish all activities in the stack without waiting. */ + 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); noActivitiesInStack = false; - if (r.finishing && !immediately) { - continue; - } - Slog.d(TAG, "finishAllActivitiesLocked: finishing " + r + " immediately"); - finishCurrentActivityLocked(r, FINISH_IMMEDIATELY, false, - "finishAllActivitiesLocked"); + Slog.d(TAG, "finishAllActivitiesImmediatelyLocked: finishing " + r); + r.finishCurrentActivityLocked(FINISH_IMMEDIATELY, false /* oomAdj */, + "finishAllActivitiesImmediatelyLocked"); } } if (noActivitiesInStack) { @@ -4248,7 +3983,8 @@ class ActivityStack extends ConfigurationContainer { // of a document, unless simply finishing it will return them to the the // correct app behind. final TaskRecord task = srec.getTaskRecord(); - if (srec.frontOfTask && task.getBaseIntent() != null && task.getBaseIntent().isDocument()) { + if (srec.isRootOfTask() && task.getBaseIntent() != null + && task.getBaseIntent().isDocument()) { // Okay, this activity is at the root of its task. What to do, what to do... if (!inFrontOfStandardStack()) { // Finishing won't return to an application, so we need to recreate. @@ -4440,7 +4176,7 @@ class ActivityStack extends ConfigurationContainer { } private void removeActivityFromHistoryLocked(ActivityRecord r, String reason) { - finishActivityResultsLocked(r, Activity.RESULT_CANCELED, null); + r.finishActivityResults(Activity.RESULT_CANCELED, null /* resultData */); r.makeFinishingLocked(); if (DEBUG_ADD_REMOVE) Slog.i(TAG_ADD_REMOVE, "Removing activity " + r + " from stack callers=" + Debug.getCallers(5)); @@ -4798,7 +4534,7 @@ class ActivityStack extends ConfigurationContainer { // it has failed more than twice. Skip activities that's already finishing // cleanly by itself. remove = false; - } else if ((!r.haveState && !r.stateNotNeeded + } else if ((!r.hasSavedState() && !r.stateNotNeeded && !r.isState(ActivityState.RESTARTING_PROCESS)) || r.finishing) { // Don't currently have state for the activity, or // it is finishing -- always remove it. @@ -4818,7 +4554,7 @@ class ActivityStack extends ConfigurationContainer { if (remove) { if (DEBUG_ADD_REMOVE || DEBUG_CLEANUP) Slog.i(TAG_ADD_REMOVE, "Removing activity " + r + " from stack at " + i - + ": haveState=" + r.haveState + + ": hasSavedState=" + r.hasSavedState() + " stateNotNeeded=" + r.stateNotNeeded + " finishing=" + r.finishing + " state=" + r.getState() + " callers=" + Debug.getCallers(5)); @@ -4841,11 +4577,6 @@ class ActivityStack extends ConfigurationContainer { // This is needed when user later tap on the dead window, we need to stop // other apps when user transfers focus to the restarted activity. r.nowVisible = r.visible; - if (!r.haveState) { - if (DEBUG_SAVED_STATE) Slog.i(TAG_SAVED_STATE, - "App died, clearing saved state of " + r); - r.icicle = null; - } } cleanUpActivityLocked(r, true, true); if (remove) { @@ -5183,7 +4914,7 @@ class ActivityStack extends ConfigurationContainer { for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { final ActivityRecord r = activities.get(activityNdx); if ((r.info.flags&ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS) != 0) { - finishActivityLocked(r, Activity.RESULT_CANCELED, null, "close-sys", true); + r.finishActivityLocked(Activity.RESULT_CANCELED, null, "close-sys", true); } } } @@ -5227,8 +4958,7 @@ class ActivityStack extends ConfigurationContainer { didSomething = true; Slog.i(TAG, " Force finishing activity " + r); lastTask = r.getTaskRecord(); - finishActivityLocked(r, Activity.RESULT_CANCELED, null, "force-stop", - true); + r.finishActivityLocked(Activity.RESULT_CANCELED, null, "force-stop", true); } } } @@ -5282,7 +5012,7 @@ class ActivityStack extends ConfigurationContainer { final ArrayList<ActivityRecord> activities = mTaskHistory.get(top).mActivities; int activityTop = activities.size() - 1; if (activityTop >= 0) { - finishActivityLocked(activities.get(activityTop), Activity.RESULT_CANCELED, null, + activities.get(activityTop).finishActivityLocked(Activity.RESULT_CANCELED, null, "unhandled-back", true); } } @@ -5319,7 +5049,7 @@ class ActivityStack extends ConfigurationContainer { r.app = null; getDisplay().mDisplayContent.prepareAppTransition( TRANSIT_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */); - finishCurrentActivityLocked(r, FINISH_IMMEDIATELY, false, + r.finishCurrentActivityLocked(FINISH_IMMEDIATELY, false /* oomAdj */, "handleAppCrashedLocked"); } } @@ -5651,7 +5381,6 @@ class ActivityStack extends ConfigurationContainer { // If the original state is resumed, there is no state change to update focused app. // So here makes sure the activity focus is set if it is the top. if (origState == RESUMED && r == mRootActivityContainer.getTopResumedActivity()) { - // TODO(b/111361570): Support multiple focused apps in WM mService.setResumedActivityUncheckLocked(r, reason); } } diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java index feb4a3653ced..e49e991c2825 100644 --- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java @@ -130,6 +130,7 @@ import android.util.Slog; import android.util.SparseArray; import android.util.SparseIntArray; +import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.content.ReferrerIntent; @@ -323,6 +324,12 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { boolean mUserLeaving = false; /** + * The system chooser activity which worked as a delegate of + * {@link com.android.internal.app.ResolverActivity}. + */ + private ComponentName mSystemChooserActivity; + + /** * We don't want to allow the device to go to sleep while in the process * of launching an activity. This is primarily to allow alarm intent * receivers to launch an activity and get that to run before the device @@ -469,6 +476,14 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { return mKeyguardController; } + ComponentName getSystemChooserActivity() { + if (mSystemChooserActivity == null) { + mSystemChooserActivity = ComponentName.unflattenFromString( + mService.mContext.getResources().getString(R.string.config_chooserActivity)); + } + return mSystemChooserActivity; + } + void setRecentTasks(RecentTasks recentTasks) { mRecentTasks = recentTasks; mRecentTasks.registerCallback(this); @@ -798,8 +813,9 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { newIntents = r.newIntents; } if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, - "Launching: " + r + " icicle=" + r.icicle + " with results=" + results - + " newIntents=" + newIntents + " andResume=" + andResume); + "Launching: " + r + " savedState=" + r.getSavedState() + + " with results=" + results + " newIntents=" + newIntents + + " andResume=" + andResume); EventLog.writeEvent(EventLogTags.AM_RESTART_ACTIVITY, r.mUserId, System.identityHashCode(r), task.taskId, r.shortComponentName); if (r.isActivityTypeHome()) { @@ -821,7 +837,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { proc.getConfiguration(), r.getMergedOverrideConfiguration()); r.setLastReportedConfiguration(mergedConfiguration); - logIfTransactionTooLarge(r.intent, r.icicle); + logIfTransactionTooLarge(r.intent, r.getSavedState()); // Create activity launch transaction. @@ -836,7 +852,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { mergedConfiguration.getGlobalConfiguration(), mergedConfiguration.getOverrideConfiguration(), r.compat, r.launchedFromPackage, task.voiceInteractor, proc.getReportedProcState(), - r.icicle, r.persistentState, results, newIntents, + r.getSavedState(), r.getPersistentSavedState(), results, newIntents, dc.isNextTransitionForward(), proc.createProfilerInfoIfNeeded(), r.assistToken)); @@ -1175,7 +1191,8 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { final PackageInfo packageInfo; try { packageInfo = mService.mContext.getPackageManager() - .getPackageInfo(callingPackage, PackageManager.GET_PERMISSIONS); + .getPackageInfoAsUser(callingPackage, PackageManager.GET_PERMISSIONS, + UserHandle.getUserId(callingUid)); } catch (PackageManager.NameNotFoundException e) { Slog.i(TAG, "Cannot find package info for " + callingPackage); return ACTIVITY_RESTRICTION_NONE; @@ -1324,8 +1341,8 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { final ActivityStack stack = r.getActivityStack(); if (stack != null) { if (r.finishing) { - stack.finishCurrentActivityLocked(r, ActivityStack.FINISH_IMMEDIATELY, false, - "activityIdleInternalLocked"); + r.finishCurrentActivityLocked(ActivityRecord.FINISH_IMMEDIATELY, + false /* oomAdj */, "activityIdleInternalLocked"); } else { stack.stopActivityLocked(r); } diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 54eb07f56983..a334ed8ad281 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -1416,7 +1416,7 @@ class ActivityStarter { // performing operations without a window container. final ActivityStack stack = mStartActivity.getActivityStack(); if (stack != null) { - stack.finishActivityLocked(mStartActivity, RESULT_CANCELED, + mStartActivity.finishActivityLocked(RESULT_CANCELED, null /* intentResultData */, "startActivity", true /* oomAdj */); } @@ -1557,7 +1557,7 @@ class ActivityStarter { } if (top != null) { - if (top.frontOfTask) { + if (top.isRootOfTask()) { // Activity aliases may mean we use different intents for the top activity, // so make sure the task now has the identity of the new intent. top.getTaskRecord().setIntent(mStartActivity); @@ -1980,8 +1980,8 @@ class ActivityStarter { // Also put noDisplay activities in the source task. These by itself can be placed // in any task/stack, however it could launch other activities like ResolverActivity, // and we want those to stay in the original task. - if ((mStartActivity.isResolverActivity() || mStartActivity.noDisplay) && mSourceRecord != null - && mSourceRecord.inFreeformWindowingMode()) { + if ((mStartActivity.isResolverOrDelegateActivity() || mStartActivity.noDisplay) + && mSourceRecord != null && mSourceRecord.inFreeformWindowingMode()) { mAddingToTask = true; } } @@ -2260,7 +2260,7 @@ class ActivityStarter { || LAUNCH_SINGLE_TOP == mLaunchMode) && intentActivity.mActivityComponent.equals( mStartActivity.mActivityComponent)) { - if (intentActivity.frontOfTask) { + if (intentActivity.isRootOfTask()) { intentActivity.getTaskRecord().setIntent(mStartActivity); } deliverNewIntent(intentActivity); diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 971a24d13643..fb393e60f93e 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -1011,6 +1011,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { public final int startActivities(IApplicationThread caller, String callingPackage, Intent[] intents, String[] resolvedTypes, IBinder resultTo, Bundle bOptions, int userId) { + assertPackageMatchesCallingUid(callingPackage); final String reason = "startActivities"; enforceNotIsolatedCaller(reason); userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId, reason); @@ -1030,10 +1031,11 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { true /*validateIncomingUser*/); } - int startActivityAsUser(IApplicationThread caller, String callingPackage, + private int startActivityAsUser(IApplicationThread caller, String callingPackage, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId, boolean validateIncomingUser) { + assertPackageMatchesCallingUid(callingPackage); enforceNotIsolatedCaller("startActivityAsUser"); userId = getActivityStartController().checkTargetUser(userId, validateIncomingUser, @@ -1206,6 +1208,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { public final WaitResult startActivityAndWait(IApplicationThread caller, String callingPackage, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) { + assertPackageMatchesCallingUid(callingPackage); final WaitResult res = new WaitResult(); synchronized (mGlobalLock) { enforceNotIsolatedCaller("startActivityAndWait"); @@ -1233,6 +1236,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { public final int startActivityWithConfig(IApplicationThread caller, String callingPackage, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, Configuration config, Bundle bOptions, int userId) { + assertPackageMatchesCallingUid(callingPackage); synchronized (mGlobalLock) { enforceNotIsolatedCaller("startActivityWithConfig"); userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId, @@ -1282,6 +1286,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, IBinder permissionToken, boolean ignoreTargetSecurity, int userId) { + assertPackageMatchesCallingUid(callingPackage); // This is very dangerous -- it allows you to perform a start activity (including // permission grants) as any app that may launch one of your own activities. So we only // allow this in two cases: @@ -1411,6 +1416,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { Intent intent, String resolvedType, IVoiceInteractionSession session, IVoiceInteractor interactor, int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) { + assertPackageMatchesCallingUid(callingPackage); mAmInternal.enforceCallingPermission(BIND_VOICE_INTERACTION, "startVoiceActivity()"); if (session == null || interactor == null) { throw new NullPointerException("null session or interactor"); @@ -1434,6 +1440,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public int startAssistantActivity(String callingPackage, int callingPid, int callingUid, Intent intent, String resolvedType, Bundle bOptions, int userId) { + assertPackageMatchesCallingUid(callingPackage); mAmInternal.enforceCallingPermission(BIND_VOICE_INTERACTION, "startAssistantActivity()"); userId = handleIncomingUser(callingPid, callingUid, userId, "startAssistantActivity"); @@ -1646,7 +1653,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { if (getLockTaskController().activityBlockedFromFinish(r)) { return false; } - return task.getStack().finishActivityAffinityLocked(r); + r.finishActivityAffinity(); + return true; } finally { Binder.restoreCallingIdentity(origId); } @@ -2359,15 +2367,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { void moveTaskToFrontLocked(@Nullable IApplicationThread appThread, @Nullable String callingPackage, int taskId, int flags, SafeActivityOptions options, boolean fromRecents) { - final int callingPid = Binder.getCallingPid(); final int callingUid = Binder.getCallingUid(); - if (!isSameApp(callingUid, callingPackage)) { - String msg = "Permission Denial: moveTaskToFrontLocked() from pid=" - + Binder.getCallingPid() + " as package " + callingPackage; - Slog.w(TAG, msg); - throw new SecurityException(msg); - } + assertPackageMatchesCallingUid(callingPackage); if (!checkAppSwitchAllowedLocked(callingPid, callingUid, -1, -1, "Task to front")) { SafeActivityOptions.abort(options); return; @@ -2419,7 +2421,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { /** * Return true if callingUid is system, or packageName belongs to that callingUid. */ - boolean isSameApp(int callingUid, @Nullable String packageName) { + private boolean isSameApp(int callingUid, @Nullable String packageName) { try { if (callingUid != 0 && callingUid != SYSTEM_UID) { if (packageName == null) { @@ -2436,6 +2438,21 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { return true; } + /** + * Checks that the provided package name matches the current calling UID, throws a security + * exception if it doesn't. + */ + void assertPackageMatchesCallingUid(@Nullable String packageName) { + final int callingUid = Binder.getCallingUid(); + if (isSameApp(callingUid, packageName)) { + return; + } + final String msg = "Permission Denial: package=" + packageName + + " does not belong to uid=" + callingUid; + Slog.w(TAG, msg); + throw new SecurityException(msg); + } + boolean checkAppSwitchAllowedLocked(int sourcePid, int sourceUid, int callingPid, int callingUid, String name) { if (mAppSwitchesAllowedTime < SystemClock.uptimeMillis()) { @@ -2969,6 +2986,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public List<IBinder> getAppTasks(String callingPackage) { int callingUid = Binder.getCallingUid(); + assertPackageMatchesCallingUid(callingPackage); long ident = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { @@ -4912,7 +4930,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } void dumpActivityContainersLocked(PrintWriter pw) { - pw.println("ACTIVITY MANAGER STARTER (dumpsys activity containers)"); + pw.println("ACTIVITY MANAGER CONTAINERS (dumpsys activity containers)"); mRootActivityContainer.dumpChildrenNames(pw, " "); pw.println(" "); } @@ -4928,6 +4946,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { * - the cmd arg isn't the flattened component name of an existing activity: * dump all activity whose component contains the cmd as a substring * - A hex number of the ActivityRecord object instance. + * <p> + * The caller should not hold lock when calling this method because it will wait for the + * activities to complete the dump. * * @param dumpVisibleStacksOnly dump activity with {@param name} only if in a visible stack * @param dumpFocusedStackOnly dump activity with {@param name} only if in the focused stack @@ -4980,29 +5001,28 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { private void dumpActivity(String prefix, FileDescriptor fd, PrintWriter pw, final ActivityRecord r, String[] args, boolean dumpAll) { String innerPrefix = prefix + " "; + IApplicationThread appThread = null; synchronized (mGlobalLock) { pw.print(prefix); pw.print("ACTIVITY "); pw.print(r.shortComponentName); pw.print(" "); pw.print(Integer.toHexString(System.identityHashCode(r))); pw.print(" pid="); - if (r.hasProcess()) pw.println(r.app.getPid()); - else pw.println("(not running)"); + if (r.hasProcess()) { + pw.println(r.app.getPid()); + appThread = r.app.getThread(); + } else { + pw.println("(not running)"); + } if (dumpAll) { r.dump(pw, innerPrefix); } } - if (r.attachedToProcess()) { + if (appThread != null) { // flush anything that is already in the PrintWriter since the thread is going // to write to the file descriptor directly pw.flush(); - try { - TransferPipe tp = new TransferPipe(); - try { - r.app.getThread().dumpActivity(tp.getWriteFd(), - r.appToken, innerPrefix, args); - tp.go(fd); - } finally { - tp.kill(); - } + try (TransferPipe tp = new TransferPipe()) { + appThread.dumpActivity(tp.getWriteFd(), r.appToken, innerPrefix, args); + tp.go(fd); } catch (IOException e) { pw.println(innerPrefix + "Failure while dumping the activity: " + e); } catch (RemoteException e) { @@ -6073,6 +6093,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { SafeActivityOptions options, int userId, boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart) { + assertPackageMatchesCallingUid(callingPackage); synchronized (mGlobalLock) { return getActivityStartController().startActivitiesInPackage(uid, realCallingPid, realCallingUid, callingPackage, intents, resolvedTypes, resultTo, options, @@ -6088,6 +6109,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { int userId, TaskRecord inTask, String reason, boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart) { + assertPackageMatchesCallingUid(callingPackage); synchronized (mGlobalLock) { return getActivityStartController().startActivityInPackage(uid, realCallingPid, realCallingUid, callingPackage, intent, resolvedType, resultTo, resultWho, @@ -7063,10 +7085,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { public boolean dumpActivity(FileDescriptor fd, PrintWriter pw, String name, String[] args, int opti, boolean dumpAll, boolean dumpVisibleStacksOnly, boolean dumpFocusedStackOnly) { - synchronized (mGlobalLock) { - return ActivityTaskManagerService.this.dumpActivity(fd, pw, name, args, opti, - dumpAll, dumpVisibleStacksOnly, dumpFocusedStackOnly); - } + return ActivityTaskManagerService.this.dumpActivity(fd, pw, name, args, opti, dumpAll, + dumpVisibleStacksOnly, dumpFocusedStackOnly); } @Override diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java index 1eb7455135c7..a8f7768efc87 100644 --- a/services/core/java/com/android/server/wm/AppTaskImpl.java +++ b/services/core/java/com/android/server/wm/AppTaskImpl.java @@ -27,7 +27,6 @@ import android.os.Binder; import android.os.Bundle; import android.os.IBinder; import android.os.UserHandle; -import android.util.Slog; /** * An implementation of IAppTask, that allows an app to manage its own tasks via @@ -97,12 +96,7 @@ class AppTaskImpl extends IAppTask.Stub { // Will bring task to front if it already has a root activity. final int callingPid = Binder.getCallingPid(); final int callingUid = Binder.getCallingUid(); - if (!mService.isSameApp(callingUid, callingPackage)) { - String msg = "Permission Denial: moveToFront() from pid=" - + Binder.getCallingPid() + " as package " + callingPackage; - Slog.w(TAG, msg); - throw new SecurityException(msg); - } + mService.assertPackageMatchesCallingUid(callingPackage); final long origId = Binder.clearCallingIdentity(); try { synchronized (mService.mGlobalLock) { @@ -134,6 +128,7 @@ class AppTaskImpl extends IAppTask.Stub { public int startActivity(IBinder whoThread, String callingPackage, Intent intent, String resolvedType, Bundle bOptions) { checkCaller(); + mService.assertPackageMatchesCallingUid(callingPackage); int callingUser = UserHandle.getCallingUserId(); TaskRecord tr; diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index 8007c0f316e0..d15081ca2665 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -260,9 +260,9 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree ActivityRecord mActivityRecord; /** - * See {@link #canTurnScreenOn()} + * @see #currentLaunchCanTurnScreenOn() */ - private boolean mCanTurnScreenOn = true; + private boolean mCurrentLaunchCanTurnScreenOn = true; /** * If we are running an animation, this determines the transition type. Must be one of @@ -1002,7 +1002,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree + " " + this); mAppStopped = false; // Allow the window to turn the screen on once the app is resumed again. - setCanTurnScreenOn(true); + setCurrentLaunchCanTurnScreenOn(true); if (!wasStopped) { destroySurfaces(true /*cleanupOnResume*/); } @@ -2420,21 +2420,25 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree } /** - * Sets whether the current launch can turn the screen on. See {@link #canTurnScreenOn()} + * Sets whether the current launch can turn the screen on. + * @see #currentLaunchCanTurnScreenOn() */ - void setCanTurnScreenOn(boolean canTurnScreenOn) { - mCanTurnScreenOn = canTurnScreenOn; + void setCurrentLaunchCanTurnScreenOn(boolean currentLaunchCanTurnScreenOn) { + mCurrentLaunchCanTurnScreenOn = currentLaunchCanTurnScreenOn; } /** * Indicates whether the current launch can turn the screen on. This is to prevent multiple * relayouts from turning the screen back on. The screen should only turn on at most * once per activity resume. + * <p> + * Note this flag is only meaningful when {@link WindowManager.LayoutParams#FLAG_TURN_SCREEN_ON} + * or {@link ActivityRecord#canTurnScreenOn} is set. * - * @return true if the screen can be turned on. + * @return {@code true} if the activity is ready to turn on the screen. */ - boolean canTurnScreenOn() { - return mCanTurnScreenOn; + boolean currentLaunchCanTurnScreenOn() { + return mCurrentLaunchCanTurnScreenOn; } /** diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index e2d592826a3a..faf75b6630ca 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -122,7 +122,6 @@ import static com.android.server.wm.WindowManagerService.CUSTOM_SCREEN_ROTATION; import static com.android.server.wm.WindowManagerService.H.REPORT_FOCUS_CHANGE; import static com.android.server.wm.WindowManagerService.H.REPORT_HARD_KEYBOARD_STATUS_CHANGE; import static com.android.server.wm.WindowManagerService.H.REPORT_LOSING_FOCUS; -import static com.android.server.wm.WindowManagerService.H.SEND_NEW_CONFIGURATION; import static com.android.server.wm.WindowManagerService.H.UPDATE_DOCKED_STACK_DIVIDER; import static com.android.server.wm.WindowManagerService.H.WINDOW_HIDE_TIMEOUT; import static com.android.server.wm.WindowManagerService.LAYOUT_REPEAT_THRESHOLD; @@ -1212,27 +1211,45 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } } - /** Notify the configuration change of this display. */ - void postNewConfigurationToHandler() { - mWmService.mH.obtainMessage(SEND_NEW_CONFIGURATION, this).sendToTarget(); + void reconfigureDisplayLocked() { + if (!isReady()) { + return; + } + configureDisplayPolicy(); + setLayoutNeeded(); + + boolean configChanged = updateOrientationFromAppTokens(); + final Configuration currentDisplayConfig = getConfiguration(); + mTmpConfiguration.setTo(currentDisplayConfig); + computeScreenConfiguration(mTmpConfiguration); + configChanged |= currentDisplayConfig.diff(mTmpConfiguration) != 0; + + if (configChanged) { + mWaitingForConfig = true; + mWmService.startFreezingDisplayLocked(0 /* exitAnim */, 0 /* enterAnim */, this); + sendNewConfiguration(); + } + + mWmService.mWindowPlacerLocked.performSurfacePlacement(); } void sendNewConfiguration() { - synchronized (mWmService.mGlobalLock) { - final boolean configUpdated = mAcitvityDisplay - .updateDisplayOverrideConfigurationLocked(); - if (!configUpdated) { - // Something changed (E.g. device rotation), but no configuration update is needed. - // E.g. changing device rotation by 180 degrees. Go ahead and perform surface - // placement to unfreeze the display since we froze it when the rotation was updated - // in DisplayContent#updateRotationUnchecked. - if (mWaitingForConfig) { - mWaitingForConfig = false; - mWmService.mLastFinishedFreezeSource = "config-unchanged"; - setLayoutNeeded(); - mWmService.mWindowPlacerLocked.performSurfacePlacement(); - } - } + if (!isReady() || mAcitvityDisplay == null) { + return; + } + final boolean configUpdated = mAcitvityDisplay.updateDisplayOverrideConfigurationLocked(); + if (configUpdated) { + return; + } + // Something changed (E.g. device rotation), but no configuration update is needed. + // E.g. changing device rotation by 180 degrees. Go ahead and perform surface placement to + // unfreeze the display since we froze it when the rotation was updated in + // DisplayContent#updateRotationUnchecked. + if (mWaitingForConfig) { + mWaitingForConfig = false; + mWmService.mLastFinishedFreezeSource = "config-unchanged"; + setLayoutNeeded(); + mWmService.mWindowPlacerLocked.performSurfacePlacement(); } } @@ -1351,7 +1368,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo boolean updateRotationAndSendNewConfigIfNeeded() { final boolean changed = updateRotationUnchecked(false /* forceUpdate */); if (changed) { - postNewConfigurationToHandler(); + sendNewConfiguration(); } return changed; } @@ -2292,7 +2309,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mInitialDisplayHeight = newHeight; mInitialDisplayDensity = newDensity; mInitialDisplayCutout = newCutout; - mWmService.reconfigureDisplayLocked(this); + reconfigureDisplayLocked(); } } @@ -2345,7 +2362,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo final boolean updateCurrent = userId == UserHandle.USER_CURRENT; if (mWmService.mCurrentUserId == userId || updateCurrent) { mBaseDisplayDensity = density; - mWmService.reconfigureDisplayLocked(this); + reconfigureDisplayLocked(); } if (updateCurrent) { // We are applying existing settings so no need to save it again. @@ -2366,7 +2383,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mDisplayScalingDisabled = (mode != FORCE_SCALING_MODE_AUTO); Slog.i(TAG_WM, "Using display scaling mode: " + (mDisplayScalingDisabled ? "off" : "auto")); - mWmService.reconfigureDisplayLocked(this); + reconfigureDisplayLocked(); mWmService.mDisplayWindowSettings.setForcedScalingMode(this, mode); } @@ -2385,7 +2402,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo Slog.i(TAG_WM, "Using new display size: " + width + "x" + height); updateBaseDisplayMetrics(width, height, mBaseDisplayDensity); - mWmService.reconfigureDisplayLocked(this); + reconfigureDisplayLocked(); if (clear) { width = height = 0; @@ -3730,7 +3747,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo if (DEBUG_LAYOUT) Slog.v(TAG, "Computing new config from layout"); if (updateOrientationFromAppTokens()) { setLayoutNeeded(); - postNewConfigurationToHandler(); + sendNewConfiguration(); } } @@ -5094,7 +5111,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo if (!mLocationInParentWindow.equals(x, y)) { mLocationInParentWindow.set(x, y); if (mWmService.mAccessibilityController != null) { - mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(); + mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(mDisplayId); } notifyLocationInParentDisplayChanged(); } diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index cf87203893cf..15768738c85c 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -284,6 +284,8 @@ public class DisplayPolicy { /** See {@link #getNavigationBarFrameHeight} */ private int[] mNavigationBarFrameHeightForRotationDefault = new int[4]; + private boolean mIsFreeformWindowOverlappingWithNavBar; + /** Cached value of {@link ScreenShapeHelper#getWindowOutsetBottomPx} */ @Px private int mWindowOutsetBottom; @@ -877,27 +879,24 @@ public class DisplayPolicy { } /** - * Preflight adding a window to the system. + * Check if a window can be added to the system. * - * Currently enforces that three window types are singletons per display: + * Currently enforces that two window types are singletons per display: * <ul> * <li>{@link WindowManager.LayoutParams#TYPE_STATUS_BAR}</li> * <li>{@link WindowManager.LayoutParams#TYPE_NAVIGATION_BAR}</li> * </ul> * - * @param win The window to be added - * @param attrs Information about the window to be added + * @param attrs Information about the window to be added. * * @return If ok, WindowManagerImpl.ADD_OKAY. If too many singletons, * WindowManagerImpl.ADD_MULTIPLE_SINGLETON */ - public int prepareAddWindowLw(WindowState win, WindowManager.LayoutParams attrs) { - + int validateAddingWindowLw(WindowManager.LayoutParams attrs) { if ((attrs.privateFlags & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.STATUS_BAR_SERVICE, "DisplayPolicy"); - mScreenDecorWindows.add(win); } switch (attrs.type) { @@ -910,6 +909,42 @@ public class DisplayPolicy { return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON; } } + break; + case TYPE_NAVIGATION_BAR: + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.STATUS_BAR_SERVICE, + "DisplayPolicy"); + if (mNavigationBar != null) { + if (mNavigationBar.isAlive()) { + return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON; + } + } + break; + case TYPE_NAVIGATION_BAR_PANEL: + case TYPE_STATUS_BAR_PANEL: + case TYPE_STATUS_BAR_SUB_PANEL: + case TYPE_VOICE_INTERACTION_STARTING: + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.STATUS_BAR_SERVICE, + "DisplayPolicy"); + break; + } + return ADD_OKAY; + } + + /** + * Called when a window is being added to the system. Must not throw an exception. + * + * @param win The window being added. + * @param attrs Information about the window to be added. + */ + void addWindowLw(WindowState win, WindowManager.LayoutParams attrs) { + if ((attrs.privateFlags & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0) { + mScreenDecorWindows.add(win); + } + + switch (attrs.type) { + case TYPE_STATUS_BAR: mStatusBar = win; mStatusBarController.setWindow(win); if (mDisplayContent.isDefaultDisplay) { @@ -925,14 +960,6 @@ public class DisplayPolicy { mDisplayContent.setInsetProvider(TYPE_TOP_TAPPABLE_ELEMENT, win, frameProvider); break; case TYPE_NAVIGATION_BAR: - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.STATUS_BAR_SERVICE, - "DisplayPolicy"); - if (mNavigationBar != null) { - if (mNavigationBar.isAlive()) { - return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON; - } - } mNavigationBar = win; mNavigationBarController.setWindow(win); mNavigationBarController.setOnBarVisibilityChangedListener( @@ -966,16 +993,7 @@ public class DisplayPolicy { }); if (DEBUG_LAYOUT) Slog.i(TAG, "NAVIGATION BAR: " + mNavigationBar); break; - case TYPE_NAVIGATION_BAR_PANEL: - case TYPE_STATUS_BAR_PANEL: - case TYPE_STATUS_BAR_SUB_PANEL: - case TYPE_VOICE_INTERACTION_STARTING: - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.STATUS_BAR_SERVICE, - "DisplayPolicy"); - break; } - return ADD_OKAY; } /** @@ -984,7 +1002,7 @@ public class DisplayPolicy { * * @param win The window being removed. */ - public void removeWindowLw(WindowState win) { + void removeWindowLw(WindowState win) { if (mStatusBar == win) { mStatusBar = null; mStatusBarController.setWindow(null); @@ -2379,6 +2397,7 @@ public class DisplayPolicy { mAllowLockscreenWhenOn = false; mShowingDream = false; mWindowSleepTokenNeeded = false; + mIsFreeformWindowOverlappingWithNavBar = false; } /** @@ -2478,6 +2497,13 @@ public class DisplayPolicy { } } + // Check if the freeform window overlaps with the navigation bar area. + final WindowState navBarWin = hasNavigationBar() ? mNavigationBar : null; + if (!mIsFreeformWindowOverlappingWithNavBar && win.inFreeformWindowingMode() + && isOverlappingWithNavBar(win, navBarWin)) { + mIsFreeformWindowOverlappingWithNavBar = true; + } + // Also keep track of any windows that are dimming but not necessarily fullscreen in the // docked stack. if (mTopDockedOpaqueOrDimmingWindowState == null && affectsSystemUi && win.isDimming() @@ -3458,7 +3484,11 @@ public class DisplayPolicy { } } else if (mNavBarOpacityMode == NAV_BAR_OPAQUE_WHEN_FREEFORM_OR_DOCKED) { if (dockedStackVisible || freeformStackVisible || isDockedDividerResizing) { - visibility = setNavBarOpaqueFlag(visibility); + if (mIsFreeformWindowOverlappingWithNavBar) { + visibility = setNavBarTranslucentFlag(visibility); + } else { + visibility = setNavBarOpaqueFlag(visibility); + } } else if (fullscreenDrawsBackground) { visibility = setNavBarTransparentFlag(visibility); } @@ -3747,4 +3777,14 @@ public class DisplayPolicy { wm.removeView(mPointerLocationView); mPointerLocationView = null; } + + @VisibleForTesting + static boolean isOverlappingWithNavBar(WindowState targetWindow, WindowState navBarWindow) { + if (navBarWindow == null || !navBarWindow.isVisibleLw() + || targetWindow.mAppToken == null || !targetWindow.isVisibleLw()) { + return false; + } + + return Rect.intersects(targetWindow.getFrameLw(), navBarWindow.getFrameLw()); + } } diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java index b16d9564ea7a..6830adebad22 100644 --- a/services/core/java/com/android/server/wm/InputManagerCallback.java +++ b/services/core/java/com/android/server/wm/InputManagerCallback.java @@ -133,7 +133,10 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal @Override public void notifyConfigurationChanged() { // TODO(multi-display): Notify proper displays that are associated with this input device. - mService.mRoot.getDisplayContent(DEFAULT_DISPLAY).sendNewConfiguration(); + + synchronized (mService.mGlobalLock) { + mService.getDefaultDisplayContentLocked().sendNewConfiguration(); + } synchronized (mInputDevicesReadyMonitor) { if (!mInputDevicesReady) { diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java index ca4749f7b365..422b6e58e0ed 100644 --- a/services/core/java/com/android/server/wm/KeyguardController.java +++ b/services/core/java/com/android/server/wm/KeyguardController.java @@ -43,11 +43,13 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLAS import android.os.IBinder; import android.os.RemoteException; import android.os.Trace; +import android.util.EventLog; import android.util.Slog; import android.util.SparseArray; import android.util.proto.ProtoOutputStream; import com.android.internal.policy.IKeyguardDismissCallback; +import com.android.server.am.EventLogTags; import com.android.server.policy.WindowManagerPolicy; import com.android.server.wm.ActivityTaskManagerInternal.SleepToken; @@ -142,6 +144,11 @@ class KeyguardController { if (!keyguardChanged && !aodChanged) { return; } + EventLog.writeEvent(EventLogTags.AM_SET_KEYGUARD_SHOWN, + keyguardShowing ? 1 : 0, + aodShowing ? 1 : 0, + mKeyguardGoingAway ? 1 : 0, + "setKeyguardShown"); mKeyguardShowing = keyguardShowing; mAodShowing = aodShowing; mWindowManager.setAodShowing(aodShowing); @@ -178,6 +185,11 @@ class KeyguardController { mWindowManager.deferSurfaceLayout(); try { setKeyguardGoingAway(true); + EventLog.writeEvent(EventLogTags.AM_SET_KEYGUARD_SHOWN, + 1 /* keyguardShowing */, + mAodShowing ? 1 : 0, + 1 /* keyguardGoingAway */, + "keyguardGoingAway"); mRootActivityContainer.getDefaultDisplay().mDisplayContent .prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, false /* alwaysKeepCurrent */, convertTransitFlags(flags), diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 8a5f52f8c453..a00bee0859ad 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -248,8 +248,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent> dc.configureDisplayPolicy(); } - mWmService.reconfigureDisplayLocked(dc); - return dc; } @@ -267,7 +265,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } displayContent.initializeDisplayOverrideConfiguration(); - mWmService.reconfigureDisplayLocked(displayContent); + displayContent.reconfigureDisplayLocked(); // We need to update global configuration as well if config of default display has // changed. Do it inline because ATMS#retrieveSettings() will soon update the diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java index 7c7fdae45a47..23515d8619dd 100644 --- a/services/core/java/com/android/server/wm/TaskRecord.java +++ b/services/core/java/com/android/server/wm/TaskRecord.java @@ -1113,16 +1113,14 @@ class TaskRecord extends ConfigurationContainer { return intent != null ? intent : affinityIntent; } - /** Returns the first non-finishing activity from the root. */ + /** Returns the first non-finishing activity from the bottom. */ ActivityRecord getRootActivity() { - for (int i = 0; i < mActivities.size(); i++) { - final ActivityRecord r = mActivities.get(i); - if (r.finishing) { - continue; - } - return r; + final int rootActivityIndex = findRootIndex(false /* effectiveRoot */); + if (rootActivityIndex == -1) { + // There are no non-finishing activities in the task. + return null; } - return null; + return mActivities.get(rootActivityIndex); } ActivityRecord getTopActivity() { @@ -1237,27 +1235,6 @@ class TaskRecord extends ConfigurationContainer { || topRunningActivityLocked() != null; } - /** Call after activity movement or finish to make sure that frontOfTask is set correctly */ - final void setFrontOfTask() { - boolean foundFront = false; - final int numActivities = mActivities.size(); - for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) { - final ActivityRecord r = mActivities.get(activityNdx); - if (foundFront || r.finishing) { - r.frontOfTask = false; - } else { - r.frontOfTask = true; - // Set frontOfTask false for every following activity. - foundFront = true; - } - } - if (!foundFront && numActivities > 0) { - // All activities of this task are finishing. As we ought to have a frontOfTask - // activity, make the bottom activity front. - mActivities.get(0).frontOfTask = true; - } - } - /** * Reorder the history stack so that the passed activity is brought to the front. */ @@ -1272,8 +1249,6 @@ class TaskRecord extends ConfigurationContainer { // Make sure window manager is aware of the position change. mTask.positionChildAtTop(newTop.mAppWindowToken); updateEffectiveIntent(); - - setFrontOfTask(); } void addActivityToTop(ActivityRecord r) { @@ -1443,8 +1418,8 @@ class TaskRecord extends ConfigurationContainer { mActivities.remove(activityNdx); --activityNdx; --numActivities; - } else if (mStack.finishActivityLocked(r, Activity.RESULT_CANCELED, null, - reason, false, pauseImmediately)) { + } else if (r.finishActivityLocked(Activity.RESULT_CANCELED, null, + reason, false /* oomAdj */, pauseImmediately)) { --activityNdx; --numActivities; } @@ -1498,8 +1473,8 @@ class TaskRecord extends ConfigurationContainer { if (opts != null) { ret.updateOptionsLocked(opts); } - if (mStack != null && mStack.finishActivityLocked( - r, Activity.RESULT_CANCELED, null, "clear-task-stack", false)) { + if (r.finishActivityLocked(Activity.RESULT_CANCELED, null /* resultData */, + "clear-task-stack", false /* oomAdj */)) { --activityNdx; --numActivities; } @@ -1512,10 +1487,8 @@ class TaskRecord extends ConfigurationContainer { && (launchFlags & Intent.FLAG_ACTIVITY_SINGLE_TOP) == 0 && !ActivityStarter.isDocumentLaunchesIntoExisting(launchFlags)) { if (!ret.finishing) { - if (mStack != null) { - mStack.finishActivityLocked( - ret, Activity.RESULT_CANCELED, null, "clear-task-top", false); - } + ret.finishActivityLocked(Activity.RESULT_CANCELED, null /* resultData */, + "clear-task-top", false /* oomAdj */); return null; } } @@ -1658,6 +1631,7 @@ class TaskRecord extends ConfigurationContainer { /** Updates the last task description values. */ void updateTaskDescription() { + // TODO(AM refactor): Cleanup to use findRootIndex() // Traverse upwards looking for any break between main task activities and // utility activities. int activityNdx; @@ -1736,8 +1710,19 @@ class TaskRecord extends ConfigurationContainer { } } - int findEffectiveRootIndex() { - int effectiveNdx = 0; + /** + * Find the index of the root activity in the task. It will be the first activity from the + * bottom that is not finishing. + * + * @param effectiveRoot Flag indicating whether 'effective root' should be returned - an + * activity that defines the task identity (its base intent). It's the + * first one that does not have + * {@link ActivityInfo#FLAG_RELINQUISH_TASK_IDENTITY}. + * @return index of the 'root' or 'effective' root in the list of activities, -1 if no eligible + * activity was found. + */ + int findRootIndex(boolean effectiveRoot) { + int effectiveNdx = -1; final int topActivityNdx = mActivities.size() - 1; for (int activityNdx = 0; activityNdx <= topActivityNdx; ++activityNdx) { final ActivityRecord r = mActivities.get(activityNdx); @@ -1745,15 +1730,22 @@ class TaskRecord extends ConfigurationContainer { continue; } effectiveNdx = activityNdx; - if ((r.info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0) { + if (!effectiveRoot || (r.info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0) { break; } } return effectiveNdx; } + // TODO (AM refactor): Invoke automatically when there is a change in children + @VisibleForTesting void updateEffectiveIntent() { - final int effectiveRootIndex = findEffectiveRootIndex(); + int effectiveRootIndex = findRootIndex(true /* effectiveRoot */); + if (effectiveRootIndex == -1) { + // All activities in the task are either finishing or relinquish task identity. + // But we still want to update the intent, so let's use the bottom activity. + effectiveRootIndex = 0; + } final ActivityRecord r = mActivities.get(effectiveRootIndex); setIntent(r); diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java index bd114d958fd0..f9a6fe725fa0 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotController.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java @@ -310,6 +310,7 @@ class TaskSnapshotController { } final boolean isWindowTranslucent = mainWindow.getAttrs().format != PixelFormat.OPAQUE; return new TaskSnapshot( + System.currentTimeMillis() /* id */, appWindowToken.mActivityComponent, screenshotBuffer.getGraphicBuffer(), screenshotBuffer.getColorSpace(), appWindowToken.getTask().getConfiguration().orientation, @@ -404,7 +405,9 @@ class TaskSnapshotController { // Note, the app theme snapshot is never translucent because we enforce a non-translucent // color above - return new TaskSnapshot(topChild.mActivityComponent, hwBitmap.createGraphicBufferHandle(), + return new TaskSnapshot( + System.currentTimeMillis() /* id */, + topChild.mActivityComponent, hwBitmap.createGraphicBufferHandle(), hwBitmap.getColorSpace(), topChild.getTask().getConfiguration().orientation, getInsets(mainWindow), ActivityManager.isLowRamDeviceStatic() /* reduced */, mFullSnapshotScale, false /* isRealSnapshot */, task.getWindowingMode(), diff --git a/services/core/java/com/android/server/wm/TaskSnapshotLoader.java b/services/core/java/com/android/server/wm/TaskSnapshotLoader.java index fcd97c1bac5d..696e1c3a2602 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotLoader.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotLoader.java @@ -92,7 +92,7 @@ class TaskSnapshotLoader { // For legacy snapshots, restore the scale based on the reduced resolution state final float legacyScale = reducedResolution ? mPersister.getReducedScale() : 1f; final float scale = Float.compare(proto.scale, 0f) != 0 ? proto.scale : legacyScale; - return new TaskSnapshot(topActivityComponent, buffer, bitmap.getColorSpace(), + return new TaskSnapshot(proto.id, topActivityComponent, buffer, bitmap.getColorSpace(), proto.orientation, new Rect(proto.insetLeft, proto.insetTop, proto.insetRight, proto.insetBottom), reducedResolution, scale, proto.isRealSnapshot, proto.windowingMode, diff --git a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java index 72fc18937ece..32a1d9061160 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java @@ -343,6 +343,7 @@ class TaskSnapshotPersister { proto.isTranslucent = mSnapshot.isTranslucent(); proto.topActivityComponent = mSnapshot.getTopActivityComponent().flattenToString(); proto.scale = mSnapshot.getScale(); + proto.id = mSnapshot.getId(); final byte[] bytes = TaskSnapshotProto.toByteArray(proto); final File file = getProtoFile(mTaskId, mUserId); final AtomicFile atomicFile = new AtomicFile(file); diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java index 6910ce91cce2..636f4e6d09d0 100644 --- a/services/core/java/com/android/server/wm/WindowManagerInternal.java +++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java @@ -263,11 +263,13 @@ public abstract class WindowManagerInternal { /** * Sets a callback for observing which windows are touchable for the purposes - * of accessibility. + * of accessibility on specified display. * + * @param displayId The logical display id. * @param callback The callback. + * @return {@code false} if display id is not valid. */ - public abstract void setWindowsForAccessibilityCallback( + public abstract boolean setWindowsForAccessibilityCallback(int displayId, WindowsForAccessibilityCallback callback); /** @@ -418,9 +420,11 @@ public abstract class WindowManagerInternal { public abstract boolean isStackVisibleLw(int windowingMode); /** - * Requests the window manager to resend the windows for accessibility. + * Requests the window manager to resend the windows for accessibility on specified display. + * + * @param displayId Display ID to be computed its windows for accessibility */ - public abstract void computeWindowsForAccessibility(); + public abstract void computeWindowsForAccessibility(int displayId); /** * Called after virtual display Id is updated by diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index f52bde9a035c..70e446c51c43 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -79,7 +79,6 @@ import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_BOOT; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_CONFIGURATION; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT; @@ -1432,7 +1431,7 @@ public class WindowManagerService extends IWindowManager.Stub Binder.getCallingUid()); win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs)); - res = displayPolicy.prepareAddWindowLw(win, attrs); + res = displayPolicy.validateAddingWindowLw(attrs); if (res != WindowManagerGlobal.ADD_OKAY) { return res; } @@ -1508,6 +1507,7 @@ public class WindowManagerService extends IWindowManager.Stub boolean imMayMove = true; win.mToken.addWindow(win); + displayPolicy.addWindowLw(win, attrs); if (type == TYPE_INPUT_METHOD) { displayContent.setInputMethodWindowLocked(win); imMayMove = false; @@ -1651,6 +1651,7 @@ public class WindowManagerService extends IWindowManager.Stub if (display != null) { displayContent = mRoot.createDisplayContent(display, null /* activityDisplay */); + displayContent.reconfigureDisplayLocked(); } } @@ -1911,10 +1912,9 @@ public class WindowManagerService extends IWindowManager.Stub mWindowPlacerLocked.performSurfacePlacement(); // We need to report touchable region changes to accessibility. - if (mAccessibilityController != null - && (w.getDisplayContent().getDisplayId() == DEFAULT_DISPLAY - || w.getDisplayContent().getParentWindow() != null)) { - mAccessibilityController.onSomeWindowResizedOrMovedLocked(); + if (mAccessibilityController != null) { + mAccessibilityController.onSomeWindowResizedOrMovedLocked( + w.getDisplayContent().getDisplayId()); } } } @@ -2044,11 +2044,10 @@ public class WindowManagerService extends IWindowManager.Stub win.mAppToken.checkKeyguardFlagsChanged(); } if (((attrChanges & LayoutParams.ACCESSIBILITY_TITLE_CHANGED) != 0) - && (mAccessibilityController != null) - && (win.getDisplayId() == DEFAULT_DISPLAY - || win.getDisplayContent().getParentWindow() != null)) { + && (mAccessibilityController != null)) { // No move or resize, but the controller checks for title changes as well - mAccessibilityController.onSomeWindowResizedOrMovedLocked(); + mAccessibilityController.onSomeWindowResizedOrMovedLocked( + win.getDisplayContent().getDisplayId()); } if ((flagChanges & SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS) != 0) { @@ -3751,7 +3750,7 @@ public class WindowManagerService extends IWindowManager.Stub layoutNeeded = true; } if (rotationChanged || alwaysSendConfiguration) { - displayContent.postNewConfigurationToHandler(); + displayContent.sendNewConfiguration(); } } @@ -4421,7 +4420,7 @@ public class WindowManagerService extends IWindowManager.Stub mDisplayReady = true; // Reconfigure all displays to make sure that forced properties and // DisplayWindowSettings are applied. - mRoot.forAllDisplays(this::reconfigureDisplayLocked); + mRoot.forAllDisplays(DisplayContent::reconfigureDisplayLocked); mIsTouchDevice = mContext.getPackageManager().hasSystemFeature( PackageManager.FEATURE_TOUCHSCREEN); } @@ -4500,7 +4499,6 @@ public class WindowManagerService extends IWindowManager.Stub public static final int FORCE_GC = 15; public static final int ENABLE_SCREEN = 16; public static final int APP_FREEZE_TIMEOUT = 17; - public static final int SEND_NEW_CONFIGURATION = 18; public static final int REPORT_WINDOWS_CHANGE = 19; public static final int REPORT_HARD_KEYBOARD_STATUS_CHANGE = 22; @@ -4555,11 +4553,7 @@ public class WindowManagerService extends IWindowManager.Stub AccessibilityController accessibilityController = null; synchronized (mGlobalLock) { - // TODO(multidisplay): Accessibility supported only of default display and - // embedded displays. - if (mAccessibilityController != null - && (displayContent.isDefaultDisplay - || displayContent.getParentWindow() != null)) { + if (mAccessibilityController != null) { accessibilityController = mAccessibilityController; } @@ -4582,7 +4576,8 @@ public class WindowManagerService extends IWindowManager.Stub // First notify the accessibility manager for the change so it has // the windows before the newly focused one starts firing eventgs. if (accessibilityController != null) { - accessibilityController.onWindowFocusChangedNotLocked(); + accessibilityController.onWindowFocusChangedNotLocked( + displayContent.getDisplayId()); } if (newFocus != null) { @@ -4712,23 +4707,6 @@ public class WindowManagerService extends IWindowManager.Stub break; } - case SEND_NEW_CONFIGURATION: { - final DisplayContent displayContent = (DisplayContent) msg.obj; - removeMessages(SEND_NEW_CONFIGURATION, displayContent); - if (displayContent.isReady()) { - displayContent.sendNewConfiguration(); - } else { - // Message could come after display has already been removed. - if (DEBUG_CONFIGURATION) { - final String reason = displayContent.getParent() == null - ? "detached" : "unready"; - Slog.w(TAG, "Trying to send configuration to " + reason + " display=" - + displayContent); - } - } - break; - } - case REPORT_WINDOWS_CHANGE: { if (mWindowsChanged) { synchronized (mGlobalLock) { @@ -5183,29 +5161,6 @@ public class WindowManagerService extends IWindowManager.Stub return 0; } - void reconfigureDisplayLocked(@NonNull DisplayContent displayContent) { - if (!displayContent.isReady()) { - return; - } - displayContent.configureDisplayPolicy(); - displayContent.setLayoutNeeded(); - - boolean configChanged = displayContent.updateOrientationFromAppTokens(); - final Configuration currentDisplayConfig = displayContent.getConfiguration(); - mTempConfiguration.setTo(currentDisplayConfig); - displayContent.computeScreenConfiguration(mTempConfiguration); - configChanged |= currentDisplayConfig.diff(mTempConfiguration) != 0; - - if (configChanged) { - displayContent.mWaitingForConfig = true; - startFreezingDisplayLocked(0 /* exitAnim */, - 0 /* enterAnim */, displayContent); - displayContent.postNewConfigurationToHandler(); - } - - mWindowPlacerLocked.performSurfacePlacement(); - } - @Override public void setOverscan(int displayId, int left, int top, int right, int bottom) { if (mContext.checkCallingOrSelfPermission(WRITE_SECURE_SETTINGS) @@ -5235,7 +5190,7 @@ public class WindowManagerService extends IWindowManager.Stub mDisplayWindowSettings.setOverscanLocked(displayInfo, left, top, right, bottom); - reconfigureDisplayLocked(displayContent); + displayContent.reconfigureDisplayLocked(); } @Override @@ -5547,7 +5502,7 @@ public class WindowManagerService extends IWindowManager.Stub } if (configChanged) { - displayContent.postNewConfigurationToHandler(); + displayContent.sendNewConfiguration(); } mLatencyTracker.onActionEnd(ACTION_ROTATE_SCREEN); } @@ -6757,6 +6712,12 @@ public class WindowManagerService extends IWindowManager.Stub return; } getDisplayContentOrCreate(displayId, null).reparentDisplayContent(win, sc); + // Notifies AccessibilityController to re-compute the window observer of + // this embedded display + if (mAccessibilityController != null) { + mAccessibilityController.handleWindowObserverOfEmbeddedDisplayLocked(displayId, + win); + } } finally { Binder.restoreCallingIdentity(token); } @@ -6863,12 +6824,11 @@ public class WindowManagerService extends IWindowManager.Stub int lastWindowingMode = displayContent.getWindowingMode(); mDisplayWindowSettings.setWindowingModeLocked(displayContent, mode); - reconfigureDisplayLocked(displayContent); + displayContent.reconfigureDisplayLocked(); if (lastWindowingMode != displayContent.getWindowingMode()) { // reconfigure won't detect this change in isolation because the windowing mode is // already set on the display, so fire off a new config now. - mH.removeMessages(H.SEND_NEW_CONFIGURATION); final long origId = Binder.clearCallingIdentity(); try { @@ -6916,7 +6876,7 @@ public class WindowManagerService extends IWindowManager.Stub mDisplayWindowSettings.setRemoveContentModeLocked(displayContent, mode); - reconfigureDisplayLocked(displayContent); + displayContent.reconfigureDisplayLocked(); } } @@ -6955,7 +6915,7 @@ public class WindowManagerService extends IWindowManager.Stub mDisplayWindowSettings.setShouldShowWithInsecureKeyguardLocked(displayContent, shouldShow); - reconfigureDisplayLocked(displayContent); + displayContent.reconfigureDisplayLocked(); } } @@ -6999,7 +6959,7 @@ public class WindowManagerService extends IWindowManager.Stub mDisplayWindowSettings.setShouldShowSystemDecorsLocked(displayContent, shouldShow); - reconfigureDisplayLocked(displayContent); + displayContent.reconfigureDisplayLocked(); } } @@ -7044,7 +7004,7 @@ public class WindowManagerService extends IWindowManager.Stub mDisplayWindowSettings.setShouldShowImeLocked(displayContent, shouldShow); - reconfigureDisplayLocked(displayContent); + displayContent.reconfigureDisplayLocked(); } } @@ -7168,16 +7128,20 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public void setWindowsForAccessibilityCallback(WindowsForAccessibilityCallback callback) { + public boolean setWindowsForAccessibilityCallback(int displayId, + WindowsForAccessibilityCallback callback) { synchronized (mGlobalLock) { if (mAccessibilityController == null) { mAccessibilityController = new AccessibilityController( WindowManagerService.this); } - mAccessibilityController.setWindowsForAccessibilityCallback(callback); + final boolean result = + mAccessibilityController.setWindowsForAccessibilityCallbackLocked( + displayId, callback); if (!mAccessibilityController.hasCallbacksLocked()) { mAccessibilityController = null; } + return result; } } @@ -7343,13 +7307,13 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public void computeWindowsForAccessibility() { + public void computeWindowsForAccessibility(int displayId) { final AccessibilityController accessibilityController; synchronized (mGlobalLock) { accessibilityController = mAccessibilityController; } if (accessibilityController != null) { - accessibilityController.performComputeChangedWindowsNotLocked(true); + accessibilityController.performComputeChangedWindowsNotLocked(displayId, true); } } diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index 932e44e7b8c2..f74e2c01699d 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -629,8 +629,8 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio for (int i = 0; i < activities.size(); i++) { final ActivityRecord r = activities.get(i); if (!r.finishing && r.isInStackLocked()) { - r.getActivityStack().finishActivityLocked(r, Activity.RESULT_CANCELED, - null, "finish-heavy", true); + r.finishActivityLocked(Activity.RESULT_CANCELED, null /* resultData */, + "finish-heavy", true /* oomAdj */); } } } @@ -734,7 +734,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio } // Don't consider any activities that are currently not in a state where they // can be destroyed. - if (r.visible || !r.stopped || !r.haveState + if (r.visible || !r.stopped || !r.hasSavedState() || r.isState(STARTED, RESUMED, PAUSING, PAUSED, STOPPING)) { if (DEBUG_RELEASE) Slog.d(TAG_RELEASE, "Not releasing in-use activity: " + r); continue; diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 8b1baebbbbc0..9f9ef24f72e1 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -22,7 +22,6 @@ import static android.app.AppOpsManager.MODE_DEFAULT; import static android.app.AppOpsManager.OP_NONE; import static android.os.PowerManager.DRAW_WAKE_LOCK; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; -import static android.view.Display.DEFAULT_DISPLAY; import static android.view.SurfaceControl.Transaction; import static android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; import static android.view.View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; @@ -1633,7 +1632,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP || !mRelayoutCalled || (atoken == null && mToken.isHidden()) || (atoken != null && atoken.hiddenRequested) - || isParentWindowHidden() + || isParentWindowGoneForLayout() || (mAnimatingExit && !isAnimatingLw()) || mDestroying; } @@ -1802,11 +1801,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP startMoveAnimation(left, top); } - // TODO (multidisplay): Accessibility supported only for the default display and - // embedded displays - if (mWmService.mAccessibilityController != null && (getDisplayId() == DEFAULT_DISPLAY - || getDisplayContent().getParentWindow() != null)) { - mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(); + if (mWmService.mAccessibilityController != null) { + mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(getDisplayId()); } updateLocationInParentDisplayIfNeeded(); @@ -2063,7 +2059,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (wasVisible) { final DisplayContent displayContent = getDisplayContent(); if (displayContent.updateOrientationFromAppTokens()) { - displayContent.postNewConfigurationToHandler(); + displayContent.sendNewConfiguration(); } } mWmService.updateFocusedWindowLocked(isFocused() @@ -2393,10 +2389,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP void prepareWindowToDisplayDuringRelayout(boolean wasVisible) { // We need to turn on screen regardless of visibility. - boolean hasTurnScreenOnFlag = (mAttrs.flags & FLAG_TURN_SCREEN_ON) != 0; + final boolean hasTurnScreenOnFlag = (mAttrs.flags & FLAG_TURN_SCREEN_ON) != 0 + || (mAppToken != null && mAppToken.mActivityRecord.canTurnScreenOn()); // The screen will turn on if the following conditions are met - // 1. The window has the flag FLAG_TURN_SCREEN_ON + // 1. The window has the flag FLAG_TURN_SCREEN_ON or ActivityRecord#canTurnScreenOn. // 2. The WMS allows theater mode. // 3. No AWT or the AWT allows the screen to be turned on. This should only be true once // per resume to prevent the screen getting getting turned on for each relayout. Set @@ -2410,7 +2407,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP boolean allowTheaterMode = mWmService.mAllowTheaterModeWakeFromLayout || Settings.Global.getInt(mWmService.mContext.getContentResolver(), Settings.Global.THEATER_MODE_ON, 0) == 0; - boolean canTurnScreenOn = mAppToken == null || mAppToken.canTurnScreenOn(); + boolean canTurnScreenOn = mAppToken == null || mAppToken.currentLaunchCanTurnScreenOn(); if (allowTheaterMode && canTurnScreenOn && !mPowerManagerWrapper.isInteractive()) { if (DEBUG_VISIBILITY || DEBUG_POWER) { @@ -2421,7 +2418,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } if (mAppToken != null) { - mAppToken.setCanTurnScreenOn(false); + mAppToken.setCurrentLaunchCanTurnScreenOn(false); } } @@ -3195,12 +3192,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP outsets, reportDraw, mergedConfiguration, reportOrientation, displayId, displayCutout); } - - // TODO (multidisplay): Accessibility supported only for the default display and - // embedded displays - if (mWmService.mAccessibilityController != null && (getDisplayId() == DEFAULT_DISPLAY - || getDisplayContent().getParentWindow() != null)) { - mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(); + if (mWmService.mAccessibilityController != null) { + mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked( + getDisplayId()); } updateLocationInParentDisplayIfNeeded(); @@ -3847,6 +3841,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return parent != null && parent.mHidden; } + private boolean isParentWindowGoneForLayout() { + final WindowState parent = getParentWindow(); + return parent != null && parent.isGoneForLayoutLw(); + } + void setWillReplaceWindow(boolean animate) { for (int i = mChildren.size() - 1; i >= 0; i--) { final WindowState c = mChildren.get(i); @@ -4295,12 +4294,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (isSelfAnimating()) { return; } - - // TODO (multidisplay): Accessibility supported only for the default display and - // embedded displays - if (mWmService.mAccessibilityController != null && (getDisplayId() == DEFAULT_DISPLAY - || getDisplayContent().getParentWindow() != null)) { - mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(); + if (mWmService.mAccessibilityController != null) { + mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(getDisplayId()); } if (!isSelfOrAncestorWindowAnimatingExit()) { diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 73344ac1a430..3ba3280be7bb 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -1508,6 +1508,12 @@ class WindowStateAnimator { } void detachChildren() { + + // Do not detach children of starting windows, as their lifecycle is well under control and + // it may lead to issues in case we relaunch when we just added the starting window. + if (mWin.mAttrs.type == TYPE_APPLICATION_STARTING) { + return; + } if (mSurfaceController != null) { mSurfaceController.detachChildren(); } diff --git a/services/core/java/com/android/server/wm/utils/RegionUtils.java b/services/core/java/com/android/server/wm/utils/RegionUtils.java index b1b30701df4f..ce7776f270fd 100644 --- a/services/core/java/com/android/server/wm/utils/RegionUtils.java +++ b/services/core/java/com/android/server/wm/utils/RegionUtils.java @@ -50,6 +50,20 @@ public class RegionUtils { /** * Applies actions on each rect contained within a {@code Region}. * + * @param region the given region. + * @param rectConsumer the action holder. + */ + public static void forEachRect(Region region, Consumer<Rect> rectConsumer) { + final RegionIterator it = new RegionIterator(region); + final Rect rect = new Rect(); + while (it.next(rect)) { + rectConsumer.accept(rect); + } + } + + /** + * Applies actions on each rect contained within a {@code Region}. + * * Order is bottom to top, then right to left. * * @param region the given region. diff --git a/services/tests/mockingservicestests/src/com/android/server/display/color/DisplayTransformManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/display/color/DisplayTransformManagerTest.java index 73b3b8b1ce3c..a785300e98a3 100644 --- a/services/tests/mockingservicestests/src/com/android/server/display/color/DisplayTransformManagerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/display/color/DisplayTransformManagerTest.java @@ -19,6 +19,7 @@ package com.android.server.display.color; import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; import static com.android.server.display.color.DisplayTransformManager.LEVEL_COLOR_MATRIX_NIGHT_DISPLAY; +import static com.android.server.display.color.DisplayTransformManager.PERSISTENT_PROPERTY_COMPOSITION_COLOR_MODE; import static com.android.server.display.color.DisplayTransformManager.PERSISTENT_PROPERTY_DISPLAY_COLOR; import static com.android.server.display.color.DisplayTransformManager.PERSISTENT_PROPERTY_SATURATION; @@ -28,6 +29,7 @@ import static org.mockito.ArgumentMatchers.any; import android.hardware.display.ColorDisplayManager; import android.os.SystemProperties; +import android.view.Display; import androidx.test.runner.AndroidJUnit4; @@ -79,7 +81,7 @@ public class DisplayTransformManagerTest { @Test public void setColorMode_natural() { - mDtm.setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL, mNightDisplayMatrix); + mDtm.setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL, mNightDisplayMatrix, -1); assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_DISPLAY_COLOR)) .isEqualTo("0" /* managed */); assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_SATURATION)) @@ -88,7 +90,7 @@ public class DisplayTransformManagerTest { @Test public void setColorMode_boosted() { - mDtm.setColorMode(ColorDisplayManager.COLOR_MODE_BOOSTED, mNightDisplayMatrix); + mDtm.setColorMode(ColorDisplayManager.COLOR_MODE_BOOSTED, mNightDisplayMatrix, -1); assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_DISPLAY_COLOR)) .isEqualTo("0" /* managed */); @@ -98,7 +100,7 @@ public class DisplayTransformManagerTest { @Test public void setColorMode_saturated() { - mDtm.setColorMode(ColorDisplayManager.COLOR_MODE_SATURATED, mNightDisplayMatrix); + mDtm.setColorMode(ColorDisplayManager.COLOR_MODE_SATURATED, mNightDisplayMatrix, -1); assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_DISPLAY_COLOR)) .isEqualTo("1" /* unmanaged */); assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_SATURATION)) @@ -107,7 +109,7 @@ public class DisplayTransformManagerTest { @Test public void setColorMode_automatic() { - mDtm.setColorMode(ColorDisplayManager.COLOR_MODE_AUTOMATIC, mNightDisplayMatrix); + mDtm.setColorMode(ColorDisplayManager.COLOR_MODE_AUTOMATIC, mNightDisplayMatrix, -1); assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_DISPLAY_COLOR)) .isEqualTo("2" /* enhanced */); assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_SATURATION)) @@ -116,7 +118,7 @@ public class DisplayTransformManagerTest { @Test public void setColorMode_vendor() { - mDtm.setColorMode(0x100, mNightDisplayMatrix); + mDtm.setColorMode(0x100, mNightDisplayMatrix, -1); assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_DISPLAY_COLOR)) .isEqualTo(Integer.toString(0x100) /* pass-through */); assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_SATURATION)) @@ -125,10 +127,38 @@ public class DisplayTransformManagerTest { @Test public void setColorMode_outOfBounds() { - mDtm.setColorMode(0x50, mNightDisplayMatrix); + mDtm.setColorMode(0x50, mNightDisplayMatrix, -1); assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_DISPLAY_COLOR)) .isEqualTo(null); assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_SATURATION)) .isEqualTo(null); } + + @Test + public void setColorMode_withoutColorSpace() { + String magicPropertyValue = "magic"; + + // Start with a known state, which we expect to remain unmodified + SystemProperties.set(PERSISTENT_PROPERTY_COMPOSITION_COLOR_MODE, magicPropertyValue); + + mDtm.setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL, mNightDisplayMatrix, + Display.COLOR_MODE_INVALID); + assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_COMPOSITION_COLOR_MODE)) + .isEqualTo(magicPropertyValue); + } + + @Test + public void setColorMode_withColorSpace() { + String magicPropertyValue = "magic"; + int testPropertyValue = Display.COLOR_MODE_SRGB; + + // Start with a known state, which we expect to get modified + SystemProperties.set(PERSISTENT_PROPERTY_COMPOSITION_COLOR_MODE, magicPropertyValue); + + mDtm.setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL, mNightDisplayMatrix, + testPropertyValue); + assertThat(mSystemProperties.get(PERSISTENT_PROPERTY_COMPOSITION_COLOR_MODE)) + .isEqualTo(Integer.toString(testPropertyValue)); + } + } diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/TimeControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/TimeControllerTest.java index 70b7bb624920..3614763fecab 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/TimeControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/TimeControllerTest.java @@ -71,7 +71,6 @@ public class TimeControllerTest { private static final String SOURCE_PACKAGE = "com.android.frameworks.mockingservicestests"; private static final int SOURCE_USER_ID = 0; - private TimeController.TcConstants mConstants; private TimeController mTimeController; private MockitoSession mMockingSession; @@ -111,7 +110,6 @@ public class TimeControllerTest { // Initialize real objects. mTimeController = new TimeController(mJobSchedulerService); - mConstants = mTimeController.getTcConstants(); spyOn(mTimeController); } @@ -159,18 +157,7 @@ public class TimeControllerTest { } @Test - public void testMaybeStartTrackingJobLocked_DelayInOrder_NoSkipping() { - mConstants.SKIP_NOT_READY_JOBS = false; - mTimeController.onConstantsUpdatedLocked(); - - runTestMaybeStartTrackingJobLocked_DelayInOrder(); - } - - @Test - public void testMaybeStartTrackingJobLocked_DelayInOrder_WithSkipping_AllReady() { - mConstants.SKIP_NOT_READY_JOBS = true; - mTimeController.onConstantsUpdatedLocked(); - + public void testMaybeStartTrackingJobLocked_DelayInOrder_AllReady() { doReturn(true).when(mTimeController).wouldBeReadyWithConstraintLocked(any(), anyInt()); runTestMaybeStartTrackingJobLocked_DelayInOrder(); @@ -201,9 +188,7 @@ public class TimeControllerTest { } @Test - public void testMaybeStartTrackingJobLocked_DelayInOrder_WithSkipping_SomeNotReady() { - mConstants.SKIP_NOT_READY_JOBS = true; - mTimeController.onConstantsUpdatedLocked(); + public void testMaybeStartTrackingJobLocked_DelayInOrder_SomeNotReady() { final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); JobStatus jobLatest = createJobStatus("testMaybeStartTrackingJobLocked_DelayInOrder", @@ -235,18 +220,7 @@ public class TimeControllerTest { } @Test - public void testMaybeStartTrackingJobLocked_DelayReverseOrder_NoSkipping() { - mConstants.SKIP_NOT_READY_JOBS = false; - mTimeController.onConstantsUpdatedLocked(); - - runTestMaybeStartTrackingJobLocked_DelayReverseOrder(); - } - - @Test - public void testMaybeStartTrackingJobLocked_DelayReverseOrder_WithSkipping_AllReady() { - mConstants.SKIP_NOT_READY_JOBS = true; - mTimeController.onConstantsUpdatedLocked(); - + public void testMaybeStartTrackingJobLocked_DelayReverseOrder_AllReady() { doReturn(true).when(mTimeController).wouldBeReadyWithConstraintLocked(any(), anyInt()); runTestMaybeStartTrackingJobLocked_DelayReverseOrder(); @@ -279,9 +253,7 @@ public class TimeControllerTest { } @Test - public void testMaybeStartTrackingJobLocked_DelayReverseOrder_WithSkipping_SomeNotReady() { - mConstants.SKIP_NOT_READY_JOBS = true; - mTimeController.onConstantsUpdatedLocked(); + public void testMaybeStartTrackingJobLocked_DelayReverseOrder_SomeNotReady() { final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); JobStatus jobLatest = createJobStatus("testMaybeStartTrackingJobLocked_DelayReverseOrder", @@ -315,18 +287,7 @@ public class TimeControllerTest { } @Test - public void testMaybeStartTrackingJobLocked_DeadlineInOrder_NoSkipping() { - mConstants.SKIP_NOT_READY_JOBS = false; - mTimeController.onConstantsUpdatedLocked(); - - runTestMaybeStartTrackingJobLocked_DeadlineInOrder(); - } - - @Test - public void testMaybeStartTrackingJobLocked_DeadlineInOrder_WithSkipping_AllReady() { - mConstants.SKIP_NOT_READY_JOBS = true; - mTimeController.onConstantsUpdatedLocked(); - + public void testMaybeStartTrackingJobLocked_DeadlineInOrder_AllReady() { doReturn(true).when(mTimeController).wouldBeReadyWithConstraintLocked(any(), anyInt()); runTestMaybeStartTrackingJobLocked_DeadlineInOrder(); @@ -357,9 +318,7 @@ public class TimeControllerTest { } @Test - public void testMaybeStartTrackingJobLocked_DeadlineInOrder_WithSkipping_SomeNotReady() { - mConstants.SKIP_NOT_READY_JOBS = true; - mTimeController.onConstantsUpdatedLocked(); + public void testMaybeStartTrackingJobLocked_DeadlineInOrder_SomeNotReady() { final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); JobStatus jobLatest = createJobStatus("testMaybeStartTrackingJobLocked_DeadlineInOrder", @@ -391,18 +350,7 @@ public class TimeControllerTest { } @Test - public void testMaybeStartTrackingJobLocked_DeadlineReverseOrder_NoSkipping() { - mConstants.SKIP_NOT_READY_JOBS = false; - mTimeController.onConstantsUpdatedLocked(); - - runTestMaybeStartTrackingJobLocked_DeadlineReverseOrder(); - } - - @Test - public void testMaybeStartTrackingJobLocked_DeadlineReverseOrder_WithSkipping_AllReady() { - mConstants.SKIP_NOT_READY_JOBS = true; - mTimeController.onConstantsUpdatedLocked(); - + public void testMaybeStartTrackingJobLocked_DeadlineReverseOrder_AllReady() { doReturn(true).when(mTimeController).wouldBeReadyWithConstraintLocked(any(), anyInt()); runTestMaybeStartTrackingJobLocked_DeadlineReverseOrder(); @@ -438,9 +386,7 @@ public class TimeControllerTest { } @Test - public void testMaybeStartTrackingJobLocked_DeadlineReverseOrder_WithSkipping_SomeNotReady() { - mConstants.SKIP_NOT_READY_JOBS = true; - mTimeController.onConstantsUpdatedLocked(); + public void testMaybeStartTrackingJobLocked_DeadlineReverseOrder_SomeNotReady() { final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); JobStatus jobLatest = createJobStatus( @@ -478,62 +424,7 @@ public class TimeControllerTest { } @Test - public void testJobSkipToggling() { - final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); - - JobStatus jobLatest = createJobStatus( - "testMaybeStartTrackingJobLocked_DeadlineReverseOrder", - createJob().setOverrideDeadline(HOUR_IN_MILLIS)); - JobStatus jobEarliest = createJobStatus( - "testMaybeStartTrackingJobLocked_DeadlineReverseOrder", - createJob().setOverrideDeadline(5 * MINUTE_IN_MILLIS)); - - doReturn(true).when(mTimeController) - .wouldBeReadyWithConstraintLocked(eq(jobLatest), anyInt()); - doReturn(false).when(mTimeController) - .wouldBeReadyWithConstraintLocked(eq(jobEarliest), anyInt()); - - // Starting off with the skipping off, we should still set an alarm for the earlier job. - mConstants.SKIP_NOT_READY_JOBS = false; - mTimeController.recheckAlarmsLocked(); - InOrder inOrder = inOrder(mAlarmManager); - - mTimeController.maybeStartTrackingJobLocked(jobEarliest, null); - mTimeController.maybeStartTrackingJobLocked(jobLatest, null); - inOrder.verify(mAlarmManager, times(1)) - .set(anyInt(), eq(now + 5 * MINUTE_IN_MILLIS), anyLong(), anyLong(), - eq(TAG_DEADLINE), any(), any(), any()); - - // Turn it on, use alarm for later job. - mConstants.SKIP_NOT_READY_JOBS = true; - mTimeController.recheckAlarmsLocked(); - - inOrder.verify(mAlarmManager, times(1)) - .set(anyInt(), eq(now + HOUR_IN_MILLIS), anyLong(), anyLong(), eq(TAG_DEADLINE), - any(), any(), any()); - - // Back off, use alarm for earlier job. - mConstants.SKIP_NOT_READY_JOBS = false; - mTimeController.recheckAlarmsLocked(); - - inOrder.verify(mAlarmManager, times(1)) - .set(anyInt(), eq(now + 5 * MINUTE_IN_MILLIS), anyLong(), anyLong(), - eq(TAG_DEADLINE), any(), any(), any()); - } - - @Test - public void testCheckExpiredDelaysAndResetAlarm_NoSkipping() { - mConstants.SKIP_NOT_READY_JOBS = false; - mTimeController.recheckAlarmsLocked(); - - runTestCheckExpiredDelaysAndResetAlarm(); - } - - @Test - public void testCheckExpiredDelaysAndResetAlarm_WithSkipping_AllReady() { - mConstants.SKIP_NOT_READY_JOBS = true; - mTimeController.recheckAlarmsLocked(); - + public void testCheckExpiredDelaysAndResetAlarm_AllReady() { doReturn(true).when(mTimeController).wouldBeReadyWithConstraintLocked(any(), anyInt()); runTestCheckExpiredDelaysAndResetAlarm(); @@ -589,9 +480,7 @@ public class TimeControllerTest { } @Test - public void testCheckExpiredDelaysAndResetAlarm_WithSkipping_SomeNotReady() { - mConstants.SKIP_NOT_READY_JOBS = true; - mTimeController.recheckAlarmsLocked(); + public void testCheckExpiredDelaysAndResetAlarm_SomeNotReady() { final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); JobStatus jobLatest = createJobStatus("testCheckExpiredDelaysAndResetAlarm", @@ -639,18 +528,7 @@ public class TimeControllerTest { } @Test - public void testCheckExpiredDeadlinesAndResetAlarm_NoSkipping() { - mConstants.SKIP_NOT_READY_JOBS = false; - mTimeController.recheckAlarmsLocked(); - - runTestCheckExpiredDeadlinesAndResetAlarm(); - } - - @Test - public void testCheckExpiredDeadlinesAndResetAlarm_WithSkipping_AllReady() { - mConstants.SKIP_NOT_READY_JOBS = true; - mTimeController.recheckAlarmsLocked(); - + public void testCheckExpiredDeadlinesAndResetAlarm_AllReady() { doReturn(true).when(mTimeController).wouldBeReadyWithConstraintLocked(any(), anyInt()); runTestCheckExpiredDeadlinesAndResetAlarm(); @@ -706,9 +584,7 @@ public class TimeControllerTest { } @Test - public void testCheckExpiredDeadlinesAndResetAlarm_WithSkipping_SomeNotReady() { - mConstants.SKIP_NOT_READY_JOBS = true; - mTimeController.recheckAlarmsLocked(); + public void testCheckExpiredDeadlinesAndResetAlarm_SomeNotReady() { final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); JobStatus jobLatest = createJobStatus("testCheckExpiredDeadlinesAndResetAlarm", @@ -756,28 +632,14 @@ public class TimeControllerTest { } @Test - public void testEvaluateStateLocked_SkippingOff() { - mConstants.SKIP_NOT_READY_JOBS = false; - mTimeController.recheckAlarmsLocked(); - JobStatus job = createJobStatus("testEvaluateStateLocked_SkippingOff", - createJob().setOverrideDeadline(HOUR_IN_MILLIS)); - - mTimeController.evaluateStateLocked(job); - verify(mAlarmManager, never()) - .set(anyInt(), anyLong(), anyLong(), anyLong(), anyString(), any(), any(), any()); - } - - @Test - public void testEvaluateStateLocked_SkippingOn_Delay() { - mConstants.SKIP_NOT_READY_JOBS = true; - mTimeController.recheckAlarmsLocked(); + public void testEvaluateStateLocked_Delay() { final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); - JobStatus jobLatest = createJobStatus("testEvaluateStateLocked_SkippingOn_Delay", + JobStatus jobLatest = createJobStatus("testEvaluateStateLocked_Delay", createJob().setMinimumLatency(HOUR_IN_MILLIS)); - JobStatus jobMiddle = createJobStatus("testEvaluateStateLocked_SkippingOn_Delay", + JobStatus jobMiddle = createJobStatus("testEvaluateStateLocked_Delay", createJob().setMinimumLatency(30 * MINUTE_IN_MILLIS)); - JobStatus jobEarliest = createJobStatus("testEvaluateStateLocked_SkippingOn_Delay", + JobStatus jobEarliest = createJobStatus("testEvaluateStateLocked_Delay", createJob().setMinimumLatency(5 * MINUTE_IN_MILLIS)); doReturn(false).when(mTimeController) @@ -827,16 +689,14 @@ public class TimeControllerTest { } @Test - public void testEvaluateStateLocked_SkippingOn_Deadline() { - mConstants.SKIP_NOT_READY_JOBS = true; - mTimeController.recheckAlarmsLocked(); + public void testEvaluateStateLocked_Deadline() { final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); - JobStatus jobLatest = createJobStatus("testEvaluateStateLocked_SkippingOn_Deadline", + JobStatus jobLatest = createJobStatus("testEvaluateStateLocked_Deadline", createJob().setOverrideDeadline(HOUR_IN_MILLIS)); - JobStatus jobMiddle = createJobStatus("testEvaluateStateLocked_SkippingOn_Deadline", + JobStatus jobMiddle = createJobStatus("testEvaluateStateLocked_Deadline", createJob().setOverrideDeadline(30 * MINUTE_IN_MILLIS)); - JobStatus jobEarliest = createJobStatus("testEvaluateStateLocked_SkippingOn_Deadline", + JobStatus jobEarliest = createJobStatus("testEvaluateStateLocked_Deadline", createJob().setOverrideDeadline(5 * MINUTE_IN_MILLIS)); doReturn(false).when(mTimeController) diff --git a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java index e9e96c9c7efb..1ad7b6e8d155 100644 --- a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java @@ -72,6 +72,7 @@ import androidx.test.filters.MediumTest; import androidx.test.runner.AndroidJUnit4; import com.android.server.devicepolicy.MockUtils; +import com.android.server.pm.permission.PermissionManagerServiceInternal; import com.google.android.collect.Lists; @@ -132,9 +133,10 @@ public class NetworkScoreServiceTest { @Mock private UnaryOperator<List<ScoredNetwork>> mScanResultsFilter; @Mock private WifiInfo mWifiInfo; @Mock private NetworkScoreService.ScoringServiceConnection mServiceConnection; - @Mock private PackageManagerInternal mPackageManagerInternal; + @Mock private PermissionManagerServiceInternal mPermissionManagerInternal; @Captor private ArgumentCaptor<List<ScoredNetwork>> mScoredNetworkCaptor; - @Captor private ArgumentCaptor<PackageManagerInternal.PackagesProvider> mPackagesProviderCaptor; + @Captor private + ArgumentCaptor<PermissionManagerServiceInternal.PackagesProvider> mPackagesProviderCaptor; private ContentResolver mContentResolver; private NetworkScoreService mNetworkScoreService; @@ -162,7 +164,8 @@ public class NetworkScoreServiceTest { when(mNetworkScorerAppManager.getActiveScorer()).thenReturn(NEW_SCORER); mHandlerThread = new HandlerThread("NetworkScoreServiceTest"); mHandlerThread.start(); - LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternal); + LocalServices.addService( + PermissionManagerServiceInternal.class, mPermissionManagerInternal); mNetworkScoreService = new NetworkScoreService(mContext, mNetworkScorerAppManager, networkScorerAppData -> mServiceConnection, mHandlerThread.getLooper()); WifiConfiguration configuration = new WifiConfiguration(); @@ -196,7 +199,7 @@ public class NetworkScoreServiceTest { Settings.Global.putString(mContentResolver, Settings.Global.USE_OPEN_WIFI_PACKAGE, "com.some.app"); - verify(mPackageManagerInternal) + verify(mPermissionManagerInternal) .setUseOpenWifiAppPackagesProvider(mPackagesProviderCaptor.capture()); String[] packages = mPackagesProviderCaptor.getValue().getPackages(0); @@ -209,7 +212,7 @@ public class NetworkScoreServiceTest { Settings.Global.putString(mContentResolver, Settings.Global.USE_OPEN_WIFI_PACKAGE, "com.some.other.app"); - verify(mPackageManagerInternal, timeout(500)) + verify(mPermissionManagerInternal, timeout(500)) .grantDefaultPermissionsToDefaultUseOpenWifiApp("com.some.other.app", 0); } diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java index 22408cc05b93..7e64cafaf7a2 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java @@ -33,6 +33,7 @@ import static org.junit.Assert.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -42,6 +43,7 @@ import android.os.IBinder; import android.os.RemoteException; import android.os.UserHandle; import android.util.SparseArray; +import android.view.Display; import android.view.IWindow; import android.view.WindowInfo; import android.view.accessibility.AccessibilityEvent; @@ -142,7 +144,9 @@ public class AccessibilityWindowManagerTest { public void startTrackingWindows_shouldEnableWindowManagerCallback() { // AccessibilityWindowManager#startTrackingWindows already invoked in setup assertTrue(mA11yWindowManager.isTrackingWindowsLocked()); - verify(mMockWindowManagerInternal).setWindowsForAccessibilityCallback(any()); + // TODO [Multi-Display] : using correct display Id to replace DEFAULT_DISPLAY + verify(mMockWindowManagerInternal).setWindowsForAccessibilityCallback( + eq(Display.DEFAULT_DISPLAY), any()); } @Test @@ -152,7 +156,9 @@ public class AccessibilityWindowManagerTest { mA11yWindowManager.stopTrackingWindows(); assertFalse(mA11yWindowManager.isTrackingWindowsLocked()); - verify(mMockWindowManagerInternal).setWindowsForAccessibilityCallback(any()); + // TODO [Multi-Display] : using correct display Id to replace DEFAULT_DISPLAY + verify(mMockWindowManagerInternal).setWindowsForAccessibilityCallback( + eq(Display.DEFAULT_DISPLAY), any()); } @Test @@ -238,7 +244,7 @@ public class AccessibilityWindowManagerTest { windowInfo.type = AccessibilityWindowInfo.TYPE_APPLICATION; windowInfo.token = token.asBinder(); windowInfo.layer = 0; - windowInfo.boundsInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + windowInfo.regionInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); mWindowInfos.set(0, windowInfo); mA11yWindowManager.onWindowsForAccessibilityChanged(SEND_ON_WINDOW_CHANGES, mWindowInfos); @@ -305,67 +311,73 @@ public class AccessibilityWindowManagerTest { } @Test - public void computePartialInteractiveRegionForWindow_wholeWindowVisible_returnWholeRegion() { + public void computePartialInteractiveRegionForWindow_wholeVisible_returnWholeRegion() { // Updates top 2 z-order WindowInfo are whole visible. WindowInfo windowInfo = mWindowInfos.get(0); - windowInfo.boundsInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT / 2); + windowInfo.regionInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT / 2); windowInfo = mWindowInfos.get(1); - windowInfo.boundsInScreen.set(0, SCREEN_HEIGHT / 2, - SCREEN_WIDTH, SCREEN_HEIGHT); + windowInfo.regionInScreen.set(0, SCREEN_HEIGHT / 2, SCREEN_WIDTH, SCREEN_HEIGHT); mA11yWindowManager.onWindowsForAccessibilityChanged(SEND_ON_WINDOW_CHANGES, mWindowInfos); final List<AccessibilityWindowInfo> a11yWindows = mA11yWindowManager.getWindowListLocked(); final Region outBounds = new Region(); int windowId = a11yWindows.get(0).getId(); - mA11yWindowManager.computePartialInteractiveRegionForWindowLocked( - windowId, outBounds); + mA11yWindowManager.computePartialInteractiveRegionForWindowLocked(windowId, outBounds); assertThat(outBounds.getBounds().width(), is(SCREEN_WIDTH)); assertThat(outBounds.getBounds().height(), is(SCREEN_HEIGHT / 2)); windowId = a11yWindows.get(1).getId(); - mA11yWindowManager.computePartialInteractiveRegionForWindowLocked( - windowId, outBounds); + mA11yWindowManager.computePartialInteractiveRegionForWindowLocked(windowId, outBounds); assertThat(outBounds.getBounds().width(), is(SCREEN_WIDTH)); assertThat(outBounds.getBounds().height(), is(SCREEN_HEIGHT / 2)); } @Test - public void computePartialInteractiveRegionForWindow_halfWindowVisible_returnHalfRegion() { + public void computePartialInteractiveRegionForWindow_halfVisible_returnHalfRegion() { // Updates z-order #1 WindowInfo is half visible WindowInfo windowInfo = mWindowInfos.get(0); - windowInfo.boundsInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT / 2); - windowInfo = mWindowInfos.get(1); - windowInfo.boundsInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + windowInfo.regionInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT / 2); mA11yWindowManager.onWindowsForAccessibilityChanged(SEND_ON_WINDOW_CHANGES, mWindowInfos); final List<AccessibilityWindowInfo> a11yWindows = mA11yWindowManager.getWindowListLocked(); final Region outBounds = new Region(); int windowId = a11yWindows.get(1).getId(); - mA11yWindowManager.computePartialInteractiveRegionForWindowLocked( - windowId, outBounds); + mA11yWindowManager.computePartialInteractiveRegionForWindowLocked(windowId, outBounds); assertThat(outBounds.getBounds().width(), is(SCREEN_WIDTH)); assertThat(outBounds.getBounds().height(), is(SCREEN_HEIGHT / 2)); } @Test - public void computePartialInteractiveRegionForWindow_windowNotVisible_returnEmptyRegion() { - // Updates z-order #1 WindowInfo is not visible + public void computePartialInteractiveRegionForWindow_notVisible_returnEmptyRegion() { + // Since z-order #0 WindowInfo is full screen, z-order #1 WindowInfo should be invisible. + final List<AccessibilityWindowInfo> a11yWindows = mA11yWindowManager.getWindowListLocked(); + final Region outBounds = new Region(); + int windowId = a11yWindows.get(1).getId(); + + mA11yWindowManager.computePartialInteractiveRegionForWindowLocked(windowId, outBounds); + assertTrue(outBounds.getBounds().isEmpty()); + } + + @Test + public void computePartialInteractiveRegionForWindow_partialVisible_returnVisibleRegion() { + // Updates z-order #0 WindowInfo to have two interact-able areas. + Region region = new Region(0, 0, SCREEN_WIDTH, 200); + region.op(0, SCREEN_HEIGHT - 200, SCREEN_WIDTH, SCREEN_HEIGHT, Region.Op.UNION); WindowInfo windowInfo = mWindowInfos.get(0); - windowInfo.boundsInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); - windowInfo = mWindowInfos.get(1); - windowInfo.boundsInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + windowInfo.regionInScreen.set(region); mA11yWindowManager.onWindowsForAccessibilityChanged(SEND_ON_WINDOW_CHANGES, mWindowInfos); final List<AccessibilityWindowInfo> a11yWindows = mA11yWindowManager.getWindowListLocked(); final Region outBounds = new Region(); int windowId = a11yWindows.get(1).getId(); - mA11yWindowManager.computePartialInteractiveRegionForWindowLocked( - windowId, outBounds); - assertTrue(outBounds.getBounds().isEmpty()); + mA11yWindowManager.computePartialInteractiveRegionForWindowLocked(windowId, outBounds); + assertFalse(outBounds.getBounds().isEmpty()); + assertThat(outBounds.getBounds().width(), is(SCREEN_WIDTH)); + assertThat(outBounds.getBounds().height(), is(SCREEN_HEIGHT - 400)); } @Test @@ -588,7 +600,7 @@ public class AccessibilityWindowManagerTest { windowInfo.type = AccessibilityWindowInfo.TYPE_APPLICATION; windowInfo.token = windowToken.asBinder(); windowInfo.layer = layer; - windowInfo.boundsInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + windowInfo.regionInScreen.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); mWindowInfos.add(windowInfo); } 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 d8545821541d..210de538d0bd 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java @@ -16,6 +16,8 @@ package com.android.server.accessibility; +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -31,6 +33,7 @@ import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; +import android.hardware.display.DisplayManager; import android.os.IBinder; import android.view.accessibility.AccessibilityEvent; @@ -78,6 +81,11 @@ public class UiAutomationManagerTest { when(mMockAccessibilityServiceClient.asBinder()).thenReturn(mMockServiceAsBinder); + final Context context = getInstrumentation().getTargetContext(); + when(mMockContext.getSystemService(Context.DISPLAY_SERVICE)).thenReturn( + context.getSystemService( + DisplayManager.class)); + mMessageCapturingHandler = new MessageCapturingHandler(null); } diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java index e6c484a8dbbc..d008ca66b762 100644 --- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java +++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java @@ -142,4 +142,32 @@ public class CompatConfigTest { CompatConfig pc = new CompatConfig(); assertThat(pc.lookupChangeId("MY_CHANGE")).isEqualTo(-1L); } + + @Test + public void testSystemAppDisabledChangeEnabled() { + CompatConfig pc = new CompatConfig(); + pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, true)); // disabled + ApplicationInfo sysApp = makeAppInfo("system.app", 1); + sysApp.flags |= ApplicationInfo.FLAG_SYSTEM; + assertThat(pc.isChangeEnabled(1234L, sysApp)).isTrue(); + } + + @Test + public void testSystemAppOverrideIgnored() { + CompatConfig pc = new CompatConfig(); + pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, false)); + pc.addOverride(1234L, "system.app", false); + ApplicationInfo sysApp = makeAppInfo("system.app", 1); + sysApp.flags |= ApplicationInfo.FLAG_SYSTEM; + assertThat(pc.isChangeEnabled(1234L, sysApp)).isTrue(); + } + + @Test + public void testSystemAppTargetSdkIgnored() { + CompatConfig pc = new CompatConfig(); + pc.addChange(new CompatChange(1234L, "MY_CHANGE", 2, false)); + ApplicationInfo sysApp = makeAppInfo("system.app", 1); + sysApp.flags |= ApplicationInfo.FLAG_SYSTEM; + assertThat(pc.isChangeEnabled(1234L, sysApp)).isTrue(); + } } diff --git a/services/tests/servicestests/src/com/android/server/display/color/ColorDisplayServiceTest.java b/services/tests/servicestests/src/com/android/server/display/color/ColorDisplayServiceTest.java index fb2913b09ace..a19b3872949e 100644 --- a/services/tests/servicestests/src/com/android/server/display/color/ColorDisplayServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/display/color/ColorDisplayServiceTest.java @@ -18,13 +18,19 @@ package com.android.server.display.color; import static com.google.common.truth.Truth.assertWithMessage; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.annotation.NonNull; import android.app.ActivityManager; import android.app.AlarmManager; import android.content.Context; import android.content.ContextWrapper; +import android.content.res.Resources; import android.hardware.display.ColorDisplayManager; import android.hardware.display.Time; import android.os.Handler; @@ -33,6 +39,7 @@ import android.provider.Settings; import android.provider.Settings.Secure; import android.provider.Settings.System; import android.test.mock.MockContentResolver; +import android.view.Display; import androidx.test.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; @@ -73,6 +80,8 @@ public class ColorDisplayServiceTest { private ColorDisplayService mCds; private ColorDisplayService.BinderService mBinderService; + private Resources mResourcesSpy; + @BeforeClass public static void setDtm() { final DisplayTransformManager dtm = Mockito.mock(DisplayTransformManager.class); @@ -84,6 +93,9 @@ public class ColorDisplayServiceTest { mContext = Mockito.spy(new ContextWrapper(InstrumentationRegistry.getTargetContext())); doReturn(mContext).when(mContext).getApplicationContext(); + mResourcesSpy = Mockito.spy(mContext.getResources()); + when(mContext.getResources()).thenReturn(mResourcesSpy); + mUserId = ActivityManager.getCurrentUser(); final MockContentResolver cr = new MockContentResolver(mContext); @@ -1050,6 +1062,80 @@ public class ColorDisplayServiceTest { assertDwbActive(true); } + @Test + public void compositionColorSpaces_noResources() { + final DisplayTransformManager dtm = LocalServices.getService(DisplayTransformManager.class); + reset(dtm); + + when(mResourcesSpy.getIntArray(R.array.config_displayCompositionColorModes)) + .thenReturn(new int[] {}); + when(mResourcesSpy.getIntArray(R.array.config_displayCompositionColorSpaces)) + .thenReturn(new int[] {}); + setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL); + startService(); + verify(dtm).setColorMode(eq(ColorDisplayManager.COLOR_MODE_NATURAL), any(), + eq(Display.COLOR_MODE_INVALID)); + } + + @Test + public void compositionColorSpaces_invalidResources() { + final DisplayTransformManager dtm = LocalServices.getService(DisplayTransformManager.class); + reset(dtm); + + when(mResourcesSpy.getIntArray(R.array.config_displayCompositionColorModes)) + .thenReturn(new int[] { + ColorDisplayManager.COLOR_MODE_NATURAL, + // Missing second color mode + }); + when(mResourcesSpy.getIntArray(R.array.config_displayCompositionColorSpaces)) + .thenReturn(new int[] { + Display.COLOR_MODE_SRGB, + Display.COLOR_MODE_DISPLAY_P3 + }); + setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL); + startService(); + verify(dtm).setColorMode(eq(ColorDisplayManager.COLOR_MODE_NATURAL), any(), + eq(Display.COLOR_MODE_INVALID)); + } + + @Test + public void compositionColorSpaces_validResources_validColorMode() { + final DisplayTransformManager dtm = LocalServices.getService(DisplayTransformManager.class); + reset(dtm); + + when(mResourcesSpy.getIntArray(R.array.config_displayCompositionColorModes)) + .thenReturn(new int[] { + ColorDisplayManager.COLOR_MODE_NATURAL + }); + when(mResourcesSpy.getIntArray(R.array.config_displayCompositionColorSpaces)) + .thenReturn(new int[] { + Display.COLOR_MODE_SRGB, + }); + setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL); + startService(); + verify(dtm).setColorMode(eq(ColorDisplayManager.COLOR_MODE_NATURAL), any(), + eq(Display.COLOR_MODE_SRGB)); + } + + @Test + public void compositionColorSpaces_validResources_invalidColorMode() { + final DisplayTransformManager dtm = LocalServices.getService(DisplayTransformManager.class); + reset(dtm); + + when(mResourcesSpy.getIntArray(R.array.config_displayCompositionColorModes)) + .thenReturn(new int[] { + ColorDisplayManager.COLOR_MODE_NATURAL + }); + when(mResourcesSpy.getIntArray(R.array.config_displayCompositionColorSpaces)) + .thenReturn(new int[] { + Display.COLOR_MODE_SRGB, + }); + setColorMode(ColorDisplayManager.COLOR_MODE_BOOSTED); + startService(); + verify(dtm).setColorMode(eq(ColorDisplayManager.COLOR_MODE_BOOSTED), any(), + eq(Display.COLOR_MODE_INVALID)); + } + /** * Configures Night display to use a custom schedule. * 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 ce1edcd584f6..09ae3a2628bf 100644 --- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java @@ -559,7 +559,25 @@ public class NetworkPolicyManagerServiceTest { .build(); mService.updateRestrictBackgroundByLowPowerModeUL(stateOff); - // RestrictBackground should be on, following its previous state + // RestrictBackground should be on, as before. + assertTrue(mService.getRestrictBackground()); + + stateOn = new PowerSaveState.Builder() + .setGlobalBatterySaverEnabled(true) + .setBatterySaverEnabled(true) + .build(); + mService.updateRestrictBackgroundByLowPowerModeUL(stateOn); + + // RestrictBackground should be on. + assertTrue(mService.getRestrictBackground()); + + stateOff = new PowerSaveState.Builder() + .setGlobalBatterySaverEnabled(false) + .setBatterySaverEnabled(false) + .build(); + mService.updateRestrictBackgroundByLowPowerModeUL(stateOff); + + // RestrictBackground should be on, as it was enabled manually before battery saver. assertTrue(mService.getRestrictBackground()); } @@ -585,6 +603,20 @@ public class NetworkPolicyManagerServiceTest { // RestrictBackground should be off, following its previous state assertFalse(mService.getRestrictBackground()); + + PowerSaveState stateOnRestrictOff = new PowerSaveState.Builder() + .setGlobalBatterySaverEnabled(true) + .setBatterySaverEnabled(false) + .build(); + + mService.updateRestrictBackgroundByLowPowerModeUL(stateOnRestrictOff); + + assertFalse(mService.getRestrictBackground()); + + mService.updateRestrictBackgroundByLowPowerModeUL(stateOff); + + // RestrictBackground should still be off. + assertFalse(mService.getRestrictBackground()); } @Test @@ -602,11 +634,49 @@ public class NetworkPolicyManagerServiceTest { // User turns off RestrictBackground manually setRestrictBackground(false); - PowerSaveState stateOff = new PowerSaveState.Builder().setBatterySaverEnabled( - false).build(); + // RestrictBackground should be off because user changed it manually + assertFalse(mService.getRestrictBackground()); + + PowerSaveState stateOff = new PowerSaveState.Builder() + .setGlobalBatterySaverEnabled(false) + .setBatterySaverEnabled(false) + .build(); + mService.updateRestrictBackgroundByLowPowerModeUL(stateOff); + + // RestrictBackground should remain off. + assertFalse(mService.getRestrictBackground()); + } + + @Test + public void updateRestrictBackgroundByLowPowerMode_RestrictOnWithGlobalOff() + throws Exception { + setRestrictBackground(false); + PowerSaveState stateOn = new PowerSaveState.Builder() + .setGlobalBatterySaverEnabled(false) + .setBatterySaverEnabled(true) + .build(); + + mService.updateRestrictBackgroundByLowPowerModeUL(stateOn); + + // RestrictBackground should be turned on because of battery saver. + assertTrue(mService.getRestrictBackground()); + + PowerSaveState stateRestrictOff = new PowerSaveState.Builder() + .setGlobalBatterySaverEnabled(true) + .setBatterySaverEnabled(false) + .build(); + mService.updateRestrictBackgroundByLowPowerModeUL(stateRestrictOff); + + // RestrictBackground should be off, returning to its state before battery saver's change. + assertFalse(mService.getRestrictBackground()); + + PowerSaveState stateOff = new PowerSaveState.Builder() + .setGlobalBatterySaverEnabled(false) + .setBatterySaverEnabled(false) + .build(); mService.updateRestrictBackgroundByLowPowerModeUL(stateOff); - // RestrictBackground should be off because user changes it manually + // RestrictBackground should still be off, back in its pre-battery saver state. assertFalse(mService.getRestrictBackground()); } diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java index 95ec3d9c0917..fc7cfec9dc87 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java @@ -115,7 +115,7 @@ public class PackageManagerServiceTest { @Test public void testPartitions() throws Exception { - String[] partitions = { "system", "vendor", "odm", "oem", "product", "product_services" }; + String[] partitions = { "system", "vendor", "odm", "oem", "product", "system_ext" }; String[] appdir = { "app", "priv-app" }; for (int i = 0; i < partitions.length; i++) { for (int j = 0; j < appdir.length; j++) { @@ -128,7 +128,7 @@ public class PackageManagerServiceTest { Assert.assertEquals(i == 1 || i == 2, PackageManagerService.locationIsVendor(path)); Assert.assertEquals(i == 3, PackageManagerService.locationIsOem(path)); Assert.assertEquals(i == 4, PackageManagerService.locationIsProduct(path)); - Assert.assertEquals(i == 5, PackageManagerService.locationIsProductServices(path)); + Assert.assertEquals(i == 5, PackageManagerService.locationIsSystemExt(path)); } } } 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 6061d51f3d79..8c3373faa0d4 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java @@ -156,7 +156,7 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { mService.setUsageStats(mUsageStats); mService.setAccessibilityManager(accessibilityManager); mService.mScreenOn = false; - mService.mInCall = false; + mService.mInCallStateOffHook = false; mService.mNotificationPulseEnabled = true; } @@ -681,7 +681,7 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { mService.buzzBeepBlinkLocked(r); Mockito.reset(mRingtonePlayer); - mService.mInCall = true; + mService.mInCallStateOffHook = true; mService.buzzBeepBlinkLocked(r); verify(mService, times(1)).playInCallNotification(); @@ -1137,7 +1137,7 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { @Test public void testLightsInCall() { - mService.mInCall = true; + mService.mInCallStateOffHook = true; NotificationRecord r = getLightsNotification(); mService.buzzBeepBlinkLocked(r); verifyNeverLights(); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index efbae2b566ad..ef11a437fcb1 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static android.os.Process.NOBODY_UID; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Surface.ROTATION_0; import static android.view.Surface.ROTATION_90; @@ -35,11 +36,14 @@ import static com.android.server.wm.ActivityStack.ActivityState.PAUSING; import static com.android.server.wm.ActivityStack.ActivityState.RESUMED; import static com.android.server.wm.ActivityStack.ActivityState.STARTED; import static com.android.server.wm.ActivityStack.ActivityState.STOPPED; +import static com.android.server.wm.ActivityStack.ActivityState.STOPPING; import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_MOVING; import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_INVISIBLE; import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_VISIBLE; import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT; +import static com.google.common.truth.Truth.assertThat; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; @@ -54,9 +58,13 @@ import android.app.ActivityOptions; import android.app.servertransaction.ActivityConfigurationChangeItem; import android.app.servertransaction.ClientTransaction; import android.app.servertransaction.PauseActivityItem; +import android.content.ComponentName; import android.content.pm.ActivityInfo; import android.content.res.Configuration; +import android.content.res.Resources; import android.graphics.Rect; +import android.os.Bundle; +import android.os.PersistableBundle; import android.platform.test.annotations.Presubmit; import android.util.MergedConfiguration; import android.util.MutableBoolean; @@ -67,6 +75,7 @@ import android.view.RemoteAnimationTarget; import androidx.test.filters.MediumTest; +import com.android.internal.R; import com.android.server.wm.utils.WmDisplayCutout; import org.junit.Before; @@ -194,7 +203,7 @@ public class ActivityRecordTests extends ActivityTestsBase { public void testRestartProcessIfVisible() { doNothing().when(mSupervisor).scheduleRestartTimeout(mActivity); mActivity.visible = true; - mActivity.haveState = false; + mActivity.setSavedState(null /* savedState */); mActivity.setState(ActivityStack.ActivityState.RESUMED, "testRestart"); prepareFixedAspectRatioUnresizableActivity(); @@ -611,6 +620,67 @@ public class ActivityRecordTests extends ActivityTestsBase { assertNull(mActivity.pendingOptions); } + @Test + public void testCanLaunchHomeActivityFromChooser() { + ComponentName chooserComponent = ComponentName.unflattenFromString( + Resources.getSystem().getString(R.string.config_chooserActivity)); + ActivityRecord chooserActivity = new ActivityBuilder(mService).setComponent( + chooserComponent).build(); + assertThat(mActivity.canLaunchHomeActivity(NOBODY_UID, chooserActivity)).isTrue(); + } + + /** + * Verify that an {@link ActivityRecord} reports that it has saved state after creation, and + * that it is cleared after activity is resumed. + */ + @Test + public void testHasSavedState() { + assertTrue(mActivity.hasSavedState()); + + ActivityRecord.activityResumedLocked(mActivity.appToken); + assertFalse(mActivity.hasSavedState()); + assertNull(mActivity.getSavedState()); + } + + /** Verify the behavior of {@link ActivityRecord#setSavedState(Bundle)}. */ + @Test + public void testUpdateSavedState() { + mActivity.setSavedState(null /* savedState */); + assertFalse(mActivity.hasSavedState()); + assertNull(mActivity.getSavedState()); + + final Bundle savedState = new Bundle(); + savedState.putString("test", "string"); + mActivity.setSavedState(savedState); + assertTrue(mActivity.hasSavedState()); + assertEquals(savedState, mActivity.getSavedState()); + } + + /** Verify the correct updates of saved state when activity client reports stop. */ + @Test + public void testUpdateSavedState_activityStopped() { + final Bundle savedState = new Bundle(); + savedState.putString("test", "string"); + final PersistableBundle persistentSavedState = new PersistableBundle(); + persistentSavedState.putString("persist", "string"); + + // Set state to STOPPING, or ActivityRecord#activityStoppedLocked() call will be ignored. + mActivity.setState(STOPPING, "test"); + mActivity.activityStoppedLocked(savedState, persistentSavedState, "desc"); + assertTrue(mActivity.hasSavedState()); + assertEquals(savedState, mActivity.getSavedState()); + assertEquals(persistentSavedState, mActivity.getPersistentSavedState()); + + // Sending 'null' for saved state can only happen due to timeout, so previously stored saved + // states should not be overridden. + mActivity.setState(STOPPING, "test"); + mActivity.activityStoppedLocked(null /* savedState */, null /* persistentSavedState */, + "desc"); + assertTrue(mActivity.hasSavedState()); + assertEquals(savedState, mActivity.getSavedState()); + assertEquals(persistentSavedState, mActivity.getPersistentSavedState()); + } + /** Setup {@link #mActivity} as a size-compat-mode-able activity without fixed orientation. */ private void prepareFixedAspectRatioUnresizableActivity() { setupDisplayContentForCompatDisplayInsets(); 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 bde0ef6aa39e..e5278d81767e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static android.app.Activity.RESULT_CANCELED; import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; @@ -892,7 +893,7 @@ public class ActivityStackTests extends ActivityTestsBase { firstActivity.app = null; // second activity will be immediately removed as it has no state. - secondActivity.haveState = false; + secondActivity.setSavedState(null /* savedState */); assertEquals(2, mTask.mActivities.size()); @@ -908,7 +909,7 @@ public class ActivityStackTests extends ActivityTestsBase { activity.mRelaunchReason = RELAUNCH_REASON_WINDOWING_MODE_RESIZE; activity.launchCount = 1; - activity.haveState = false; + activity.setSavedState(null /* savedState */); mStack.handleAppDiedLocked(activity.app); @@ -922,7 +923,7 @@ public class ActivityStackTests extends ActivityTestsBase { activity.mRelaunchReason = RELAUNCH_REASON_WINDOWING_MODE_RESIZE; activity.launchCount = 3; - activity.haveState = false; + activity.setSavedState(null /* savedState */); mStack.handleAppDiedLocked(activity.app); @@ -936,7 +937,7 @@ public class ActivityStackTests extends ActivityTestsBase { activity.mRelaunchReason = RELAUNCH_REASON_FREE_RESIZE; activity.launchCount = 1; - activity.haveState = false; + activity.setSavedState(null /* savedState */); mStack.handleAppDiedLocked(activity.app); @@ -950,7 +951,7 @@ public class ActivityStackTests extends ActivityTestsBase { activity.mRelaunchReason = RELAUNCH_REASON_FREE_RESIZE; activity.launchCount = 3; - activity.haveState = false; + activity.setSavedState(null /* savedState */); mStack.handleAppDiedLocked(activity.app); @@ -969,7 +970,7 @@ public class ActivityStackTests extends ActivityTestsBase { homeStask.removeTask(homeTask, "testAdjustFocusedStack", REMOVE_TASK_MODE_DESTROYING); // Finish the only activity. - mStack.finishActivityLocked(topActivity, 0 /* resultCode */, null /* resultData */, + topActivity.finishActivityLocked(RESULT_CANCELED /* resultCode */, null /* resultData */, "testAdjustFocusedStack", false /* oomAdj */); // Although home stack is empty, it should still be the focused stack. assertEquals(homeStask, mDefaultDisplay.getFocusedStack()); @@ -1014,7 +1015,7 @@ public class ActivityStackTests extends ActivityTestsBase { assertNotNull(activity); activity.setState(PAUSED, "finishCurrentActivity"); activity.makeFinishingLocked(); - stack.finishCurrentActivityLocked(activity, ActivityStack.FINISH_AFTER_VISIBLE, + activity.finishCurrentActivityLocked(ActivityRecord.FINISH_AFTER_VISIBLE, false /* oomAdj */, "finishCurrentActivity"); return activity; } diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java index 1684f97d28e3..6a3c81ab73bc 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java @@ -32,6 +32,7 @@ import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLES import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; +import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; import static android.view.WindowManager.LayoutParams.TYPE_TOAST; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; @@ -49,10 +50,12 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import android.graphics.PixelFormat; +import android.graphics.Rect; import android.platform.test.annotations.Presubmit; import android.view.Surface; import android.view.WindowManager; +import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; import org.junit.Test; @@ -273,4 +276,37 @@ public class DisplayPolicyTests extends WindowTestsBase { win.mHasSurface = true; return win; } + + @Test + @FlakyTest(bugId = 131005232) + public void testOverlappingWithNavBar() { + final WindowState targetWin = createApplicationWindow(); + final WindowFrames winFrame = targetWin.getWindowFrames(); + winFrame.mFrame.set(new Rect(100, 100, 200, 200)); + + final WindowState navigationBar = createNavigationBarWindow(); + + navigationBar.getFrameLw().set(new Rect(100, 200, 200, 300)); + + assertFalse("Freeform is overlapping with navigation bar", + DisplayPolicy.isOverlappingWithNavBar(targetWin, navigationBar)); + + winFrame.mFrame.set(new Rect(100, 101, 200, 201)); + assertTrue("Freeform should be overlapping with navigation bar (bottom)", + DisplayPolicy.isOverlappingWithNavBar(targetWin, navigationBar)); + + winFrame.mFrame.set(new Rect(99, 200, 199, 300)); + assertTrue("Freeform should be overlapping with navigation bar (right)", + DisplayPolicy.isOverlappingWithNavBar(targetWin, navigationBar)); + + winFrame.mFrame.set(new Rect(199, 200, 299, 300)); + assertTrue("Freeform should be overlapping with navigation bar (left)", + DisplayPolicy.isOverlappingWithNavBar(targetWin, navigationBar)); + } + + private WindowState createNavigationBarWindow() { + final WindowState win = createWindow(null, TYPE_NAVIGATION_BAR, "NavigationBar"); + win.mHasSurface = true; + return win; + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java index a735b099e58a..2933b4a460d8 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java @@ -102,7 +102,8 @@ public class DisplayPolicyTestsBase extends WindowTestsBase { mDisplayPolicy.adjustWindowParamsLw(win, win.mAttrs, Binder.getCallingPid(), Binder.getCallingUid()); assertEquals(WindowManagerGlobal.ADD_OKAY, - mDisplayPolicy.prepareAddWindowLw(win, win.mAttrs)); + mDisplayPolicy.validateAddingWindowLw(win.mAttrs)); + mDisplayPolicy.addWindowLw(win, win.mAttrs); win.mHasSurface = true; } diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java index 2bffc167f418..bfede51f5a3c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java @@ -23,7 +23,6 @@ import static android.view.WindowManager.REMOVE_CONTENT_MODE_MOVE_TO_PRIMARY; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeastOnce; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; @@ -624,11 +623,8 @@ public class DisplayWindowSettingsTests extends WindowTestsBase { assertFalse(dc.mWaitingForConfig); // Notify WM that the displays are ready and check that they are reconfigured. - spyOn(mWm); mWm.displayReady(); waitUntilHandlersIdle(); - verify(mWm, atLeastOnce()).reconfigureDisplayLocked(eq(mPrimaryDisplay)); - verify(mWm, atLeastOnce()).reconfigureDisplayLocked(eq(dc)); final Configuration config = new Configuration(); mPrimaryDisplay.computeScreenConfiguration(config); diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java index d0ee63435280..df7c9a44dd12 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java +++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java @@ -177,7 +177,9 @@ public class SystemServicesTestRule implements TestRule { final Display display = mWindowManagerService.mDisplayManager.getDisplay(DEFAULT_DISPLAY); // Display creation is driven by the ActivityManagerService via // ActivityStackSupervisor. We emulate those steps here. - mWindowManagerService.mRoot.createDisplayContent(display, mock(ActivityDisplay.class)); + DisplayContent displayContent = mWindowManagerService.mRoot + .createDisplayContent(display, mock(ActivityDisplay.class)); + displayContent.reconfigureDisplayLocked(); mMockTracker.stopTracking(); } diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java index 0ecd87841574..a0302f69029b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java @@ -16,11 +16,13 @@ package com.android.server.wm; +import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME; +import static android.content.pm.ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; @@ -29,6 +31,9 @@ import static android.view.Surface.ROTATION_0; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static com.android.server.wm.WindowContainer.POSITION_TOP; import static org.hamcrest.Matchers.not; @@ -36,6 +41,7 @@ import static org.hamcrest.Matchers.sameInstance; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; @@ -51,6 +57,7 @@ import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.res.Configuration; import android.graphics.Rect; +import android.os.IBinder; import android.platform.test.annotations.Presubmit; import android.service.voice.IVoiceInteractionSession; import android.util.DisplayMetrics; @@ -443,6 +450,342 @@ public class TaskRecordTests extends ActivityTestsBase { task.isSameIntentFilter(defaultActivity)); } + /** Test that root activity index is reported correctly for several activities in the task. */ + @Test + public void testFindRootIndex() { + final TaskRecord task = getTestTask(); + // Add an extra activity on top of the root one + new ActivityBuilder(mService).setTask(task).build(); + + assertEquals("The root activity in the task must be reported.", + 0, task.findRootIndex(false /* effectiveRoot*/)); + } + + /** + * Test that root activity index is reported correctly for several activities in the task when + * the activities on the bottom are finishing. + */ + @Test + public void testFindRootIndex_finishing() { + final TaskRecord task = getTestTask(); + // Add extra two activities and mark the two on the bottom as finishing. + final ActivityRecord activity0 = task.getChildAt(0); + activity0.finishing = true; + final ActivityRecord activity1 = new ActivityBuilder(mService).setTask(task).build(); + activity1.finishing = true; + new ActivityBuilder(mService).setTask(task).build(); + + assertEquals("The first non-finishing activity in the task must be reported.", + 2, task.findRootIndex(false /* effectiveRoot*/)); + } + + /** + * Test that root activity index is reported correctly for several activities in the task when + * looking for the 'effective root'. + */ + @Test + public void testFindRootIndex_effectiveRoot() { + final TaskRecord task = getTestTask(); + // Add an extra activity on top of the root one + new ActivityBuilder(mService).setTask(task).build(); + + assertEquals("The root activity in the task must be reported.", + 0, task.findRootIndex(true /* effectiveRoot*/)); + } + + /** + * Test that root activity index is reported correctly when looking for the 'effective root' in + * case when bottom activities are relinquishing task identity or finishing. + */ + @Test + public void testFindRootIndex_effectiveRoot_finishingAndRelinquishing() { + final TaskRecord task = getTestTask(); + // Add extra two activities. Mark the one on the bottom with "relinquishTaskIdentity" and + // one above as finishing. + final ActivityRecord activity0 = task.getChildAt(0); + activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY; + final ActivityRecord activity1 = new ActivityBuilder(mService).setTask(task).build(); + activity1.finishing = true; + new ActivityBuilder(mService).setTask(task).build(); + + assertEquals("The first non-finishing activity and non-relinquishing task identity " + + "must be reported.", 2, task.findRootIndex(true /* effectiveRoot*/)); + } + + /** + * Test that root activity index is reported correctly when looking for the 'effective root' + * for the case when there is only a single activity that also has relinquishTaskIdentity set. + */ + @Test + public void testFindRootIndex_effectiveRoot_relinquishingAndSingleActivity() { + final TaskRecord task = getTestTask(); + // Set relinquishTaskIdentity for the only activity in the task + task.getChildAt(0).info.flags |= FLAG_RELINQUISH_TASK_IDENTITY; + + assertEquals("The root activity in the task must be reported.", + 0, task.findRootIndex(true /* effectiveRoot*/)); + } + + /** + * Test that the topmost activity index is reported correctly when looking for the + * 'effective root' for the case when all activities have relinquishTaskIdentity set. + */ + @Test + public void testFindRootIndex_effectiveRoot_relinquishingMultipleActivities() { + final TaskRecord task = getTestTask(); + // Set relinquishTaskIdentity for all activities in the task + final ActivityRecord activity0 = task.getChildAt(0); + activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY; + final ActivityRecord activity1 = new ActivityBuilder(mService).setTask(task).build(); + activity1.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY; + + assertEquals("The topmost activity in the task must be reported.", + task.getChildCount() - 1, task.findRootIndex(true /* effectiveRoot*/)); + } + + /** Test that bottom-most activity is reported in {@link TaskRecord#getRootActivity()}. */ + @Test + public void testGetRootActivity() { + final TaskRecord task = getTestTask(); + // Add an extra activity on top of the root one + new ActivityBuilder(mService).setTask(task).build(); + + assertEquals("The root activity in the task must be reported.", + task.getChildAt(0), task.getRootActivity()); + } + + /** + * Test that first non-finishing activity is reported in {@link TaskRecord#getRootActivity()}. + */ + @Test + public void testGetRootActivity_finishing() { + final TaskRecord task = getTestTask(); + // Add an extra activity on top of the root one + new ActivityBuilder(mService).setTask(task).build(); + // Mark the root as finishing + task.getChildAt(0).finishing = true; + + assertEquals("The first non-finishing activity in the task must be reported.", + task.getChildAt(1), task.getRootActivity()); + } + + /** + * Test that relinquishTaskIdentity flag is ignored in {@link TaskRecord#getRootActivity()}. + */ + @Test + public void testGetRootActivity_relinquishTaskIdentity() { + final TaskRecord task = getTestTask(); + // Mark the bottom-most activity with FLAG_RELINQUISH_TASK_IDENTITY. + final ActivityRecord activity0 = task.getChildAt(0); + activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY; + // Add an extra activity on top of the root one. + new ActivityBuilder(mService).setTask(task).build(); + + assertEquals("The root activity in the task must be reported.", + task.getChildAt(0), task.getRootActivity()); + } + + /** + * Test that no activity is reported in {@link TaskRecord#getRootActivity()} when all activities + * in the task are finishing. + */ + @Test + public void testGetRootActivity_allFinishing() { + final TaskRecord task = getTestTask(); + // Mark the bottom-most activity as finishing. + final ActivityRecord activity0 = task.getChildAt(0); + activity0.finishing = true; + // Add an extra activity on top of the root one and mark it as finishing + final ActivityRecord activity1 = new ActivityBuilder(mService).setTask(task).build(); + activity1.finishing = true; + + assertNull("No activity must be reported if all are finishing", task.getRootActivity()); + } + + /** + * Test that first non-finishing activity is the root of task. + */ + @Test + public void testIsRootActivity() { + final TaskRecord task = getTestTask(); + // Mark the bottom-most activity as finishing. + final ActivityRecord activity0 = task.getChildAt(0); + activity0.finishing = true; + // Add an extra activity on top of the root one. + final ActivityRecord activity1 = new ActivityBuilder(mService).setTask(task).build(); + + assertFalse("Finishing activity must not be the root of task", activity0.isRootOfTask()); + assertTrue("Non-finishing activity must be the root of task", activity1.isRootOfTask()); + } + + /** + * Test that if all activities in the task are finishing, then the one on the bottom is the + * root of task. + */ + @Test + public void testIsRootActivity_allFinishing() { + final TaskRecord task = getTestTask(); + // Mark the bottom-most activity as finishing. + final ActivityRecord activity0 = task.getChildAt(0); + activity0.finishing = true; + // Add an extra activity on top of the root one and mark it as finishing + final ActivityRecord activity1 = new ActivityBuilder(mService).setTask(task).build(); + activity1.finishing = true; + + assertTrue("Bottom activity must be the root of task", activity0.isRootOfTask()); + assertFalse("Finishing activity on top must not be the root of task", + activity1.isRootOfTask()); + } + + /** + * Test {@link ActivityRecord#getTaskForActivityLocked(IBinder, boolean)}. + */ + @Test + public void testGetTaskForActivity() { + final TaskRecord task0 = getTestTask(); + final ActivityRecord activity0 = task0.getChildAt(0); + + final TaskRecord task1 = getTestTask(); + final ActivityRecord activity1 = task0.getChildAt(0); + + assertEquals(task0.taskId, + ActivityRecord.getTaskForActivityLocked(activity0.appToken, false /* onlyRoot */)); + assertEquals(task1.taskId, + ActivityRecord.getTaskForActivityLocked(activity1.appToken, false /* onlyRoot */)); + } + + /** + * Test {@link ActivityRecord#getTaskForActivityLocked(IBinder, boolean)} with finishing + * activity. + */ + @Test + public void testGetTaskForActivity_onlyRoot_finishing() { + final TaskRecord task = getTestTask(); + // Make the current root activity finishing + final ActivityRecord activity0 = task.getChildAt(0); + activity0.finishing = true; + // Add an extra activity on top - this will be the new root + final ActivityRecord activity1 = new ActivityBuilder(mService).setTask(task).build(); + // Add one more on top + final ActivityRecord activity2 = new ActivityBuilder(mService).setTask(task).build(); + + assertEquals(task.taskId, + ActivityRecord.getTaskForActivityLocked(activity0.appToken, true /* onlyRoot */)); + assertEquals(task.taskId, + ActivityRecord.getTaskForActivityLocked(activity1.appToken, true /* onlyRoot */)); + assertEquals("No task must be reported for activity that is above root", INVALID_TASK_ID, + ActivityRecord.getTaskForActivityLocked(activity2.appToken, true /* onlyRoot */)); + } + + /** + * Test {@link ActivityRecord#getTaskForActivityLocked(IBinder, boolean)} with activity that + * relinquishes task identity. + */ + @Test + public void testGetTaskForActivity_onlyRoot_relinquishTaskIdentity() { + final TaskRecord task = getTestTask(); + // Make the current root activity relinquish task identity + final ActivityRecord activity0 = task.getChildAt(0); + activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY; + // Add an extra activity on top - this will be the new root + final ActivityRecord activity1 = new ActivityBuilder(mService).setTask(task).build(); + // Add one more on top + final ActivityRecord activity2 = new ActivityBuilder(mService).setTask(task).build(); + + assertEquals(task.taskId, + ActivityRecord.getTaskForActivityLocked(activity0.appToken, true /* onlyRoot */)); + assertEquals(task.taskId, + ActivityRecord.getTaskForActivityLocked(activity1.appToken, true /* onlyRoot */)); + assertEquals("No task must be reported for activity that is above root", INVALID_TASK_ID, + ActivityRecord.getTaskForActivityLocked(activity2.appToken, true /* onlyRoot */)); + } + + /** + * Test {@link ActivityRecord#getTaskForActivityLocked(IBinder, boolean)} allowing non-root + * entries. + */ + @Test + public void testGetTaskForActivity_notOnlyRoot() { + final TaskRecord task = getTestTask(); + // Mark the bottom-most activity as finishing. + final ActivityRecord activity0 = task.getChildAt(0); + activity0.finishing = true; + + // Add an extra activity on top of the root one and make it relinquish task identity + final ActivityRecord activity1 = new ActivityBuilder(mService).setTask(task).build(); + activity1.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY; + + // Add one more activity on top + final ActivityRecord activity2 = new ActivityBuilder(mService).setTask(task).build(); + + assertEquals(task.taskId, + ActivityRecord.getTaskForActivityLocked(activity0.appToken, false /* onlyRoot */)); + assertEquals(task.taskId, + ActivityRecord.getTaskForActivityLocked(activity1.appToken, false /* onlyRoot */)); + assertEquals(task.taskId, + ActivityRecord.getTaskForActivityLocked(activity2.appToken, false /* onlyRoot */)); + } + + /** + * Test {@link TaskRecord#updateEffectiveIntent()}. + */ + @Test + public void testUpdateEffectiveIntent() { + // Test simple case with a single activity. + final TaskRecord task = getTestTask(); + final ActivityRecord activity0 = task.getChildAt(0); + + spyOn(task); + task.updateEffectiveIntent(); + verify(task).setIntent(eq(activity0)); + } + + /** + * Test {@link TaskRecord#updateEffectiveIntent()} with root activity marked as finishing. This + * should make the task use the second activity when updating the intent. + */ + @Test + public void testUpdateEffectiveIntent_rootFinishing() { + // Test simple case with a single activity. + final TaskRecord task = getTestTask(); + final ActivityRecord activity0 = task.getChildAt(0); + // Mark the bottom-most activity as finishing. + activity0.finishing = true; + // Add an extra activity on top of the root one + final ActivityRecord activity1 = new ActivityBuilder(mService).setTask(task).build(); + + spyOn(task); + task.updateEffectiveIntent(); + verify(task).setIntent(eq(activity1)); + } + + /** + * Test {@link TaskRecord#updateEffectiveIntent()} when all activities are finishing or + * relinquishing task identity. In this case the root activity should still be used when + * updating the intent (legacy behavior). + */ + @Test + public void testUpdateEffectiveIntent_allFinishing() { + // Test simple case with a single activity. + final TaskRecord task = getTestTask(); + final ActivityRecord activity0 = task.getChildAt(0); + // Mark the bottom-most activity as finishing. + activity0.finishing = true; + // Add an extra activity on top of the root one and make it relinquish task identity + final ActivityRecord activity1 = new ActivityBuilder(mService).setTask(task).build(); + activity1.finishing = true; + + // Task must still update the intent using the root activity (preserving legacy behavior). + spyOn(task); + task.updateEffectiveIntent(); + verify(task).setIntent(eq(activity0)); + } + + private TaskRecord getTestTask() { + final ActivityStack stack = new StackBuilder(mRootActivityContainer).build(); + return stack.getChildAt(0); + } + private void testStackBoundsConfiguration(int windowingMode, Rect parentBounds, Rect bounds, Rect expectedConfigBounds) { diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java index f958867274cd..b29453a4fa94 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java @@ -64,6 +64,7 @@ public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBa assertTrueForFiles(files, File::exists, " must exist"); final TaskSnapshot snapshot = mLoader.loadTask(1, mTestUserId, false /* reduced */); assertNotNull(snapshot); + assertEquals(MOCK_SNAPSHOT_ID, snapshot.getId()); assertEquals(TEST_INSETS, snapshot.getContentInsets()); assertNotNull(snapshot.getSnapshot()); assertEquals(Configuration.ORIENTATION_PORTRAIT, snapshot.getOrientation()); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java index e004cd3fb0c4..f7496229f66b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterTestBase.java @@ -45,6 +45,7 @@ class TaskSnapshotPersisterTestBase extends WindowTestsBase { private static final Rect TEST_INSETS = new Rect(10, 20, 30, 40); static final File FILES_DIR = getInstrumentation().getTargetContext().getFilesDir(); + static final long MOCK_SNAPSHOT_ID = 12345678; TaskSnapshotPersister mPersister; TaskSnapshotLoader mLoader; @@ -129,7 +130,7 @@ class TaskSnapshotPersisterTestBase extends WindowTestsBase { Canvas c = buffer.lockCanvas(); c.drawColor(Color.RED); buffer.unlockCanvasAndPost(c); - return new TaskSnapshot(new ComponentName("", ""), buffer, + return new TaskSnapshot(MOCK_SNAPSHOT_ID, new ComponentName("", ""), buffer, ColorSpace.get(ColorSpace.Named.SRGB), ORIENTATION_PORTRAIT, TEST_INSETS, mReducedResolution, mScale, mIsRealSnapshot, mWindowingMode, mSystemUiVisibility, mIsTranslucent); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java index 4ca01eca238b..74db8202614d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java @@ -64,7 +64,9 @@ public class TaskSnapshotSurfaceTest extends WindowTestsBase { int windowFlags, Rect taskBounds) { final GraphicBuffer buffer = GraphicBuffer.create(width, height, PixelFormat.RGBA_8888, GraphicBuffer.USAGE_SW_READ_RARELY | GraphicBuffer.USAGE_SW_WRITE_NEVER); - final TaskSnapshot snapshot = new TaskSnapshot(new ComponentName("", ""), buffer, + final TaskSnapshot snapshot = new TaskSnapshot( + System.currentTimeMillis(), + new ComponentName("", ""), buffer, ColorSpace.get(ColorSpace.Named.SRGB), ORIENTATION_PORTRAIT, contentInsets, false, 1.0f, true /* isRealSnapshot */, WINDOWING_MODE_FULLSCREEN, 0 /* systemUiVisibility */, false /* isTranslucent */); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java index 1b57c7924ecb..36698eae274c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -302,43 +302,38 @@ public class WindowStateTests extends WindowTestsBase { @Test public void testPrepareWindowToDisplayDuringRelayout() { - testPrepareWindowToDisplayDuringRelayout(false /*wasVisible*/); - testPrepareWindowToDisplayDuringRelayout(true /*wasVisible*/); - - // Call prepareWindowToDisplayDuringRelayout for a window without FLAG_TURN_SCREEN_ON - // before calling prepareWindowToDisplayDuringRelayout for windows with flag in the same - // appWindowToken. + // Call prepareWindowToDisplayDuringRelayout for a window without FLAG_TURN_SCREEN_ON before + // calling setCurrentLaunchCanTurnScreenOn for windows with flag in the same appWindowToken. final AppWindowToken appWindowToken = createAppWindowToken(mDisplayContent, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); final WindowState first = createWindow(null, TYPE_APPLICATION, appWindowToken, "first"); final WindowState second = createWindow(null, TYPE_APPLICATION, appWindowToken, "second"); second.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON; - reset(sPowerManagerWrapper); - first.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/); - verify(sPowerManagerWrapper, never()).wakeUp(anyLong(), anyInt(), anyString()); - assertTrue(appWindowToken.canTurnScreenOn()); - - reset(sPowerManagerWrapper); - second.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/); - verify(sPowerManagerWrapper).wakeUp(anyLong(), anyInt(), anyString()); - assertFalse(appWindowToken.canTurnScreenOn()); + testPrepareWindowToDisplayDuringRelayout(first, false /* expectedWakeupCalled */, + true /* expectedCurrentLaunchCanTurnScreenOn */); + testPrepareWindowToDisplayDuringRelayout(second, true /* expectedWakeupCalled */, + false /* expectedCurrentLaunchCanTurnScreenOn */); // Call prepareWindowToDisplayDuringRelayout for two window that have FLAG_TURN_SCREEN_ON // from the same appWindowToken. Only one should trigger the wakeup. - appWindowToken.setCanTurnScreenOn(true); + appWindowToken.setCurrentLaunchCanTurnScreenOn(true); first.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON; second.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON; - reset(sPowerManagerWrapper); - first.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/); - verify(sPowerManagerWrapper).wakeUp(anyLong(), anyInt(), anyString()); - assertFalse(appWindowToken.canTurnScreenOn()); + testPrepareWindowToDisplayDuringRelayout(first, true /* expectedWakeupCalled */, + false /* expectedCurrentLaunchCanTurnScreenOn */); + testPrepareWindowToDisplayDuringRelayout(second, false /* expectedWakeupCalled */, + false /* expectedCurrentLaunchCanTurnScreenOn */); - reset(sPowerManagerWrapper); - second.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/); - verify(sPowerManagerWrapper, never()).wakeUp(anyLong(), anyInt(), anyString()); - assertFalse(appWindowToken.canTurnScreenOn()); + // Without window flags, the state of ActivityRecord.canTurnScreenOn should still be able to + // turn on the screen. + appWindowToken.setCurrentLaunchCanTurnScreenOn(true); + first.mAttrs.flags &= ~WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON; + doReturn(true).when(appWindowToken.mActivityRecord).canTurnScreenOn(); + + testPrepareWindowToDisplayDuringRelayout(first, true /* expectedWakeupCalled */, + false /* expectedCurrentLaunchCanTurnScreenOn */); // Call prepareWindowToDisplayDuringRelayout for a windows that are not children of an // appWindowToken. Both windows have the FLAG_TURNS_SCREEN_ON so both should call wakeup @@ -360,6 +355,22 @@ public class WindowStateTests extends WindowTestsBase { verify(sPowerManagerWrapper).wakeUp(anyLong(), anyInt(), anyString()); } + private void testPrepareWindowToDisplayDuringRelayout(WindowState appWindow, + boolean expectedWakeupCalled, boolean expectedCurrentLaunchCanTurnScreenOn) { + reset(sPowerManagerWrapper); + appWindow.prepareWindowToDisplayDuringRelayout(false /* wasVisible */); + + if (expectedWakeupCalled) { + verify(sPowerManagerWrapper).wakeUp(anyLong(), anyInt(), anyString()); + } else { + verify(sPowerManagerWrapper, never()).wakeUp(anyLong(), anyInt(), anyString()); + } + // If wakeup is expected to be called, the currentLaunchCanTurnScreenOn should be false + // because the state will be consumed. + assertThat(appWindow.mAppToken.currentLaunchCanTurnScreenOn(), + is(expectedCurrentLaunchCanTurnScreenOn)); + } + @Test public void testCanAffectSystemUiFlags() { final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); @@ -487,15 +498,6 @@ public class WindowStateTests extends WindowTestsBase { assertThat(app.getWmDisplayCutout().getDisplayCutout(), is(cutout.inset(7, 10, 5, 20))); } - private void testPrepareWindowToDisplayDuringRelayout(boolean wasVisible) { - reset(sPowerManagerWrapper); - final WindowState root = createWindow(null, TYPE_APPLICATION, "root"); - root.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON; - - root.prepareWindowToDisplayDuringRelayout(wasVisible /*wasVisible*/); - verify(sPowerManagerWrapper).wakeUp(anyLong(), anyInt(), anyString()); - } - @Test public void testVisibilityChangeSwitchUser() { final WindowState window = createWindow(null, TYPE_APPLICATION, "app"); 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 3a702cb9521c..dc461d149cee 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -435,7 +435,9 @@ class WindowTestsBase { // Display creation is driven by DisplayWindowController via ActivityStackSupervisor. // We skip those steps here. final ActivityDisplay mockAd = mock(ActivityDisplay.class); - return mWm.mRoot.createDisplayContent(display, mockAd); + final DisplayContent displayContent = mWm.mRoot.createDisplayContent(display, mockAd); + displayContent.reconfigureDisplayLocked(); + return displayContent; } } diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index e1ffb0f179f8..b2fde548e506 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -33,7 +33,6 @@ import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; -import android.content.pm.PackageManagerInternal; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.pm.ShortcutServiceInternal; @@ -79,6 +78,7 @@ import com.android.server.FgThread; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.UiThread; +import com.android.server.pm.permission.PermissionManagerServiceInternal; import com.android.server.soundtrigger.SoundTriggerInternal; import com.android.server.wm.ActivityTaskManagerInternal; @@ -120,10 +120,10 @@ public class VoiceInteractionManagerService extends SystemService { mUserManager = Preconditions.checkNotNull( context.getSystemService(UserManager.class)); - PackageManagerInternal packageManagerInternal = LocalServices.getService( - PackageManagerInternal.class); - packageManagerInternal.setVoiceInteractionPackagesProvider( - new PackageManagerInternal.PackagesProvider() { + PermissionManagerServiceInternal permissionManagerInternal = LocalServices.getService( + PermissionManagerServiceInternal.class); + permissionManagerInternal.setVoiceInteractionPackagesProvider( + new PermissionManagerServiceInternal.PackagesProvider() { @Override public String[] getPackages(int userId) { mServiceStub.initForUser(userId); diff --git a/startop/scripts/app_startup/run_app_with_prefetch.py b/startop/scripts/app_startup/run_app_with_prefetch.py index 8a9135bb3dea..464742d16d13 100644 --- a/startop/scripts/app_startup/run_app_with_prefetch.py +++ b/startop/scripts/app_startup/run_app_with_prefetch.py @@ -101,7 +101,7 @@ def parse_options(argv: List[str] = None): return parser.parse_args(argv) -def validate_options(args: argparse.Namespace) -> Tuple[bool, RunCommandArgs]: +def validate_options(args: RunCommandArgs) -> Tuple[bool, RunCommandArgs]: """Validates the activity and trace file if needed. Returns: diff --git a/startop/scripts/app_startup/run_app_with_prefetch_test.py b/startop/scripts/app_startup/run_app_with_prefetch_test.py index a642385b37d7..8536ce5f917e 100644 --- a/startop/scripts/app_startup/run_app_with_prefetch_test.py +++ b/startop/scripts/app_startup/run_app_with_prefetch_test.py @@ -156,7 +156,8 @@ def test_main(): args = '--package com.fake.package --activity act -s' opts = run.parse_options(shlex.split(args)) - result = run.run_test(opts) + args = run.get_args_from_opts(opts) + result = run.run_test(args) assert result == [('TotalTime', '123')] def test_set_up_adb_env(): diff --git a/startop/scripts/iorap/compiler.py b/startop/scripts/iorap/compiler.py index 79149601acb0..1e15736b13a1 100755 --- a/startop/scripts/iorap/compiler.py +++ b/startop/scripts/iorap/compiler.py @@ -23,20 +23,18 @@ # $> pip3 install --user protobuf sqlalchemy sqlite3 # -import collections import optparse import os import re import sys +from typing import Iterable, Optional -from typing import Iterable - -from lib.inode2filename import Inode2Filename from generated.TraceFile_pb2 import * +from lib.inode2filename import Inode2Filename parent_dir_name = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) sys.path.append(parent_dir_name + "/trace_analyzer") -from lib.trace2db import Trace2Db, MmFilemapAddToPageCache +from lib.trace2db import Trace2Db, MmFilemapAddToPageCache, RawFtraceEntry _PAGE_SIZE = 4096 # adb shell getconf PAGESIZE ## size of a memory page in bytes. @@ -165,9 +163,32 @@ def build_protobuf(page_runs, inode2filename, filters=[]): return trace_file -def query_add_to_page_cache(trace2db: Trace2Db): +def calc_trace_end_time(trace2db: Trace2Db, + trace_duration: Optional[int]) -> float: + """ + Calculates the end time based on the trace duration. + The start time is the first receiving mm file map event. + The end time is the start time plus the trace duration. + All of them are in milliseconds. + """ + # If the duration is not set, assume all time is acceptable. + if trace_duration is None: + # float('inf') + return RawFtraceEntry.__table__.c.timestamp.type.python_type('inf') + + first_event = trace2db.session.query(MmFilemapAddToPageCache).join( + MmFilemapAddToPageCache.raw_ftrace_entry).order_by( + RawFtraceEntry.timestamp).first() + + return first_event.raw_ftrace_entry.timestamp + trace_duration + +def query_add_to_page_cache(trace2db: Trace2Db, trace_duration: Optional[int]): + end_time = calc_trace_end_time(trace2db, trace_duration) # SELECT * FROM tbl ORDER BY id; - return trace2db.session.query(MmFilemapAddToPageCache).order_by(MmFilemapAddToPageCache.id).all() + return trace2db.session.query(MmFilemapAddToPageCache).join( + MmFilemapAddToPageCache.raw_ftrace_entry).filter( + RawFtraceEntry.timestamp <= end_time).order_by( + MmFilemapAddToPageCache.id).all() def main(argv): parser = optparse.OptionParser(usage="Usage: %prog [options]", description="Compile systrace file into TraceFile.pb") @@ -188,6 +209,9 @@ def main(argv): parser.add_option('-o', dest='output_file', metavar='FILE', help='Output protobuf file') + parser.add_option('--duration', dest='trace_duration', action="store", + type=int, help='The duration of trace in milliseconds.') + options, categories = parser.parse_args(argv[1:]) # TODO: OptionParser should have some flags to make these mandatory. @@ -217,7 +241,8 @@ def main(argv): # TODO: parse multiple trace files here. parse_count = trace2db.parse_file_into_db(options.trace_file) - mm_filemap_add_to_page_cache_rows = query_add_to_page_cache(trace2db) + mm_filemap_add_to_page_cache_rows = query_add_to_page_cache(trace2db, + options.trace_duration) print("DONE. Parsed %d entries into sql db." %(len(mm_filemap_add_to_page_cache_rows))) page_runs = page_cache_entries_to_runs(mm_filemap_add_to_page_cache_rows) @@ -241,4 +266,6 @@ def main(argv): return 0 -sys.exit(main(sys.argv)) +if __name__ == '__main__': + print(sys.argv) + sys.exit(main(sys.argv)) diff --git a/startop/scripts/iorap/compiler_test.py b/startop/scripts/iorap/compiler_test.py new file mode 100644 index 000000000000..fcb7269ed8d2 --- /dev/null +++ b/startop/scripts/iorap/compiler_test.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 +# +# Copyright 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. +# + +""" +Unit tests for the compiler.py script. + +Install: + $> sudo apt-get install python3-pytest ## OR + $> pip install -U pytest +See also https://docs.pytest.org/en/latest/getting-started.html + +Usage: + $> pytest compiler_test.py + +See also https://docs.pytest.org/en/latest/usage.html +""" +import os + +import compiler + +DIR = os.path.abspath(os.path.dirname(__file__)) +TEXTCACHE = os.path.join(DIR, 'test_fixtures/compiler/common_textcache') +SYSTRACE = os.path.join(DIR, 'test_fixtures/compiler/common_systrace') +ARGV = [os.path.join(DIR, 'compiler.py'), '-i', TEXTCACHE, '-t', SYSTRACE] + +def assert_compile_result(output, expected, *extra_argv): + argv = ARGV + ['-o', output] + [args for args in extra_argv] + + compiler.main(argv) + + with open(output, 'rb') as f1, open(expected, 'rb') as f2: + assert f1.read() == f2.read() + +def test_compiler_main(tmpdir): + output = tmpdir.mkdir('compiler').join('output') + + # No duration + expected = os.path.join(DIR, + 'test_fixtures/compiler/test_result_without_duration.TraceFile.pb') + assert_compile_result(output, expected) + + # 10ms duration + expected = os.path.join(DIR, + 'test_fixtures/compiler/test_result_with_duration.TraceFile.pb') + assert_compile_result(output, expected, '--duration', '10') + + # 30ms duration + expected = os.path.join(DIR, + 'test_fixtures/compiler/test_result_without_duration.TraceFile.pb') + assert_compile_result(output, expected, '--duration', '30') diff --git a/startop/scripts/iorap/test_fixtures/compiler/common_systrace b/startop/scripts/iorap/test_fixtures/compiler/common_systrace new file mode 100644 index 000000000000..4573738db5c6 --- /dev/null +++ b/startop/scripts/iorap/test_fixtures/compiler/common_systrace @@ -0,0 +1,5 @@ +<...>-2965 (-----) [001] .... 10000.746629: mm_filemap_add_to_page_cache: dev 253:6 ino 1 page=00000000679ee1ec pfn=1299913 ofs=192512 +<...>-2965 (-----) [001] .... 10010.746664: mm_filemap_add_to_page_cache: dev 253:6 ino 2 page=0000000006cd2fb7 pfn=1296251 ofs=196608 +<...>-2965 (-----) [001] .... 10020.746677: mm_filemap_add_to_page_cache: dev 253:6 ino 3 page=00000000af82f3d6 pfn=1419330 ofs=200704 +<...>-2965 (-----) [001] .... 10030.746693: mm_filemap_add_to_page_cache: dev 253:6 ino 4 page=000000002840f054 pfn=1304928 ofs=204800 +<...>-2965 (-----) [001] .... 10040.746706: mm_filemap_add_to_page_cache: dev 253:6 ino 5 page=000000004a59da17 pfn=1288069 ofs=208896 diff --git a/startop/scripts/iorap/test_fixtures/compiler/common_textcache b/startop/scripts/iorap/test_fixtures/compiler/common_textcache new file mode 100644 index 000000000000..da03004ec6fb --- /dev/null +++ b/startop/scripts/iorap/test_fixtures/compiler/common_textcache @@ -0,0 +1,2 @@ +64774 1 -1 /system/test1 +64774 3 -1 /data/test2 diff --git a/startop/scripts/iorap/test_fixtures/compiler/test_result_with_duration.TraceFile.pb b/startop/scripts/iorap/test_fixtures/compiler/test_result_with_duration.TraceFile.pb Binary files differnew file mode 100644 index 000000000000..ab3df45f8c54 --- /dev/null +++ b/startop/scripts/iorap/test_fixtures/compiler/test_result_with_duration.TraceFile.pb diff --git a/startop/scripts/iorap/test_fixtures/compiler/test_result_without_duration.TraceFile.pb b/startop/scripts/iorap/test_fixtures/compiler/test_result_without_duration.TraceFile.pb Binary files differnew file mode 100644 index 000000000000..17cb11662172 --- /dev/null +++ b/startop/scripts/iorap/test_fixtures/compiler/test_result_without_duration.TraceFile.pb diff --git a/startop/scripts/trace_analyzer/lib/trace2db.py b/startop/scripts/trace_analyzer/lib/trace2db.py index f60d6ab5245b..42a33aff046d 100644 --- a/startop/scripts/trace_analyzer/lib/trace2db.py +++ b/startop/scripts/trace_analyzer/lib/trace2db.py @@ -19,6 +19,7 @@ import sys from sqlalchemy import create_engine from sqlalchemy import Column, Date, Integer, Float, String, ForeignKey from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import relationship from sqlalchemy.orm import sessionmaker @@ -43,6 +44,10 @@ class RawFtraceEntry(Base): function = Column(String, nullable=False) function_args = Column(String, nullable=False) + # 1:1 relation with MmFilemapAddToPageCache. + mm_filemap_add_to_page_cache = relationship("MmFilemapAddToPageCache", + back_populates="raw_ftrace_entry") + @staticmethod def parse_dict(line): # ' <...>-5521 (-----) [003] ...1 17148.446877: tracing_mark_write: trace_event_clock_sync: parent_ts=17148.447266' @@ -155,6 +160,9 @@ class MmFilemapAddToPageCache(Base): pfn = Column(Integer, nullable=False) ofs = Column(Integer, nullable=False) + # 1:1 relation with RawFtraceEntry. + raw_ftrace_entry = relationship("RawFtraceEntry", uselist=False) + @staticmethod def parse_dict(function_args, id = None): # dev 253:6 ino b2c7 page=00000000ec787cd9 pfn=1478539 ofs=4096 @@ -251,6 +259,8 @@ def parse_file(filename: str, *args, **kwargs) -> int: def parse_file_buf(filebuf, session, engine, raw_ftrace_entry_filter, limit=None) -> int: global _FLUSH_LIMIT count = 0 + # count and id are not equal, because count still increases for invalid lines. + id = 0 pending_entries = [] pending_sched_switch = [] @@ -305,9 +315,10 @@ def parse_file_buf(filebuf, session, engine, raw_ftrace_entry_filter, limit=None continue pending_entries.append(raw_ftrace_entry) + id = id + 1 if raw_ftrace_entry['function'] == 'sched_switch': - sched_switch = SchedSwitch.parse_dict(raw_ftrace_entry['function_args'], count) + sched_switch = SchedSwitch.parse_dict(raw_ftrace_entry['function_args'], id) if not sched_switch: print("WARNING: Failed to parse sched_switch: " + l) @@ -315,7 +326,7 @@ def parse_file_buf(filebuf, session, engine, raw_ftrace_entry_filter, limit=None pending_sched_switch.append(sched_switch) elif raw_ftrace_entry['function'] == 'sched_blocked_reason': - sbr = SchedBlockedReason.parse_dict(raw_ftrace_entry['function_args'], count) + sbr = SchedBlockedReason.parse_dict(raw_ftrace_entry['function_args'], id) if not sbr: print("WARNING: Failed to parse sched_blocked_reason: " + l) @@ -323,7 +334,8 @@ def parse_file_buf(filebuf, session, engine, raw_ftrace_entry_filter, limit=None pending_sched_blocked_reasons.append(sbr) elif raw_ftrace_entry['function'] == 'mm_filemap_add_to_page_cache': - d = MmFilemapAddToPageCache.parse_dict(raw_ftrace_entry['function_args'], count) + d = MmFilemapAddToPageCache.parse_dict(raw_ftrace_entry['function_args'], + id) if not d: print("WARNING: Failed to parse mm_filemap_add_to_page_cache: " + l) else: diff --git a/startop/scripts/trace_analyzer/lib/trace2db_test.py b/startop/scripts/trace_analyzer/lib/trace2db_test.py index b67cffa51cde..3b326f000a7d 100755 --- a/startop/scripts/trace_analyzer/lib/trace2db_test.py +++ b/startop/scripts/trace_analyzer/lib/trace2db_test.py @@ -32,17 +32,10 @@ See also https://docs.pytest.org/en/latest/usage.html """ # global imports -from contextlib import contextmanager import io -import shlex -import sys -import typing - from copy import deepcopy # pip imports -import pytest - # local imports from trace2db import * @@ -197,6 +190,33 @@ NonUserFacing6-5246 ( 1322) [005] .... 16138.357581: mm_filemap_add_to_page_cac assert_eq_ignore_id(MmFilemapAddToPageCache(dev=64774, dev_major=253, dev_minor=6, ino=0x9a64, page=0x000000006e0f8322, pfn=797894, ofs=4096), second_to_last_row) +def test_timestamp_filter(): + test_contents = """ + MediaStoreImpor-27212 (27176) [000] .... 16136.595194: mm_filemap_add_to_page_cache: dev 253:6 ino 7580 page=0000000060e990c7 pfn=677646 ofs=159744 + NonUserFacing6-5246 ( 1322) [005] .... 16139.357581: mm_filemap_add_to_page_cache: dev 253:6 ino 9a64 page=000000006e0f8322 pfn=797894 ofs=4096 + MediaStoreImpor-27212 (27176) [000] .... 16136.604126: mm_filemap_add_to_page_cache: dev 253:6 ino b1d8 page=0000000098d4d2e2 pfn=829676 ofs=0 + """ + + t2d = parse_trace_file_to_db(test_contents) + session = t2d.session + + end_time = 16137.0 + + results = session.query(MmFilemapAddToPageCache).join( + MmFilemapAddToPageCache.raw_ftrace_entry).filter( + RawFtraceEntry.timestamp <= end_time).order_by( + MmFilemapAddToPageCache.id).all() + + assert len(results) == 2 + assert_eq_ignore_id( + MmFilemapAddToPageCache(dev=64774, dev_major=253, dev_minor=6, + ino=0x7580, page=0x0000000060e990c7, pfn=677646, + ofs=159744), results[0]) + assert_eq_ignore_id( + MmFilemapAddToPageCache(dev=64774, dev_major=253, dev_minor=6, + ino=0xb1d8, page=0x0000000098d4d2e2, pfn=829676, + ofs=0), results[1]) + if __name__ == '__main__': pytest.main() diff --git a/startop/scripts/trace_analyzer/test_fixtures/common_systrace b/startop/scripts/trace_analyzer/test_fixtures/common_systrace new file mode 100644 index 000000000000..802cb5562593 --- /dev/null +++ b/startop/scripts/trace_analyzer/test_fixtures/common_systrace @@ -0,0 +1,518 @@ +# tracer: nop +# +# entries-in-buffer/entries-written: 411983/411983 #P:8 +# +# _-----=> irqs-off +# / _----=> need-resched +# | / _---=> hardirq/softirq +# || / _--=> preempt-depth +# ||| / delay +# TASK-PID TGID CPU# |||| TIMESTAMP FUNCTION +# | | | | |||| | | + <...>-14603 (-----) [000] ...1 14592.893157: tracing_mark_write: trace_event_clock_sync: parent_ts=14592.892578 + <...>-14603 (-----) [000] ...1 14592.893172: tracing_mark_write: trace_event_clock_sync: realtime_ts=1557129597951 + <...>-18150 (-----) [004] d..2 14594.182110: sched_switch: prev_comm=kworker/u16:18 prev_pid=18150 prev_prio=120 prev_state=D ==> next_comm=kworker/u16:16 next_pid=23269 next_prio=120 + kworker/u16:16-23269 (23269) [004] d.h3 14594.182228: sched_blocked_reason: pid=18150 iowait=0 caller=a6xx_oob_set+0x194/0x3dc + kworker/u16:16-23269 (23269) [004] d..2 14594.182248: sched_switch: prev_comm=kworker/u16:16 prev_pid=23269 prev_prio=120 prev_state=D ==> next_comm=kworker/u16:18 next_pid=18150 next_prio=120 + <...>-18150 (-----) [004] d..2 14594.182312: sched_switch: prev_comm=kworker/u16:18 prev_pid=18150 prev_prio=120 prev_state=D ==> next_comm=swapper/4 next_pid=0 next_prio=120 + <...>-18150 (-----) [004] d..2 14594.182488: sched_switch: prev_comm=kworker/u16:18 prev_pid=18150 prev_prio=120 prev_state=D ==> next_comm=swapper/4 next_pid=0 next_prio=120 + kworker/u16:16-23269 (23269) [005] d..2 14594.182610: sched_switch: prev_comm=kworker/u16:16 prev_pid=23269 prev_prio=120 prev_state=S ==> next_comm=swapper/5 next_pid=0 next_prio=120 + <...>-18150 (-----) [004] d..2 14594.182626: sched_switch: prev_comm=kworker/u16:18 prev_pid=18150 prev_prio=120 prev_state=D ==> next_comm=swapper/4 next_pid=0 next_prio=120 + <...>-18150 (-----) [004] d..2 14594.182755: sched_switch: prev_comm=kworker/u16:18 prev_pid=18150 prev_prio=120 prev_state=D ==> next_comm=swapper/4 next_pid=0 next_prio=120 + <...>-18150 (-----) [004] d..2 14594.182975: sched_switch: prev_comm=kworker/u16:18 prev_pid=18150 prev_prio=120 prev_state=D ==> next_comm=swapper/4 next_pid=0 next_prio=120 + <...>-18150 (-----) [004] d..2 14594.183209: sched_switch: prev_comm=kworker/u16:18 prev_pid=18150 prev_prio=120 prev_state=D ==> next_comm=swapper/4 next_pid=0 next_prio=120 + <...>-18150 (-----) [004] d..2 14594.183371: sched_switch: prev_comm=kworker/u16:18 prev_pid=18150 prev_prio=120 prev_state=D ==> next_comm=swapper/4 next_pid=0 next_prio=120 + <...>-18150 (-----) [004] d..2 14594.184286: sched_switch: prev_comm=kworker/u16:18 prev_pid=18150 prev_prio=120 prev_state=S ==> next_comm=swapper/4 next_pid=0 next_prio=120 + kworker/u16:16-23269 (23269) [005] d..2 14594.184495: sched_switch: prev_comm=kworker/u16:16 prev_pid=23269 prev_prio=120 prev_state=S ==> next_comm=swapper/5 next_pid=0 next_prio=120 + <...>-18150 (-----) [004] d..2 14594.184498: sched_switch: prev_comm=kworker/u16:18 prev_pid=18150 prev_prio=120 prev_state=S ==> next_comm=swapper/4 next_pid=0 next_prio=120 + ksoftirqd/4-47 ( 47) [004] d..2 14594.185678: sched_switch: prev_comm=ksoftirqd/4 prev_pid=47 prev_prio=120 prev_state=S ==> next_comm=swapper/4 next_pid=0 next_prio=120 + kworker/6:2-10610 (10610) [006] d..2 14594.186012: sched_switch: prev_comm=kworker/6:2 prev_pid=10610 prev_prio=120 prev_state=S ==> next_comm=swapper/6 next_pid=0 next_prio=120 + <...>-656 (-----) [001] .... 14594.219464: binder_set_priority: proc=625 thread=656 old=110 => new=120 desired=120 + <...>-1803 (-----) [000] d..2 14594.219595: sched_switch: prev_comm=ndroid.systemui prev_pid=1803 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120 + <...>-3359 (-----) [001] ...1 14594.219856: tracing_mark_write: S|1368|launching: com.google.android.dialer|0 + <...>-3359 (-----) [001] ...1 14594.219863: tracing_mark_write: B|1368|MetricsLogger:launchObserverNotifyActivityLaunched + <...>-3359 (-----) [001] ...1 14594.219869: tracing_mark_write: B|1368|MetricsLogger:convertActivityRecordToProto + <...>-1398 (-----) [006] ...1 14594.220160: tracing_mark_write: B|1368|updateInputWindows + <...>-3359 (-----) [001] .... 14594.220230: binder_set_priority: proc=1368 thread=3359 old=110 => new=120 desired=120 + <...>-1398 (-----) [006] ...1 14594.220588: tracing_mark_write: B|1368|android.os.Handler: com.android.server.wm.AppWindowToken$1 + <...>-1398 (-----) [006] ...1 14594.220722: tracing_mark_write: B|1368|ResourcesManager#getResources + <...>-1052 (-----) [002] d..2 14594.220884: sched_switch: prev_comm=statsd.writer prev_pid=1052 prev_prio=120 prev_state=S ==> next_comm=UiThreadHelper next_pid=2045 next_prio=118 + <...>-1398 (-----) [006] ...1 14594.220926: tracing_mark_write: B|1368|Theme::ApplyStyle + <...>-1398 (-----) [006] ...1 14594.220929: tracing_mark_write: B|1368|AssetManager::GetBag + <...>-2007 (-----) [007] ...1 14594.220996: tracing_mark_write: B|2007|Choreographer#doFrame + <...>-2007 (-----) [007] ...1 14594.221005: tracing_mark_write: B|2007|animation + <...>-1398 (-----) [006] ...1 14594.221015: tracing_mark_write: B|1368|ResourcesManager#getResources + <...>-2045 (-----) [002] ...2 14594.221035: binder_set_priority: proc=1368 thread=1903 old=120 => new=118 desired=118 + <...>-2045 (-----) [002] d..2 14594.221065: sched_switch: prev_comm=UiThreadHelper prev_pid=2045 prev_prio=118 prev_state=S ==> next_comm=Binder:1368_4 next_pid=1903 next_prio=118 + <...>-1398 (-----) [006] ...1 14594.221080: tracing_mark_write: B|1368|AssetManager::SetApkAssets + <...>-2007 (-----) [007] ...1 14594.221110: tracing_mark_write: B|2007|traversal + <...>-656 (-----) [000] ...1 14594.221137: tracing_mark_write: B|625|requestNextVsync + <...>-656 (-----) [000] ...1 14594.221141: tracing_mark_write: B|625|resetIdleTimer + <...>-2007 (-----) [007] ...1 14594.221146: tracing_mark_write: B|2007|draw + <...>-2007 (-----) [007] ...1 14594.221160: tracing_mark_write: B|2007|Record View#draw() + <...>-660 (-----) [005] d..2 14594.221285: sched_switch: prev_comm=app prev_pid=660 prev_prio=97 prev_state=S ==> next_comm=RenderThread next_pid=2738 next_prio=110 + <...>-658 (-----) [004] d..2 14594.221327: sched_switch: prev_comm=DispSync prev_pid=658 prev_prio=97 prev_state=S ==> next_comm=android.display next_pid=1397 next_prio=117 + <...>-2738 (-----) [005] ...1 14594.221342: tracing_mark_write: B|2007|notifyFramePending + <...>-2738 (-----) [005] ...1 14594.221362: tracing_mark_write: B|2007|DrawFrame + <...>-2738 (-----) [005] ...1 14594.221369: tracing_mark_write: B|2007|query + <...>-2007 (-----) [007] d..2 14594.221369: sched_switch: prev_comm=s.nexuslauncher prev_pid=2007 prev_prio=110 prev_state=S ==> next_comm=swapper/7 next_pid=0 next_prio=120 + <...>-1903 (-----) [002] .... 14594.221397: binder_set_priority: proc=1368 thread=1903 old=118 => new=120 desired=120 + <...>-2738 (-----) [005] ...2 14594.221400: binder_set_priority: proc=625 thread=656 old=120 => new=110 desired=110 + <...>-2738 (-----) [005] d..2 14594.221430: sched_switch: prev_comm=RenderThread prev_pid=2738 prev_prio=110 prev_state=S ==> next_comm=Binder:625_1 next_pid=656 next_prio=110 + <...>-1368 (-----) [003] ...1 14594.221431: tracing_mark_write: B|1368|Lock contention on GC thread flip lock (owner tid: 0) + <...>-656 (-----) [005] ...1 14594.221460: tracing_mark_write: B|625|query + <...>-656 (-----) [005] .... 14594.221528: binder_set_priority: proc=625 thread=656 old=110 => new=120 desired=120 + <...>-2738 (-----) [007] ...1 14594.221552: tracing_mark_write: B|2007|query + <...>-2738 (-----) [007] ...2 14594.221563: binder_set_priority: proc=625 thread=656 old=120 => new=110 desired=110 + <...>-2738 (-----) [007] d..2 14594.221600: sched_switch: prev_comm=RenderThread prev_pid=2738 prev_prio=110 prev_state=S ==> next_comm=Binder:625_1 next_pid=656 next_prio=110 + <...>-1368 (-----) [003] d..2 14594.221623: sched_switch: prev_comm=system_server prev_pid=1368 prev_prio=118 prev_state=S ==> next_comm=swapper/3 next_pid=0 next_prio=120 + <...>-656 (-----) [007] ...1 14594.221628: tracing_mark_write: B|625|query + <...>-23031 (-----) [001] d..2 14594.221643: sched_switch: prev_comm=UiAutomation prev_pid=23031 prev_prio=120 prev_state=S ==> next_comm=swapper/1 next_pid=0 next_prio=120 + <...>-2738 (-----) [007] ...1 14594.221664: tracing_mark_write: B|2007|syncFrameState + <...>-2738 (-----) [007] ...1 14594.221697: tracing_mark_write: B|2007|prepareTree + <...>-23008 (-----) [005] d..2 14594.221706: sched_switch: prev_comm=hub.uiautomator prev_pid=23008 prev_prio=120 prev_state=S ==> next_comm=swapper/5 next_pid=0 next_prio=120 + <...>-656 (-----) [000] .... 14594.221737: binder_set_priority: proc=625 thread=656 old=110 => new=120 desired=120 + <...>-1803 (-----) [003] d..2 14594.221747: sched_switch: prev_comm=ndroid.systemui prev_pid=1803 prev_prio=120 prev_state=S ==> next_comm=swapper/3 next_pid=0 next_prio=120 + <...>-1397 (-----) [004] d..2 14594.221806: sched_switch: prev_comm=android.display prev_pid=1397 prev_prio=117 prev_state=S ==> next_comm=Binder:2007_A next_pid=4180 next_prio=120 + <...>-1398 (-----) [006] d..2 14594.221816: sched_switch: prev_comm=android.anim prev_pid=1398 prev_prio=110 prev_state=R ==> next_comm=s.nexuslauncher next_pid=2007 next_prio=110 + <...>-2738 (-----) [007] ...1 14594.221824: tracing_mark_write: B|2007|query + <...>-2738 (-----) [007] ...1 14594.221830: tracing_mark_write: B|2007|query + <...>-2738 (-----) [007] ...1 14594.221834: tracing_mark_write: B|2007|query + <...>-2738 (-----) [007] ...1 14594.221841: tracing_mark_write: B|2007|query + <...>-2738 (-----) [007] ...1 14594.221843: tracing_mark_write: B|2007|query + <...>-2738 (-----) [007] ...1 14594.221846: tracing_mark_write: B|2007|query + <...>-2738 (-----) [007] ...1 14594.221850: tracing_mark_write: B|2007|dequeueBuffer + <...>-2738 (-----) [007] ...2 14594.221864: binder_set_priority: proc=625 thread=656 old=120 => new=110 desired=110 + <...>-2738 (-----) [007] d..2 14594.221985: sched_switch: prev_comm=RenderThread prev_pid=2738 prev_prio=110 prev_state=R+ ==> next_comm=crtc_event:97 next_pid=303 next_prio=83 + <...>-2007 (-----) [006] ...1 14594.221989: tracing_mark_write: B|2007|topResumedActivityChangeItem + <...>-303 (-----) [007] d..2 14594.222016: sched_switch: prev_comm=crtc_event:97 prev_pid=303 prev_prio=83 prev_state=S ==> next_comm=rcu_preempt next_pid=7 next_prio=120 + rcu_preempt-7 ( 7) [007] d..2 14594.222035: sched_switch: prev_comm=rcu_preempt prev_pid=7 prev_prio=120 prev_state=S ==> next_comm=RenderThread next_pid=2738 next_prio=110 + migration/4-46 ( 46) [004] d..2 14594.222037: sched_switch: prev_comm=migration/4 prev_pid=46 prev_prio=0 prev_state=S ==> next_comm=Binder:625_1 next_pid=656 next_prio=110 + <...>-2738 (-----) [007] d..2 14594.222039: sched_switch: prev_comm=RenderThread prev_pid=2738 prev_prio=110 prev_state=S ==> next_comm=kworker/u16:18 next_pid=18150 next_prio=120 + <...>-656 (-----) [004] ...1 14594.222100: tracing_mark_write: B|625|dequeueBuffer + <...>-656 (-----) [004] ...1 14594.222114: tracing_mark_write: B|625|com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity#1: 2 + <...>-2007 (-----) [006] ...2 14594.222131: binder_set_priority: proc=1368 thread=1903 old=120 => new=110 desired=110 + <...>-2007 (-----) [006] d..2 14594.222143: sched_switch: prev_comm=s.nexuslauncher prev_pid=2007 prev_prio=110 prev_state=S ==> next_comm=UiThreadHelper next_pid=2045 next_prio=118 + <...>-2613 (-----) [001] d..2 14594.222158: sched_switch: prev_comm=ogle.android.as prev_pid=2613 prev_prio=120 prev_state=S ==> next_comm=swapper/1 next_pid=0 next_prio=120 + <...>-18150 (-----) [007] d..2 14594.222193: sched_switch: prev_comm=kworker/u16:18 prev_pid=18150 prev_prio=120 prev_state=S ==> next_comm=swapper/7 next_pid=0 next_prio=120 + <...>-656 (-----) [004] .... 14594.222220: binder_set_priority: proc=625 thread=656 old=110 => new=120 desired=120 + <...>-2738 (-----) [007] ...1 14594.222267: tracing_mark_write: B|2007|HWC release fence 36027 has signaled + <...>-656 (-----) [007] ...1 14594.223842: tracing_mark_write: B|625|queueBuffer + <...>-656 (-----) [007] ...1 14594.223845: tracing_mark_write: B|625|com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity#1: 2 + <...>-656 (-----) [007] ...1 14594.223871: tracing_mark_write: B|625|requestNextVsync + <...>-656 (-----) [007] ...1 14594.223873: tracing_mark_write: B|625|resetIdleTimer + <...>-656 (-----) [007] ...1 14594.223881: tracing_mark_write: B|625|addAndGetFrameTimestamps + <...>-1395 (-----) [001] d..2 14594.223909: sched_switch: prev_comm=android.ui prev_pid=1395 prev_prio=118 prev_state=S ==> next_comm=swapper/1 next_pid=0 next_prio=120 + <...>-2738 (-----) [007] ...1 14594.223959: tracing_mark_write: B|2007|Trace GPU completion fence 36027 + <...>-11799 (-----) [006] ...1 14594.224006: tracing_mark_write: B|2007|waiting for GPU completion 36027 + <...>-11799 (-----) [006] ...1 14594.224009: tracing_mark_write: B|2007|waitForever + <...>-2613 (-----) [004] d..2 14594.224014: sched_switch: prev_comm=ogle.android.as prev_pid=2613 prev_prio=120 prev_state=S ==> next_comm=Binder:1803_6 next_pid=2173 next_prio=120 + <...>-11799 (-----) [006] d..1 14594.224014: fence_enable_signal: driver=kgsl-timeline timeline=kgsl-3d0_13-s.nexuslauncher(200 context=27 seqno=78002 + <...>-11799 (-----) [006] d..2 14594.224021: sched_switch: prev_comm=GPU completion prev_pid=11799 prev_prio=110 prev_state=S ==> next_comm=rcuop/6 next_pid=68 next_prio=120 + rcuop/6-68 ( 68) [006] d..2 14594.224044: sched_switch: prev_comm=rcuop/6 prev_pid=68 prev_prio=120 prev_state=S ==> next_comm=swapper/6 next_pid=0 next_prio=120 + <...>-259 (-----) [006] d..2 14594.224132: sched_switch: prev_comm=kgsl_worker_thr prev_pid=259 prev_prio=97 prev_state=S ==> next_comm=Binder:2007_A next_pid=4180 next_prio=120 + <...>-3206 (-----) [001] d..2 14594.224167: sched_switch: prev_comm=aiai-vc-0 prev_pid=3206 prev_prio=139 prev_state=R ==> next_comm=ndroid.systemui next_pid=1803 next_prio=120 + lowpool[847]-14589 ( 2446) [005] d..1 14594.224300: mm_filemap_delete_from_page_cache: dev 0:1 ino 3d0034 page=000000008247d586 pfn=676904 ofs=0 + <...>-1803 (-----) [001] d..2 14594.224302: sched_switch: prev_comm=ndroid.systemui prev_pid=1803 prev_prio=120 prev_state=S ==> next_comm=aiai-vc-0 next_pid=3206 next_prio=139 + <...>-3206 (-----) [001] d..2 14594.224433: sched_switch: prev_comm=aiai-vc-0 prev_pid=3206 prev_prio=139 prev_state=S ==> next_comm=swapper/1 next_pid=0 next_prio=120 + <...>-1903 (-----) [003] ...1 14594.224490: tracing_mark_write: B|1368|dispatchingStartProcess:com.google.android.dialer + <...>-1903 (-----) [003] ...1 14594.224659: tracing_mark_write: B|1368|wmLayout + <...>-1903 (-----) [003] ...1 14594.224666: tracing_mark_write: B|1368|performSurfacePlacement + <...>-1903 (-----) [003] ...1 14594.224683: tracing_mark_write: B|1368|applySurfaceChanges + <...>-1903 (-----) [003] ...1 14594.224688: tracing_mark_write: B|1368|openSurfaceTransaction + <...>-2738 (-----) [007] ...1 14594.224711: tracing_mark_write: B|2007|query + <...>-1903 (-----) [003] ...1 14594.224714: tracing_mark_write: B|1368|performLayout + <...>-2738 (-----) [007] ...1 14594.224714: tracing_mark_write: B|2007|query + <...>-1903 (-----) [003] ...1 14594.224723: tracing_mark_write: B|1368|applyPostLayoutPolicy + <...>-2738 (-----) [007] d..2 14594.224752: sched_switch: prev_comm=RenderThread prev_pid=2738 prev_prio=110 prev_state=S ==> next_comm=Binder:625_1 next_pid=656 next_prio=110 + <...>-656 (-----) [007] .... 14594.224766: binder_set_priority: proc=625 thread=656 old=110 => new=120 desired=120 + <...>-1398 (-----) [002] ...1 14594.224801: tracing_mark_write: B|1368|Theme::ApplyStyle + <...>-1398 (-----) [002] ...1 14594.224805: tracing_mark_write: B|1368|AssetManager::GetBag + <...>-1398 (-----) [002] ...1 14594.224820: tracing_mark_write: B|1368|AssetManager::GetBag + <...>-1398 (-----) [002] ...1 14594.224826: tracing_mark_write: B|1368|AssetManager::GetBag + <...>-1398 (-----) [002] ...1 14594.224833: tracing_mark_write: B|1368|AssetManager::GetBag + <...>-1398 (-----) [002] ...1 14594.224838: tracing_mark_write: B|1368|AssetManager::GetBag + <...>-1398 (-----) [002] ...1 14594.224846: tracing_mark_write: B|1368|AssetManager::GetBag + <...>-1398 (-----) [002] ...1 14594.224853: tracing_mark_write: B|1368|AssetManager::GetBag + <...>-1398 (-----) [002] ...1 14594.224859: tracing_mark_write: B|1368|AssetManager::GetBag + <...>-1398 (-----) [002] ...1 14594.224864: tracing_mark_write: B|1368|AssetManager::GetBag + <...>-18150 (-----) [006] d..2 14594.228407: sched_switch: prev_comm=kworker/u16:18 prev_pid=18150 prev_prio=120 prev_state=R+ ==> next_comm=mmc-cmdqd/0 next_pid=440 next_prio=98 + <...>-2738 (-----) [007] d..2 14594.228411: sched_switch: prev_comm=RenderThread prev_pid=2738 prev_prio=110 prev_state=R+ ==> next_comm=kworker/7:0H next_pid=76 next_prio=100 + <...>-1409 (-----) [004] ...1 14594.228417: tracing_mark_write: B|1368|Start proc: com.google.android.dialer + <...>-440 (-----) [006] d..2 14594.228418: sched_switch: prev_comm=mmc-cmdqd/0 prev_pid=440 prev_prio=98 prev_state=D ==> next_comm=kworker/u16:18 next_pid=18150 next_prio=120 + <...>-76 (-----) [007] d..2 14594.228430: sched_switch: prev_comm=kworker/7:0H prev_pid=76 prev_prio=100 prev_state=R+ ==> next_comm=mmc-cmdqd/0 next_pid=440 next_prio=98 + <...>-440 (-----) [007] d..2 14594.228434: sched_switch: prev_comm=mmc-cmdqd/0 prev_pid=440 prev_prio=98 prev_state=D ==> next_comm=kworker/7:0H next_pid=76 next_prio=100 + <...>-18150 (-----) [006] d..3 14594.228442: sched_blocked_reason: pid=1398 iowait=1 caller=wait_on_page_bit_common+0x2a8/0x5f8 + <...>-76 (-----) [007] d..2 14594.228442: sched_switch: prev_comm=kworker/7:0H prev_pid=76 prev_prio=100 prev_state=S ==> next_comm=RenderThread next_pid=2738 next_prio=110 + <...>-2738 (-----) [007] ...2 14594.228446: binder_set_priority: proc=625 thread=656 old=120 => new=110 desired=110 + <...>-18150 (-----) [006] d..2 14594.228447: sched_switch: prev_comm=kworker/u16:18 prev_pid=18150 prev_prio=120 prev_state=R+ ==> next_comm=android.anim next_pid=1398 next_prio=110 + <...>-2738 (-----) [007] d..2 14594.228479: sched_switch: prev_comm=RenderThread prev_pid=2738 prev_prio=110 prev_state=S ==> next_comm=Binder:625_1 next_pid=656 next_prio=110 + <...>-1409 (-----) [004] d..2 14594.228499: sched_switch: prev_comm=ActivityManager prev_pid=1409 prev_prio=118 prev_state=D ==> next_comm=Binder:965_2 next_pid=1041 next_prio=120 + <...>-625 (-----) [003] ...1 14594.229271: tracing_mark_write: B|625|handleTransaction + <...>-1773 (-----) [004] .... 14594.229285: binder_set_priority: proc=625 thread=1773 old=110 => new=120 desired=120 + <...>-440 (-----) [007] d..2 14594.229301: sched_switch: prev_comm=mmc-cmdqd/0 prev_pid=440 prev_prio=98 prev_state=D ==> next_comm=RenderThread next_pid=2738 next_prio=110 + <...>-2738 (-----) [007] ...1 14594.229318: tracing_mark_write: B|2007|HWC release fence 36028 has signaled + <...>-2738 (-----) [007] ...1 14594.229331: tracing_mark_write: B|2007|query + <...>-2738 (-----) [007] ...1 14594.229337: tracing_mark_write: B|2007|eglBeginFrame + <...>-2738 (-----) [007] ...1 14594.229352: tracing_mark_write: B|2007|query + <...>-2738 (-----) [007] ...1 14594.229354: tracing_mark_write: B|2007|query + <...>-791 (-----) [000] d..2 14594.229357: sched_switch: prev_comm=main prev_pid=791 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120 + <...>-625 (-----) [003] ...1 14594.229440: tracing_mark_write: B|625|doTransaction + <...>-13916 (-----) [002] d..2 14594.229482: sched_switch: prev_comm=HeapTaskDaemon prev_pid=13916 prev_prio=124 prev_state=D|K ==> next_comm=swapper/2 next_pid=0 next_prio=120 + <...>-13917 (-----) [001] d..2 14594.229492: sched_blocked_reason: pid=13916 iowait=0 caller=__rwsem_down_write_failed_common+0x3e8/0x754 + <...>-625 (-----) [003] ...1 14594.229492: tracing_mark_write: B|625|doTransaction + <...>-625 (-----) [003] ...1 14594.229507: tracing_mark_write: B|625|doTransaction + <...>-13917 (-----) [001] d..2 14594.229523: sched_switch: prev_comm=ReferenceQueueD prev_pid=13917 prev_prio=124 prev_state=D ==> next_comm=swapper/1 next_pid=0 next_prio=120 + <...>-13916 (-----) [002] d..2 14594.229535: sched_blocked_reason: pid=13917 iowait=0 caller=do_page_fault+0x550/0x5fc + <...>-625 (-----) [003] ...1 14594.229538: tracing_mark_write: B|625|doTransaction + <...>-2738 (-----) [007] ...1 14594.229543: tracing_mark_write: B|2007|flush commands + <...>-13916 (-----) [002] .... 14594.229562: sched_process_exit: comm=HeapTaskDaemon pid=13916 prio=124 + <...>-625 (-----) [003] ...1 14594.229567: tracing_mark_write: B|625|doTransaction + <...>-625 (-----) [003] ...1 14594.229588: tracing_mark_write: B|625|doTransaction + <...>-625 (-----) [003] ...1 14594.229628: tracing_mark_write: B|625|doTransaction + <...>-625 (-----) [003] ...1 14594.229652: tracing_mark_write: B|625|doTransaction + <...>-13916 (-----) [002] d..2 14594.229676: sched_switch: prev_comm=HeapTaskDaemon prev_pid=13916 prev_prio=124 prev_state=x ==> next_comm=swapper/2 next_pid=0 next_prio=120 + <...>-625 (-----) [003] ...1 14594.229676: tracing_mark_write: B|625|doTransaction + <...>-2007 (-----) [006] d..2 14594.229688: sched_switch: prev_comm=s.nexuslauncher prev_pid=2007 prev_prio=110 prev_state=S ==> next_comm=swapper/6 next_pid=0 next_prio=120 + <...>-625 (-----) [003] ...1 14594.229703: tracing_mark_write: B|625|doTransaction + <...>-625 (-----) [003] ...1 14594.229725: tracing_mark_write: B|625|doTransaction + <...>-625 (-----) [003] ...1 14594.229750: tracing_mark_write: B|625|doTransaction + <...>-625 (-----) [003] ...1 14594.229772: tracing_mark_write: B|625|doTransaction + <...>-625 (-----) [003] ...1 14594.229792: tracing_mark_write: B|625|doTransaction + <...>-791 (-----) [000] d..2 14594.229811: sched_switch: prev_comm=main prev_pid=791 prev_prio=120 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120 + <...>-625 (-----) [003] ...1 14594.229824: tracing_mark_write: B|625|doTransaction + <...>-2738 (-----) [007] ...1 14594.229827: tracing_mark_write: B|2007|eglSwapBuffersWithDamageKHR + <...>-13917 (-----) [001] d..2 14594.229836: sched_switch: prev_comm=ReferenceQueueD prev_pid=13917 prev_prio=124 prev_state=D ==> next_comm=swapper/1 next_pid=0 next_prio=120 + <...>-2738 (-----) [007] ...1 14594.229837: tracing_mark_write: B|2007|setSurfaceDamage + <...>-625 (-----) [003] ...1 14594.229850: tracing_mark_write: B|625|doTransaction + <...>-13918 (-----) [002] d..2 14594.229856: sched_blocked_reason: pid=13917 iowait=0 caller=SyS_madvise+0xd34/0xd3c + <...>-5281 (-----) [001] d..2 14594.229932: sched_switch: prev_comm=writer prev_pid=5281 prev_prio=96 prev_state=D ==> next_comm=swapper/1 next_pid=0 next_prio=120 + <...>-89 (-----) [006] d..2 14594.229951: sched_switch: prev_comm=lpass_smem_glin prev_pid=89 prev_prio=98 prev_state=S ==> next_comm=swapper/6 next_pid=0 next_prio=120 + <...>-625 (-----) [003] ...1 14594.229982: tracing_mark_write: B|625|handleMessageInvalidate + <...>-625 (-----) [003] ...1 14594.229984: tracing_mark_write: B|625|handlePageFlip + <...>-625 (-----) [003] ...1 14594.230013: tracing_mark_write: B|625|latchBuffer + <...>-13917 (-----) [000] .... 14594.230015: sched_process_exit: comm=ReferenceQueueD pid=13917 prio=124 + <...>-625 (-----) [003] ...1 14594.230020: tracing_mark_write: B|625|query + <...>-625 (-----) [003] ...1 14594.230028: tracing_mark_write: B|625|updateTexImage + <...>-625 (-----) [003] ...1 14594.230035: tracing_mark_write: B|625|acquireBuffer + <...>-625 (-----) [003] ...1 14594.230044: tracing_mark_write: B|625|com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity#1: 2 + <...>-2738 (-----) [007] d..2 14594.230057: sched_switch: prev_comm=RenderThread prev_pid=2738 prev_prio=110 prev_state=D ==> next_comm=smem_native_lpa next_pid=88 next_prio=120 + <...>-14607 (-----) [000] d..2 14594.259609: sched_blocked_reason: pid=14624 iowait=0 caller=__rwsem_down_write_failed_common+0x3e8/0x754 + <...>-2738 (-----) [005] d..2 14594.259620: sched_switch: prev_comm=RenderThread prev_pid=2738 prev_prio=120 prev_state=S ==> next_comm=Binder:625_4 next_pid=1773 next_prio=120 + <...>-1773 (-----) [005] ...1 14594.259649: tracing_mark_write: B|625|query + <...>-2738 (-----) [005] ...1 14594.259714: tracing_mark_write: B|2007|query + <...>-2738 (-----) [005] d..2 14594.259743: sched_switch: prev_comm=RenderThread prev_pid=2738 prev_prio=120 prev_state=S ==> next_comm=Binder:625_4 next_pid=1773 next_prio=120 + <...>-1773 (-----) [005] ...1 14594.259757: tracing_mark_write: B|625|query + <...>-2738 (-----) [005] ...1 14594.259810: tracing_mark_write: B|2007|syncFrameState + <...>-2738 (-----) [005] ...1 14594.259856: tracing_mark_write: B|2007|prepareTree + Binder:14607_1-14624 (14607) [002] ...1 14594.259863: tracing_mark_write: B|14607|AttachCurrentThread + Binder:14607_1-14624 (14607) [002] ...1 14594.259869: tracing_mark_write: B|14607|Thread::Attach + Binder:14607_1-14624 (14607) [002] ...1 14594.259873: tracing_mark_write: B|14607|Thread birth + Binder:14607_1-14624 (14607) [002] ...1 14594.259916: tracing_mark_write: B|14607|Thread::Init + Binder:14607_1-14624 (14607) [002] ...1 14594.259920: tracing_mark_write: B|14607|InitStackHwm + <...>-14607 (-----) [000] d..2 14594.259932: sched_switch: prev_comm=.android.dialer prev_pid=14607 prev_prio=120 prev_state=D ==> next_comm=swapper/0 next_pid=0 next_prio=120 + Binder:14607_1-14624 (14607) [002] d..2 14594.259941: sched_blocked_reason: pid=14607 iowait=0 caller=do_page_fault+0x550/0x5fc + <...>-3198 (-----) [001] ...1 14594.259942: tracing_mark_write: B|2007|Update SurfaceView position + Binder:14607_1-14624 (14607) [002] ...1 14594.259963: tracing_mark_write: B|14607|InitTlsEntryPoints + Binder:14607_1-14624 (14607) [002] ...1 14594.259974: tracing_mark_write: B|14607|InitInterpreterTls + <...>-14607 (-----) [000] d..2 14594.260005: sched_blocked_reason: pid=14624 iowait=0 caller=__rwsem_down_write_failed_common+0x3e8/0x754 + <...>-3198 (-----) [001] d..2 14594.260007: sched_switch: prev_comm=hwuiTask1 prev_pid=3198 prev_prio=118 prev_state=S ==> next_comm=swapper/1 next_pid=0 next_prio=120 + <...>-14607 (-----) [000] d..2 14594.260024: sched_switch: prev_comm=.android.dialer prev_pid=14607 prev_prio=120 prev_state=D ==> next_comm=swapper/0 next_pid=0 next_prio=120 + Binder:14607_1-14624 (14607) [002] d..2 14594.260038: sched_blocked_reason: pid=14607 iowait=0 caller=do_page_fault+0x550/0x5fc + <...>-14607 (-----) [000] d..2 14594.260064: sched_blocked_reason: pid=14624 iowait=0 caller=__rwsem_down_write_failed_common+0x3e8/0x754 + Binder:14607_1-14624 (14607) [002] ...1 14594.260101: tracing_mark_write: B|14607|ThreadList::Register + <...>-2738 (-----) [005] ...1 14594.260128: tracing_mark_write: B|2007|query + <...>-2738 (-----) [005] ...1 14594.260140: tracing_mark_write: B|2007|query + <...>-2738 (-----) [005] ...1 14594.260148: tracing_mark_write: B|2007|query + <...>-2738 (-----) [005] ...1 14594.260155: tracing_mark_write: B|2007|query + <...>-2738 (-----) [005] ...1 14594.260161: tracing_mark_write: B|2007|query + <...>-2738 (-----) [005] ...1 14594.260167: tracing_mark_write: B|2007|query + <...>-2738 (-----) [005] ...1 14594.260173: tracing_mark_write: B|2007|dequeueBuffer + <...>-2007 (-----) [001] d..2 14594.260201: sched_switch: prev_comm=s.nexuslauncher prev_pid=2007 prev_prio=120 prev_state=S ==> next_comm=swapper/1 next_pid=0 next_prio=120 + <...>-2738 (-----) [005] d..2 14594.260214: sched_switch: prev_comm=RenderThread prev_pid=2738 prev_prio=120 prev_state=S ==> next_comm=Binder:625_4 next_pid=1773 next_prio=120 + <...>-1773 (-----) [005] ...1 14594.260236: tracing_mark_write: B|625|dequeueBuffer + <...>-1773 (-----) [005] ...1 14594.260249: tracing_mark_write: B|625|com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity#1: 2 + <...>-14607 (-----) [000] d..2 14594.260334: sched_switch: prev_comm=.android.dialer prev_pid=14607 prev_prio=120 prev_state=D ==> next_comm=swapper/0 next_pid=0 next_prio=120 + Binder:14607_1-14624 (14607) [002] d..2 14594.260343: sched_blocked_reason: pid=14607 iowait=0 caller=do_page_fault+0x550/0x5fc + <...>-14607 (-----) [000] d..2 14594.260376: sched_blocked_reason: pid=14624 iowait=0 caller=__rwsem_down_write_failed_common+0x3e8/0x754 + <...>-14607 (-----) [000] d..2 14594.260387: sched_switch: prev_comm=.android.dialer prev_pid=14607 prev_prio=120 prev_state=D ==> next_comm=swapper/0 next_pid=0 next_prio=120 + <...>-2738 (-----) [005] ...1 14594.260401: tracing_mark_write: B|2007|HWC release fence 36030 has signaled + Binder:14607_1-14624 (14607) [002] d..2 14594.260407: sched_blocked_reason: pid=14607 iowait=0 caller=do_page_fault+0x550/0x5fc + <...>-2738 (-----) [005] ...1 14594.260419: tracing_mark_write: B|2007|query + <...>-2738 (-----) [005] ...1 14594.260427: tracing_mark_write: B|2007|eglBeginFrame + <...>-2738 (-----) [005] ...1 14594.260445: tracing_mark_write: B|2007|query + <...>-2738 (-----) [005] ...1 14594.260450: tracing_mark_write: B|2007|query + Binder:14607_1-14624 (14607) [002] .... 14594.260472: task_newtask: pid=14625 comm=Binder:14607_1 clone_flags=3d0f00 oom_score_adj=-1000 + <...>-14607 (-----) [000] d..2 14594.260517: sched_switch: prev_comm=.android.dialer prev_pid=14607 prev_prio=120 prev_state=D ==> next_comm=swapper/0 next_pid=0 next_prio=120 + Binder:14607_2-14625 (14607) [001] d..2 14594.260525: sched_blocked_reason: pid=14607 iowait=0 caller=do_page_fault+0x550/0x5fc + <...>-14607 (-----) [000] d..2 14594.260555: sched_blocked_reason: pid=14625 iowait=0 caller=__rwsem_down_write_failed_common+0x3e8/0x754 + <...>-14607 (-----) [000] ...1 14594.260569: tracing_mark_write: B|14607|ActivityThreadMain + <...>-14607 (-----) [000] d..2 14594.260581: sched_switch: prev_comm=.android.dialer prev_pid=14607 prev_prio=120 prev_state=D ==> next_comm=swapper/0 next_pid=0 next_prio=120 + Binder:14607_2-14625 (14607) [001] d..2 14594.260588: sched_blocked_reason: pid=14607 iowait=0 caller=do_page_fault+0x550/0x5fc + <...>-14607 (-----) [000] d..2 14594.260611: sched_blocked_reason: pid=14625 iowait=0 caller=__rwsem_down_write_failed_common+0x3e8/0x754 + <...>-14607 (-----) [000] d..2 14594.260623: sched_switch: prev_comm=.android.dialer prev_pid=14607 prev_prio=120 prev_state=D ==> next_comm=swapper/0 next_pid=0 next_prio=120 + Binder:14607_2-14625 (14607) [001] d..2 14594.260636: sched_blocked_reason: pid=14607 iowait=0 caller=do_page_fault+0x550/0x5fc + <...>-14607 (-----) [000] d..2 14594.260663: sched_blocked_reason: pid=14625 iowait=0 caller=__rwsem_down_write_failed_common+0x3e8/0x754 + <...>-14607 (-----) [000] d..2 14594.260674: sched_switch: prev_comm=.android.dialer prev_pid=14607 prev_prio=120 prev_state=D ==> next_comm=swapper/0 next_pid=0 next_prio=120 + Binder:14607_2-14625 (14607) [001] d..2 14594.260694: sched_blocked_reason: pid=14607 iowait=0 caller=do_page_fault+0x550/0x5fc + <...>-14607 (-----) [000] d..2 14594.260724: sched_blocked_reason: pid=14625 iowait=0 caller=__rwsem_down_write_failed_common+0x3e8/0x754 + <...>-2738 (-----) [005] ...1 14594.260734: tracing_mark_write: B|2007|flush commands + <...>-14607 (-----) [000] d..2 14594.260735: sched_switch: prev_comm=.android.dialer prev_pid=14607 prev_prio=120 prev_state=D ==> next_comm=swapper/0 next_pid=0 next_prio=120 + Binder:14607_2-14625 (14607) [001] d..2 14594.260753: sched_blocked_reason: pid=14607 iowait=0 caller=do_page_fault+0x550/0x5fc + Binder:14607_2-14625 (14607) [001] ...1 14594.260925: tracing_mark_write: B|14607|AttachCurrentThread + Binder:14607_2-14625 (14607) [001] ...1 14594.260930: tracing_mark_write: B|14607|Thread::Attach + Binder:14607_2-14625 (14607) [001] ...1 14594.260933: tracing_mark_write: B|14607|Thread birth + Binder:14607_2-14625 (14607) [001] ...1 14594.260973: tracing_mark_write: B|14607|Thread::Init + Binder:14607_2-14625 (14607) [001] ...1 14594.260977: tracing_mark_write: B|14607|InitStackHwm + <...>-14607 (-----) [000] d..2 14594.260990: sched_switch: prev_comm=.android.dialer prev_pid=14607 prev_prio=120 prev_state=D ==> next_comm=swapper/0 next_pid=0 next_prio=120 + Binder:14607_2-14625 (14607) [001] d..2 14594.260998: sched_blocked_reason: pid=14607 iowait=0 caller=do_page_fault+0x550/0x5fc + Binder:14607_2-14625 (14607) [001] ...1 14594.261023: tracing_mark_write: B|14607|InitTlsEntryPoints + Binder:14607_2-14625 (14607) [001] ...1 14594.261034: tracing_mark_write: B|14607|InitInterpreterTls + <...>-14607 (-----) [000] d..2 14594.261064: sched_blocked_reason: pid=14625 iowait=0 caller=__rwsem_down_write_failed_common+0x3e8/0x754 + <...>-14607 (-----) [000] d..2 14594.261075: sched_switch: prev_comm=.android.dialer prev_pid=14607 prev_prio=120 prev_state=D ==> next_comm=swapper/0 next_pid=0 next_prio=120 + Binder:14607_2-14625 (14607) [001] d..2 14594.261094: sched_blocked_reason: pid=14607 iowait=0 caller=do_page_fault+0x550/0x5fc + <...>-14607 (-----) [000] d..2 14594.261120: sched_blocked_reason: pid=14625 iowait=0 caller=__rwsem_down_write_failed_common+0x3e8/0x754 + <...>-14607 (-----) [000] d..2 14594.261132: sched_switch: prev_comm=.android.dialer prev_pid=14607 prev_prio=120 prev_state=D ==> next_comm=swapper/0 next_pid=0 next_prio=120 + Binder:14607_2-14625 (14607) [001] d..2 14594.261146: sched_blocked_reason: pid=14607 iowait=0 caller=do_page_fault+0x550/0x5fc + Binder:14607_2-14625 (14607) [001] ...1 14594.261167: tracing_mark_write: B|14607|ThreadList::Register + <...>-14607 (-----) [000] d..2 14594.261209: sched_blocked_reason: pid=14625 iowait=0 caller=__rwsem_down_write_failed_common+0x3e8/0x754 + <...>-2738 (-----) [005] ...1 14594.261212: tracing_mark_write: B|2007|waitOnFences + <...>-14607 (-----) [000] d..2 14594.261220: sched_switch: prev_comm=.android.dialer prev_pid=14607 prev_prio=120 prev_state=D ==> next_comm=swapper/0 next_pid=0 next_prio=120 + <...>-2738 (-----) [005] ...1 14594.261232: tracing_mark_write: B|2007|eglSwapBuffersWithDamageKHR + <...>-2738 (-----) [005] ...1 14594.261244: tracing_mark_write: B|2007|setSurfaceDamage + Binder:14607_2-14625 (14607) [001] d..2 14594.261246: sched_blocked_reason: pid=14607 iowait=0 caller=do_page_fault+0x550/0x5fc + <...>-14607 (-----) [000] ...1 14594.261326: tracing_mark_write: B|14607|VerifyClass com.android.org.conscrypt.TrustedCertificateStore$PreloadHolder + <...>-2738 (-----) [005] .... 14594.261621: fence_init: driver=kgsl-timeline timeline=kgsl-3d0_13-s.nexuslauncher(200 context=27 seqno=78005 + <...>-625 (-----) [003] ...1 14594.263903: tracing_mark_write: B|625|resetIdleTimer + <...>-625 (-----) [003] ...1 14594.263912: tracing_mark_write: B|625|rebuildLayerStacks + <...>-625 (-----) [003] ...1 14594.263915: tracing_mark_write: B|625|rebuildLayerStacks VR Dirty + <...>-625 (-----) [003] ...1 14594.263919: tracing_mark_write: B|625|computeVisibleRegions + <...>-1398 (-----) [006] d..2 14594.263966: sched_switch: prev_comm=android.anim prev_pid=1398 prev_prio=110 prev_state=S ==> next_comm=Binder:625_4 next_pid=1773 next_prio=120 + <...>-1695 (-----) [001] d..2 14594.264086: sched_switch: prev_comm=InputDispatcher prev_pid=1695 prev_prio=112 prev_state=S ==> next_comm=Binder:1368_14 next_pid=3253 next_prio=120 + <...>-625 (-----) [003] ...1 14594.264293: tracing_mark_write: B|625|calculateWorkingSet + <...>-625 (-----) [003] ...1 14594.264500: tracing_mark_write: B|625|prepare + <...>-625 (-----) [003] ...1 14594.264513: tracing_mark_write: B|625|HIDL::IComposerClient::executeCommands_2_2::client + <...>-625 (-----) [003] ...2 14594.264584: binder_set_priority: proc=627 thread=627 old=97 => new=98 desired=98 + <...>-625 (-----) [003] d..2 14594.264617: sched_switch: prev_comm=surfaceflinger prev_pid=625 prev_prio=98 prev_state=S ==> next_comm=logd.writer next_pid=588 next_prio=130 + <...>-588 (-----) [003] d..2 14594.264851: sched_switch: prev_comm=logd.writer prev_pid=588 prev_prio=130 prev_state=S ==> next_comm=swapper/3 next_pid=0 next_prio=120 + rcu_preempt-7 ( 7) [007] d..2 14594.265273: sched_switch: prev_comm=rcu_preempt prev_pid=7 prev_prio=120 prev_state=S ==> next_comm=kworker/u16:3 next_pid=18008 next_prio=120 + <...>-18008 (-----) [007] d..2 14594.265404: sched_switch: prev_comm=kworker/u16:3 prev_pid=18008 prev_prio=120 prev_state=D ==> next_comm=swapper/7 next_pid=0 next_prio=120 + <...>-18008 (-----) [007] d..2 14594.265471: sched_switch: prev_comm=kworker/u16:3 prev_pid=18008 prev_prio=120 prev_state=S ==> next_comm=swapper/7 next_pid=0 next_prio=120 + <...>-625 (-----) [003] ...1 14594.265496: tracing_mark_write: B|625|doComposition + <...>-625 (-----) [003] ...1 14594.265507: tracing_mark_write: B|625|doComposeSurfaces + <...>-625 (-----) [003] ...1 14594.265552: tracing_mark_write: B|625|acquireBuffer + <...>-625 (-----) [003] ...1 14594.265563: tracing_mark_write: B|625|postFramebuffer + <...>-625 (-----) [003] ...1 14594.265567: tracing_mark_write: B|625|presentAndGetReleaseFences + <...>-625 (-----) [003] d..1 14594.265601: fence_enable_signal: driver=sde_fence:crtc97:91650 timeline=crtc97 context=3 seqno=91650 + <...>-625 (-----) [003] ...1 14594.265735: tracing_mark_write: B|625|logLayerStats + <...>-625 (-----) [003] ...1 14594.265744: tracing_mark_write: B|625|postComposition + <...>-625 (-----) [003] ...1 14594.265749: tracing_mark_write: B|625|releaseBuffer + <...>-625 (-----) [003] ...1 14594.265753: tracing_mark_write: B|625|com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity#1: 1 + <...>-625 (-----) [003] ...1 14594.265791: tracing_mark_write: B|625|releaseBuffer + <...>-440 (-----) [007] d..2 14594.342366: sched_switch: prev_comm=mmc-cmdqd/0 prev_pid=440 prev_prio=98 prev_state=D ==> next_comm=kworker/u17:2 next_pid=1778 next_prio=100 + <...>-2007 (-----) [006] ...1 14594.342375: tracing_mark_write: B|2007|input + <...>-2007 (-----) [006] ...1 14594.342399: tracing_mark_write: B|2007|animation + <...>-625 (-----) [003] ...1 14594.342447: tracing_mark_write: B|625|doTransaction + <...>-625 (-----) [003] ...1 14594.342489: tracing_mark_write: B|625|doTransaction + kworker/u17:2-1778 ( 1778) [007] d..3 14594.342532: sched_blocked_reason: pid=14607 iowait=1 caller=wait_on_page_bit_common+0x2a8/0x5f8 + kworker/u17:2-1778 ( 1778) [007] d..2 14594.342544: sched_switch: prev_comm=kworker/u17:2 prev_pid=1778 prev_prio=100 prev_state=S ==> next_comm=kworker/u16:2 next_pid=27544 next_prio=120 + <...>-1773 (-----) [000] ...1 14594.342575: tracing_mark_write: B|625|requestNextVsync + <...>-1773 (-----) [000] ...1 14594.342579: tracing_mark_write: B|625|resetIdleTimer + <...>-27544 (-----) [007] d..2 14594.342589: sched_switch: prev_comm=kworker/u16:2 prev_pid=27544 prev_prio=120 prev_state=S ==> next_comm=swapper/7 next_pid=0 next_prio=120 + <...>-656 (-----) [002] d.h3 14594.342604: sched_blocked_reason: pid=1233 iowait=0 caller=geni_i2c_xfer+0x4d8/0x1398 + <...>-1803 (-----) [001] d..2 14594.342605: sched_switch: prev_comm=ndroid.systemui prev_pid=1803 prev_prio=120 prev_state=S ==> next_comm=swapper/1 next_pid=0 next_prio=120 + <...>-625 (-----) [003] ...1 14594.342632: tracing_mark_write: B|625|handleMessageInvalidate + <...>-625 (-----) [003] ...1 14594.342634: tracing_mark_write: B|625|handlePageFlip + <...>-2738 (-----) [007] ...1 14594.342641: tracing_mark_write: B|2007|notifyFramePending + <...>-658 (-----) [002] d..2 14594.342653: sched_switch: prev_comm=DispSync prev_pid=658 prev_prio=97 prev_state=S ==> next_comm=Binder:625_1 next_pid=656 next_prio=120 + <...>-656 (-----) [002] ...1 14594.342656: tracing_mark_write: B|625|requestNextVsync + <...>-2738 (-----) [007] d..2 14594.342658: sched_switch: prev_comm=RenderThread prev_pid=2738 prev_prio=110 prev_state=S ==> next_comm=swapper/7 next_pid=0 next_prio=120 + <...>-656 (-----) [002] ...1 14594.342660: tracing_mark_write: B|625|resetIdleTimer + <...>-660 (-----) [005] d..2 14594.342663: sched_switch: prev_comm=app prev_pid=660 prev_prio=97 prev_state=S ==> next_comm=swapper/5 next_pid=0 next_prio=120 + <...>-625 (-----) [003] ...1 14594.342665: tracing_mark_write: B|625|latchBuffer + <...>-625 (-----) [003] ...1 14594.342673: tracing_mark_write: B|625|query + <...>-625 (-----) [003] ...1 14594.342682: tracing_mark_write: B|625|updateTexImage + <...>-625 (-----) [003] ...1 14594.342693: tracing_mark_write: B|625|acquireBuffer + <...>-625 (-----) [003] ...1 14594.342703: tracing_mark_write: B|625|com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity#1: 1 + <...>-660 (-----) [005] d..2 14594.342709: sched_switch: prev_comm=app prev_pid=660 prev_prio=97 prev_state=S ==> next_comm=swapper/5 next_pid=0 next_prio=120 + <...>-2007 (-----) [006] ...1 14594.342733: tracing_mark_write: B|2007|traversal + <...>-2007 (-----) [006] ...1 14594.342776: tracing_mark_write: B|2007|draw + <...>-2007 (-----) [006] ...1 14594.342791: tracing_mark_write: B|2007|Record View#draw() + <...>-625 (-----) [003] ...1 14594.342849: tracing_mark_write: B|625|updateInputFlinger + <...>-2007 (-----) [006] d..2 14594.342903: sched_switch: prev_comm=s.nexuslauncher prev_pid=2007 prev_prio=110 prev_state=S ==> next_comm=kworker/6:2H next_pid=24261 next_prio=100 + <...>-2738 (-----) [007] ...1 14594.342910: tracing_mark_write: B|2007|DrawFrame + <...>-2738 (-----) [007] d..2 14594.342917: sched_switch: prev_comm=RenderThread prev_pid=2738 prev_prio=110 prev_state=R+ ==> next_comm=mmc-cmdqd/0 next_pid=440 next_prio=98 + <...>-24261 (-----) [006] d..2 14594.342918: sched_switch: prev_comm=kworker/6:2H prev_pid=24261 prev_prio=100 prev_state=S ==> next_comm=.android.dialer next_pid=14607 next_prio=110 + <...>-440 (-----) [007] d..2 14594.342926: sched_switch: prev_comm=mmc-cmdqd/0 prev_pid=440 prev_prio=98 prev_state=D ==> next_comm=RenderThread next_pid=2738 next_prio=110 + <...>-2738 (-----) [007] ...1 14594.342927: tracing_mark_write: B|2007|query + <...>-2738 (-----) [007] ...2 14594.342959: binder_set_priority: proc=625 thread=656 old=120 => new=110 desired=110 + <...>-2738 (-----) [007] d..2 14594.342975: sched_switch: prev_comm=RenderThread prev_pid=2738 prev_prio=110 prev_state=R+ ==> next_comm=Binder:625_1 next_pid=656 next_prio=110 + <...>-656 (-----) [007] ...1 14594.343021: tracing_mark_write: B|625|query + <...>-656 (-----) [007] .... 14594.343033: binder_set_priority: proc=625 thread=656 old=110 => new=120 desired=120 + <...>-2738 (-----) [007] ...1 14594.343070: tracing_mark_write: B|2007|query + <...>-1233 (-----) [004] d..2 14594.343074: sched_switch: prev_comm=sound trigger c prev_pid=1233 prev_prio=120 prev_state=R+ ==> next_comm=irq/144-1436400 next_pid=2522 next_prio=49 + <...>-2738 (-----) [007] ...2 14594.343078: binder_set_priority: proc=625 thread=656 old=120 => new=110 desired=110 + <...>-625 (-----) [003] ...1 14594.343084: tracing_mark_write: B|625|onMessageReceived + <...>-625 (-----) [003] ...1 14594.343087: tracing_mark_write: B|625|handleMessageRefresh + <...>-625 (-----) [003] ...1 14594.343090: tracing_mark_write: B|625|preComposition + <...>-2738 (-----) [007] d..2 14594.343090: sched_switch: prev_comm=RenderThread prev_pid=2738 prev_prio=110 prev_state=R+ ==> next_comm=Binder:625_1 next_pid=656 next_prio=110 + <...>-625 (-----) [003] ...1 14594.343122: tracing_mark_write: B|625|rebuildLayerStacks + <...>-625 (-----) [003] ...1 14594.343124: tracing_mark_write: B|625|rebuildLayerStacks VR Dirty + <...>-89 (-----) [007] d..2 14594.343126: sched_switch: prev_comm=lpass_smem_glin prev_pid=89 prev_prio=98 prev_state=S ==> next_comm=Binder:625_1 next_pid=656 next_prio=110 + <...>-625 (-----) [003] ...1 14594.343129: tracing_mark_write: B|625|computeVisibleRegions + <...>-656 (-----) [007] ...1 14594.343136: tracing_mark_write: B|625|query + <...>-14607 (-----) [006] ...2 14594.343141: binder_set_priority: proc=1368 thread=3253 old=120 => new=110 desired=110 + <...>-2965 (-----) [001] .... 14596.746610: mm_filemap_add_to_page_cache: dev 253:6 ino a359 page=000000002ae8fcff pfn=1522884 ofs=188416 + <idle>-0 (-----) [002] d..2 14596.746619: sched_switch: prev_comm=swapper/2 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=mmc-cmdqd/0 next_pid=440 next_prio=98 + <...>-2965 (-----) [001] .... 14596.746629: mm_filemap_add_to_page_cache: dev 253:6 ino a359 page=00000000679ee1ec pfn=1299913 ofs=192512 + <...>-2965 (-----) [001] .... 14596.746664: mm_filemap_add_to_page_cache: dev 253:6 ino a359 page=0000000006cd2fb7 pfn=1296251 ofs=196608 + <...>-2965 (-----) [001] .... 14596.746677: mm_filemap_add_to_page_cache: dev 253:6 ino a359 page=00000000af82f3d6 pfn=1419330 ofs=200704 + <...>-2965 (-----) [001] .... 14596.746693: mm_filemap_add_to_page_cache: dev 253:6 ino a359 page=000000002840f054 pfn=1304928 ofs=204800 + <...>-2965 (-----) [001] .... 14596.746706: mm_filemap_add_to_page_cache: dev 253:6 ino a359 page=000000004a59da17 pfn=1288069 ofs=208896 + <...>-2965 (-----) [001] .... 14596.746717: mm_filemap_add_to_page_cache: dev 253:6 ino a359 page=0000000023a80dca pfn=1419686 ofs=212992 + <...>-2965 (-----) [001] .... 14596.746730: mm_filemap_add_to_page_cache: dev 253:6 ino a359 page=000000001cf89eab pfn=1315372 ofs=217088 + <...>-2965 (-----) [001] .... 14596.746743: mm_filemap_add_to_page_cache: dev 253:6 ino a359 page=000000005b4c6cb6 pfn=1380698 ofs=221184 + <...>-2965 (-----) [001] .... 14596.746760: mm_filemap_add_to_page_cache: dev 253:6 ino a359 page=00000000f8304ae7 pfn=1206753 ofs=225280 + <...>-2965 (-----) [001] .... 14596.746773: mm_filemap_add_to_page_cache: dev 253:6 ino a359 page=00000000cb912305 pfn=1325465 ofs=229376 + <...>-2965 (-----) [001] .... 14596.746785: mm_filemap_add_to_page_cache: dev 253:6 ino a359 page=00000000f16f3774 pfn=1408056 ofs=233472 + <...>-2965 (-----) [001] .... 14596.746801: mm_filemap_add_to_page_cache: dev 253:6 ino a359 page=0000000056d4c926 pfn=1418352 ofs=237568 + <...>-2965 (-----) [001] .... 14596.746815: mm_filemap_add_to_page_cache: dev 253:6 ino a359 page=00000000f3eeb42c pfn=1320957 ofs=241664 + <...>-440 (-----) [002] d..2 14596.746916: sched_switch: prev_comm=mmc-cmdqd/0 prev_pid=440 prev_prio=98 prev_state=D ==> next_comm=swapper/2 next_pid=0 next_prio=120 + + <...>-656 (-----) [007] .... 14594.343145: binder_set_priority: proc=625 thread=656 old=110 => new=120 desired=120 + <...>-14607 (-----) [006] d..2 14594.343164: sched_switch: prev_comm=.android.dialer prev_pid=14607 prev_prio=110 prev_state=S ==> next_comm=swapper/6 next_pid=0 next_prio=120 + <...>-5281 (-----) [002] d..2 14594.343177: sched_switch: prev_comm=writer prev_pid=5281 prev_prio=96 prev_state=S ==> next_comm=RenderThread next_pid=2738 next_prio=110 + irq/144-1436400-2522 ( 2522) [004] d..2 14594.343223: sched_switch: prev_comm=irq/144-1436400 prev_pid=2522 prev_prio=49 prev_state=D ==> next_comm=sound trigger c next_pid=1233 next_prio=120 + <...>-88 (-----) [006] d..2 14594.343240: sched_switch: prev_comm=smem_native_lpa prev_pid=88 prev_prio=98 prev_state=S ==> next_comm=swapper/6 next_pid=0 next_prio=120 + <...>-1238 (-----) [001] d..2 14594.343243: sched_switch: prev_comm=FastMixer prev_pid=1238 prev_prio=96 prev_state=S ==> next_comm=swapper/1 next_pid=0 next_prio=120 + <...>-2738 (-----) [002] ...1 14594.343244: tracing_mark_write: B|2007|syncFrameState + <...>-2738 (-----) [002] ...1 14594.343293: tracing_mark_write: B|2007|prepareTree + <...>-1695 (-----) [001] d..2 14594.343318: sched_switch: prev_comm=InputDispatcher prev_pid=1695 prev_prio=112 prev_state=R+ ==> next_comm=FastMixer next_pid=1238 next_prio=96 + <...>-5281 (-----) [005] d..2 14594.343322: sched_switch: prev_comm=writer prev_pid=5281 prev_prio=96 prev_state=S ==> next_comm=Binder:1368_14 next_pid=3253 next_prio=110 + <...>-1238 (-----) [001] d..2 14594.343442: sched_switch: prev_comm=FastMixer prev_pid=1238 prev_prio=96 prev_state=S ==> next_comm=InputDispatcher next_pid=1695 next_prio=112 + <...>-1695 (-----) [001] d..2 14594.343467: sched_switch: prev_comm=InputDispatcher prev_pid=1695 prev_prio=112 prev_state=S ==> next_comm=swapper/1 next_pid=0 next_prio=120 + <...>-5281 (-----) [000] d..2 14594.343484: sched_switch: prev_comm=writer prev_pid=5281 prev_prio=96 prev_state=S ==> next_comm=swapper/0 next_pid=0 next_prio=120 + <...>-625 (-----) [003] ...1 14594.343519: tracing_mark_write: B|625|calculateWorkingSet + <...>-2738 (-----) [002] ...1 14594.343568: tracing_mark_write: B|2007|query + <...>-2738 (-----) [002] ...1 14594.343577: tracing_mark_write: B|2007|query + <...>-2738 (-----) [002] ...1 14594.343586: tracing_mark_write: B|2007|query + <...>-2738 (-----) [002] ...1 14594.343591: tracing_mark_write: B|2007|query + <...>-2738 (-----) [002] ...1 14594.343597: tracing_mark_write: B|2007|query + <...>-2738 (-----) [002] ...1 14594.343602: tracing_mark_write: B|2007|query + <...>-2738 (-----) [002] ...1 14594.343609: tracing_mark_write: B|2007|dequeueBuffer + <...>-2007 (-----) [006] d..2 14594.343612: sched_switch: prev_comm=s.nexuslauncher prev_pid=2007 prev_prio=110 prev_state=S ==> next_comm=swapper/6 next_pid=0 next_prio=120 + <...>-2738 (-----) [002] ...2 14594.343633: binder_set_priority: proc=625 thread=656 old=120 => new=110 desired=110 + <...>-2738 (-----) [002] d..2 14594.343683: sched_switch: prev_comm=RenderThread prev_pid=2738 prev_prio=110 prev_state=R+ ==> next_comm=Binder:625_1 next_pid=656 next_prio=110 + <...>-625 (-----) [003] ...1 14594.343704: tracing_mark_write: B|625|prepare + <...>-656 (-----) [002] ...1 14594.343707: tracing_mark_write: B|625|dequeueBuffer + <...>-625 (-----) [004] ...1 14594.812869: tracing_mark_write: B|625|com.google.android.dialer/com.google.android.dialer.extensions.GoogleDialtactsActivity#0: 2 + <...>-2048 (-----) [000] d..2 14594.812895: sched_switch: prev_comm=RenderThread prev_pid=2048 prev_prio=120 prev_state=R+ ==> next_comm=Binder:625_3 next_pid=1431 next_prio=120 + <...>-1431 (-----) [000] ...1 14594.812911: tracing_mark_write: B|625|query + <...>-625 (-----) [004] ...1 14594.812914: tracing_mark_write: B|625|latchBuffer + <...>-625 (-----) [004] ...1 14594.812919: tracing_mark_write: B|625|query + <...>-625 (-----) [004] ...1 14594.812925: tracing_mark_write: B|625|updateTexImage + <...>-625 (-----) [004] ...1 14594.812928: tracing_mark_write: B|625|acquireBuffer + <...>-625 (-----) [004] ...1 14594.812934: tracing_mark_write: B|625|StatusBar#0: 1 + <...>-2048 (-----) [000] ...1 14594.812962: tracing_mark_write: B|1803|syncFrameState + <...>-656 (-----) [002] ...1 14594.813044: tracing_mark_write: B|625|setTransactionState + <...>-14607 (-----) [007] ...2 14594.813083: binder_set_priority: proc=10691 thread=18733 old=120 => new=110 desired=110 + <...>-14607 (-----) [007] d..2 14594.813114: sched_switch: prev_comm=.android.dialer prev_pid=14607 prev_prio=110 prev_state=S ==> next_comm=kworker/7:1 next_pid=7092 next_prio=120 + <...>-14655 (-----) [006] d..2 14594.813128: sched_switch: prev_comm=DialerExecutors prev_pid=14655 prev_prio=130 prev_state=R ==> next_comm=lpass_smem_glin next_pid=89 next_prio=98 + <...>-89 (-----) [006] d..2 14594.813163: sched_switch: prev_comm=lpass_smem_glin prev_pid=89 prev_prio=98 prev_state=S ==> next_comm=DialerExecutors next_pid=14655 next_prio=130 + <...>-656 (-----) [002] ...1 14594.813218: tracing_mark_write: B|625|requestNextVsync + <...>-656 (-----) [002] ...1 14594.813222: tracing_mark_write: B|625|resetIdleTimer + kworker/7:1-7092 ( 7092) [007] d..2 14594.813239: sched_switch: prev_comm=kworker/7:1 prev_pid=7092 prev_prio=120 prev_state=R+ ==> next_comm=smem_native_lpa next_pid=88 next_prio=98 + <...>-5281 (-----) [001] d..2 14594.813245: sched_switch: prev_comm=writer prev_pid=5281 prev_prio=96 prev_state=S ==> next_comm=Binder:10691_B next_pid=18733 next_prio=110 + <...>-88 (-----) [007] d..2 14594.813248: sched_switch: prev_comm=smem_native_lpa prev_pid=88 prev_prio=98 prev_state=R ==> next_comm=kgsl_worker_thr next_pid=259 next_prio=97 + <...>-2048 (-----) [000] d..2 14594.813249: sched_switch: prev_comm=RenderThread prev_pid=2048 prev_prio=120 prev_state=R+ ==> next_comm=FastMixer next_pid=1238 next_prio=96 + <...>-14655 (-----) [006] d..2 14594.813263: sched_switch: prev_comm=DialerExecutors prev_pid=14655 prev_prio=130 prev_state=R+ ==> next_comm=smem_native_lpa next_pid=88 next_prio=98 + <...>-661 (-----) [002] d..2 14594.813265: sched_switch: prev_comm=sf prev_pid=661 prev_prio=97 prev_state=S ==> next_comm=Binder:625_1 next_pid=656 next_prio=116 + <...>-259 (-----) [007] d..2 14594.813265: sched_switch: prev_comm=kgsl_worker_thr prev_pid=259 prev_prio=97 prev_state=S ==> next_comm=kworker/7:1 next_pid=7092 next_prio=120 + kworker/7:1-7092 ( 7092) [007] d..2 14594.813271: sched_switch: prev_comm=kworker/7:1 prev_pid=7092 prev_prio=120 prev_state=S ==> next_comm=system next_pid=108 next_prio=120 + <...>-108 (-----) [007] .... 14594.813275: ion_heap_shrink: heap_name=system, len=9469952, total_allocated=189620224 + <...>-88 (-----) [006] d..2 14594.813294: sched_switch: prev_comm=smem_native_lpa prev_pid=88 prev_prio=98 prev_state=S ==> next_comm=DialerExecutors next_pid=14655 next_prio=130 + <...>-625 (-----) [004] ...1 14594.813310: tracing_mark_write: B|625|updateInputFlinger + <...>-1238 (-----) [000] d..2 14594.813312: sched_switch: prev_comm=FastMixer prev_pid=1238 prev_prio=96 prev_state=S ==> next_comm=RenderThread next_pid=2048 next_prio=120 + <...>-661 (-----) [002] d..2 14594.813317: sched_switch: prev_comm=sf prev_pid=661 prev_prio=97 prev_state=S ==> next_comm=Binder:625_1 next_pid=656 next_prio=116 + <...>-14640 (-----) [005] d..2 14594.813319: sched_switch: prev_comm=DialerExecutors prev_pid=14640 prev_prio=130 prev_state=R ==> next_comm=DispSync next_pid=658 next_prio=97 + <...>-656 (-----) [002] ...1 14594.813336: tracing_mark_write: B|625|~GraphicBuffer + <...>-658 (-----) [005] d..2 14594.813345: sched_switch: prev_comm=DispSync prev_pid=658 prev_prio=97 prev_state=S ==> next_comm=DialerExecutors next_pid=14640 next_prio=130 + <...>-656 (-----) [002] ...1 14594.813345: tracing_mark_write: B|625|~GraphicBuffer + <...>-656 (-----) [002] ...1 14594.813353: tracing_mark_write: B|625|~GraphicBuffer + <...>-2048 (-----) [000] d..2 14594.813358: sched_switch: prev_comm=RenderThread prev_pid=2048 prev_prio=120 prev_state=R+ ==> next_comm=FastMixer next_pid=1238 next_prio=96 + <...>-656 (-----) [002] ...1 14594.813364: tracing_mark_write: B|625|~GraphicBuffer + <...>-5281 (-----) [001] d..2 14594.813369: sched_switch: prev_comm=writer prev_pid=5281 prev_prio=96 prev_state=S ==> next_comm=Binder:10691_B next_pid=18733 next_prio=110 + <...>-656 (-----) [002] ...1 14594.813372: tracing_mark_write: B|625|~GraphicBuffer + <...>-656 (-----) [002] ...1 14594.813380: tracing_mark_write: B|625|~GraphicBuffer + <...>-656 (-----) [002] ...1 14594.813391: tracing_mark_write: B|625|~GraphicBuffer + <...>-656 (-----) [002] ...1 14594.813398: tracing_mark_write: B|625|~GraphicBuffer + <...>-656 (-----) [002] ...1 14594.813408: tracing_mark_write: B|625|~GraphicBuffer + <...>-656 (-----) [002] ...1 14594.813416: tracing_mark_write: B|625|~GraphicBuffer + <...>-656 (-----) [002] ...1 14594.813424: tracing_mark_write: B|625|~GraphicBuffer + <...>-656 (-----) [002] ...1 14594.813432: tracing_mark_write: B|625|~GraphicBuffer + <...>-656 (-----) [002] .n.1 14594.813443: tracing_mark_write: B|625|~GraphicBuffer + <...>-1238 (-----) [000] d..2 14594.813464: sched_switch: prev_comm=FastMixer prev_pid=1238 prev_prio=96 prev_state=S ==> next_comm=RenderThread next_pid=2048 next_prio=120 + <...>-5281 (-----) [002] d..2 14594.813525: sched_switch: prev_comm=writer prev_pid=5281 prev_prio=96 prev_state=S ==> next_comm=Binder:625_1 next_pid=656 next_prio=116 + <...>-656 (-----) [002] ...1 14594.813544: tracing_mark_write: B|625|~GraphicBuffer + <...>-656 (-----) [002] ...1 14594.813557: tracing_mark_write: B|625|~GraphicBuffer + <...>-2048 (-----) [000] d..2 14594.813594: sched_switch: prev_comm=RenderThread prev_pid=2048 prev_prio=120 prev_state=R+ ==> next_comm=Binder:1368_15 next_pid=3359 next_prio=120 + <...>-18733 (-----) [001] ...2 14594.813635: binder_set_priority: proc=1368 thread=3514 old=120 => new=110 desired=110 + <...>-656 (-----) [002] .... 14594.813637: binder_set_priority: proc=625 thread=656 old=116 => new=120 desired=120 + <...>-108 (-----) [007] d..2 14594.813646: sched_switch: prev_comm=system prev_pid=108 prev_prio=120 prev_state=R+ ==> next_comm=android.anim next_pid=1398 next_prio=116 + <...>-625 (-----) [004] ...1 14594.813646: tracing_mark_write: B|625|onMessageReceived + <...>-625 (-----) [004] ...1 14594.813649: tracing_mark_write: B|625|handleMessageRefresh + <...>-625 (-----) [004] ...1 14594.813651: tracing_mark_write: B|625|preComposition + <...>-625 (-----) [004] ...1 14594.813693: tracing_mark_write: B|625|rebuildLayerStacks + <...>-625 (-----) [004] ...1 14594.813696: tracing_mark_write: B|625|rebuildLayerStacks VR Dirty + <...>-625 (-----) [004] ...1 14594.813701: tracing_mark_write: B|625|computeVisibleRegions + <...>-1398 (-----) [007] d..2 14594.813718: sched_switch: prev_comm=android.anim prev_pid=1398 prev_prio=116 prev_state=S ==> next_comm=system next_pid=108 next_prio=120 + <...>-108 (-----) [007] d..2 14594.813739: sched_switch: prev_comm=system prev_pid=108 prev_prio=120 prev_state=R+ ==> next_comm=android.anim next_pid=1398 next_prio=116 + <...>-1695 (-----) [002] d..2 14594.813970: sched_switch: prev_comm=InputDispatcher prev_pid=1695 prev_prio=112 prev_state=S ==> next_comm=system next_pid=108 next_prio=120 + <...>-1398 (-----) [007] ...1 14594.814029: tracing_mark_write: B|1368|wmLayout + <...>-1398 (-----) [007] ...1 14594.814033: tracing_mark_write: B|1368|performSurfacePlacement + <...>-1398 (-----) [007] ...1 14594.814040: tracing_mark_write: B|1368|applySurfaceChanges + <...>-1398 (-----) [007] ...1 14594.814043: tracing_mark_write: B|1368|openSurfaceTransaction + <...>-1398 (-----) [007] ...1 14594.814063: tracing_mark_write: B|1368|performLayout + <...>-625 (-----) [004] ...1 14594.814119: tracing_mark_write: B|625|calculateWorkingSet + <...>-1398 (-----) [007] ...1 14594.814241: tracing_mark_write: B|1368|layoutInputConsumer + <...>-2048 (-----) [000] ...1 14594.814260: tracing_mark_write: B|1803|prepareTree + <...>-1398 (-----) [007] ...1 14594.814263: tracing_mark_write: B|1368|applyPostLayoutPolicy + <...>-2048 (-----) [000] d..2 14594.814408: sched_switch: prev_comm=RenderThread prev_pid=2048 prev_prio=120 prev_state=R ==> next_comm=Binder:1368_15 next_pid=3359 next_prio=120 + <...>-625 (-----) [004] ...1 14594.814411: tracing_mark_write: B|625|prepare + <...>-625 (-----) [004] ...1 14594.814428: tracing_mark_write: B|625|HIDL::IComposerClient::executeCommands_2_2::client + <...>-2048 (-----) [000] d..2 14594.814533: sched_switch: prev_comm=RenderThread prev_pid=2048 prev_prio=120 prev_state=R+ ==> next_comm=ndroid.systemui next_pid=1803 next_prio=120 + <...>-1803 (-----) [000] d..2 14594.814558: sched_switch: prev_comm=ndroid.systemui prev_pid=1803 prev_prio=120 prev_state=S ==> next_comm=RenderThread next_pid=2048 next_prio=120 + <...>-2048 (-----) [000] d..2 14594.814572: sched_switch: prev_comm=RenderThread prev_pid=2048 prev_prio=120 prev_state=R+ ==> next_comm=ndroid.systemui next_pid=1803 next_prio=120 + <...>-625 (-----) [004] ...2 14594.814589: binder_set_priority: proc=627 thread=627 old=97 => new=98 desired=98 + <...>-108 (-----) [002] d..2 14594.814650: sched_switch: prev_comm=system prev_pid=108 prev_prio=120 prev_state=R+ ==> next_comm=composer@2.2-se next_pid=627 next_prio=98 + <...>-625 (-----) [004] d..2 14594.814664: sched_switch: prev_comm=surfaceflinger prev_pid=625 prev_prio=98 prev_state=S ==> next_comm=ashmemd next_pid=854 next_prio=129 + <...>-1398 (-----) [007] ...1 14594.814723: tracing_mark_write: B|1368|applyWindowSurfaceChanges + <...>-854 (-----) [004] .... 14594.814746: binder_set_priority: proc=854 thread=854 old=129 => new=120 desired=120 + <...>-854 (-----) [004] d..2 14594.814757: sched_switch: prev_comm=ashmemd prev_pid=854 prev_prio=120 prev_state=R+ ==> next_comm=highpool[0] next_pid=3493 next_prio=129 + <...>-1803 (-----) [000] d..2 14594.814763: sched_switch: prev_comm=ndroid.systemui prev_pid=1803 prev_prio=120 prev_state=S ==> next_comm=RenderThread next_pid=2048 next_prio=120 + <...>-18733 (-----) [001] d..1 14594.814819: mm_filemap_delete_from_page_cache: dev 0:1 ino 3ce5e7 page=0000000083f10c7a pfn=1298474 ofs=0 + <...>-2048 (-----) [000] ...1 14594.814842: tracing_mark_write: B|1803|dequeueBuffer + <...>-1398 (-----) [007] ...1 14594.814850: tracing_mark_write: F|1368|launching: com.google.android.dialer|0 + <...>-1398 (-----) [007] ...1 14594.814855: tracing_mark_write: B|1368|MetricsLogger:launchObserverNotifyActivityLaunchFinished + <...>-1398 (-----) [007] ...1 14594.814857: tracing_mark_write: B|1368|MetricsLogger:convertActivityRecordToProto + <...>-2048 (-----) [000] d..2 14594.814905: sched_switch: prev_comm=RenderThread prev_pid=2048 prev_prio=120 prev_state=R+ ==> next_comm=Binder:625_1 next_pid=656 next_prio=120 + <...>-1410 (-----) [006] .... 14592.997816: mm_filemap_add_to_page_cache: dev 253:6 ino b785 page=00000000615a8f24 pfn=1134764 ofs=0 + <...>-1410 (-----) [006] .... 14592.997831: mm_filemap_add_to_page_cache: dev 253:6 ino b785 page=000000008768a58f pfn=1134751 ofs=4096 + + <...>-18733 (-----) [001] .... 14594.814914: binder_set_priority: proc=10691 thread=18733 old=110 => new=120 desired=120 + <...>-14655 (-----) [006] d..2 14594.814932: sched_switch: prev_comm=DialerExecutors prev_pid=14655 prev_prio=130 prev_state=R ==> next_comm=.android.dialer next_pid=14607 next_prio=110 + <...>-656 (-----) [000] ...1 14594.814948: tracing_mark_write: B|625|dequeueBuffer + <...>-3514 (-----) [001] .... 14594.814954: binder_set_priority: proc=1368 thread=3514 old=110 => new=120 desired=120 + <...>-656 (-----) [000] ...1 14594.814963: tracing_mark_write: B|625|NavigationBar0#0: 2 + <...>-14607 (-----) [006] ...2 14594.815022: binder_set_priority: proc=1368 thread=3514 old=120 => new=110 desired=110 + <...>-1398 (-----) [007] ...1 14594.815039: tracing_mark_write: B|1368|prepareSurfaces + <...>-14607 (-----) [006] d..2 14594.815041: sched_switch: prev_comm=.android.dialer prev_pid=14607 prev_prio=110 prev_state=S ==> next_comm=DialerExecutors next_pid=14655 next_prio=130 + <...>-3493 (-----) [004] d..2 14594.815057: sched_switch: prev_comm=highpool[0] prev_pid=3493 prev_prio=129 prev_state=R ==> next_comm=Binder:1368_18 next_pid=3514 next_prio=110 + <...>-2048 (-----) [000] ...1 14594.815088: tracing_mark_write: B|1803|HWC release fence 45750 has signaled + <...>-2048 (-----) [000] ...1 14594.815119: tracing_mark_write: B|1803|eglBeginFrame + <...>-14655 (-----) [006] d..2 14594.815190: sched_switch: prev_comm=DialerExecutors prev_pid=14655 prev_prio=130 prev_state=R ==> next_comm=crtc_commit:97 next_pid=301 next_prio=83 + <...>-3514 (-----) [004] .... 14594.815193: binder_set_priority: proc=1368 thread=3514 old=110 => new=120 desired=120 + <...>-1398 (-----) [007] ...1 14594.815322: tracing_mark_write: B|1368|closeSurfaceTransaction + <...>-3493 (-----) [004] .... 14594.815353: mm_filemap_add_to_page_cache: dev 253:6 ino 113b page=0000000069e2b98a pfn=628464 ofs=2723840 + <...>-1398 (-----) [007] ...2 14594.815393: binder_set_priority: proc=625 thread=656 old=120 => new=116 desired=116 + rcu_sched-8 ( 8) [007] d..2 14594.815449: sched_switch: prev_comm=rcu_sched prev_pid=8 prev_prio=120 prev_state=S ==> next_comm=Binder:625_1 next_pid=656 next_prio=116 diff --git a/startop/scripts/trace_analyzer/trace_analyzer_test.py b/startop/scripts/trace_analyzer/trace_analyzer_test.py new file mode 100644 index 000000000000..579529c6f6d5 --- /dev/null +++ b/startop/scripts/trace_analyzer/trace_analyzer_test.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 +# +# Copyright 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. +# + +""" +Unit tests for trace_analyzer module. + +Install: + $> sudo apt-get install python3-pytest ## OR + $> pip install -U pytest +See also https://docs.pytest.org/en/latest/getting-started.html + +Usage: + $> pytest trace_analyzer_test.py + +See also https://docs.pytest.org/en/latest/usage.html +""" + +# global imports +import os +import sys + +DIR = os.path.abspath(os.path.dirname(__file__)) + +sys.path.append(os.path.dirname(DIR)) +import lib.cmd_utils as cmd_utils + +def test_trace_analyzer(tmpdir): + # Setup + bin = os.path.join(DIR, 'trace_analyzer') + systrace = os.path.join(DIR, 'test_fixtures/common_systrace') + db_file = tmpdir.mkdir('trace_analyzer').join('test.db') + + # Act + passed, output = cmd_utils.execute_arbitrary_command([bin, systrace, + str(db_file)], + timeout=300, + shell=False, + simulate=False) + + # Assert + assert passed + assert output == """\ +'blocked_iowait_duration_ms',\ +'process_name',\ +'launching_duration_ms',\ +'launching_started_timestamp_ms',\ +'launching_finished_timestamp_ms' +81.697999999960302375,\ +'com.google.android.dialer',\ +594.99400000095192808,\ +14594219.85600000061,\ +14594814.85000000149""" diff --git a/telephony/java/com/android/internal/telephony/CarrierAppUtils.java b/telephony/java/com/android/internal/telephony/CarrierAppUtils.java index a1bea4d417f9..6498e498bfbe 100644 --- a/telephony/java/com/android/internal/telephony/CarrierAppUtils.java +++ b/telephony/java/com/android/internal/telephony/CarrierAppUtils.java @@ -22,6 +22,7 @@ import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.os.RemoteException; +import android.permission.IPermissionManager; import android.provider.Settings; import android.telephony.TelephonyManager; import android.util.ArrayMap; @@ -71,8 +72,8 @@ public final class CarrierAppUtils { * privileged apps may have changed. */ public synchronized static void disableCarrierAppsUntilPrivileged(String callingPackage, - IPackageManager packageManager, TelephonyManager telephonyManager, - ContentResolver contentResolver, int userId) { + IPackageManager packageManager, IPermissionManager permissionManager, + TelephonyManager telephonyManager, ContentResolver contentResolver, int userId) { if (DEBUG) { Slog.d(TAG, "disableCarrierAppsUntilPrivileged"); } @@ -81,8 +82,8 @@ public final class CarrierAppUtils { config.getDisabledUntilUsedPreinstalledCarrierApps(); ArrayMap<String, List<String>> systemCarrierAssociatedAppsDisabledUntilUsed = config.getDisabledUntilUsedPreinstalledCarrierAssociatedApps(); - disableCarrierAppsUntilPrivileged(callingPackage, packageManager, telephonyManager, - contentResolver, userId, systemCarrierAppsDisabledUntilUsed, + disableCarrierAppsUntilPrivileged(callingPackage, packageManager, permissionManager, + telephonyManager, contentResolver, userId, systemCarrierAppsDisabledUntilUsed, systemCarrierAssociatedAppsDisabledUntilUsed); } @@ -98,7 +99,8 @@ public final class CarrierAppUtils { * Manager can kill it, and this can lead to crashes as the app is in an unexpected state. */ public synchronized static void disableCarrierAppsUntilPrivileged(String callingPackage, - IPackageManager packageManager, ContentResolver contentResolver, int userId) { + IPackageManager packageManager, IPermissionManager permissionManager, + ContentResolver contentResolver, int userId) { if (DEBUG) { Slog.d(TAG, "disableCarrierAppsUntilPrivileged"); } @@ -109,7 +111,7 @@ public final class CarrierAppUtils { ArrayMap<String, List<String>> systemCarrierAssociatedAppsDisabledUntilUsed = config.getDisabledUntilUsedPreinstalledCarrierAssociatedApps(); - disableCarrierAppsUntilPrivileged(callingPackage, packageManager, + disableCarrierAppsUntilPrivileged(callingPackage, packageManager, permissionManager, null /* telephonyManager */, contentResolver, userId, systemCarrierAppsDisabledUntilUsed, systemCarrierAssociatedAppsDisabledUntilUsed); } @@ -117,7 +119,8 @@ public final class CarrierAppUtils { // Must be public b/c framework unit tests can't access package-private methods. @VisibleForTesting public static void disableCarrierAppsUntilPrivileged(String callingPackage, - IPackageManager packageManager, @Nullable TelephonyManager telephonyManager, + IPackageManager packageManager, IPermissionManager permissionManager, + @Nullable TelephonyManager telephonyManager, ContentResolver contentResolver, int userId, ArraySet<String> systemCarrierAppsDisabledUntilUsed, ArrayMap<String, List<String>> systemCarrierAssociatedAppsDisabledUntilUsed) { @@ -256,7 +259,7 @@ public final class CarrierAppUtils { // apps. String[] packageNames = new String[enabledCarrierPackages.size()]; enabledCarrierPackages.toArray(packageNames); - packageManager.grantDefaultPermissionsToEnabledCarrierApps(packageNames, userId); + permissionManager.grantDefaultPermissionsToEnabledCarrierApps(packageNames, userId); } } catch (RemoteException e) { Slog.w(TAG, "Could not reach PackageManager", e); diff --git a/test-mock/src/android/test/mock/MockContentProvider.java b/test-mock/src/android/test/mock/MockContentProvider.java index e9a5ff70a7cc..4d8c7d930bde 100644 --- a/test-mock/src/android/test/mock/MockContentProvider.java +++ b/test-mock/src/android/test/mock/MockContentProvider.java @@ -16,6 +16,7 @@ package android.test.mock; +import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ContentProvider; import android.content.ContentProviderOperation; @@ -23,6 +24,7 @@ import android.content.ContentProviderResult; import android.content.ContentValues; import android.content.Context; import android.content.IContentProvider; +import android.content.Intent; import android.content.OperationApplicationException; import android.content.pm.PathPermission; import android.content.pm.ProviderInfo; @@ -154,6 +156,11 @@ public class MockContentProvider extends ContentProvider { ICancellationSignal cancellationSignal) throws RemoteException { return MockContentProvider.this.refresh(url, args); } + + @Override + public int checkUriPermission(String callingPkg, Uri uri, int uid, int modeFlags) { + return MockContentProvider.this.checkUriPermission(uri, uid, modeFlags); + } } private final InversionIContentProvider mIContentProvider = new InversionIContentProvider(); @@ -266,6 +273,12 @@ public class MockContentProvider extends ContentProvider { throw new UnsupportedOperationException("unimplemented mock method call"); } + /** {@hide} */ + @Override + public int checkUriPermission(@NonNull Uri uri, int uid, @Intent.AccessUriMode int modeFlags) { + throw new UnsupportedOperationException("unimplemented mock method call"); + } + /** * Returns IContentProvider which calls back same methods in this class. * By overriding this class, we avoid the mechanism hidden behind ContentProvider diff --git a/test-mock/src/android/test/mock/MockIContentProvider.java b/test-mock/src/android/test/mock/MockIContentProvider.java index fc2a4644b994..b072d7440de4 100644 --- a/test-mock/src/android/test/mock/MockIContentProvider.java +++ b/test-mock/src/android/test/mock/MockIContentProvider.java @@ -16,12 +16,14 @@ package android.test.mock; +import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ContentProviderOperation; import android.content.ContentProviderResult; import android.content.ContentValues; import android.content.EntityIterator; import android.content.IContentProvider; +import android.content.Intent; import android.content.res.AssetFileDescriptor; import android.database.Cursor; import android.net.Uri; @@ -144,4 +146,10 @@ public class MockIContentProvider implements IContentProvider { ICancellationSignal cancellationSignal) throws RemoteException { throw new UnsupportedOperationException("unimplemented mock method"); } + + /** {@hide} */ + @Override + public int checkUriPermission(String callingPkg, Uri uri, int uid, int modeFlags) { + throw new UnsupportedOperationException("unimplemented mock method call"); + } } diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/errorCalculator.rs b/tests/CanvasCompare/src/com/android/test/hwuicompare/errorCalculator.rscript index 0a1742ef3867..0a1742ef3867 100644 --- a/tests/CanvasCompare/src/com/android/test/hwuicompare/errorCalculator.rs +++ b/tests/CanvasCompare/src/com/android/test/hwuicompare/errorCalculator.rscript diff --git a/tests/Codegen/Android.bp b/tests/Codegen/Android.bp new file mode 100644 index 000000000000..966c5602959c --- /dev/null +++ b/tests/Codegen/Android.bp @@ -0,0 +1,25 @@ +android_test { + name: "CodegenTests", + srcs: [ + "**/*.java", + ], + + platform_apis: true, + test_suites: ["device-tests"], + certificate: "platform", + + optimize: { + enabled: false, + }, + + plugins: [ + "staledataclass-annotation-processor", + ], + static_libs: [ + "junit", + "hamcrest", + "hamcrest-library", + "androidx.test.runner", + "androidx.test.rules", + ], +} diff --git a/tests/Codegen/AndroidManifest.xml b/tests/Codegen/AndroidManifest.xml new file mode 100644 index 000000000000..2f1855035cd8 --- /dev/null +++ b/tests/Codegen/AndroidManifest.xml @@ -0,0 +1,26 @@ +<!-- + ~ Copyright (C) 2015 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.codegentest"> + + <application/> + + <instrumentation + android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.codegentest" + android:label="Codegen test" /> +</manifest> diff --git a/tests/Codegen/AndroidTest.xml b/tests/Codegen/AndroidTest.xml new file mode 100644 index 000000000000..4dbbc5556c64 --- /dev/null +++ b/tests/Codegen/AndroidTest.xml @@ -0,0 +1,23 @@ +<?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. +--> +<configuration description="Runs Codegen Tests."> + + <test class="com.android.tradefed.testtype.AndroidJUnitTest" > + <option name="package" value="com.android.codegentest" /> + <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> + <option name="hidden-api-checks" value="false"/> + </test> +</configuration> diff --git a/tests/Codegen/OWNERS b/tests/Codegen/OWNERS new file mode 100644 index 000000000000..da723b3b67da --- /dev/null +++ b/tests/Codegen/OWNERS @@ -0,0 +1 @@ +eugenesusla@google.com
\ No newline at end of file diff --git a/tests/Codegen/runTest.sh b/tests/Codegen/runTest.sh new file mode 100755 index 000000000000..fe3adf9b2a70 --- /dev/null +++ b/tests/Codegen/runTest.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +if [[ "$0" = *"/Codegen/runTest.sh" ]]; then + #running in subshell - print code to eval and exit + echo "source $0" +else + function header_and_eval() { + printf "\n[ $* ]\n" 1>&2 + eval "$@" + return $? + } + + header_and_eval m -j16 codegen && \ + header_and_eval codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/SampleDataClass.java && \ + cd $ANDROID_BUILD_TOP && + header_and_eval mmma -j16 frameworks/base/tests/Codegen && \ + header_and_eval adb install -r -t $ANDROID_PRODUCT_OUT/testcases/CodegenTests/arm64/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=$? + + # header_and_eval adb shell am clear-debug-app + + return $exitCode +fi
\ No newline at end of file diff --git a/tests/Codegen/src/com/android/codegentest/DateParcelling.java b/tests/Codegen/src/com/android/codegentest/DateParcelling.java new file mode 100644 index 000000000000..b0b00d032553 --- /dev/null +++ b/tests/Codegen/src/com/android/codegentest/DateParcelling.java @@ -0,0 +1,51 @@ +/* + * 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.codegentest; + +import android.os.Parcel; + +import com.android.internal.util.Parcelling; + +import java.util.Date; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Sample {@link Parcelling} implementation for {@link Date}. + * + * See {@link SampleDataClass#mDate} for usage. + * See {@link SampleDataClass#writeToParcel} + {@link SampleDataClass#sParcellingForDate} + * for resulting generated code. + * + * Ignore {@link #sInstanceCount} - used for testing. + */ +public class DateParcelling implements Parcelling<Date> { + + static AtomicInteger sInstanceCount = new AtomicInteger(0); + + public DateParcelling() { + sInstanceCount.getAndIncrement(); + } + + @Override + public void parcel(Date item, Parcel dest, int parcelFlags) { + dest.writeLong(item.getTime()); + } + + @Override + public Date unparcel(Parcel source) { + return new Date(source.readLong()); + } +} diff --git a/tests/Codegen/src/com/android/codegentest/SampleDataClass.aidl b/tests/Codegen/src/com/android/codegentest/SampleDataClass.aidl new file mode 100644 index 000000000000..f14d47cde13f --- /dev/null +++ b/tests/Codegen/src/com/android/codegentest/SampleDataClass.aidl @@ -0,0 +1,18 @@ +/* + * 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.codegentest; + +parcelable SampleDataClass; diff --git a/tests/Codegen/src/com/android/codegentest/SampleDataClass.java b/tests/Codegen/src/com/android/codegentest/SampleDataClass.java new file mode 100644 index 000000000000..03127ec2814b --- /dev/null +++ b/tests/Codegen/src/com/android/codegentest/SampleDataClass.java @@ -0,0 +1,1542 @@ +/* + * 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.codegentest; + +import android.annotation.FloatRange; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.Size; +import android.annotation.StringDef; +import android.annotation.StringRes; +import android.annotation.UserIdInt; +import android.net.LinkAddress; +import android.os.Parcel; +import android.os.Parcelable; +import android.view.accessibility.AccessibilityNodeInfo; + +import com.android.internal.util.AnnotationValidations; +import com.android.internal.util.DataClass; +import com.android.internal.util.DataClass.Each; +import com.android.internal.util.Parcelling; +import com.android.internal.util.Preconditions; + +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Objects; +import java.util.regex.Pattern; + +/** + * Sample data class, showing off various code generation features. + * + * See javadoc on non-generated code for the explanation of the various features. + * + * See {@link SampleDataClassTest} for various invariants the generated code is expected to hold. + */ +@DataClass( +// genParcelable = true, // implied by `implements Parcelable` +// genAidl = true, // implied by `implements Parcelable` +// genGetters = true, // on by default +// genConstDefs = true, // implied by presence of constants with common prefix + genEqualsHashCode = true, + genBuilder = true, + genToString = true, + genForEachField = true, + genConstructor = true // on by default but normally suppressed by genBuilder +) +public final class SampleDataClass implements Parcelable { + + /** + * For any group of {@link int} or {@link String} constants like these, a corresponding + * {@link IntDef}/{@link StringDef} will get generated, with name based on common prefix + * by default. + * + * When {@link #SampleDataClass constructing} an instance, fields annotated with these + * annotations get automatically validated, with only provided constants being a valid value. + * + * @see StateName, the generated {@link StringDef} + * @see #mStateName annotated with {@link StateName} + */ + public static final String STATE_NAME_UNDEFINED = "?"; + public static final String STATE_NAME_ON = "on"; + public static final String STATE_NAME_OFF = "off"; + + /** + * Additionally, for any generated {@link IntDef} a corresponding static + * *ToString method will be also generated, and used in {@link #toString()}. + * + * @see #stateToString(int) + * @see #toString() + * @see State + */ + public static final int STATE_UNDEFINED = -1; + public static final int STATE_ON = 1; + public static final int STATE_OFF = 0; + + /** + * {@link IntDef}s with values specified in hex("0x...") are considered to be + * {@link IntDef#flag flags}, while ones specified with regular int literals are considered + * not to be flags. + * + * This affects their string representation, e.g. see the difference in + * {@link #requestFlagsToString} vs {@link #stateToString}. + * + * This also affects the validation logic when {@link #SampleDataClass constructing} + * an instance, with any flag combination("|") being valid. + * + * You can customize the name of the generated {@link IntDef}/{@link StringDef} annotation + * by annotating each constant with the desired name before running the generation. + * + * Here the annotation is named {@link RequestFlags} instead of the default {@code Flags}. + */ + public static final @RequestFlags int FLAG_MANUAL_REQUEST = 0x1; + public static final @RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST = 0x2; + public static final @RequestFlags int FLAG_AUGMENTED_REQUEST = 0x80000000; + + + /** + * Any property javadoc should go onto the field, and will be copied where appropriate, + * including getters, constructor parameters, builder setters, etc. + * + * <p> + * This allows to avoid the burden of maintaining copies of the same documentation + * pieces in multiple places for each field. + */ + private int mNum; + /** + * Various javadoc features should work as expected when copied, e.g {@code code}, + * {@link #mName links}, <a href="https://google.com">html links</a>, etc. + * + * @see #mNum2 ..and so should blocks at the bottom, e.g. {@code @see} blocks. + */ + private int mNum2; + /** + * {@code @hide} javadoc annotation is also propagated, which can be used to adjust the + * desired public API surface. + * + * @see #getNum4() is hidden + * @see Builder#setNum4(int) also hidden + * @hide + */ + private int mNum4; + + /** + * {@link Nullable} fields are considered optional and will not throw an exception if omitted + * (or set to null) when creating an instance either via a {@link Builder} or constructor. + */ + private @Nullable String mName; + /** + * Fields with default value expressions ("mFoo = ...") are also optional, and are automatically + * initialized to the provided default expression, unless explicitly set. + */ + private String mName2 = "Bob"; + /** + * Fields without {@link Nullable} annotation or default value are considered required. + * + * {@link NonNull} annotation is recommended on such non-primitive fields for documentation. + */ + private @NonNull String mName4; + + /** + * For parcelling, any field type supported by {@link Parcel} is supported out of the box. + * E.g. {@link Parcelable} subclasses, {@link String}, {@link int}, {@link boolean}, etc. + */ + private AccessibilityNodeInfo mOtherParcelable = null; + /** + * Additionally, support for parcelling other types can be added by implementing a + * {@link Parcelling}, and referencing it in the {@link DataClass.ParcelWith} field annotation. + * + * @see DateParcelling an example {@link Parcelling} implementation + */ + @DataClass.ParcelWith(DateParcelling.class) + private Date mDate = new Date(42 * 42); + /** + * If a {@link Parcelling} is fairly common, consider putting it in {@link Parcelling.BuiltIn} + * to encourage its reuse. + */ + @DataClass.ParcelWith(Parcelling.BuiltIn.ForPattern.class) + private Pattern mPattern = Pattern.compile(""); + + /** + * For lists, when using a {@link Builder}, other than a regular + * {@link Builder#setLinkAddresses2(List) setter}, and additional + * {@link Builder#addLinkAddresses2(LinkAddress) add} method is generated for convenience. + */ + private List<LinkAddress> mLinkAddresses2 = new ArrayList<>(); + /** + * For aesthetics, you may want to consider providing a singular version of the plural field + * name, which would be used for the {@link #mLinkAddresses2 above mentioned} "add" method. + * + * @see Builder#addLinkAddress(LinkAddress) + */ + @DataClass.PluralOf("linkAddress") + private ArrayList<LinkAddress> mLinkAddresses = new ArrayList<>(); + /** + * For array fields, when using a {@link Builder}, vararg argument format is used for + * convenience. + * + * @see Builder#setLinkAddresses4(LinkAddress...) + */ + private @Nullable LinkAddress[] mLinkAddresses4 = null; + /** + * For boolean fields, when using a {@link Builder}, in addition to a regular setter, methods + * like {@link Builder#markActive()} and {@link Builder#markNotActive()} are generated. + */ + private boolean mActive = true; + + /** + * {@link IntDef}/{@link StringDef}-annotated fields propagate the annotation to + * getter/constructor/setter/builder parameters, making for a nicer api. + * + * @see #getStateName + * @see Builder#setStateName + */ + private @StateName String mStateName = STATE_NAME_UNDEFINED; + /** + * Fields annotated with {@link IntDef} annotations also get a proper {@link #toString()} value. + */ + private @RequestFlags int mFlags; + /** + * Above is true for both {@link IntDef#flag flags} and enum-like {@link IntDef}s + */ + private @State int mState = STATE_UNDEFINED; + + + /** + * Making a field public will suppress getter generation in favor of accessing it directly. + */ + public CharSequence charSeq = ""; + /** + * Final fields suppress generating a setter (when setters are requested). + */ + private final LinkAddress[] mLinkAddresses5; + /** + * Transient fields are completely ignored and can be used for caching. + */ + private transient LinkAddress[] mLinkAddresses6; + /** + * When using transient fields for caching it's often also a good idea to initialize them + * lazily. + * + * You can declare a special method like {@link #lazyInitTmpStorage()}, to let the + * {@link #getTmpStorage getter} lazily-initialize the value on demand. + */ + transient int[] mTmpStorage; + private int[] lazyInitTmpStorage() { + return new int[100]; + } + + /** + * Fields with certain annotations are automatically validated in constructor + * + * You can see overloads in {@link AnnotationValidations} for a list of currently + * supported ones. + * + * You can also extend support to your custom annotations by creating another corresponding + * overloads like + * {@link AnnotationValidations#validate(Class, UserIdInt, int)}. + * + * @see #SampleDataClass + */ + private @StringRes int mStringRes = 0; + /** + * Validation annotations may also have parameters. + * + * Parameter values will be supplied to validation method as name-value pairs. + * + * @see AnnotationValidations#validate(Class, Size, int, String, int, String, int) + */ + private @android.annotation.IntRange(from = 0, to = 4) int mLimited = 3; + /** + * Unnamed validation annotation parameter gets supplied to the validating method named as + * "value". + * + * Validation annotations following {@link Each} annotation, will be applied for each + * array/collection element instead. + * + * @see AnnotationValidations#validate(Class, Size, int, String, int) + */ + @Size(2) + @Each @FloatRange(from = 0f) + private float[] mCoords = new float[] {0f, 0f}; + + + /** + * Manually declaring any method that would otherwise be generated suppresses its generation, + * allowing for fine-grained overrides of the generated behavior. + */ + public LinkAddress[] getLinkAddresses4() { + //Suppress autogen + return null; + } + + /** + * Additionally, some methods like {@link #equals}, {@link #hashCode}, {@link #toString}, + * {@link #writeToParcel}, {@link Parcelable.Creator#createFromParcel} allow you to define + * special methods to override their behavior on a per-field basis. + * + * See the generateted methods' descriptions for the detailed instructions of what the method + * signatures for such methods are expected to be. + * + * Here we use this to "fix" {@link Pattern} not implementing equals/hashCode. + * + * @see #equals + * @see #hashCode + */ + private boolean patternEquals(Pattern other) { + return Objects.equals(mPattern.pattern(), other.pattern()); + } + private int patternHashCode() { + return Objects.hashCode(mPattern.pattern()); + } + + /** + * Similarly, {@link #onConstructed()}, if defined, gets called at the end of constructing an + * instance. + * + * At this point all fields should be in place, so this is the right place to put any custom + * validation logic. + */ + private void onConstructed() { + Preconditions.checkState(mNum2 == mNum4); + } + + /** + * {@link DataClass#genForEachField} can be used to generate a generic {@link #forEachField} + * utility, which can be used for various use-cases not covered out of the box. + * Callback passed to {@link #forEachField} will be called once per each property with its name + * and value. + * + * Here for example it's used to implement a typical dump method. + * + * Note that there are 2 {@link #forEachField} versions provided, one that treats each field + * value as an {@link Object}, thus boxing primitives if any, and one that additionally takes + * specialized callbacks for particular primitive field types used in given class. + * + * Some primitives like {@link Boolean}s and {@link Integer}s within [-128, 127] don't allocate + * when boxed, so it's up to you to decide which one to use for a given use-case. + */ + public void dump(PrintWriter pw) { + forEachField((self, name, value) -> { + pw.append(" ").append(name).append(": ").append(String.valueOf(value)).append('\n'); + }); + } + + + + // Code below generated by codegen v0.0.1. + // on Jul 17, 2019, 5:10:26 PM PDT + // + // DO NOT MODIFY! + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/SampleDataClass.java + // + // CHECKSTYLE:OFF Generated code + + @IntDef(prefix = "STATE_", value = { + STATE_UNDEFINED, + STATE_ON, + STATE_OFF + }) + @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) + @DataClass.Generated.Member + public @interface State {} + + @DataClass.Generated.Member + public static String stateToString(@State int value) { + switch (value) { + case STATE_UNDEFINED: + return "STATE_UNDEFINED"; + case STATE_ON: + return "STATE_ON"; + case STATE_OFF: + return "STATE_OFF"; + default: return Integer.toHexString(value); + } + } + + @IntDef(flag = true, prefix = "FLAG_", value = { + FLAG_MANUAL_REQUEST, + FLAG_COMPATIBILITY_MODE_REQUEST, + FLAG_AUGMENTED_REQUEST + }) + @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) + @DataClass.Generated.Member + public @interface RequestFlags {} + + @DataClass.Generated.Member + public static String requestFlagsToString(@RequestFlags int value) { + return com.android.internal.util.BitUtils.flagsToString( + value, SampleDataClass::singleRequestFlagsToString); + } + + @DataClass.Generated.Member + static String singleRequestFlagsToString(@RequestFlags int value) { + switch (value) { + case FLAG_MANUAL_REQUEST: + return "FLAG_MANUAL_REQUEST"; + case FLAG_COMPATIBILITY_MODE_REQUEST: + return "FLAG_COMPATIBILITY_MODE_REQUEST"; + case FLAG_AUGMENTED_REQUEST: + return "FLAG_AUGMENTED_REQUEST"; + default: return Integer.toHexString(value); + } + } + + @StringDef(prefix = "STATE_NAME_", value = { + STATE_NAME_UNDEFINED, + STATE_NAME_ON, + STATE_NAME_OFF + }) + @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) + @DataClass.Generated.Member + public @interface StateName {} + + @DataClass.Generated( + time = 1563408627046L, + codegenVersion = "0.0.1", + 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 java.lang.String mName2\nprivate @android.annotation.NonNull java.lang.String mName4\nprivate android.view.accessibility.AccessibilityNodeInfo mOtherParcelable\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.codegentest.DateParcelling.class) java.util.Date mDate\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForPattern.class) java.util.regex.Pattern mPattern\nprivate java.util.List<android.net.LinkAddress> mLinkAddresses2\nprivate @com.android.internal.util.DataClass.PluralOf(\"linkAddress\") java.util.ArrayList<android.net.LinkAddress> mLinkAddresses\nprivate @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses4\nprivate boolean mActive\nprivate @com.android.codegentest.SampleDataClass.StateName java.lang.String mStateName\nprivate @com.android.codegentest.SampleDataClass.RequestFlags int mFlags\nprivate @com.android.codegentest.SampleDataClass.State int mState\npublic java.lang.CharSequence charSeq\nprivate final 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=4L) int mLimited\nprivate @android.annotation.Size(2L) @com.android.internal.util.DataClass.Each @android.annotation.FloatRange(from=0.0) float[] mCoords\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)") + +/** + * @param num + * Any property javadoc should go onto the field, and will be copied where appropriate, + * including getters, constructor parameters, builder setters, etc. + * + * <p> + * This allows to avoid the burden of maintaining copies of the same documentation + * pieces in multiple places for each field. + * @param num2 + * Various javadoc features should work as expected when copied, e.g {@code code}, + * {@link #mName links}, <a href="https://google.com">html links</a>, etc. + * @param num4 + * {@code @hide} javadoc annotation is also propagated, which can be used to adjust the + * desired public API surface. + * @param name + * {@link Nullable} fields are considered optional and will not throw an exception if omitted + * (or set to null) when creating an instance either via a {@link Builder} or constructor. + * @param name2 + * Fields with default value expressions ("mFoo = ...") are also optional, and are automatically + * initialized to the provided default expression, unless explicitly set. + * @param name4 + * Fields without {@link Nullable} annotation or default value are considered required. + * + * {@link NonNull} annotation is recommended on such non-primitive fields for documentation. + * @param otherParcelable + * For parcelling, any field type supported by {@link Parcel} is supported out of the box. + * E.g. {@link Parcelable} subclasses, {@link String}, {@link int}, {@link boolean}, etc. + * @param date + * Additionally, support for parcelling other types can be added by implementing a + * {@link Parcelling}, and referencing it in the {@link DataClass.ParcelWith} field annotation. + * @param pattern + * If a {@link Parcelling} is fairly common, consider putting it in {@link Parcelling.BuiltIn} + * to encourage its reuse. + * @param linkAddresses2 + * For lists, when using a {@link Builder}, other than a regular + * {@link Builder#setLinkAddresses2(List) setter}, and additional + * {@link Builder#addLinkAddresses2(LinkAddress) add} method is generated for convenience. + * @param linkAddresses + * For aesthetics, you may want to consider providing a singular version of the plural field + * name, which would be used for the {@link #mLinkAddresses2 above mentioned} "add" method. + * @param linkAddresses4 + * For array fields, when using a {@link Builder}, vararg argument format is used for + * convenience. + * @param active + * For boolean fields, when using a {@link Builder}, in addition to a regular setter, methods + * like {@link Builder#markActive()} and {@link Builder#markNotActive()} are generated. + * @param stateName + * {@link IntDef}/{@link StringDef}-annotated fields propagate the annotation to + * getter/constructor/setter/builder parameters, making for a nicer api. + * @param flags + * Fields annotated with {@link IntDef} annotations also get a proper {@link #toString()} value. + * @param state + * Above is true for both {@link IntDef#flag flags} and enum-like {@link IntDef}s + * @param charSeq + * Making a field public will suppress getter generation in favor of accessing it directly. + * @param linkAddresses5 + * Final fields suppress generating a setter (when setters are requested). + * @param stringRes + * Fields with certain annotations are automatically validated in constructor + * + * You can see overloads in {@link AnnotationValidations} for a list of currently + * supported ones. + * + * You can also extend support to your custom annotations by creating another corresponding + * overloads like + * {@link AnnotationValidations#validate(Class, UserIdInt, int)}. + * @param limited + * Validation annotations may also have parameters. + * + * Parameter values will be supplied to validation method as name-value pairs. + * @param coords + * Unnamed validation annotation parameter gets supplied to the validating method named as + * "value". + * + * Validation annotations following {@link Each} annotation, will be applied for each + * array/collection element instead. + */ + @DataClass.Generated.Member + public SampleDataClass( + int num, + int num2, + int num4, + @Nullable String name, + String name2, + @NonNull String name4, + AccessibilityNodeInfo otherParcelable, + Date date, + Pattern pattern, + List<LinkAddress> linkAddresses2, + ArrayList<LinkAddress> linkAddresses, + @Nullable LinkAddress[] linkAddresses4, + boolean active, + @StateName String stateName, + @RequestFlags int flags, + @State int state, + CharSequence charSeq, + LinkAddress[] linkAddresses5, + @StringRes int stringRes, + @android.annotation.IntRange(from = 0, to = 4) int limited, + @Size(2) @FloatRange(from = 0f) float[] coords) { + this.mNum = num; + this.mNum2 = num2; + this.mNum4 = num4; + this.mName = name; + this.mName2 = name2; + this.mName4 = Preconditions.checkNotNull(name4); + this.mOtherParcelable = otherParcelable; + this.mDate = date; + this.mPattern = pattern; + this.mLinkAddresses2 = linkAddresses2; + this.mLinkAddresses = linkAddresses; + this.mLinkAddresses4 = linkAddresses4; + this.mActive = active; + this.mStateName = stateName; + this.mFlags = flags; + this.mState = state; + this.charSeq = charSeq; + this.mLinkAddresses5 = linkAddresses5; + this.mStringRes = stringRes; + this.mLimited = limited; + this.mCoords = coords; + AnnotationValidations.validate( + NonNull.class, null, mName4); + + //noinspection PointlessBooleanExpression + if (true + && !(Objects.equals(mStateName, STATE_NAME_UNDEFINED)) + && !(Objects.equals(mStateName, STATE_NAME_ON)) + && !(Objects.equals(mStateName, STATE_NAME_OFF))) { + throw new java.lang.IllegalArgumentException( + "stateName was " + mStateName + " but must be one of: " + + "STATE_NAME_UNDEFINED(" + STATE_NAME_UNDEFINED + "), " + + "STATE_NAME_ON(" + STATE_NAME_ON + "), " + + "STATE_NAME_OFF(" + STATE_NAME_OFF + ")"); + } + + + //noinspection PointlessBitwiseExpression + Preconditions.checkFlagsArgument( + mFlags, 0 + | FLAG_MANUAL_REQUEST + | FLAG_COMPATIBILITY_MODE_REQUEST + | FLAG_AUGMENTED_REQUEST); + + //noinspection PointlessBooleanExpression + if (true + && !(mState == STATE_UNDEFINED) + && !(mState == STATE_ON) + && !(mState == STATE_OFF)) { + throw new java.lang.IllegalArgumentException( + "state was " + mState + " but must be one of: " + + "STATE_UNDEFINED(" + STATE_UNDEFINED + "), " + + "STATE_ON(" + STATE_ON + "), " + + "STATE_OFF(" + STATE_OFF + ")"); + } + + AnnotationValidations.validate( + StringRes.class, null, mStringRes); + AnnotationValidations.validate( + android.annotation.IntRange.class, null, mLimited, + "from", 0, + "to", 4); + AnnotationValidations.validate( + Size.class, null, mCoords.length, + "value", 2); + int coordsSize = mCoords.length; + for (int i = 0; i < coordsSize; i++) { + AnnotationValidations.validate( + FloatRange.class, null, mCoords[i], + "from", 0f); + } + + + onConstructed(); + } + + /** + * Any property javadoc should go onto the field, and will be copied where appropriate, + * including getters, constructor parameters, builder setters, etc. + * + * <p> + * This allows to avoid the burden of maintaining copies of the same documentation + * pieces in multiple places for each field. + */ + @DataClass.Generated.Member + public int getNum() { + return mNum; + } + + /** + * Various javadoc features should work as expected when copied, e.g {@code code}, + * {@link #mName links}, <a href="https://google.com">html links</a>, etc. + * + * @see #mNum2 ..and so should blocks at the bottom, e.g. {@code @see} blocks. + */ + @DataClass.Generated.Member + public int getNum2() { + return mNum2; + } + + /** + * {@code @hide} javadoc annotation is also propagated, which can be used to adjust the + * desired public API surface. + * + * @see #getNum4() is hidden + * @see Builder#setNum4(int) also hidden + * @hide + */ + @DataClass.Generated.Member + public int getNum4() { + return mNum4; + } + + /** + * {@link Nullable} fields are considered optional and will not throw an exception if omitted + * (or set to null) when creating an instance either via a {@link Builder} or constructor. + */ + @DataClass.Generated.Member + public @Nullable String getName() { + return mName; + } + + /** + * Fields with default value expressions ("mFoo = ...") are also optional, and are automatically + * initialized to the provided default expression, unless explicitly set. + */ + @DataClass.Generated.Member + public String getName2() { + return mName2; + } + + /** + * Fields without {@link Nullable} annotation or default value are considered required. + * + * {@link NonNull} annotation is recommended on such non-primitive fields for documentation. + */ + @DataClass.Generated.Member + public @NonNull String getName4() { + return mName4; + } + + /** + * For parcelling, any field type supported by {@link Parcel} is supported out of the box. + * E.g. {@link Parcelable} subclasses, {@link String}, {@link int}, {@link boolean}, etc. + */ + @DataClass.Generated.Member + public AccessibilityNodeInfo getOtherParcelable() { + return mOtherParcelable; + } + + /** + * Additionally, support for parcelling other types can be added by implementing a + * {@link Parcelling}, and referencing it in the {@link DataClass.ParcelWith} field annotation. + * + * @see DateParcelling an example {@link Parcelling} implementation + */ + @DataClass.Generated.Member + public Date getDate() { + return mDate; + } + + /** + * If a {@link Parcelling} is fairly common, consider putting it in {@link Parcelling.BuiltIn} + * to encourage its reuse. + */ + @DataClass.Generated.Member + public Pattern getPattern() { + return mPattern; + } + + /** + * For lists, when using a {@link Builder}, other than a regular + * {@link Builder#setLinkAddresses2(List) setter}, and additional + * {@link Builder#addLinkAddresses2(LinkAddress) add} method is generated for convenience. + */ + @DataClass.Generated.Member + public List<LinkAddress> getLinkAddresses2() { + return mLinkAddresses2; + } + + /** + * For aesthetics, you may want to consider providing a singular version of the plural field + * name, which would be used for the {@link #mLinkAddresses2 above mentioned} "add" method. + * + * @see Builder#addLinkAddress(LinkAddress) + */ + @DataClass.Generated.Member + public ArrayList<LinkAddress> getLinkAddresses() { + return mLinkAddresses; + } + + /** + * For boolean fields, when using a {@link Builder}, in addition to a regular setter, methods + * like {@link Builder#markActive()} and {@link Builder#markNotActive()} are generated. + */ + @DataClass.Generated.Member + public boolean isActive() { + return mActive; + } + + /** + * {@link IntDef}/{@link StringDef}-annotated fields propagate the annotation to + * getter/constructor/setter/builder parameters, making for a nicer api. + * + * @see #getStateName + * @see Builder#setStateName + */ + @DataClass.Generated.Member + public @StateName String getStateName() { + return mStateName; + } + + /** + * Fields annotated with {@link IntDef} annotations also get a proper {@link #toString()} value. + */ + @DataClass.Generated.Member + public @RequestFlags int getFlags() { + return mFlags; + } + + /** + * Above is true for both {@link IntDef#flag flags} and enum-like {@link IntDef}s + */ + @DataClass.Generated.Member + public @State int getState() { + return mState; + } + + /** + * Final fields suppress generating a setter (when setters are requested). + */ + @DataClass.Generated.Member + public LinkAddress[] getLinkAddresses5() { + return mLinkAddresses5; + } + + /** + * Fields with certain annotations are automatically validated in constructor + * + * You can see overloads in {@link AnnotationValidations} for a list of currently + * supported ones. + * + * You can also extend support to your custom annotations by creating another corresponding + * overloads like + * {@link AnnotationValidations#validate(Class, UserIdInt, int)}. + * + * @see #SampleDataClass + */ + @DataClass.Generated.Member + public @StringRes int getStringRes() { + return mStringRes; + } + + /** + * Validation annotations may also have parameters. + * + * Parameter values will be supplied to validation method as name-value pairs. + * + * @see AnnotationValidations#validate(Class, Size, int, String, int, String, int) + */ + @DataClass.Generated.Member + public @android.annotation.IntRange(from = 0, to = 4) int getLimited() { + return mLimited; + } + + /** + * Unnamed validation annotation parameter gets supplied to the validating method named as + * "value". + * + * Validation annotations following {@link Each} annotation, will be applied for each + * array/collection element instead. + * + * @see AnnotationValidations#validate(Class, Size, int, String, int) + */ + @DataClass.Generated.Member + public @Size(2) @FloatRange(from = 0f) float[] getCoords() { + return mCoords; + } + + /** + * When using transient fields for caching it's often also a good idea to initialize them + * lazily. + * + * You can declare a special method like {@link #lazyInitTmpStorage()}, to let the + * {@link #getTmpStorage getter} lazily-initialize the value on demand. + */ + @DataClass.Generated.Member + public int[] getTmpStorage() { + int[] tmpStorage = mTmpStorage; + if (tmpStorage == null) { + // You can mark field as volatile for thread-safe double-check init + tmpStorage = mTmpStorage = lazyInitTmpStorage(); + } + return tmpStorage; + } + + @Override + @DataClass.Generated.Member + public String toString() { + // You can override field toString logic by defining methods like: + // String fieldNameToString() { ... } + + return "SampleDataClass { " + + "num = " + mNum + ", " + + "num2 = " + mNum2 + ", " + + "num4 = " + mNum4 + ", " + + "name = " + mName + ", " + + "name2 = " + mName2 + ", " + + "name4 = " + mName4 + ", " + + "otherParcelable = " + mOtherParcelable + ", " + + "date = " + mDate + ", " + + "pattern = " + mPattern + ", " + + "linkAddresses2 = " + mLinkAddresses2 + ", " + + "linkAddresses = " + mLinkAddresses + ", " + + "linkAddresses4 = " + java.util.Arrays.toString(mLinkAddresses4) + ", " + + "active = " + mActive + ", " + + "stateName = " + mStateName + ", " + + "flags = " + requestFlagsToString(mFlags) + ", " + + "state = " + stateToString(mState) + ", " + + "charSeq = " + charSeq + ", " + + "linkAddresses5 = " + java.util.Arrays.toString(mLinkAddresses5) + ", " + + "stringRes = " + mStringRes + ", " + + "limited = " + mLimited + ", " + + "coords = " + java.util.Arrays.toString(mCoords) + + " }"; + } + + @Override + @DataClass.Generated.Member + public boolean equals(Object o) { + // You can override field equality logic by defining either of the methods like: + // boolean fieldNameEquals(SampleDataClass other) { ... } + // boolean fieldNameEquals(FieldType otherValue) { ... } + + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + @SuppressWarnings("unchecked") + SampleDataClass that = (SampleDataClass) o; + //noinspection PointlessBooleanExpression + return true + && mNum == that.mNum + && mNum2 == that.mNum2 + && mNum4 == that.mNum4 + && Objects.equals(mName, that.mName) + && Objects.equals(mName2, that.mName2) + && Objects.equals(mName4, that.mName4) + && Objects.equals(mOtherParcelable, that.mOtherParcelable) + && Objects.equals(mDate, that.mDate) + && patternEquals(that.mPattern) + && Objects.equals(mLinkAddresses2, that.mLinkAddresses2) + && Objects.equals(mLinkAddresses, that.mLinkAddresses) + && java.util.Arrays.equals(mLinkAddresses4, that.mLinkAddresses4) + && mActive == that.mActive + && Objects.equals(mStateName, that.mStateName) + && mFlags == that.mFlags + && mState == that.mState + && Objects.equals(charSeq, that.charSeq) + && java.util.Arrays.equals(mLinkAddresses5, that.mLinkAddresses5) + && mStringRes == that.mStringRes + && mLimited == that.mLimited + && java.util.Arrays.equals(mCoords, that.mCoords); + } + + @Override + @DataClass.Generated.Member + public int hashCode() { + // You can override field hashCode logic by defining methods like: + // int fieldNameHashCode() { ... } + + int _hash = 1; + _hash = 31 * _hash + mNum; + _hash = 31 * _hash + mNum2; + _hash = 31 * _hash + mNum4; + _hash = 31 * _hash + Objects.hashCode(mName); + _hash = 31 * _hash + Objects.hashCode(mName2); + _hash = 31 * _hash + Objects.hashCode(mName4); + _hash = 31 * _hash + Objects.hashCode(mOtherParcelable); + _hash = 31 * _hash + Objects.hashCode(mDate); + _hash = 31 * _hash + patternHashCode(); + _hash = 31 * _hash + Objects.hashCode(mLinkAddresses2); + _hash = 31 * _hash + Objects.hashCode(mLinkAddresses); + _hash = 31 * _hash + java.util.Arrays.hashCode(mLinkAddresses4); + _hash = 31 * _hash + Boolean.hashCode(mActive); + _hash = 31 * _hash + Objects.hashCode(mStateName); + _hash = 31 * _hash + mFlags; + _hash = 31 * _hash + mState; + _hash = 31 * _hash + Objects.hashCode(charSeq); + _hash = 31 * _hash + java.util.Arrays.hashCode(mLinkAddresses5); + _hash = 31 * _hash + mStringRes; + _hash = 31 * _hash + mLimited; + _hash = 31 * _hash + java.util.Arrays.hashCode(mCoords); + return _hash; + } + + @DataClass.Generated.Member + void forEachField( + DataClass.PerIntFieldAction<SampleDataClass> actionInt, + DataClass.PerObjectFieldAction<SampleDataClass> actionObject) { + actionInt.acceptInt(this, "num", mNum); + actionInt.acceptInt(this, "num2", mNum2); + actionInt.acceptInt(this, "num4", mNum4); + actionObject.acceptObject(this, "name", mName); + actionObject.acceptObject(this, "name2", mName2); + actionObject.acceptObject(this, "name4", mName4); + actionObject.acceptObject(this, "otherParcelable", mOtherParcelable); + actionObject.acceptObject(this, "date", mDate); + actionObject.acceptObject(this, "pattern", mPattern); + actionObject.acceptObject(this, "linkAddresses2", mLinkAddresses2); + actionObject.acceptObject(this, "linkAddresses", mLinkAddresses); + actionObject.acceptObject(this, "linkAddresses4", mLinkAddresses4); + actionObject.acceptObject(this, "active", mActive); + actionObject.acceptObject(this, "stateName", mStateName); + actionInt.acceptInt(this, "flags", mFlags); + actionInt.acceptInt(this, "state", mState); + actionObject.acceptObject(this, "charSeq", charSeq); + actionObject.acceptObject(this, "linkAddresses5", mLinkAddresses5); + actionInt.acceptInt(this, "stringRes", mStringRes); + actionInt.acceptInt(this, "limited", mLimited); + actionObject.acceptObject(this, "coords", mCoords); + } + + /** @deprecated May cause boxing allocations - use with caution! */ + @Deprecated + @DataClass.Generated.Member + void forEachField(DataClass.PerObjectFieldAction<SampleDataClass> action) { + action.acceptObject(this, "num", mNum); + action.acceptObject(this, "num2", mNum2); + action.acceptObject(this, "num4", mNum4); + action.acceptObject(this, "name", mName); + action.acceptObject(this, "name2", mName2); + action.acceptObject(this, "name4", mName4); + action.acceptObject(this, "otherParcelable", mOtherParcelable); + action.acceptObject(this, "date", mDate); + action.acceptObject(this, "pattern", mPattern); + action.acceptObject(this, "linkAddresses2", mLinkAddresses2); + action.acceptObject(this, "linkAddresses", mLinkAddresses); + action.acceptObject(this, "linkAddresses4", mLinkAddresses4); + action.acceptObject(this, "active", mActive); + action.acceptObject(this, "stateName", mStateName); + action.acceptObject(this, "flags", mFlags); + action.acceptObject(this, "state", mState); + action.acceptObject(this, "charSeq", charSeq); + action.acceptObject(this, "linkAddresses5", mLinkAddresses5); + action.acceptObject(this, "stringRes", mStringRes); + action.acceptObject(this, "limited", mLimited); + action.acceptObject(this, "coords", mCoords); + } + + @DataClass.Generated.Member + static Parcelling<Date> sParcellingForDate = + Parcelling.Cache.get( + DateParcelling.class); + static { + if (sParcellingForDate == null) { + sParcellingForDate = Parcelling.Cache.put( + new DateParcelling()); + } + } + + @DataClass.Generated.Member + static Parcelling<Pattern> sParcellingForPattern = + Parcelling.Cache.get( + Parcelling.BuiltIn.ForPattern.class); + static { + if (sParcellingForPattern == null) { + sParcellingForPattern = Parcelling.Cache.put( + new Parcelling.BuiltIn.ForPattern()); + } + } + + @Override + @DataClass.Generated.Member + public void writeToParcel(Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + + long flg = 0; + if (mActive) flg |= 0x1000; + if (mName != null) flg |= 0x8; + if (mName2 != null) flg |= 0x10; + if (mOtherParcelable != null) flg |= 0x40; + if (mDate != null) flg |= 0x80; + if (mPattern != null) flg |= 0x100; + if (mLinkAddresses2 != null) flg |= 0x200; + if (mLinkAddresses != null) flg |= 0x400; + if (mLinkAddresses4 != null) flg |= 0x800; + if (mStateName != null) flg |= 0x2000; + if (charSeq != null) flg |= 0x10000; + if (mLinkAddresses5 != null) flg |= 0x20000; + if (mCoords != null) flg |= 0x100000; + dest.writeLong(flg); + dest.writeInt(mNum); + dest.writeInt(mNum2); + dest.writeInt(mNum4); + if (mName != null) dest.writeString(mName); + if (mName2 != null) dest.writeString(mName2); + dest.writeString(mName4); + if (mOtherParcelable != null) dest.writeTypedObject(mOtherParcelable, flags); + sParcellingForDate.parcel(mDate, dest, flags); + sParcellingForPattern.parcel(mPattern, dest, flags); + if (mLinkAddresses2 != null) dest.writeParcelableList(mLinkAddresses2, flags); + if (mLinkAddresses != null) dest.writeParcelableList(mLinkAddresses, flags); + if (mLinkAddresses4 != null) dest.writeTypedArray(mLinkAddresses4, flags); + if (mStateName != null) dest.writeString(mStateName); + dest.writeInt(mFlags); + dest.writeInt(mState); + if (charSeq != null) dest.writeCharSequence(charSeq); + if (mLinkAddresses5 != null) dest.writeTypedArray(mLinkAddresses5, flags); + dest.writeInt(mStringRes); + dest.writeInt(mLimited); + if (mCoords != null) dest.writeFloatArray(mCoords); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + + @DataClass.Generated.Member + public static final @NonNull Parcelable.Creator<SampleDataClass> CREATOR + = new Parcelable.Creator<SampleDataClass>() { + @Override + public SampleDataClass[] newArray(int size) { + return new SampleDataClass[size]; + } + + @Override + @SuppressWarnings({"unchecked", "RedundantCast"}) + public SampleDataClass createFromParcel(Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + long flg = in.readLong(); + boolean active = (flg & 0x1000) != 0; + int num = in.readInt(); + int num2 = in.readInt(); + int num4 = in.readInt(); + String name = (flg & 0x8) == 0 ? null : in.readString(); + String name2 = (flg & 0x10) == 0 ? null : in.readString(); + String name4 = in.readString(); + AccessibilityNodeInfo otherParcelable = (flg & 0x40) == 0 ? null : (AccessibilityNodeInfo) in.readTypedObject(AccessibilityNodeInfo.CREATOR); + Date date = sParcellingForDate.unparcel(in); + Pattern pattern = sParcellingForPattern.unparcel(in); + List<LinkAddress> linkAddresses2 = null; + if ((flg & 0x200) != 0) { + linkAddresses2 = new ArrayList<>(); + in.readParcelableList(linkAddresses2, LinkAddress.class.getClassLoader()); + } + ArrayList<LinkAddress> linkAddresses = null; + if ((flg & 0x400) != 0) { + linkAddresses = new ArrayList<>(); + in.readParcelableList(linkAddresses, LinkAddress.class.getClassLoader()); + } + LinkAddress[] linkAddresses4 = (flg & 0x800) == 0 ? null : (LinkAddress[]) in.createTypedArray(LinkAddress.CREATOR); + String stateName = (flg & 0x2000) == 0 ? null : in.readString(); + int flags = in.readInt(); + int state = in.readInt(); + CharSequence _charSeq = (flg & 0x10000) == 0 ? null : (CharSequence) in.readCharSequence(); + LinkAddress[] linkAddresses5 = (flg & 0x20000) == 0 ? null : (LinkAddress[]) in.createTypedArray(LinkAddress.CREATOR); + int stringRes = in.readInt(); + int limited = in.readInt(); + float[] coords = (flg & 0x100000) == 0 ? null : in.createFloatArray(); + return new SampleDataClass( + num, + num2, + num4, + name, + name2, + name4, + otherParcelable, + date, + pattern, + linkAddresses2, + linkAddresses, + linkAddresses4, + active, + stateName, + flags, + state, + _charSeq, + linkAddresses5, + stringRes, + limited, + coords); + } + }; + + /** + * A builder for {@link SampleDataClass} + */ + @SuppressWarnings("WeakerAccess") + @DataClass.Generated.Member + public static class Builder + extends android.provider.OneTimeUseBuilder<SampleDataClass> { + + protected int mNum; + protected int mNum2; + protected int mNum4; + protected @Nullable String mName; + protected String mName2; + protected @NonNull String mName4; + protected AccessibilityNodeInfo mOtherParcelable; + protected Date mDate; + protected Pattern mPattern; + protected List<LinkAddress> mLinkAddresses2; + protected ArrayList<LinkAddress> mLinkAddresses; + protected @Nullable LinkAddress[] mLinkAddresses4; + protected boolean mActive; + protected @StateName String mStateName; + protected @RequestFlags int mFlags; + protected @State int mState; + protected CharSequence charSeq; + protected LinkAddress[] mLinkAddresses5; + protected @StringRes int mStringRes; + protected @android.annotation.IntRange(from = 0, to = 4) int mLimited; + protected @Size(2) @FloatRange(from = 0f) float[] mCoords; + + protected long mBuilderFieldsSet = 0L; + + public Builder() {}; + + /** + * Any property javadoc should go onto the field, and will be copied where appropriate, + * including getters, constructor parameters, builder setters, etc. + * + * <p> + * This allows to avoid the burden of maintaining copies of the same documentation + * pieces in multiple places for each field. + */ + @DataClass.Generated.Member + public Builder setNum(int value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x1; + mNum = value; + return this; + } + + /** + * Various javadoc features should work as expected when copied, e.g {@code code}, + * {@link #mName links}, <a href="https://google.com">html links</a>, etc. + * + * @see #mNum2 ..and so should blocks at the bottom, e.g. {@code @see} blocks. + */ + @DataClass.Generated.Member + public Builder setNum2(int value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x2; + mNum2 = value; + return this; + } + + /** + * {@code @hide} javadoc annotation is also propagated, which can be used to adjust the + * desired public API surface. + * + * @see #getNum4() is hidden + * @see Builder#setNum4(int) also hidden + * @hide + */ + @DataClass.Generated.Member + public Builder setNum4(int value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x4; + mNum4 = value; + return this; + } + + /** + * {@link Nullable} fields are considered optional and will not throw an exception if omitted + * (or set to null) when creating an instance either via a {@link Builder} or constructor. + */ + @DataClass.Generated.Member + public Builder setName(@Nullable String value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x8; + mName = value; + return this; + } + + /** + * Fields with default value expressions ("mFoo = ...") are also optional, and are automatically + * initialized to the provided default expression, unless explicitly set. + */ + @DataClass.Generated.Member + public Builder setName2(String value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x10; + mName2 = value; + return this; + } + + /** + * Fields without {@link Nullable} annotation or default value are considered required. + * + * {@link NonNull} annotation is recommended on such non-primitive fields for documentation. + */ + @DataClass.Generated.Member + public Builder setName4(@NonNull String value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x20; + mName4 = value; + return this; + } + + /** + * For parcelling, any field type supported by {@link Parcel} is supported out of the box. + * E.g. {@link Parcelable} subclasses, {@link String}, {@link int}, {@link boolean}, etc. + */ + @DataClass.Generated.Member + public Builder setOtherParcelable(AccessibilityNodeInfo value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x40; + mOtherParcelable = value; + return this; + } + + /** + * Additionally, support for parcelling other types can be added by implementing a + * {@link Parcelling}, and referencing it in the {@link DataClass.ParcelWith} field annotation. + * + * @see DateParcelling an example {@link Parcelling} implementation + */ + @DataClass.Generated.Member + public Builder setDate(Date value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x80; + mDate = value; + return this; + } + + /** + * If a {@link Parcelling} is fairly common, consider putting it in {@link Parcelling.BuiltIn} + * to encourage its reuse. + */ + @DataClass.Generated.Member + public Builder setPattern(Pattern value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x100; + mPattern = value; + return this; + } + + /** + * For lists, when using a {@link Builder}, other than a regular + * {@link Builder#setLinkAddresses2(List) setter}, and additional + * {@link Builder#addLinkAddresses2(LinkAddress) add} method is generated for convenience. + */ + @DataClass.Generated.Member + public Builder setLinkAddresses2(List<LinkAddress> value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x200; + mLinkAddresses2 = value; + return this; + } + + /** @see #setLinkAddresses2 */ + @DataClass.Generated.Member + public Builder addLinkAddresses2(@NonNull LinkAddress value) { + // You can refine this method's name by providing item's singular name, e.g.: + // @DataClass.PluralOf("item")) mItems = ... + + if (mLinkAddresses2 == null) setLinkAddresses2(new ArrayList<>()); + mLinkAddresses2.add(value); + return this; + } + + /** + * For aesthetics, you may want to consider providing a singular version of the plural field + * name, which would be used for the {@link #mLinkAddresses2 above mentioned} "add" method. + * + * @see Builder#addLinkAddress(LinkAddress) + */ + @DataClass.Generated.Member + public Builder setLinkAddresses(ArrayList<LinkAddress> value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x400; + mLinkAddresses = value; + return this; + } + + /** @see #setLinkAddresses */ + @DataClass.Generated.Member + public Builder addLinkAddress(@NonNull LinkAddress value) { + if (mLinkAddresses == null) setLinkAddresses(new ArrayList<>()); + mLinkAddresses.add(value); + return this; + } + + /** + * For array fields, when using a {@link Builder}, vararg argument format is used for + * convenience. + * + * @see Builder#setLinkAddresses4(LinkAddress...) + */ + @DataClass.Generated.Member + public Builder setLinkAddresses4(@Nullable LinkAddress... value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x800; + mLinkAddresses4 = value; + return this; + } + + /** + * For boolean fields, when using a {@link Builder}, in addition to a regular setter, methods + * like {@link Builder#markActive()} and {@link Builder#markNotActive()} are generated. + */ + @DataClass.Generated.Member + public Builder setActive(boolean value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x1000; + mActive = value; + return this; + } + + /** @see #setActive */ + @DataClass.Generated.Member + public Builder markActive() { + return setActive(true); + } + + /** @see #setActive */ + @DataClass.Generated.Member + public Builder markNotActive() { + return setActive(false); + } + + /** + * {@link IntDef}/{@link StringDef}-annotated fields propagate the annotation to + * getter/constructor/setter/builder parameters, making for a nicer api. + * + * @see #getStateName + * @see Builder#setStateName + */ + @DataClass.Generated.Member + public Builder setStateName(@StateName String value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x2000; + mStateName = value; + return this; + } + + /** + * Fields annotated with {@link IntDef} annotations also get a proper {@link #toString()} value. + */ + @DataClass.Generated.Member + public Builder setFlags(@RequestFlags int value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x4000; + mFlags = value; + return this; + } + + /** + * Above is true for both {@link IntDef#flag flags} and enum-like {@link IntDef}s + */ + @DataClass.Generated.Member + public Builder setState(@State int value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x8000; + mState = value; + return this; + } + + /** + * Making a field public will suppress getter generation in favor of accessing it directly. + */ + @DataClass.Generated.Member + public Builder setCharSeq(CharSequence value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x10000; + charSeq = value; + return this; + } + + /** + * Final fields suppress generating a setter (when setters are requested). + */ + @DataClass.Generated.Member + public Builder setLinkAddresses5(LinkAddress... value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x20000; + mLinkAddresses5 = value; + return this; + } + + /** + * Fields with certain annotations are automatically validated in constructor + * + * You can see overloads in {@link AnnotationValidations} for a list of currently + * supported ones. + * + * You can also extend support to your custom annotations by creating another corresponding + * overloads like + * {@link AnnotationValidations#validate(Class, UserIdInt, int)}. + * + * @see #SampleDataClass + */ + @DataClass.Generated.Member + public Builder setStringRes(@StringRes int value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x40000; + mStringRes = value; + return this; + } + + /** + * Validation annotations may also have parameters. + * + * Parameter values will be supplied to validation method as name-value pairs. + * + * @see AnnotationValidations#validate(Class, Size, int, String, int, String, int) + */ + @DataClass.Generated.Member + public Builder setLimited(@android.annotation.IntRange(from = 0, to = 4) int value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x80000; + mLimited = value; + return this; + } + + /** + * Unnamed validation annotation parameter gets supplied to the validating method named as + * "value". + * + * Validation annotations following {@link Each} annotation, will be applied for each + * array/collection element instead. + * + * @see AnnotationValidations#validate(Class, Size, int, String, int) + */ + @DataClass.Generated.Member + public Builder setCoords(@Size(2) @FloatRange(from = 0f) float... value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x100000; + mCoords = value; + return this; + } + + /** Builds the instance. This builder should not be touched after calling this! */ + public SampleDataClass build() { + markUsed(); + if ((mBuilderFieldsSet & 0x1) == 0) { + throw new IllegalStateException("Required field not set: num"); + } + if ((mBuilderFieldsSet & 0x2) == 0) { + throw new IllegalStateException("Required field not set: num2"); + } + if ((mBuilderFieldsSet & 0x4) == 0) { + throw new IllegalStateException("Required field not set: num4"); + } + if ((mBuilderFieldsSet & 0x10) == 0) { + mName2 = "Bob"; + } + if ((mBuilderFieldsSet & 0x20) == 0) { + throw new IllegalStateException("Required field not set: name4"); + } + if ((mBuilderFieldsSet & 0x40) == 0) { + mOtherParcelable = null; + } + if ((mBuilderFieldsSet & 0x80) == 0) { + mDate = new Date(42 * 42); + } + if ((mBuilderFieldsSet & 0x100) == 0) { + mPattern = Pattern.compile(""); + } + if ((mBuilderFieldsSet & 0x200) == 0) { + mLinkAddresses2 = new ArrayList<>(); + } + if ((mBuilderFieldsSet & 0x400) == 0) { + mLinkAddresses = new ArrayList<>(); + } + if ((mBuilderFieldsSet & 0x800) == 0) { + mLinkAddresses4 = null; + } + if ((mBuilderFieldsSet & 0x1000) == 0) { + mActive = true; + } + if ((mBuilderFieldsSet & 0x2000) == 0) { + mStateName = STATE_NAME_UNDEFINED; + } + if ((mBuilderFieldsSet & 0x4000) == 0) { + throw new IllegalStateException("Required field not set: flags"); + } + if ((mBuilderFieldsSet & 0x8000) == 0) { + mState = STATE_UNDEFINED; + } + if ((mBuilderFieldsSet & 0x10000) == 0) { + charSeq = ""; + } + if ((mBuilderFieldsSet & 0x20000) == 0) { + throw new IllegalStateException("Required field not set: linkAddresses5"); + } + if ((mBuilderFieldsSet & 0x40000) == 0) { + mStringRes = 0; + } + if ((mBuilderFieldsSet & 0x80000) == 0) { + mLimited = 3; + } + if ((mBuilderFieldsSet & 0x100000) == 0) { + mCoords = new float[] { 0f, 0f }; + } + SampleDataClass o = new SampleDataClass( + mNum, + mNum2, + mNum4, + mName, + mName2, + mName4, + mOtherParcelable, + mDate, + mPattern, + mLinkAddresses2, + mLinkAddresses, + mLinkAddresses4, + mActive, + mStateName, + mFlags, + mState, + charSeq, + mLinkAddresses5, + mStringRes, + mLimited, + mCoords); + return o; + } + } + +} diff --git a/tests/Codegen/src/com/android/codegentest/SampleDataClassTest.java b/tests/Codegen/src/com/android/codegentest/SampleDataClassTest.java new file mode 100644 index 000000000000..71e85ab00eab --- /dev/null +++ b/tests/Codegen/src/com/android/codegentest/SampleDataClassTest.java @@ -0,0 +1,219 @@ +/* + * 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.codegentest; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.Matchers.greaterThanOrEqualTo; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertThat; + +import android.net.LinkAddress; +import android.os.Parcel; +import android.os.Parcelable; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Arrays; +import java.util.Date; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Tests {@link SampleDataClass} after it's augmented with dataclass codegen. + * + * Use {@code $ . runTest.sh} to run. + */ +@RunWith(AndroidJUnit4.class) +public class SampleDataClassTest { + + private SampleDataClass mSpecimen = newBuilder().build(); + + private static SampleDataClass.Builder newBuilder() { + return newIncompleteBuilder() + .setNum(42) + .setNum2(42) + .setNum4(42) + .setName4("foobar") + .setLinkAddresses5(); + } + + private static SampleDataClass.Builder newIncompleteBuilder() { + return new SampleDataClass.Builder() + .markActive() + .setName("some parcelable") + .setFlags(SampleDataClass.FLAG_MANUAL_REQUEST); + } + + @Test + public void testParcelling_producesEqualInstance() { + SampleDataClass copy = parcelAndUnparcel(mSpecimen, SampleDataClass.CREATOR); + assertEquals(mSpecimen, copy); + assertEquals(mSpecimen.hashCode(), copy.hashCode()); + } + + @Test + public void testParcelling_producesInstanceWithEqualFields() { + SampleDataClass copy = parcelAndUnparcel(mSpecimen, SampleDataClass.CREATOR); + copy.forEachField((self, copyFieldName, copyFieldValue) -> { + mSpecimen.forEachField((self2, specimenFieldName, specimenFieldValue) -> { + if (copyFieldName.equals(specimenFieldName) + && !copyFieldName.equals("pattern") + && (specimenFieldValue == null + || !specimenFieldValue.getClass().isArray())) { + assertEquals("Mismatched field values for " + copyFieldName, + specimenFieldValue, copyFieldValue); + } + }); + }); + } + + @Test + public void testCustomParcelling_instanceIsCached() { + parcelAndUnparcel(mSpecimen, SampleDataClass.CREATOR); + parcelAndUnparcel(mSpecimen, SampleDataClass.CREATOR); + assertEquals(1, DateParcelling.sInstanceCount.get()); + } + + @Test + public void testDefaultFieldValue_isPropagated() { + assertEquals(new Date(42 * 42), mSpecimen.getDate()); + } + + @Test + public void testForEachField_avoidsBoxing() { + AtomicInteger intFieldCount = new AtomicInteger(0); + mSpecimen.forEachField( + (self, name, intValue) -> intFieldCount.getAndIncrement(), + (self, name, objectValue) -> { + if (objectValue != null) { + assertThat("Boxed field " + name, + objectValue, not(instanceOf(Integer.class))); + } + }); + assertThat(intFieldCount.get(), greaterThanOrEqualTo(1)); + } + + @Test + public void testToString_containsEachField() { + String toString = mSpecimen.toString(); + + mSpecimen.forEachField((self, name, value) -> { + assertThat(toString, containsString(name)); + if (value instanceof Integer) { + // Could be flags, their special toString tested separately + } else if (value instanceof Object[]) { + assertThat(toString, containsString(Arrays.toString((Object[]) value))); + } else if (value != null && value.getClass().isArray()) { + // Primitive array, uses multiple specialized Arrays.toString overloads + } else { + assertThat(toString, containsString("" + value)); + } + }); + } + + @Test + public void testBuilder_propagatesValuesToInstance() { + assertEquals(43, newBuilder().setNum(43).build().getNum()); + } + + @Test + public void testPluralFields_canHaveCustomSingularBuilderName() { + newBuilder().addLinkAddress(new LinkAddress("127.0.0.1/24")); + } + + @Test(expected = IllegalStateException.class) + public void testBuilder_usableOnlyOnce() { + SampleDataClass.Builder builder = newBuilder(); + builder.build(); + builder.build(); + } + + @Test(expected = IllegalStateException.class) + public void testBuilder_throwsWhenRequiredFieldMissing() { + newIncompleteBuilder().build(); + } + + @Test + public void testIntDefs_haveCorrectToString() { + int flagsAsInt = SampleDataClass.FLAG_MANUAL_REQUEST + | SampleDataClass.FLAG_COMPATIBILITY_MODE_REQUEST; + String flagsAsString = SampleDataClass.requestFlagsToString(flagsAsInt); + + assertThat(flagsAsString, containsString("MANUAL_REQUEST")); + assertThat(flagsAsString, containsString("COMPATIBILITY_MODE_REQUEST")); + assertThat(flagsAsString, not(containsString("1"))); + assertThat(flagsAsString, not(containsString("" + flagsAsInt))); + + String dataclassToString = newBuilder() + .setFlags(flagsAsInt) + .setState(SampleDataClass.STATE_UNDEFINED) + .build() + .toString(); + assertThat(dataclassToString, containsString(flagsAsString)); + assertThat(dataclassToString, containsString("UNDEFINED")); + } + + @Test(expected = IllegalArgumentException.class) + public void testFlags_getValidated() { + newBuilder().setFlags(12345).build(); + } + + @Test(expected = IllegalArgumentException.class) + public void testIntEnums_getValidated() { + newBuilder().setState(12345).build(); + } + + @Test(expected = IllegalArgumentException.class) + public void testStringEnums_getValidated() { + newBuilder().setStateName("foo").build(); + } + + @Test(expected = IllegalStateException.class) + public void testCustomValidation_isTriggered() { + newBuilder().setNum2(-1).setNum4(1).build(); + } + + @Test + public void testLazyInit_isLazilyCalledOnce() { + assertNull(mSpecimen.mTmpStorage); + + int[] tmpStorage = mSpecimen.getTmpStorage(); + assertNotNull(tmpStorage); + assertSame(tmpStorage, mSpecimen.mTmpStorage); + + int[] tmpStorageAgain = mSpecimen.getTmpStorage(); + assertSame(tmpStorage, tmpStorageAgain); + } + + private static <T extends Parcelable> T parcelAndUnparcel( + T original, Parcelable.Creator<T> creator) { + Parcel p = Parcel.obtain(); + try { + original.writeToParcel(p, 0); + p.setDataPosition(0); + return creator.createFromParcel(p); + } finally { + p.recycle(); + } + } +} diff --git a/tests/libs-permissions/Android.bp b/tests/libs-permissions/Android.bp index c7c4b10a8405..330bfc9022df 100644 --- a/tests/libs-permissions/Android.bp +++ b/tests/libs-permissions/Android.bp @@ -14,16 +14,16 @@ prebuilt_etc { } java_library { - name: "com.android.test.libs.product_services", + name: "com.android.test.libs.system_ext", installable: true, - product_services_specific: true, - srcs: ["product_services/java/**/*.java"], - required: ["com.android.test.libs.product_services.xml"], + system_ext_specific: true, + srcs: ["system_ext/java/**/*.java"], + required: ["com.android.test.libs.system_ext.xml"], } prebuilt_etc { - name: "com.android.test.libs.product_services.xml", - src: "product_services/com.android.test.libs.product_services.xml", + name: "com.android.test.libs.system_ext.xml", + src: "system_ext/com.android.test.libs.system_ext.xml", sub_dir: "permissions", - product_services_specific: true, + system_ext_specific: true, } diff --git a/tests/libs-permissions/product_services/com.android.test.libs.product_services.xml b/tests/libs-permissions/system_ext/com.android.test.libs.system_ext.xml index 082a9be80779..fa56004415f9 100644 --- a/tests/libs-permissions/product_services/com.android.test.libs.product_services.xml +++ b/tests/libs-permissions/system_ext/com.android.test.libs.system_ext.xml @@ -15,6 +15,6 @@ --> <permissions> - <library name="com.android.test.libs.product_services" - file="/product_services/framework/com.android.test.libs.product_services.jar" /> + <library name="com.android.test.libs.system_ext" + file="/system_ext/framework/com.android.test.libs.system_ext.jar" /> </permissions> diff --git a/tests/libs-permissions/product_services/java/com/android/test/libs/product_services/LibsProductServicesTest.java b/tests/libs-permissions/system_ext/java/com/android/test/libs/system_ext/LibsSystemExtTest.java index dcbdae809889..9999aba37d8d 100644 --- a/tests/libs-permissions/product_services/java/com/android/test/libs/product_services/LibsProductServicesTest.java +++ b/tests/libs-permissions/system_ext/java/com/android/test/libs/system_ext/LibsSystemExtTest.java @@ -14,12 +14,12 @@ * limitations under the License. */ -package com.android.test.libs.product_services; +package com.android.test.libs.system_ext; /** - * Test class for product_services libs. + * Test class for system_ext libs. */ -public class LibsProductServicesTest { +public class LibsSystemExtTest { /** * Dummy method for testing. diff --git a/tests/privapp-permissions/Android.bp b/tests/privapp-permissions/Android.bp index ca7864f047c7..b6618508d95d 100644 --- a/tests/privapp-permissions/Android.bp +++ b/tests/privapp-permissions/Android.bp @@ -45,17 +45,17 @@ prebuilt_etc { } android_app { - name: "ProductServicesPrivAppPermissionTest", + name: "SystemExtPrivAppPermissionTest", sdk_version: "current", privileged: true, - manifest: "product_services/AndroidManifest.xml", - product_services_specific: true, - required: ["product_servicesprivapp-permissions-test.xml"], + manifest: "system_ext/AndroidManifest.xml", + system_ext_specific: true, + required: ["system_extprivapp-permissions-test.xml"], } prebuilt_etc { - name: "product_servicesprivapp-permissions-test.xml", - src: "product_services/privapp-permissions-test.xml", + name: "system_extprivapp-permissions-test.xml", + src: "system_ext/privapp-permissions-test.xml", sub_dir: "permissions", - product_services_specific: true, + system_ext_specific: true, } diff --git a/tests/privapp-permissions/product_services/AndroidManifest.xml b/tests/privapp-permissions/system_ext/AndroidManifest.xml index 511ddee729ca..4a0e82f1b599 100644 --- a/tests/privapp-permissions/product_services/AndroidManifest.xml +++ b/tests/privapp-permissions/system_ext/AndroidManifest.xml @@ -16,7 +16,7 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.framework.permission.privapp.tests.product_services"> + package="com.android.framework.permission.privapp.tests.system_ext"> <!-- MANAGE_USB is signature|privileged --> <uses-permission android:name="android.permission.MANAGE_USB"/> diff --git a/tests/privapp-permissions/product_services/privapp-permissions-test.xml b/tests/privapp-permissions/system_ext/privapp-permissions-test.xml index 43baebbb0aad..ad63e8643e97 100644 --- a/tests/privapp-permissions/product_services/privapp-permissions-test.xml +++ b/tests/privapp-permissions/system_ext/privapp-permissions-test.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <permissions> - <privapp-permissions package="com.android.framework.permission.privapp.tests.product_services"> + <privapp-permissions package="com.android.framework.permission.privapp.tests.system_ext"> <permission name="android.permission.MANAGE_USB"/> </privapp-permissions> </permissions> diff --git a/tools/aapt2/optimize/ResourcePathShortener.cpp b/tools/aapt2/optimize/ResourcePathShortener.cpp index 6b11de759d2d..7f5d10475f93 100644 --- a/tools/aapt2/optimize/ResourcePathShortener.cpp +++ b/tools/aapt2/optimize/ResourcePathShortener.cpp @@ -16,7 +16,6 @@ #include "optimize/ResourcePathShortener.h" -#include <math.h> #include <unordered_set> #include "androidfw/StringPiece.h" @@ -51,18 +50,15 @@ std::string ShortenFileName(const android::StringPiece& file_path, int output_le } -// Calculate the optimal hash length such that an average of 10% of resources -// collide in their shortened path. +// Return the optimal hash length such that at most 10% of resources collide in +// their shortened path. // Reference: http://matt.might.net/articles/counting-hash-collisions/ int OptimalShortenedLength(int num_resources) { - int num_chars = 2; - double N = 64*64; // hash space when hash is 2 chars long - double max_collisions = num_resources * 0.1; - while (num_resources - N + N * pow((N - 1) / N, num_resources) > max_collisions) { - N *= 64; - num_chars++; + if (num_resources > 4000) { + return 3; + } else { + return 2; } - return num_chars; } std::string GetShortenedPath(const android::StringPiece& shortened_filename, diff --git a/tools/codegen/.gitignore b/tools/codegen/.gitignore new file mode 100755 index 000000000000..9fb18b42668f --- /dev/null +++ b/tools/codegen/.gitignore @@ -0,0 +1,2 @@ +.idea +out diff --git a/tools/codegen/Android.bp b/tools/codegen/Android.bp new file mode 100644 index 000000000000..805b2968bca0 --- /dev/null +++ b/tools/codegen/Android.bp @@ -0,0 +1,18 @@ +java_binary_host { + name: "codegen", + manifest: "manifest.txt", + srcs: [ + "src/**/*.kt", + ], + static_libs: [ + "javaparser", + ], +} + +java_library_host { + name: "codegen-version-info", + + srcs: [ + "src/**/SharedConstants.kt", + ], +} diff --git a/tools/codegen/OWNERS b/tools/codegen/OWNERS new file mode 100644 index 000000000000..da723b3b67da --- /dev/null +++ b/tools/codegen/OWNERS @@ -0,0 +1 @@ +eugenesusla@google.com
\ No newline at end of file diff --git a/tools/codegen/manifest.txt b/tools/codegen/manifest.txt new file mode 100644 index 000000000000..6e1018ba6b55 --- /dev/null +++ b/tools/codegen/manifest.txt @@ -0,0 +1 @@ +Main-class: com.android.codegen.MainKt diff --git a/tools/codegen/src/com/android/codegen/ClassInfo.kt b/tools/codegen/src/com/android/codegen/ClassInfo.kt new file mode 100644 index 000000000000..7ee79f651274 --- /dev/null +++ b/tools/codegen/src/com/android/codegen/ClassInfo.kt @@ -0,0 +1,49 @@ +package com.android.codegen + +import com.github.javaparser.JavaParser +import com.github.javaparser.ParseProblemException +import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration + +open class ClassInfo(val sourceLines: List<String>) { + + private val userSourceCode = (sourceLines + "}").joinToString("\n") + val fileAst = try { + JavaParser.parse(userSourceCode)!! + } catch (e: ParseProblemException) { + throw RuntimeException("Failed to parse code:\n" + + userSourceCode + .lines() + .mapIndexed { lnNum, ln -> "/*$lnNum*/$ln" } + .joinToString("\n"), + e) + } + val classAst = fileAst.types[0] as ClassOrInterfaceDeclaration + + fun hasMethod(name: String, vararg argTypes: String): Boolean { + return classAst.methods.any { + it.name.asString() == name && + it.parameters.map { it.type.asString() } == argTypes.toList() + } + } + + val superInterfaces = (fileAst.types[0] as ClassOrInterfaceDeclaration) + .implementedTypes.map { it.asString() } + + val ClassName = classAst.nameAsString + private val genericArgsAst = classAst.typeParameters + val genericArgs = if (genericArgsAst.isEmpty()) "" else { + genericArgsAst.map { it.nameAsString }.joinToString(", ").let { "<$it>" } + } + val ClassType = ClassName + genericArgs + + val constDefs = mutableListOf<ConstDef>() + + val fields = classAst.fields + .filterNot { it.isTransient || it.isStatic } + .mapIndexed { i, node -> FieldInfo(index = i, fieldAst = node, classInfo = this) } + .apply { lastOrNull()?.isLast = true } + val lazyTransientFields = classAst.fields + .filter { it.isTransient && !it.isStatic } + .mapIndexed { i, node -> FieldInfo(index = i, fieldAst = node, classInfo = this) } + .filter { hasMethod("lazyInit${it.NameUpperCamel}") } +}
\ No newline at end of file diff --git a/tools/codegen/src/com/android/codegen/ClassPrinter.kt b/tools/codegen/src/com/android/codegen/ClassPrinter.kt new file mode 100644 index 000000000000..33256b787964 --- /dev/null +++ b/tools/codegen/src/com/android/codegen/ClassPrinter.kt @@ -0,0 +1,311 @@ +package com.android.codegen + +import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration +import com.github.javaparser.ast.body.TypeDeclaration +import com.github.javaparser.ast.expr.BooleanLiteralExpr +import com.github.javaparser.ast.expr.NormalAnnotationExpr +import com.github.javaparser.ast.type.ClassOrInterfaceType + +/** + * [ClassInfo] + utilities for printing out new class code with proper indentation and imports + */ +class ClassPrinter( + source: List<String>, + private val stringBuilder: StringBuilder, + var cliArgs: Array<String> +) : ClassInfo(source) { + + val GENERATED_MEMBER_HEADER by lazy { "@$GeneratedMember" } + + // Imports + val NonNull by lazy { classRef("android.annotation.NonNull") } + val NonEmpty by lazy { classRef("android.annotation.NonEmpty") } + val Nullable by lazy { classRef("android.annotation.Nullable") } + val TextUtils by lazy { classRef("android.text.TextUtils") } + val LinkedHashMap by lazy { classRef("java.util.LinkedHashMap") } + val Collections by lazy { classRef("java.util.Collections") } + val Preconditions by lazy { classRef("com.android.internal.util.Preconditions") } + val ArrayList by lazy { classRef("java.util.ArrayList") } + val DataClass by lazy { classRef("com.android.internal.util.DataClass") } + val DataClassEnum by lazy { classRef("com.android.internal.util.DataClass.Enum") } + val ParcelWith by lazy { classRef("com.android.internal.util.DataClass.ParcelWith") } + val PluralOf by lazy { classRef("com.android.internal.util.DataClass.PluralOf") } + val Each by lazy { classRef("com.android.internal.util.DataClass.Each") } + val DataClassGenerated by lazy { classRef("com.android.internal.util.DataClass.Generated") } + val GeneratedMember by lazy { classRef("com.android.internal.util.DataClass.Generated.Member") } + val Parcelling by lazy { classRef("com.android.internal.util.Parcelling") } + val UnsupportedAppUsage by lazy { classRef("android.annotation.UnsupportedAppUsage") } + + + /** + * Optionally shortens a class reference if there's a corresponding import present + */ + fun classRef(fullName: String): String { + if (cliArgs.contains(FLAG_NO_FULL_QUALIFIERS)) { + return fullName.split(".").dropWhile { it[0].isLowerCase() }.joinToString(".") + } + + val pkg = fullName.substringBeforeLast(".") + val simpleName = fullName.substringAfterLast(".") + if (fileAst.imports.any { imprt -> + imprt.nameAsString == fullName + || (imprt.isAsterisk && imprt.nameAsString == pkg) + }) { + return simpleName + } else { + val outerClass = pkg.substringAfterLast(".", "") + if (outerClass.firstOrNull()?.isUpperCase() ?: false) { + return classRef(pkg) + "." + simpleName + } + } + return fullName + } + + /** @see classRef */ + inline fun <reified T : Any> classRef(): String { + return classRef(T::class.java.name) + } + + /** @see classRef */ + fun memberRef(fullName: String): String { + val className = fullName.substringBeforeLast(".") + val methodName = fullName.substringAfterLast(".") + return if (fileAst.imports.any { + it.isStatic + && (it.nameAsString == fullName + || (it.isAsterisk && it.nameAsString == className)) + }) { + className.substringAfterLast(".") + "." + methodName + } else { + classRef(className) + "." + methodName + } + } + + val dataClassAnnotationFeatures = classAst.annotations + .find { it.nameAsString == DataClass } + ?.let { it as? NormalAnnotationExpr } + ?.pairs + ?.map { pair -> pair.nameAsString to (pair.value as BooleanLiteralExpr).value } + ?.toMap() + ?: emptyMap() + + val internalAnnotations = setOf(ParcelWith, DataClassEnum, PluralOf, Each, UnsupportedAppUsage) + + /** + * @return whether the given feature is enabled + */ + operator fun FeatureFlag.invoke(): Boolean { + if (cliArgs.contains("--no-$kebabCase")) return false + if (cliArgs.contains("--$kebabCase")) return true + + val annotationKey = "gen$upperCamelCase" + if (dataClassAnnotationFeatures.containsKey(annotationKey)) { + return dataClassAnnotationFeatures[annotationKey]!! + } + + if (cliArgs.contains("--all")) return true + if (hidden) return true + + return when (this) { + FeatureFlag.SETTERS -> + !FeatureFlag.CONSTRUCTOR() && !FeatureFlag.BUILDER() && fields.any { !it.isFinal } + FeatureFlag.BUILDER -> cliArgs.contains(FLAG_BUILDER_PROTECTED_SETTERS) || onByDefault + FeatureFlag.CONSTRUCTOR -> !FeatureFlag.BUILDER() + FeatureFlag.PARCELABLE -> "Parcelable" in superInterfaces + FeatureFlag.AIDL -> FeatureFlag.PARCELABLE() + FeatureFlag.IMPLICIT_NONNULL -> fields.any { it.isNullable } + && fields.none { "@$NonNull" in it.annotations } + else -> onByDefault + } + } + + val FeatureFlag.hidden + get(): Boolean = when { + cliArgs.contains("--hidden-$kebabCase") -> true + this == FeatureFlag.BUILD_UPON -> FeatureFlag.BUILDER.hidden + else -> false + } + + var currentIndent = INDENT_SINGLE + private set + + fun pushIndent() { + currentIndent += INDENT_SINGLE + } + + fun popIndent() { + currentIndent = currentIndent.substring(0, currentIndent.length - INDENT_SINGLE.length) + } + + fun backspace() = stringBuilder.setLength(stringBuilder.length - 1) + val lastChar get() = stringBuilder[stringBuilder.length - 1] + + private fun appendRaw(s: String) { + stringBuilder.append(s) + } + + fun append(s: String) { + if (s.isBlank() && s != "\n") { + appendRaw(s) + } else { + appendRaw(s.lines().map { line -> + if (line.startsWith(" *")) line else line.trimStart() + }.joinToString("\n$currentIndent")) + } + } + + fun appendSameLine(s: String) { + while (lastChar.isWhitespace() || lastChar.isNewline()) { + backspace() + } + appendRaw(s) + } + + fun rmEmptyLine() { + while (lastChar.isWhitespaceNonNewline()) backspace() + if (lastChar.isNewline()) backspace() + } + + /** + * Syntactic sugar for: + * ``` + * +"code()"; + * ``` + * to append the given string plus a newline + */ + operator fun String.unaryPlus() = append("$this\n") + + /** + * Syntactic sugar for: + * ``` + * !"code()"; + * ``` + * to append the given string without a newline + */ + operator fun String.not() = append(this) + + /** + * Syntactic sugar for: + * ``` + * ... { + * ... + * }+";" + * ``` + * to append a ';' on same line after a block, and a newline afterwards + */ + operator fun Unit.plus(s: String) { + appendSameLine(s) + +"" + } + + /** + * A multi-purpose syntactic sugar for appending the given string plus anything generated in + * the given [block], the latter with the appropriate deeper indent, + * and resetting the indent back to original at the end + * + * Usage examples: + * + * ``` + * "if (...)" { + * ... + * } + * ``` + * to append a corresponding if block appropriate indentation + * + * ``` + * "void foo(...)" { + * ... + * } + * ``` + * similar to the previous one, plus an extra empty line after the function body + * + * ``` + * "void foo(" { + * <args code> + * } + * ``` + * to use proper indentation for args code and close the bracket on same line at end + * + * ``` + * "..." { + * ... + * } + * to use the correct indentation for inner code, resetting it at the end + */ + inline operator fun String.invoke(block: ClassPrinter.() -> Unit) { + if (this == " {") { + appendSameLine(this) + } else { + append(this) + } + when { + endsWith("(") -> { + indentedBy(2, block) + appendSameLine(")") + } + endsWith("{") || endsWith(")") -> { + if (!endsWith("{")) appendSameLine(" {") + indentedBy(1, block) + +"}" + if ((endsWith(") {") || endsWith(")") || this == " {") + && !startsWith("synchronized") + && !startsWith("switch") + && !startsWith("if ") + && !contains(" else ") + && !contains("new ") + && !contains("return ")) { + +"" // extra line after function definitions + } + } + else -> indentedBy(2, block) + } + } + + inline fun indentedBy(level: Int, block: ClassPrinter.() -> Unit) { + append("\n") + level times { + append(INDENT_SINGLE) + pushIndent() + } + block() + level times { popIndent() } + rmEmptyLine() + +"" + } + + inline fun Iterable<FieldInfo>.forEachTrimmingTrailingComma(b: FieldInfo.() -> Unit) { + forEachApply { + b() + if (isLast) { + while (lastChar == ' ' || lastChar == '\n') backspace() + if (lastChar == ',') backspace() + } + } + } + + inline operator fun <R> invoke(f: ClassPrinter.() -> R): R = run(f) + + var BuilderClass = CANONICAL_BUILDER_CLASS + var BuilderType = BuilderClass + genericArgs + + init { + val builderFactoryOverride = classAst.methods.find { + it.isStatic && it.nameAsString == "builder" + } + if (builderFactoryOverride != null) { + BuilderClass = (builderFactoryOverride.type as ClassOrInterfaceType).nameAsString + BuilderType = builderFactoryOverride.type.asString() + } else { + val builderExtension = (fileAst.types + + classAst.childNodes.filterIsInstance(TypeDeclaration::class.java)).find { + it.nameAsString == CANONICAL_BUILDER_CLASS + } + if (builderExtension != null) { + BuilderClass = GENERATED_BUILDER_CLASS + val tp = (builderExtension as ClassOrInterfaceDeclaration).typeParameters + BuilderType = if (tp.isEmpty()) BuilderClass + else "$BuilderClass<${tp.map { it.nameAsString }.joinToString(", ")}>" + } + } + } +}
\ No newline at end of file diff --git a/tools/codegen/src/com/android/codegen/ConstDef.kt b/tools/codegen/src/com/android/codegen/ConstDef.kt new file mode 100644 index 000000000000..f559d6f87027 --- /dev/null +++ b/tools/codegen/src/com/android/codegen/ConstDef.kt @@ -0,0 +1,17 @@ +package com.android.codegen + +import com.github.javaparser.ast.body.FieldDeclaration + +/** + * `@IntDef` or `@StringDef` + */ +data class ConstDef(val type: Type, val AnnotationName: String, val values: List<FieldDeclaration>) { + + enum class Type { + INT, INT_FLAGS, STRING; + + val isInt get() = this == INT || this == INT_FLAGS + } + + val CONST_NAMES get() = values.flatMap { it.variables }.map { it.nameAsString } +}
\ No newline at end of file diff --git a/tools/codegen/src/com/android/codegen/FeatureFlag.kt b/tools/codegen/src/com/android/codegen/FeatureFlag.kt new file mode 100644 index 000000000000..24150d637a7b --- /dev/null +++ b/tools/codegen/src/com/android/codegen/FeatureFlag.kt @@ -0,0 +1,27 @@ +package com.android.codegen + + +/** + * See also [ClassPrinter.invoke] for more default flag values resolution rules + */ +enum class FeatureFlag(val onByDefault: Boolean, val desc: String = "") { + PARCELABLE(false, "implement Parcelable contract"), + AIDL(false, "generate a 'parcelable declaration' .aidl file alongside"), + CONSTRUCTOR(true, "an all-argument constructor"), + BUILDER(false, "e.g. MyClass.builder().setFoo(..).build();"), + GETTERS(true, "getters, e.g. getFoo()"), + SETTERS(false, "chainable/fluent setters, e.g. setFoo(..).setBar(..)"), + WITHERS(false, "'immutable setters' returning a new instance, " + + "e.g. newFoo = foo.withBar(barValue)"), + EQUALS_HASH_CODE(false, "equals + hashCode based on fields"), + TO_STRING(false, "toString based on fields"), + BUILD_UPON(false, "builder factory from existing instance, " + + "e.g. instance.buildUpon().setFoo(..).build()"), + IMPLICIT_NONNULL(true, "treat lack of @Nullable as @NonNull for Object fields"), + COPY_CONSTRUCTOR(false, "a constructor for an instance identical to the given one"), + CONST_DEFS(true, "@Int/StringDef's based on declared static constants"), + FOR_EACH_FIELD(false, "forEachField((name, value) -> ...)"); + + val kebabCase = name.toLowerCase().replace("_", "-") + val upperCamelCase = name.split("_").map { it.toLowerCase().capitalize() }.joinToString("") +} diff --git a/tools/codegen/src/com/android/codegen/FieldInfo.kt b/tools/codegen/src/com/android/codegen/FieldInfo.kt new file mode 100644 index 000000000000..f326fd5601fe --- /dev/null +++ b/tools/codegen/src/com/android/codegen/FieldInfo.kt @@ -0,0 +1,216 @@ +package com.android.codegen + +import com.github.javaparser.JavaParser +import com.github.javaparser.ast.body.FieldDeclaration +import com.github.javaparser.ast.expr.ClassExpr +import com.github.javaparser.ast.expr.Name +import com.github.javaparser.ast.expr.SingleMemberAnnotationExpr +import com.github.javaparser.ast.expr.StringLiteralExpr +import com.github.javaparser.ast.type.ArrayType +import com.github.javaparser.ast.type.ClassOrInterfaceType +import com.github.javaparser.javadoc.Javadoc +import java.lang.Long + +data class FieldInfo( + val index: Int, + val fieldAst: FieldDeclaration, + private val classInfo: ClassInfo +) { + + val classPrinter = classInfo as ClassPrinter + + // AST + internal val variableAst = fieldAst.variables[0] + val typeAst = variableAst.type + + // Field type + val Type = typeAst.asString() + val FieldClass = Type.takeWhile { it != '<' } + val isPrimitive = Type in PRIMITIVE_TYPES + + // Javadoc + val javadoc: Javadoc? = fieldAst.javadoc.orElse(null) + private val javadocText = javadoc?.toText()?.let { + // Workaround for a bug in Javaparser for javadocs starting with { + if (it.hasUnbalancedCurlyBrace()) "{$it" else it + } + val javadocTextNoAnnotationLines = javadocText + ?.lines() + ?.dropLastWhile { it.startsWith("@") || it.isBlank() } + ?.let { if (it.isEmpty()) null else it } + val javadocFull = javadocText + ?.trimBlankLines() + ?.mapLines { " * $this" } + ?.let { "/**\n$it\n */" } + + + // Field name + val name = variableAst.name.asString()!! + private val isNameHungarian = name[0] == 'm' && name[1].isUpperCase() + val NameUpperCamel = if (isNameHungarian) name.substring(1) else name.capitalize() + val nameLowerCamel = if (isNameHungarian) NameUpperCamel.decapitalize() else name + val _name = if (name != nameLowerCamel) nameLowerCamel else "_$nameLowerCamel" + val SingularNameOrNull by lazy { + classPrinter { + fieldAst.annotations + .find { it.nameAsString == PluralOf } + ?.let { it as? SingleMemberAnnotationExpr } + ?.memberValue + ?.let { it as? StringLiteralExpr } + ?.value + ?.toLowerCamel() + ?.capitalize() + } + } + val SingularName by lazy { SingularNameOrNull ?: NameUpperCamel } + + + // Field value + val mayBeNull: Boolean + get() = when { + isPrimitive -> false + "@${classPrinter.NonNull}" in annotations -> false + "@${classPrinter.NonEmpty}" in annotations -> false + isNullable -> true + lazyInitializer != null -> true + else -> classPrinter { !FeatureFlag.IMPLICIT_NONNULL() } + } + val lazyInitializer + get() = classInfo.classAst.methods.find { method -> + method.nameAsString == "lazyInit$NameUpperCamel" && method.parameters.isEmpty() + }?.nameAsString + val internalGetter get() = if (lazyInitializer != null) "get$NameUpperCamel()" else name + val defaultExpr: Any? + get() { + variableAst.initializer.orElse(null)?.let { return it } + classInfo.classAst.methods.find { + it.nameAsString == "default$NameUpperCamel" && it.parameters.isEmpty() + }?.run { "$nameAsString()" }?.let { return it } + if (FieldClass == "List") return "${classPrinter.memberRef("java.util.Collections.emptyList")}()" + return null + } + val hasDefault get() = defaultExpr != null + + + // Generic args + val isArray = Type.endsWith("[]") + val isList = FieldClass == "List" || FieldClass == "ArrayList" + val fieldBit = "0x${Long.toHexString(1L shl index)}" + var isLast = false + val isFinal = fieldAst.isFinal + val fieldTypeGenegicArgs = when (typeAst) { + is ArrayType -> listOf(fieldAst.elementType.asString()) + is ClassOrInterfaceType -> { + typeAst.typeArguments.orElse(null)?.map { it.asString() } ?: emptyList() + } + else -> emptyList() + } + val FieldInnerType = fieldTypeGenegicArgs.firstOrNull() + val FieldInnerClass = FieldInnerType?.takeWhile { it != '<' } + + + // Annotations + var intOrStringDef = null as ConstDef? + val annotations by lazy { + if (FieldClass in BUILTIN_SPECIAL_PARCELLINGS) { + classPrinter { + fieldAst.addAnnotation(SingleMemberAnnotationExpr( + Name(ParcelWith), + ClassExpr(JavaParser.parseClassOrInterfaceType( + "$Parcelling.BuiltIn.For$FieldClass")))) + } + } + fieldAst.annotations.map { it.removeComment().toString() } + } + val annotationsNoInternal by lazy { + annotations.filterNot { ann -> + classPrinter { + internalAnnotations.any { + it in ann + } + } + } + } + + fun hasAnnotation(a: String) = annotations.any { it.startsWith(a) } + val isNullable by lazy { hasAnnotation("@Nullable") } + val isNonEmpty by lazy { hasAnnotation("@${classPrinter.NonEmpty}") } + val customParcellingClass by lazy { + fieldAst.annotations.find { it.nameAsString == classPrinter.ParcelWith } + ?.singleArgAs<ClassExpr>() + ?.type + ?.asString() + } + val annotationsAndType by lazy { (annotationsNoInternal + Type).joinToString(" ") } + val sParcelling by lazy { customParcellingClass?.let { "sParcellingFor$NameUpperCamel" } } + val annotatedTypeForSetterParam by lazy { + (annotationsNoInternal + if (isArray) "$FieldInnerType..." else Type).joinToString(" ") + } + + // Utilities + + /** + * `mFoo.size()` + */ + val ClassPrinter.sizeExpr get() = when { + isArray && FieldInnerClass !in PRIMITIVE_TYPES -> + memberRef("com.android.internal.util.ArrayUtils.size") + "($name)" + isArray -> "$name.length" + listOf("List", "Set", "Map").any { FieldClass.endsWith(it) } -> + memberRef("com.android.internal.util.CollectionUtils.size") + "($name)" + Type == "String" -> memberRef("android.text.TextUtils.length") + "($name)" + Type == "CharSequence" -> "$name.length()" + else -> "$name.size()" + } + /** + * `mFoo.get(0)` + */ + fun elemAtIndexExpr(indexExpr: String) = when { + isArray -> "$name[$indexExpr]" + FieldClass == "ArraySet" -> "$name.valueAt($indexExpr)" + else -> "$name.get($indexExpr)" + } + /** + * `mFoo.isEmpty()` + */ + val ClassPrinter.isEmptyExpr get() = when { + isArray || Type == "CharSequence" -> "$sizeExpr == 0" + else -> "$name.isEmpty()" + } + + /** + * `mFoo == that` or `Objects.equals(mFoo, that)`, etc. + */ + fun ClassPrinter.isEqualToExpr(that: String) = when { + Type in PRIMITIVE_TYPES -> "$internalGetter == $that" + isArray -> "${memberRef("java.util.Arrays.equals")}($internalGetter, $that)" + else -> "${memberRef("java.util.Objects.equals")}($internalGetter, $that)" + } + + /** + * Parcel.write* and Parcel.read* method name wildcard values + */ + val ParcelMethodsSuffix = when { + FieldClass in PRIMITIVE_TYPES - "char" - "boolean" + + listOf("String", "CharSequence", "Exception", "Size", "SizeF", "Bundle", + "FileDescriptor", "SparseBooleanArray", "SparseIntArray", "SparseArray") -> + FieldClass + FieldClass == "Map" && fieldTypeGenegicArgs[0] == "String" -> "Map" + isArray -> when { + FieldInnerType!! in (PRIMITIVE_TYPES + "String") -> FieldInnerType + "Array" + isBinder(FieldInnerType) -> "BinderArray" + else -> "TypedArray" + } + isList -> when { + FieldInnerType == "String" -> "StringList" + isBinder(FieldInnerType!!) -> "BinderList" + else -> "ParcelableList" + } + isIInterface(Type) -> "StrongInterface" + isBinder(Type) -> "StrongBinder" + else -> "TypedObject" + }.capitalize() + + private fun isBinder(type: String) = type == "Binder" || type == "IBinder" || isIInterface(type) + private fun isIInterface(type: String) = type.length >= 2 && type[0] == 'I' && type[1].isUpperCase() +}
\ No newline at end of file diff --git a/tools/codegen/src/com/android/codegen/Generators.kt b/tools/codegen/src/com/android/codegen/Generators.kt new file mode 100644 index 000000000000..ab64f4efc8d8 --- /dev/null +++ b/tools/codegen/src/com/android/codegen/Generators.kt @@ -0,0 +1,847 @@ +package com.android.codegen + +import com.github.javaparser.ast.body.FieldDeclaration +import com.github.javaparser.ast.body.VariableDeclarator +import com.github.javaparser.ast.expr.* +import java.io.File + + +/** + * IntDefs and StringDefs based on constants + */ +fun ClassPrinter.generateConstDefs() { + val consts = classAst.fields.filter { + it.isStatic && it.isFinal && it.variables.all { variable -> + val initializer = variable.initializer.orElse(null) + val isLiteral = initializer is LiteralExpr + || (initializer is UnaryExpr && initializer.expression is LiteralExpr) + isLiteral && variable.type.asString() in listOf("int", "String") + } + }.flatMap { field -> field.variables.map { it to field } } + val intConsts = consts.filter { it.first.type.asString() == "int" } + val strConsts = consts.filter { it.first.type.asString() == "String" } + val intGroups = intConsts.groupBy { it.first.nameAsString.split("_")[0] }.values + val strGroups = strConsts.groupBy { it.first.nameAsString.split("_")[0] }.values + intGroups.forEach { + generateConstDef(it) + } + strGroups.forEach { + generateConstDef(it) + } +} + +fun ClassPrinter.generateConstDef(consts: List<Pair<VariableDeclarator, FieldDeclaration>>) { + if (consts.size <= 1) return + + val names = consts.map { it.first.nameAsString!! } + val prefix = names + .reduce { a, b -> a.commonPrefixWith(b) } + .dropLastWhile { it != '_' } + .dropLast(1) + if (prefix.isEmpty()) { + println("Failed to generate const def for $names") + return + } + var AnnotationName = prefix.split("_") + .filterNot { it.isBlank() } + .map { it.toLowerCase().capitalize() } + .joinToString("") + val annotatedConst = consts.find { it.second.annotations.isNonEmpty } + if (annotatedConst != null) { + AnnotationName = annotatedConst.second.annotations.first().nameAsString + } + val type = consts[0].first.type.asString() + val flag = type == "int" && consts.all { it.first.initializer.get().toString().startsWith("0x") } + val constDef = ConstDef(type = when { + type == "String" -> ConstDef.Type.STRING + flag -> ConstDef.Type.INT_FLAGS + else -> ConstDef.Type.INT + }, + AnnotationName = AnnotationName, + values = consts.map { it.second } + ) + constDefs += constDef + fields.forEachApply { + if (fieldAst.annotations.any { it.nameAsString == AnnotationName }) { + this.intOrStringDef = constDef + } + } + + val visibility = if (consts[0].second.isPublic) "public" else "/* package-*/" + + val Retention = classRef("java.lang.annotation.Retention") + val RetentionPolicySource = memberRef("java.lang.annotation.RetentionPolicy.SOURCE") + val ConstDef = classRef("android.annotation.${type.capitalize()}Def") + + "@$ConstDef(${if_(flag, "flag = true, ")}prefix = \"${prefix}_\", value = {" { + names.forEachLastAware { name, isLast -> + +"$name${if_(!isLast, ",")}" + } + } + ")" + +"@$Retention($RetentionPolicySource)" + +GENERATED_MEMBER_HEADER + +"$visibility @interface $AnnotationName {}" + +"" + + if (type == "int") { + +GENERATED_MEMBER_HEADER + val methodDefLine = "$visibility static String ${AnnotationName.decapitalize()}ToString(" + + "@$AnnotationName int value)" + if (flag) { + val flg2str = memberRef("com.android.internal.util.BitUtils.flagsToString") + methodDefLine { + "return $flg2str(" { + +"value, $ClassName::single${AnnotationName}ToString" + } + ";" + } + +GENERATED_MEMBER_HEADER + !"static String single${AnnotationName}ToString(@$AnnotationName int value)" + } else { + !methodDefLine + } + " {" { + "switch (value) {" { + names.forEach { name -> + "case $name:" { + +"return \"$name\";" + } + } + +"default: return Integer.toHexString(value);" + } + } + } +} + +fun ClassPrinter.generateAidl(javaFile: File) { + val aidl = File(javaFile.path.substringBeforeLast(".java") + ".aidl") + if (aidl.exists()) return + aidl.writeText(buildString { + sourceLines.dropLastWhile { !it.startsWith("package ") }.forEach { + appendln(it) + } + append("\nparcelable $ClassName;\n") + }) +} + +/** + * ``` + * Foo newFoo = oldFoo.withBar(newBar); + * ``` + */ +fun ClassPrinter.generateWithers() { + fields.forEachApply { + val metodName = "with$NameUpperCamel" + if (!hasMethod(metodName, Type)) { + generateFieldJavadoc(forceHide = FeatureFlag.WITHERS.hidden) + """@$NonNull + $GENERATED_MEMBER_HEADER + public $ClassType $metodName($annotatedTypeForSetterParam value)""" { + val changedFieldName = name + + "return new $ClassType(" { + fields.forEachTrimmingTrailingComma { + if (name == changedFieldName) +"value," else +"$name," + } + } + ";" + } + } + } +} + +fun ClassPrinter.generateCopyConstructor() { + if (classAst.constructors.any { + it.parameters.size == 1 && + it.parameters[0].type.asString() == ClassType + }) { + return + } + + +"/** Copy constructor */" + +GENERATED_MEMBER_HEADER + "public $ClassName(@$NonNull $ClassName orig)" { + fields.forEachApply { + +"$name = orig.$name;" + } + } +} + +/** + * ``` + * Foo newFoo = oldFoo.buildUpon().setBar(newBar).build(); + * ``` + */ +fun ClassPrinter.generateBuildUpon() { + if (hasMethod("buildUpon")) return + + +"/**" + +" * Provides an instance of {@link $BuilderClass} with state corresponding to this instance." + if (FeatureFlag.BUILD_UPON.hidden) { + +" * @hide" + } + +" */" + +GENERATED_MEMBER_HEADER + "public $BuilderType buildUpon()" { + "return new $BuilderType()" { + fields.forEachApply { + +".set$NameUpperCamel($internalGetter)" + } + ";" + } + } +} + +fun ClassPrinter.generateBuilder() { + val setterVisibility = if (cliArgs.contains(FLAG_BUILDER_PROTECTED_SETTERS)) + "protected" else "public" + val constructorVisibility = if (BuilderClass == CANONICAL_BUILDER_CLASS) + "public" else "/* package-*/" + + val OneTimeUseBuilder = classRef("android.provider.OneTimeUseBuilder") + + +"/**" + +" * A builder for {@link $ClassName}" + if (FeatureFlag.BUILDER.hidden) +" * @hide" + +" */" + +"@SuppressWarnings(\"WeakerAccess\")" + +GENERATED_MEMBER_HEADER + "public static class $BuilderClass$genericArgs" { + +"extends $OneTimeUseBuilder<$ClassType>" + } + " {" { + + +"" + fields.forEachApply { + +"protected $annotationsAndType $name;" + } + +"" + +"protected long mBuilderFieldsSet = 0L;" + +"" + +"$constructorVisibility $BuilderClass() {};" + +"" + + generateBuilderSetters(setterVisibility) + + generateBuilderBuild() + + rmEmptyLine() + } +} + +private fun ClassPrinter.generateBuilderSetters(visibility: String) { + + fields.forEachApply { + val maybeCast = + if_(BuilderClass != CANONICAL_BUILDER_CLASS, " ($CANONICAL_BUILDER_CLASS)") + + generateFieldJavadoc() + +GENERATED_MEMBER_HEADER + "$visibility $CANONICAL_BUILDER_CLASS set$NameUpperCamel($annotatedTypeForSetterParam value)" { + +"checkNotUsed();" + +"mBuilderFieldsSet |= $fieldBit;" + +"$name = value;" + +"return$maybeCast this;" + } + + + val javadocSeeSetter = "/** @see #set$NameUpperCamel */" + val singularNameCustomizationHint = if (SingularNameOrNull == null) { + "// You can refine this method's name by providing item's singular name, e.g.:\n" + + "// @DataClass.PluralOf(\"item\")) mItems = ...\n\n" + } else "" + + if (isList && FieldInnerType != null) { + + +javadocSeeSetter + +GENERATED_MEMBER_HEADER + "$visibility $CANONICAL_BUILDER_CLASS add$SingularName(@$NonNull $FieldInnerType value)" { + !singularNameCustomizationHint + +"if ($name == null) set$NameUpperCamel(new $ArrayList<>());" + +"$name.add(value);" + +"return$maybeCast this;" + } + } + + if (Type.contains("Map<")) { + val (Key, Value) = fieldTypeGenegicArgs + + +javadocSeeSetter + +GENERATED_MEMBER_HEADER + "$visibility $CANONICAL_BUILDER_CLASS add$SingularName($Key key, $Value value)" { + !singularNameCustomizationHint + +"if ($name == null) set$NameUpperCamel(new $LinkedHashMap());" + +"$name.put(key, value);" + +"return$maybeCast this;" + } + } + + if (Type == "boolean") { + +javadocSeeSetter + +GENERATED_MEMBER_HEADER + "$visibility $CANONICAL_BUILDER_CLASS mark$NameUpperCamel()" { + +"return set$NameUpperCamel(true);" + } + + +javadocSeeSetter + +GENERATED_MEMBER_HEADER + "$visibility $CANONICAL_BUILDER_CLASS markNot$NameUpperCamel()" { + +"return set$NameUpperCamel(false);" + } + } + } +} + +private fun ClassPrinter.generateBuilderBuild() { + +"/** Builds the instance. This builder should not be touched after calling this! */" + "public $ClassType build()" { + +"markUsed();" + fields.forEachApply { + if (!isNullable || hasDefault) { + "if ((mBuilderFieldsSet & $fieldBit) == 0)" { + if (!isNullable && !hasDefault) { + +"throw new IllegalStateException(\"Required field not set: $nameLowerCamel\");" + } else { + +"$name = $defaultExpr;" + } + } + } + } + "$ClassType o = new $ClassType(" { + fields.forEachTrimmingTrailingComma { + +"$name," + } + } + ";" + +"return o;" + } +} + +fun ClassPrinter.generateParcelable() { + val booleanFields = fields.filter { it.Type == "boolean" } + val objectFields = fields.filter { it.Type !in PRIMITIVE_TYPES } + val nullableFields = objectFields.filter { it.mayBeNull } + val nonBooleanFields = fields - booleanFields + + + val flagStorageType = when (fields.size) { + in 0..7 -> "byte" + in 8..15 -> "int" + in 16..31 -> "long" + else -> throw NotImplementedError("32+ field classes not yet supported") + } + val FlagStorageType = flagStorageType.capitalize() + + fields.forEachApply { + if (sParcelling != null) { + +GENERATED_MEMBER_HEADER + "static $Parcelling<$Type> $sParcelling =" { + "$Parcelling.Cache.get(" { + +"$customParcellingClass.class" + } + ";" + } + "static {" { + "if ($sParcelling == null)" { + "$sParcelling = $Parcelling.Cache.put(" { + +"new $customParcellingClass()" + } + ";" + } + } + +"" + } + } + + val Parcel = classRef("android.os.Parcel") + if (!hasMethod("writeToParcel", Parcel, "int")) { + +"@Override" + +GENERATED_MEMBER_HEADER + "public void writeToParcel($Parcel dest, int flags)" { + +"// You can override field parcelling by defining methods like:" + +"// void parcelFieldName(Parcel dest, int flags) { ... }" + +"" + + if (booleanFields.isNotEmpty() || nullableFields.isNotEmpty()) { + +"$flagStorageType flg = 0;" + booleanFields.forEachApply { + +"if ($internalGetter) flg |= $fieldBit;" + } + nullableFields.forEachApply { + +"if ($internalGetter != null) flg |= $fieldBit;" + } + +"dest.write$FlagStorageType(flg);" + } + + nonBooleanFields.forEachApply { + val customParcellingMethod = "parcel$NameUpperCamel" + when { + hasMethod(customParcellingMethod, Parcel, "int") -> + +"$customParcellingMethod(dest, flags);" + customParcellingClass != null -> +"$sParcelling.parcel($name, dest, flags);" + hasAnnotation("@$DataClassEnum") -> + +"dest.writeInt($internalGetter == null ? -1 : $internalGetter.ordinal());" + else -> { + if (mayBeNull) !"if ($internalGetter != null) " + var args = internalGetter + if (ParcelMethodsSuffix.startsWith("Parcelable") + || ParcelMethodsSuffix.startsWith("TypedObject") + || ParcelMethodsSuffix == "TypedArray") { + args += ", flags" + } + +"dest.write$ParcelMethodsSuffix($args);" + } + } + } + } + } + + if (!hasMethod("describeContents")) { + +"@Override" + +GENERATED_MEMBER_HEADER + +"public int describeContents() { return 0; }" + +"" + } + + if (classAst.fields.none { it.variables[0].nameAsString == "CREATOR" }) { + val Creator = classRef("android.os.Parcelable.Creator") + + +GENERATED_MEMBER_HEADER + "public static final @$NonNull $Creator<$ClassName> CREATOR" { + +"= new $Creator<$ClassName>()" + }; " {" { + + +"@Override" + "public $ClassName[] newArray(int size)" { + +"return new $ClassName[size];" + } + + +"@Override" + +"@SuppressWarnings({\"unchecked\", \"RedundantCast\"})" + "public $ClassName createFromParcel($Parcel in)" { + +"// You can override field unparcelling by defining methods like:" + +"// static FieldType unparcelFieldName(Parcel in) { ... }" + +"" + if (booleanFields.isNotEmpty() || nullableFields.isNotEmpty()) { + +"$flagStorageType flg = in.read$FlagStorageType();" + } + booleanFields.forEachApply { + +"$Type $_name = (flg & $fieldBit) != 0;" + } + nonBooleanFields.forEachApply { + + // Handle customized parceling + val customParcellingMethod = "unparcel$NameUpperCamel" + if (hasMethod(customParcellingMethod, Parcel)) { + +"$Type $_name = $customParcellingMethod(in);" + } else if (customParcellingClass != null) { + +"$Type $_name = $sParcelling.unparcel(in);" + } else if (hasAnnotation("@$DataClassEnum")) { + val ordinal = "${_name}Ordinal" + +"int $ordinal = in.readInt();" + +"$Type $_name = $ordinal < 0 ? null : $FieldClass.values()[$ordinal];" + } else { + val methodArgs = mutableListOf<String>() + + // Create container if any + val containerInitExpr = when { + FieldClass.endsWith("Map") -> "new $LinkedHashMap<>()" + FieldClass == "List" || FieldClass == "ArrayList" -> + "new ${classRef("java.util.ArrayList")}<>()" +// isArray && FieldInnerType in (PRIMITIVE_TYPES + "String") -> +// "new $FieldInnerType[in.readInt()]" + else -> "" + } + val passContainer = containerInitExpr.isNotEmpty() + + // nullcheck + + // "FieldType fieldName = (FieldType)" + if (passContainer) { + methodArgs.add(_name) + !"$Type $_name = " + if (mayBeNull) { + +"null;" + !"if ((flg & $fieldBit) != 0) {" + pushIndent() + +"" + !"$_name = " + } + +"$containerInitExpr;" + } else { + !"$Type $_name = " + if (mayBeNull) !"(flg & $fieldBit) == 0 ? null : " + if (ParcelMethodsSuffix == "StrongInterface") { + !"$FieldClass.Stub.asInterface(" + } else if (Type !in PRIMITIVE_TYPES + "String" + "Bundle" && + (!isArray || FieldInnerType !in PRIMITIVE_TYPES + "String") && + ParcelMethodsSuffix != "Parcelable") { + !"($Type) " + } + } + + // Determine method args + when { + ParcelMethodsSuffix == "Parcelable" -> + methodArgs += "$FieldClass.class.getClassLoader()" + ParcelMethodsSuffix == "TypedObject" -> + methodArgs += "$FieldClass.CREATOR" + ParcelMethodsSuffix == "TypedArray" -> + methodArgs += "$FieldInnerClass.CREATOR" + ParcelMethodsSuffix.startsWith("Parcelable") + || FieldClass == "Map" + || (isList || isArray) + && FieldInnerType !in PRIMITIVE_TYPES + "String" -> + methodArgs += "$FieldInnerClass.class.getClassLoader()" + } + + // ...in.readFieldType(args...); + when { + ParcelMethodsSuffix == "StrongInterface" -> !"in.readStrongBinder" + isArray -> !"in.create$ParcelMethodsSuffix" + else -> !"in.read$ParcelMethodsSuffix" + } + !"(${methodArgs.joinToString(", ")})" + if (ParcelMethodsSuffix == "StrongInterface") !")" + +";" + + // Cleanup if passContainer + if (passContainer && mayBeNull) { + popIndent() + rmEmptyLine() + +"\n}" + } + } + } + "return new $ClassType(" { + fields.forEachTrimmingTrailingComma { + +"$_name," + } + } + ";" + } + rmEmptyLine() + } + ";" + +"" + } +} + +fun ClassPrinter.generateEqualsHashcode() { + if (!hasMethod("equals", "Object")) { + +"@Override" + +GENERATED_MEMBER_HEADER + "public boolean equals(Object o)" { + +"// You can override field equality logic by defining either of the methods like:" + +"// boolean fieldNameEquals($ClassName other) { ... }" + +"// boolean fieldNameEquals(FieldType otherValue) { ... }" + +"" + """if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + @SuppressWarnings("unchecked") + $ClassType that = ($ClassType) o; + //noinspection PointlessBooleanExpression + return true""" { + fields.forEachApply { + val sfx = if (isLast) ";" else "" + val customEquals = "${nameLowerCamel}Equals" + when { + hasMethod(customEquals, Type) -> +"&& $customEquals(that.$internalGetter)$sfx" + hasMethod(customEquals, ClassType) -> +"&& $customEquals(that)$sfx" + else -> +"&& ${isEqualToExpr("that.$internalGetter")}$sfx" + } + } + } + } + } + + if (!hasMethod("hashCode")) { + +"@Override" + +GENERATED_MEMBER_HEADER + "public int hashCode()" { + +"// You can override field hashCode logic by defining methods like:" + +"// int fieldNameHashCode() { ... }" + +"" + +"int _hash = 1;" + fields.forEachApply { + !"_hash = 31 * _hash + " + val customHashCode = "${nameLowerCamel}HashCode" + when { + hasMethod(customHashCode) -> +"$customHashCode();" + Type == "int" || Type == "byte" -> +"$internalGetter;" + Type in PRIMITIVE_TYPES -> +"${Type.capitalize()}.hashCode($internalGetter);" + isArray -> +"${memberRef("java.util.Arrays.hashCode")}($internalGetter);" + else -> +"${memberRef("java.util.Objects.hashCode")}($internalGetter);" + } + } + +"return _hash;" + } + } +} + +//TODO support IntDef flags? +fun ClassPrinter.generateToString() { + if (!hasMethod("toString")) { + +"@Override" + +GENERATED_MEMBER_HEADER + "public String toString()" { + +"// You can override field toString logic by defining methods like:" + +"// String fieldNameToString() { ... }" + +"" + "return \"$ClassName { \" +" { + fields.forEachApply { + val customToString = "${nameLowerCamel}ToString" + val expr = when { + hasMethod(customToString) -> "$customToString()" + isArray -> "${memberRef("java.util.Arrays.toString")}($internalGetter)" + intOrStringDef?.type?.isInt == true -> + "${intOrStringDef!!.AnnotationName.decapitalize()}ToString($name)" + else -> internalGetter + } + +"\"$nameLowerCamel = \" + $expr${if_(!isLast, " + \", \"")} +" + } + } + +"\" }\";" + } + } +} + +fun ClassPrinter.generateSetters() { + fields.forEachApply { + if (!hasMethod("set$NameUpperCamel", Type) + && !fieldAst.isPublic + && !isFinal) { + + generateFieldJavadoc(forceHide = FeatureFlag.SETTERS.hidden) + +GENERATED_MEMBER_HEADER + "public $ClassType set$NameUpperCamel($annotatedTypeForSetterParam value)" { + generateSetFrom("value") + +"return this;" + } + } + } +} + +fun ClassPrinter.generateGetters() { + (fields + lazyTransientFields).forEachApply { + val methodPrefix = if (Type == "boolean") "is" else "get" + val methodName = methodPrefix + NameUpperCamel + + if (!hasMethod(methodName) && !fieldAst.isPublic) { + + generateFieldJavadoc(forceHide = FeatureFlag.GETTERS.hidden) + +GENERATED_MEMBER_HEADER + "public $annotationsAndType $methodName()" { + if (lazyInitializer == null) { + +"return $name;" + } else { + +"$Type $_name = $name;" + "if ($_name == null)" { + if (fieldAst.isVolatile) { + "synchronized(this)" { + +"$_name = $name;" + "if ($_name == null)" { + +"$_name = $name = $lazyInitializer();" + } + } + } else { + +"// You can mark field as volatile for thread-safe double-check init" + +"$_name = $name = $lazyInitializer();" + } + } + +"return $_name;" + } + } + } + } +} + +fun FieldInfo.generateFieldJavadoc(forceHide: Boolean = false) = classPrinter { + if (javadocFull != null || forceHide) { + var hidden = false + (javadocFull ?: "/**\n */").lines().forEach { + if (it.contains("@hide")) hidden = true + if (it.contains("*/") && forceHide && !hidden) { + if (javadocFull != null) +" *" + +" * @hide" + } + +it + } + } +} + +fun FieldInfo.generateSetFrom(source: String) = classPrinter { + !"$name = " + if (Type in PRIMITIVE_TYPES || mayBeNull) { + +"$source;" + } else if (defaultExpr != null) { + "$source != null" { + +"? $source" + +": $defaultExpr;" + } + } else { + val checkNotNull = memberRef("com.android.internal.util.Preconditions.checkNotNull") + +"$checkNotNull($source);" + } + if (isNonEmpty) { + "if ($isEmptyExpr)" { + +"throw new IllegalArgumentException(\"$nameLowerCamel cannot be empty\");" + } + } +} + +fun ClassPrinter.generateConstructor(visibility: String = "public") { + if (visibility == "public") { + generateConstructorJavadoc() + } + +GENERATED_MEMBER_HEADER + "$visibility $ClassName(" { + fields.forEachApply { + +"$annotationsAndType $nameLowerCamel${if_(!isLast, ",")}" + } + } + " {" { + fields.forEachApply { + !"this." + generateSetFrom(nameLowerCamel) + } + + generateStateValidation() + + generateOnConstructedCallback() + } +} + +private fun ClassPrinter.generateConstructorJavadoc() { + if (fields.all { it.javadoc == null } && !FeatureFlag.CONSTRUCTOR.hidden) return + +"/**" + fields.filter { it.javadoc != null }.forEachApply { + javadocTextNoAnnotationLines?.apply { + +" * @param $nameLowerCamel" + forEach { + +" * $it" + } + } + } + if (FeatureFlag.CONSTRUCTOR.hidden) +" * @hide" + +" */" +} + +private fun ClassPrinter.generateStateValidation() { + val Size = classRef("android.annotation.Size") + val knownNonValidationAnnotations = internalAnnotations + Nullable + + val validate = memberRef("com.android.internal.util.AnnotationValidations.validate") + fun appendValidateCall(annotation: AnnotationExpr, valueToValidate: String) { + "$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 -> + !",\n\"$name\", $value" + } + } + +";" + } + + fields.forEachApply { + if (intOrStringDef != null) { + if (intOrStringDef!!.type == ConstDef.Type.INT_FLAGS) { + +"" + +"//noinspection PointlessBitwiseExpression" + "$Preconditions.checkFlagsArgument(" { + "$name, 0" { + intOrStringDef!!.CONST_NAMES.forEach { + +"| $it" + } + } + } + +";" + } else { + +"" + +"//noinspection PointlessBooleanExpression" + "if (true" { + intOrStringDef!!.CONST_NAMES.forEach { CONST_NAME -> + +"&& !(${isEqualToExpr(CONST_NAME)})" + } + }; rmEmptyLine(); ") {" { + "throw new ${classRef<IllegalArgumentException>()}(" { + "\"$nameLowerCamel was \" + $internalGetter + \" but must be one of: \"" { + + intOrStringDef!!.CONST_NAMES.forEachLastAware { CONST_NAME, isLast -> + +"""+ "$CONST_NAME(" + $CONST_NAME + ")${if_(!isLast, ", ")}"""" + } + } + } + +";" + } + } + } + + val eachLine = fieldAst.annotations.find { it.nameAsString == Each }?.range?.orElse(null)?.end?.line + val perElementValidations = if (eachLine == null) emptyList() else fieldAst.annotations.filter { + it.nameAsString != Each && + it.range.orElse(null)?.begin?.line?.let { it >= eachLine } ?: false + } + + fieldAst.annotations.filterNot { + it.nameAsString == intOrStringDef?.AnnotationName + || it.nameAsString in knownNonValidationAnnotations + || it in perElementValidations + }.forEach { annotation -> + appendValidateCall(annotation, + valueToValidate = if (annotation.nameAsString == Size) sizeExpr else name) + } + + if (perElementValidations.isNotEmpty()) { + +"int ${nameLowerCamel}Size = $sizeExpr;" + "for (int i = 0; i < ${nameLowerCamel}Size; i++) {" { + perElementValidations.forEach { annotation -> + appendValidateCall(annotation, + valueToValidate = elemAtIndexExpr("i")) + } + } + } + } +} + +private fun ClassPrinter.generateOnConstructedCallback(prefix: String = "") { + +"" + val call = "${prefix}onConstructed();" + if (hasMethod("onConstructed")) { + +call + } else { + +"// $call // You can define this method to get a callback" + } +} + +fun ClassPrinter.generateForEachField() { + val specializations = listOf("Object", "int") + val usedSpecializations = fields.map { if (it.Type in specializations) it.Type else "Object" } + val usedSpecializationsSet = usedSpecializations.toSet() + + val PerObjectFieldAction = classRef("com.android.internal.util.DataClass.PerObjectFieldAction") + + +GENERATED_MEMBER_HEADER + "void forEachField(" { + usedSpecializationsSet.toList().forEachLastAware { specType, isLast -> + val SpecType = specType.capitalize() + val ActionClass = classRef("com.android.internal.util.DataClass.Per${SpecType}FieldAction") + +"$ActionClass<$ClassType> action$SpecType${if_(!isLast, ",")}" + } + }; " {" { + usedSpecializations.forEachIndexed { i, specType -> + val SpecType = specType.capitalize() + fields[i].apply { + +"action$SpecType.accept$SpecType(this, \"$nameLowerCamel\", $name);" + } + } + } + + if (usedSpecializationsSet.size > 1) { + +"/** @deprecated May cause boxing allocations - use with caution! */" + +"@Deprecated" + +GENERATED_MEMBER_HEADER + "void forEachField($PerObjectFieldAction<$ClassType> action)" { + fields.forEachApply { + +"action.acceptObject(this, \"$nameLowerCamel\", $name);" + } + } + } +} diff --git a/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt b/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt new file mode 100644 index 000000000000..d1dc88f4a773 --- /dev/null +++ b/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt @@ -0,0 +1,122 @@ +package com.android.codegen + +import com.github.javaparser.ast.body.TypeDeclaration +import com.github.javaparser.ast.expr.* +import com.github.javaparser.ast.nodeTypes.NodeWithAnnotations +import com.github.javaparser.ast.type.ClassOrInterfaceType +import com.github.javaparser.ast.type.Type + + +fun ClassPrinter.getInputSignatures(): List<String> { + return classAst.fields.map { fieldAst -> + buildString { + append(fieldAst.modifiers.joinToString(" ") {it.asString()}) + append(" ") + append(annotationsToString(fieldAst)) + append(" ") + append(getFullClassName(fieldAst.commonType)) + append(" ") + append(fieldAst.variables.joinToString(", ") { it.nameAsString }) + } + } + classAst.methods.map { methodAst -> + buildString { + append(methodAst.modifiers.joinToString(" ") {it.asString()}) + append(" ") + append(annotationsToString(methodAst)) + append(" ") + append(getFullClassName(methodAst.type)) + append(" ") + append(methodAst.nameAsString) + append("(") + append(methodAst.parameters.joinToString(",") {getFullClassName(it.type)}) + append(")") + } + } +} + +private fun ClassPrinter.annotationsToString(annotatedAst: NodeWithAnnotations<*>): String { + return annotatedAst.annotations.joinToString(" ") { + annotationToString(it) + } +} + +private fun ClassPrinter.annotationToString(ann: AnnotationExpr): String { + return buildString { + append("@") + append(getFullClassName(ann.nameAsString)) + if (ann is MarkerAnnotationExpr) return@buildString + + append("(") + + when (ann) { + is SingleMemberAnnotationExpr -> { + appendExpr(this, ann.memberValue) + } + is NormalAnnotationExpr -> { + ann.pairs.forEachLastAware { pair, isLast -> + append(pair.nameAsString) + append("=") + appendExpr(this, pair.value) + if (!isLast) append(", ") + } + } + } + + append(")") + }.replace("\"", "\\\"") +} + +private fun ClassPrinter.appendExpr(sb: StringBuilder, ex: Expression?) { + when (ex) { + is ClassExpr -> sb.append(getFullClassName(ex.typeAsString)).append(".class") + is IntegerLiteralExpr -> sb.append(ex.asInt()).append("L") + is LongLiteralExpr -> sb.append(ex.asLong()).append("L") + is DoubleLiteralExpr -> sb.append(ex.asDouble()) + else -> sb.append(ex) + } +} + +private fun ClassPrinter.getFullClassName(type: Type): String { + return if (type is ClassOrInterfaceType) { + getFullClassName(buildString { + type.scope.ifPresent { append(it).append(".") } + type.isArrayType + append(type.nameAsString) + }) + (type.typeArguments.orElse(null)?.let { args -> args.joinToString(", ") {getFullClassName(it)}}?.let { "<$it>" } ?: "") + } else getFullClassName(type.asString()) +} + +private fun ClassPrinter.getFullClassName(className: String): String { + if (className.endsWith("[]")) return getFullClassName(className.removeSuffix("[]")) + "[]" + + if (className.matches("\\.[a-z]".toRegex())) return className //qualified name + + if ("." in className) return getFullClassName(className.substringBeforeLast(".")) + "." + className.substringAfterLast(".") + + fileAst.imports.find { imp -> + imp.nameAsString.endsWith(".$className") + }?.nameAsString?.let { return it } + + val thisPackagePrefix = fileAst.packageDeclaration.map { it.nameAsString + "." }.orElse("") + val thisClassPrefix = thisPackagePrefix + classAst.nameAsString + "." + + classAst.childNodes.filterIsInstance<TypeDeclaration<*>>().find { + it.nameAsString == className + }?.let { return thisClassPrefix + it.nameAsString } + + constDefs.find { it.AnnotationName == className }?.let { return thisClassPrefix + className } + + if (tryOrNull { Class.forName("java.lang.$className") } != null) { + return "java.lang.$className" + } + + if (className[0].isLowerCase()) return className //primitive + + return thisPackagePrefix + className +} + +private inline fun <T> tryOrNull(f: () -> T?) = try { + f() +} catch (e: Exception) { + null +} diff --git a/tools/codegen/src/com/android/codegen/Main.kt b/tools/codegen/src/com/android/codegen/Main.kt new file mode 100755 index 000000000000..8fafa7ce9b1e --- /dev/null +++ b/tools/codegen/src/com/android/codegen/Main.kt @@ -0,0 +1,199 @@ +package com.android.codegen + +import java.io.File + + +const val THIS_SCRIPT_LOCATION = "" +const val GENERATED_WARNING_PREFIX = "Code below generated by $CODEGEN_NAME" +const val INDENT_SINGLE = " " + +val PRIMITIVE_TYPES = listOf("byte", "short", "int", "long", "char", "float", "double", "boolean") + +const val CANONICAL_BUILDER_CLASS = "Builder" +const val GENERATED_BUILDER_CLASS = "GeneratedBuilder" + +val BUILTIN_SPECIAL_PARCELLINGS = listOf("Pattern") + +const val FLAG_BUILDER_PROTECTED_SETTERS = "--builder-protected-setters" +const val FLAG_NO_FULL_QUALIFIERS = "--no-full-qualifiers" + + +/** @see [FeatureFlag] */ +val USAGE = """ +Usage: $CODEGEN_NAME [--[PREFIX-]FEATURE...] JAVAFILE + +Generates boilerplade parcelable/data class code at the bottom of JAVAFILE, based o fields' declaration in the given JAVAFILE's top-level class + +FEATURE represents some generatable code, and can be among: +${FeatureFlag.values().map { feature -> + " ${feature.kebabCase}" to feature.desc +}.columnize(" - ")} + +And PREFIX can be: + <empty> - request to generate the feature + no - suppress generation of the feature + hidden - request to generate the feature with @hide + +Extra options: + --help - view this help + --update-only - auto-detect flags from the previously auto-generated comment within the file + $FLAG_NO_FULL_QUALIFIERS + - when referring to classes don't use package name prefix; handy with IDE auto-import + $FLAG_BUILDER_PROTECTED_SETTERS + - make builder's setters protected to expose them as public in a subclass on a whitelist basis + + +Special field modifiers and annotations: + transient - ignore the field completely + @Nullable - support null value when parcelling, and never throw on null input + @NonNull - throw on null input and don't parcel the nullness bit for the field + @DataClass.Enum - parcel field as an enum value by ordinal + @DataClass.PluralOf(..) - provide a singular version of a collection field name to be used in the builder's 'addFoo(..)' + @DataClass.ParcelWith(..) - provide a custom Parcelling class, specifying the custom (un)parcelling logic for this field + = <initializer>; - provide default value and never throw if this field was not provided e.g. when using builder + /** ... */ - copy given javadoc on field's getters/setters/constructor params/builder setters etc. + @hide (in javadoc) - force field's getters/setters/withers/builder setters to be @hide-den if generated + + +Special methods/etc. you can define: + + <any auto-generatable method> + For any method to be generated, if a method with same name and argument types is already + defined, than that method will not be generated. + This allows you to override certain details on granular basis. + + void onConstructed() + Will be called in constructor, after all the fields have been initialized. + This is a good place to put any custom validation logic that you may have + + static class $CANONICAL_BUILDER_CLASS extends $GENERATED_BUILDER_CLASS + If a class extending $GENERATED_BUILDER_CLASS is specified, generated builder's setters will + return the provided $CANONICAL_BUILDER_CLASS type. + $GENERATED_BUILDER_CLASS's constructor(s) will be package-private to encourage using $CANONICAL_BUILDER_CLASS instead + This allows you to extend the generated builder, adding or overriding any methods you may want + + +In addition, for any field mMyField(or myField) of type FieldType you can define the following methods: + + void parcelMyField(Parcel dest, int flags) + Allows you to provide custom logic for storing mMyField into a Parcel + + static FieldType unparcelMyField(Parcel in) + Allows you to provide custom logic to deserialize the value of mMyField from a Parcel + + String myFieldToString() + Allows you to provide a custom toString representation of mMyField's value + + FieldType lazyInitMyField() + Requests a lazy initialization in getMyField(), with the provided method being the constructor + You may additionally mark the fields as volatile to cause this to generate a thread-safe + double-check locking lazy initialization + + FieldType defaultMyField() + Allows you to provide a default value to initialize the field to, in case an explicit one + was not provided. + This is an alternative to providing a field initializer that, unlike the initializer, + you can use with final fields. + +Version: $CODEGEN_VERSION +Questions? Feedback? Contact: eugenesusla@ +""" + +fun main(args: Array<String>) { + if (args.contains("--help")) { + println(USAGE) + System.exit(0) + } + if (args.contains("--version")) { + println(CODEGEN_VERSION) + System.exit(0) + } + val file = File(args.last()) + val sourceLinesNoClosingBrace = file.readLines().dropLastWhile { + it.startsWith("}") || it.all(Char::isWhitespace) + } + val cliArgs = handleUpdateFlag(args, sourceLinesNoClosingBrace) + val sourceLinesAsIs = discardGeneratedCode(sourceLinesNoClosingBrace) + val sourceLines = sourceLinesAsIs + .filterNot { it.trim().startsWith("//") } + .map { it.trimEnd().dropWhile { it == '\n' } } + + val stringBuilder = StringBuilder(sourceLinesAsIs.joinToString("\n")) + ClassPrinter(sourceLines, stringBuilder, cliArgs).run { + + val cliExecutable = "$THIS_SCRIPT_LOCATION$CODEGEN_NAME" + val fileEscaped = file.absolutePath.replace( + System.getenv("ANDROID_BUILD_TOP"), "\$ANDROID_BUILD_TOP") + + + +""" + + + + // $GENERATED_WARNING_PREFIX v$CODEGEN_VERSION. + // on ${currentTimestamp()} + // + // DO NOT MODIFY! + // + // To regenerate run: + // $ $cliExecutable ${cliArgs.dropLast(1).joinToString("") { "$it " }}$fileEscaped + // + // CHECKSTYLE:OFF Generated code + """ + + if (FeatureFlag.CONST_DEFS()) generateConstDefs() + + "@$DataClassGenerated(" { + +"time = ${System.currentTimeMillis()}L," + +"codegenVersion = \"$CODEGEN_VERSION\"," + +"sourceFile = \"${file.relativeTo(File(System.getenv("ANDROID_BUILD_TOP")))}\"," + +"inputSignatures = \"${getInputSignatures().joinToString("\\n")}\"" + } + +"\n" + + + if (FeatureFlag.CONSTRUCTOR()) { + generateConstructor("public") + } else if (FeatureFlag.BUILDER() + || FeatureFlag.COPY_CONSTRUCTOR() + || FeatureFlag.WITHERS() + || FeatureFlag.PARCELABLE()) { + generateConstructor("/* package-private */") + } + + if (FeatureFlag.GETTERS()) generateGetters() + if (FeatureFlag.SETTERS()) generateSetters() + if (FeatureFlag.TO_STRING()) generateToString() + if (FeatureFlag.EQUALS_HASH_CODE()) generateEqualsHashcode() + + if (FeatureFlag.FOR_EACH_FIELD()) generateForEachField() + + if (FeatureFlag.COPY_CONSTRUCTOR()) generateCopyConstructor() + if (FeatureFlag.WITHERS()) generateWithers() + + if (FeatureFlag.PARCELABLE()) generateParcelable() + + if (FeatureFlag.BUILDER() && FeatureFlag.BUILD_UPON()) generateBuildUpon() + if (FeatureFlag.BUILDER()) generateBuilder() + + if (FeatureFlag.AIDL()) generateAidl(file) + + rmEmptyLine() + } + stringBuilder.append("\n}\n") + file.writeText(stringBuilder.toString().mapLines { trimEnd() }) +} + +internal fun discardGeneratedCode(sourceLinesNoClosingBrace: List<String>): List<String> { + return sourceLinesNoClosingBrace + .takeWhile { GENERATED_WARNING_PREFIX !in it } + .dropLastWhile(String::isBlank) +} + +private fun handleUpdateFlag(cliArgs: Array<String>, sourceLines: List<String>): Array<String> { + if ("--update-only" in cliArgs + && sourceLines.none { GENERATED_WARNING_PREFIX in it || it.startsWith("@DataClass") }) { + System.exit(0) + } + return cliArgs - "--update-only" +}
\ No newline at end of file diff --git a/tools/codegen/src/com/android/codegen/SharedConstants.kt b/tools/codegen/src/com/android/codegen/SharedConstants.kt new file mode 100644 index 000000000000..41641f6dab47 --- /dev/null +++ b/tools/codegen/src/com/android/codegen/SharedConstants.kt @@ -0,0 +1,4 @@ +package com.android.codegen + +const val CODEGEN_NAME = "codegen" +const val CODEGEN_VERSION = "0.0.1"
\ No newline at end of file diff --git a/tools/codegen/src/com/android/codegen/Utils.kt b/tools/codegen/src/com/android/codegen/Utils.kt new file mode 100644 index 000000000000..95c99092e2ab --- /dev/null +++ b/tools/codegen/src/com/android/codegen/Utils.kt @@ -0,0 +1,76 @@ +package com.android.codegen + +import com.github.javaparser.ast.expr.AnnotationExpr +import com.github.javaparser.ast.expr.Expression +import com.github.javaparser.ast.expr.SingleMemberAnnotationExpr +import java.time.Instant +import java.time.ZoneId +import java.time.format.DateTimeFormatter +import java.time.format.FormatStyle + +/** + * [Iterable.forEach] + [Any.apply] + */ +inline fun <T> Iterable<T>.forEachApply(block: T.() -> Unit) = forEach(block) + +inline fun String.mapLines(f: String.() -> String?) = lines().mapNotNull(f).joinToString("\n") +inline fun <T> Iterable<T>.trim(f: T.() -> Boolean) = dropWhile(f).dropLastWhile(f) +fun String.trimBlankLines() = lines().trim { isBlank() }.joinToString("\n") + +fun Char.isNewline() = this == '\n' || this == '\r' +fun Char.isWhitespaceNonNewline() = isWhitespace() && !isNewline() + +fun if_(cond: Boolean, then: String) = if (cond) then else "" + +inline infix fun Int.times(action: () -> Unit) { + for (i in 1..this) action() +} + +/** + * a bbb + * cccc dd + * + * -> + * + * a bbb + * cccc dd + */ +fun Iterable<Pair<String, String>>.columnize(separator: String = " | "): String { + val col1w = map { (a, _) -> a.length }.max()!! + val col2w = map { (_, b) -> b.length }.max()!! + return map { it.first.padEnd(col1w) + separator + it.second.padEnd(col2w) }.joinToString("\n") +} + +fun String.hasUnbalancedCurlyBrace(): Boolean { + var braces = 0 + forEach { + if (it == '{') braces++ + if (it == '}') braces-- + if (braces < 0) return true + } + return false +} + +fun String.toLowerCamel(): String { + if (length >= 2 && this[0] == 'm' && this[1].isUpperCase()) return substring(1).capitalize() + if (all { it.isLetterOrDigit() }) return decapitalize() + return split("[^a-zA-Z0-9]".toRegex()) + .map { it.toLowerCase().capitalize() } + .joinToString("") + .decapitalize() +} + +inline fun <T> List<T>.forEachLastAware(f: (T, Boolean) -> Unit) { + forEachIndexed { index, t -> f(t, index == size - 1) } +} + +@Suppress("UNCHECKED_CAST") +fun <T : Expression> AnnotationExpr.singleArgAs() + = ((this as SingleMemberAnnotationExpr).memberValue as T) + +inline operator fun <reified T> Array<T>.minus(item: T) = toList().minus(item).toTypedArray() + +fun currentTimestamp() = DateTimeFormatter + .ofLocalizedDateTime(/* date */ FormatStyle.MEDIUM, /* time */ FormatStyle.LONG) + .withZone(ZoneId.systemDefault()) + .format(Instant.now())
\ No newline at end of file diff --git a/tools/lock_agent/Android.bp b/tools/lock_agent/Android.bp index 408946b28836..79dce4a8ce09 100644 --- a/tools/lock_agent/Android.bp +++ b/tools/lock_agent/Android.bp @@ -12,13 +12,9 @@ cc_library { ], sdk_version: "current", stl: "c++_static", - include_dirs: [ - // NDK headers aren't available in platform NDK builds. - "libnativehelper/include_jni", - // Use ScopedUtfChars. - "libnativehelper/header_only_include", - ], header_libs: [ + // Use ScopedUtfChars. + "libnativehelper_header_only", "libopenjdkjvmti_headers", ], compile_multilib: "both", @@ -32,13 +28,9 @@ cc_binary_host { "libz", "slicer", ], - include_dirs: [ - // NDK headers aren't available in platform NDK builds. - "libnativehelper/include_jni", - // Use ScopedUtfChars. - "libnativehelper/header_only_include", - ], header_libs: [ + // Use ScopedUtfChars. + "libnativehelper_header_only", "libopenjdkjvmti_headers", ], } diff --git a/tools/processors/staledataclass/Android.bp b/tools/processors/staledataclass/Android.bp new file mode 100644 index 000000000000..c81d410c5e4b --- /dev/null +++ b/tools/processors/staledataclass/Android.bp @@ -0,0 +1,27 @@ + +java_plugin { + name: "staledataclass-annotation-processor", + processor_class: "android.processor.staledataclass.StaleDataclassProcessor", + + java_resources: [ + "META-INF/**/*", + ], + srcs: [ + "src/**/*.java", + "src/**/*.kt", + ], + static_libs: [ + "codegen-version-info", + ], + openjdk9: { + javacflags: [ + "--add-modules=jdk.compiler", + "--add-exports jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED", + "--add-exports jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED", + "--add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED", + "--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED", + ], + }, + + use_tools_jar: true, +} diff --git a/tools/processors/staledataclass/META-INF/services/javax.annotation.processing.Processor b/tools/processors/staledataclass/META-INF/services/javax.annotation.processing.Processor new file mode 100644 index 000000000000..15ee6230c023 --- /dev/null +++ b/tools/processors/staledataclass/META-INF/services/javax.annotation.processing.Processor @@ -0,0 +1 @@ +android.processor.staledataclass.StaleDataclassProcessorOld diff --git a/tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt b/tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt new file mode 100644 index 000000000000..9e51180509a8 --- /dev/null +++ b/tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt @@ -0,0 +1,178 @@ +/* + * 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.processor.staledataclass + +import com.android.codegen.CODEGEN_NAME +import com.android.codegen.CODEGEN_VERSION +import com.sun.tools.javac.code.Symbol +import com.sun.tools.javac.code.Type +import java.io.File +import java.io.FileNotFoundException +import javax.annotation.processing.AbstractProcessor +import javax.annotation.processing.RoundEnvironment +import javax.annotation.processing.SupportedAnnotationTypes +import javax.lang.model.SourceVersion +import javax.lang.model.element.AnnotationMirror +import javax.lang.model.element.Element +import javax.lang.model.element.TypeElement +import javax.tools.Diagnostic + +private const val STALE_FILE_THRESHOLD_MS = 1000 +private val WORKING_DIR = File(".").absoluteFile + +private const val DATACLASS_ANNOTATION_NAME = "com.android.internal.util.DataClass" +private const val GENERATED_ANNOTATION_NAME = "com.android.internal.util.DataClass.Generated" +private const val GENERATED_MEMBER_ANNOTATION_NAME + = "com.android.internal.util.DataClass.Generated.Member" + + +@SupportedAnnotationTypes(DATACLASS_ANNOTATION_NAME, GENERATED_ANNOTATION_NAME) +class StaleDataclassProcessor: AbstractProcessor() { + + private var dataClassAnnotation: TypeElement? = null + private var generatedAnnotation: TypeElement? = null + private var repoRoot: File? = null + + private val stale = mutableListOf<Stale>() + + /** + * This is the main entry point in the processor, called by the compiler. + */ + override fun process(annotations: Set<TypeElement>, roundEnv: RoundEnvironment): Boolean { + + if (generatedAnnotation == null) { + generatedAnnotation = annotations.find { + it.qualifiedName.toString() == GENERATED_ANNOTATION_NAME + } + } + if (dataClassAnnotation == null) { + dataClassAnnotation = annotations.find { + it.qualifiedName.toString() == DATACLASS_ANNOTATION_NAME + } + } + + val generatedAnnotatedElements = roundEnv.getElementsAnnotatedWith(generatedAnnotation) + generatedAnnotatedElements.forEach { + processSingleFile(it) + } + + + val dataClassesWithoutGeneratedPart = + roundEnv.getElementsAnnotatedWith(dataClassAnnotation) - + generatedAnnotatedElements.map { it.enclosingElement } + + dataClassesWithoutGeneratedPart.forEach { dataClass -> + stale += Stale(dataClass.toString(), file = null, lastGenerated = 0L) + } + + + if (!stale.isEmpty()) { + error("Stale generated dataclass(es) detected. " + + "Run the following command(s) to update them:" + + stale.joinToString("") { "\n" + it.refreshCmd }) + } + return true + } + + private fun elemToString(elem: Element): String { + return buildString { + append(elem.modifiers.joinToString(" ") { it.name.toLowerCase() }).append(" ") + append(elem.annotationMirrors.joinToString(" ")).append(" ") + if (elem is Symbol) { + if (elem.type is Type.MethodType) { + append((elem.type as Type.MethodType).returnType) + } else { + append(elem.type) + } + append(" ") + } + append(elem) + } + } + + private fun processSingleFile(elementAnnotatedWithGenerated: Element) { + + val inputSignatures = elementAnnotatedWithGenerated + .enclosingElement + .enclosedElements + .filterNot { + it.annotationMirrors.any { "Generated" in it.annotationType.toString() } + }.map { + elemToString(it) + }.toSet() + + val annotationParams = elementAnnotatedWithGenerated + .annotationMirrors + .find { ann -> isGeneratedAnnotation(ann) }!! + .elementValues + .map { (k, v) -> k.getSimpleName().toString() to v.getValue() } + .toMap() + + val lastGenerated = annotationParams["time"] as Long + val codegenVersion = annotationParams["codegenVersion"] as String + val sourceRelative = File(annotationParams["sourceFile"] as String) + + val lastGenInputSignatures = (annotationParams["inputSignatures"] as String).lines().toSet() + + if (repoRoot == null) { + repoRoot = generateSequence(WORKING_DIR) { it.parentFile } + .find { it.resolve(sourceRelative).isFile } + ?.canonicalFile + ?: throw FileNotFoundException( + "Failed to detect repository root: " + + "no parent of $WORKING_DIR contains $sourceRelative") + } + + val source = repoRoot!!.resolve(sourceRelative) + val clazz = elementAnnotatedWithGenerated.enclosingElement.toString() + + if (inputSignatures != lastGenInputSignatures) { + error(buildString { + append(sourceRelative).append(":\n") + append(" Added:\n").append((inputSignatures-lastGenInputSignatures).joinToString("\n")) + append("\n") + append(" Removed:\n").append((lastGenInputSignatures-inputSignatures).joinToString("\n")) + }) + stale += Stale(clazz, source, lastGenerated) + } + + if (codegenVersion != CODEGEN_VERSION) { + stale += Stale(clazz, source, lastGenerated) + } + } + + private fun error(msg: String) { + processingEnv.messager.printMessage(Diagnostic.Kind.ERROR, msg) + } + + private fun isGeneratedAnnotation(ann: AnnotationMirror): Boolean { + return generatedAnnotation!!.qualifiedName.toString() == ann.annotationType.toString() + } + + data class Stale(val clazz: String, val file: File?, val lastGenerated: Long) { + val refreshCmd = if (file != null) { + "$CODEGEN_NAME $file" + } else { + "find \$ANDROID_BUILD_TOP -path */${clazz.replace('.', '/')}.java -exec $CODEGEN_NAME {} \\;" + } + } + + override fun getSupportedSourceVersion(): SourceVersion { + return SourceVersion.latest() + } +}
\ No newline at end of file diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index 1a16aaa1036b..89ed0800e31c 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -1691,6 +1691,8 @@ public class WifiManager { /** * Remove some or all of the network suggestions that were previously provided by the app. + * If the current network is a suggestion being removed and if it was only provided by this app + * and is not a saved network then the framework will immediately disconnect. * See {@link WifiNetworkSuggestion} for a detailed explanation of the parameters. * See {@link WifiNetworkSuggestion#equals(Object)} for the equivalence evaluation used. * diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java index cd659e2a934a..d37c4a2e78db 100644 --- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java +++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java @@ -190,7 +190,7 @@ public class WifiP2pManager { * has changed. One extra {@link #EXTRA_WIFI_P2P_INFO} provides the p2p connection info in * the form of a {@link WifiP2pInfo} object. Another extra {@link #EXTRA_NETWORK_INFO} provides * the network info in the form of a {@link android.net.NetworkInfo}. A third extra provides - * the details of the group. + * the details of the group and may contain a {@code null}. * * All of these permissions are required to receive this broadcast: * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} and |