diff options
456 files changed, 16148 insertions, 11345 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 e85559601146..363ce8238f94 100644 --- a/api/current.txt +++ b/api/current.txt @@ -2966,6 +2966,7 @@ package android.accessibilityservice { } public final class GestureDescription { + method public int getDisplayId(); method public static long getMaxGestureDuration(); method public static int getMaxStrokeCount(); method public android.accessibilityservice.GestureDescription.StrokeDescription getStroke(@IntRange(from=0) int); @@ -2976,6 +2977,7 @@ package android.accessibilityservice { ctor public GestureDescription.Builder(); method public android.accessibilityservice.GestureDescription.Builder addStroke(@NonNull android.accessibilityservice.GestureDescription.StrokeDescription); method public android.accessibilityservice.GestureDescription build(); + method @NonNull public android.accessibilityservice.GestureDescription.Builder setDisplayId(int); } public static class GestureDescription.StrokeDescription { @@ -10834,7 +10836,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 { @@ -10854,6 +10858,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); @@ -11751,6 +11759,9 @@ package android.content.pm { field public static final String FEATURE_SENSOR_RELATIVE_HUMIDITY = "android.hardware.sensor.relative_humidity"; field public static final String FEATURE_SENSOR_STEP_COUNTER = "android.hardware.sensor.stepcounter"; field public static final String FEATURE_SENSOR_STEP_DETECTOR = "android.hardware.sensor.stepdetector"; + field public static final String FEATURE_SE_OMAPI_ESE = "android.hardware.se.omapi.ese"; + field public static final String FEATURE_SE_OMAPI_SD = "android.hardware.se.omapi.sd"; + field public static final String FEATURE_SE_OMAPI_UICC = "android.hardware.se.omapi.uicc"; field public static final String FEATURE_SIP = "android.software.sip"; field public static final String FEATURE_SIP_VOIP = "android.software.sip.voip"; field public static final String FEATURE_STRONGBOX_KEYSTORE = "android.hardware.strongbox_keystore"; @@ -13123,8 +13134,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); @@ -13132,6 +13146,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[]); } @@ -41053,6 +41069,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 @@ -43979,6 +43996,7 @@ package android.telephony { public class CarrierConfigManager { method @Nullable public android.os.PersistableBundle getConfig(); + method @Nullable public android.os.PersistableBundle getConfigByComponentForSubId(String, int); method @Nullable public android.os.PersistableBundle getConfigForSubId(int); method public static boolean isConfigForIdentifiedCarrier(android.os.PersistableBundle); method public void notifyConfigChangedForSubId(int); @@ -44156,6 +44174,10 @@ package android.telephony { field public static final String KEY_WORLD_PHONE_BOOL = "world_phone_bool"; } + public static final class CarrierConfigManager.Ims { + field public static final String KEY_PREFIX = "ims."; + } + public abstract class CellIdentity implements android.os.Parcelable { method public int describeContents(); method @Nullable public CharSequence getOperatorAlphaLong(); 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/test-current.txt b/api/test-current.txt index d2179d3f4262..2624eee7d7d1 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -697,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(); } @@ -3027,6 +3028,7 @@ package android.util { field public static final String PERSIST_PREFIX = "persist.sys.fflag.override."; field public static final String SCREENRECORD_LONG_PRESS = "settings_screenrecord_long_press"; field public static final String SEAMLESS_TRANSFER = "settings_seamless_transfer"; + field public static final String SETTINGS_WIFITRACKER2 = "settings_wifitracker2"; } public class TimeUtils { diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp index 30f4ad4c5c69..db384baff4d7 100644 --- a/cmds/bootanimation/BootAnimation.cpp +++ b/cmds/bootanimation/BootAnimation.cpp @@ -63,6 +63,10 @@ #include "BootAnimation.h" +#define ANIM_PATH_MAX 255 +#define STR(x) #x +#define STRTO(x) STR(x) + namespace android { static const char OEM_BOOTANIMATION_FILE[] = "/oem/media/bootanimation.zip"; @@ -96,7 +100,7 @@ static constexpr size_t FONT_NUM_ROWS = FONT_NUM_CHARS / FONT_NUM_COLS; static const int TEXT_CENTER_VALUE = INT_MAX; static const int TEXT_MISSING_VALUE = INT_MIN; static const char EXIT_PROP_NAME[] = "service.bootanim.exit"; -static const int ANIM_ENTRY_NAME_MAX = 256; +static const int ANIM_ENTRY_NAME_MAX = ANIM_PATH_MAX + 1; static constexpr size_t TEXT_POS_LEN_MAX = 16; // --------------------------------------------------------------------------- @@ -694,7 +698,7 @@ bool BootAnimation::parseAnimationDesc(Animation& animation) animation.width = width; animation.height = height; animation.fps = fps; - } else if (sscanf(l, " %c %d %d %s #%6s %16s %16s", + } else if (sscanf(l, " %c %d %d %" STRTO(ANIM_PATH_MAX) "s #%6s %16s %16s", &pathType, &count, &pause, path, color, clockPos1, clockPos2) >= 4) { //SLOGD("> type=%c, count=%d, pause=%d, path=%s, color=%s, clockPos1=%s, clockPos2=%s", // pathType, count, pause, path, color, clockPos1, clockPos2); diff --git a/cmds/locksettings/TEST_MAPPING b/cmds/locksettings/TEST_MAPPING new file mode 100644 index 000000000000..c1cba5f7f22d --- /dev/null +++ b/cmds/locksettings/TEST_MAPPING @@ -0,0 +1,15 @@ +{ + "presubmit": [ + { + "name": "CtsDevicePolicyManagerTestCases", + "options": [ + { + "include-annotation": "com.android.cts.devicepolicy.annotations.LockSettingsTest" + }, + { + "exclude-annotation": "android.platform.test.annotations.FlakyTest" + } + ] + } + ] +} 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/tools/dogfood/Android.bp b/cmds/statsd/tools/dogfood/Android.bp deleted file mode 100644 index bb494a6025af..000000000000 --- a/cmds/statsd/tools/dogfood/Android.bp +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (C) 2017 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// - -android_app { - name: "StatsdDogfood", - platform_apis: true, - - srcs: ["src/**/*.java"], - - resource_dirs: ["res"], - static_libs: [ - "platformprotoslite", - "statsdprotolite", - ], - - privileged: true, - dex_preopt: { - enabled: false, - }, - certificate: "platform", - optimize: { - enabled: false, - }, -} diff --git a/cmds/statsd/tools/dogfood/AndroidManifest.xml b/cmds/statsd/tools/dogfood/AndroidManifest.xml deleted file mode 100644 index 52673fbdcf92..000000000000 --- a/cmds/statsd/tools/dogfood/AndroidManifest.xml +++ /dev/null @@ -1,43 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** Copyright 2007, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.statsd.dogfood" - android:sharedUserId="android.uid.system" - android:versionCode="1" - android:versionName="1.0" > - - <uses-permission android:name="android.permission.DUMP" /> - - <application - android:allowBackup="true" - android:icon="@drawable/ic_launcher" - android:label="@string/app_name" > - <activity - android:name=".MainActivity" - android:label="@string/app_name" - android:launchMode="singleTop" > - <intent-filter> - <action android:name="android.intent.action.MAIN" /> - <category android:name="android.intent.category.LAUNCHER" /> - </intent-filter> - </activity> - - <service android:name=".MainActivity$ReceiverIntentService" android:exported="true" /> - </application> -</manifest> diff --git a/cmds/statsd/tools/dogfood/res/drawable-hdpi/ic_launcher.png b/cmds/statsd/tools/dogfood/res/drawable-hdpi/ic_launcher.png Binary files differdeleted file mode 100644 index 55621cc1074f..000000000000 --- a/cmds/statsd/tools/dogfood/res/drawable-hdpi/ic_launcher.png +++ /dev/null diff --git a/cmds/statsd/tools/dogfood/res/drawable-mdpi/ic_launcher.png b/cmds/statsd/tools/dogfood/res/drawable-mdpi/ic_launcher.png Binary files differdeleted file mode 100644 index 11ec2068be19..000000000000 --- a/cmds/statsd/tools/dogfood/res/drawable-mdpi/ic_launcher.png +++ /dev/null diff --git a/cmds/statsd/tools/dogfood/res/drawable-xhdpi/ic_launcher.png b/cmds/statsd/tools/dogfood/res/drawable-xhdpi/ic_launcher.png Binary files differdeleted file mode 100644 index 7c02b784aa5d..000000000000 --- a/cmds/statsd/tools/dogfood/res/drawable-xhdpi/ic_launcher.png +++ /dev/null diff --git a/cmds/statsd/tools/dogfood/res/drawable-xxhdpi/ic_launcher.png b/cmds/statsd/tools/dogfood/res/drawable-xxhdpi/ic_launcher.png Binary files differdeleted file mode 100644 index 915d91441349..000000000000 --- a/cmds/statsd/tools/dogfood/res/drawable-xxhdpi/ic_launcher.png +++ /dev/null diff --git a/cmds/statsd/tools/dogfood/res/layout/activity_main.xml b/cmds/statsd/tools/dogfood/res/layout/activity_main.xml deleted file mode 100644 index 784ed40ce2c2..000000000000 --- a/cmds/statsd/tools/dogfood/res/layout/activity_main.xml +++ /dev/null @@ -1,162 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** Copyright 2007, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> -<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="match_parent" > - - <LinearLayout - android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical"> - - <Button - android:id="@+id/push_config" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:background="@android:color/holo_green_light" - android:text="@string/push_config"/> - <Button - android:id="@+id/set_receiver" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:background="@android:color/holo_green_light" - android:text="@string/set_receiver"/> - <Button - android:id="@+id/remove_receiver" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:background="@android:color/holo_green_light" - android:text="@string/remove_receiver"/> - - <LinearLayout android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="horizontal"> - <Button android:id="@+id/app_a_wake_lock_acquire1" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/app_a_get_wl1"/> - <Button android:id="@+id/app_a_wake_lock_release1" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/app_a_release_wl1"/> - </LinearLayout> - - <LinearLayout android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="horizontal"> - <Button android:id="@+id/app_a_wake_lock_acquire2" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/app_a_get_wl2"/> - <Button android:id="@+id/app_a_wake_lock_release2" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/app_a_release_wl2"/> - </LinearLayout> - - <LinearLayout android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="horizontal"> - <Button android:id="@+id/app_b_wake_lock_acquire1" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/app_b_get_wl1"/> - <Button android:id="@+id/app_b_wake_lock_release1" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/app_b_release_wl1"/> - </LinearLayout> - <LinearLayout android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="horizontal"> - <Button android:id="@+id/app_b_wake_lock_acquire2" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/app_b_get_wl2"/> - <Button android:id="@+id/app_b_wake_lock_release2" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/app_b_release_wl2"/> - </LinearLayout> - - <LinearLayout android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="horizontal"> - <Button android:id="@+id/plug" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/plug"/> - - <Button android:id="@+id/unplug" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/unplug"/> - </LinearLayout> - - <LinearLayout android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="horizontal"> - <Button android:id="@+id/screen_on" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/screen_on"/> - - <Button android:id="@+id/screen_off" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/screen_off"/> - </LinearLayout> - - <LinearLayout android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="horizontal"> - - <Button - android:id="@+id/custom_start" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/custom_start" /> - - <Button - android:id="@+id/custom_stop" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/custom_stop" /> - </LinearLayout> - - <Button android:id="@+id/dump" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:background="@android:color/holo_purple" - android:text="@string/dump"/> - - <TextView - android:id="@+id/header" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:text="@string/report_header"/> - - <TextView - android:id="@+id/report_text" - android:layout_width="match_parent" - android:layout_height="wrap_content" /> - - </LinearLayout> - -</ScrollView>
\ No newline at end of file diff --git a/cmds/statsd/tools/dogfood/res/raw/statsd_baseline_config b/cmds/statsd/tools/dogfood/res/raw/statsd_baseline_config deleted file mode 100644 index d05006124994..000000000000 --- a/cmds/statsd/tools/dogfood/res/raw/statsd_baseline_config +++ /dev/null @@ -1,109 +0,0 @@ -¹ÓõÕ¯¤”årÝèåâÕ»éÉ7¬ìŸ‘Ψê.–´šÄÁŒç¶ÚšäÑùôºZªÑ€¸ëÐé푘‹Ý™ûÆŽ6ˆ™ÁÖ΂õ›Ã¥¼à¨É—ÿQÝàåÈ߀äÍ@ÆÙÑ„ÓІ„èѿ̲±™¨lŸÄ€ìúˆÖü¶ú̳Šéâ‡ãà§ØÈË´Ù%óÓÚ«¹¼ÜyÛ®±ë¸«”º?¹•繡¸Œ€â鿨ŒÙœ®‘)çæ©Á”§äéîÀ‚Ò®ö‡.ÐÙ•°‹ÁÈy"'(WòóÀ–ðßæ¬ùÕàÀ™ýëZ”ÔÆ™Ž¤š¹»" -(2!ûÄߪæþªâÚ -V¢¦€¡’€Ç€¡ùÕàÀ™ýëZÈ‹ÄÂë¢Ä¯Ø" -(2 ª–¨Œ×ÿ¹¡k -V‡úÌòá㿘>ùÕàÀ™ýëZáž‹â‘Ý»ä" -(2!÷‘–ä–Œ¿¯ -WØõé¼ÃóÀàþùÕàÀ™ýëZ°ÔØùΣ‘¯" -(2!Ïä†÷ɯ -VÚœäÌþ½¬jùÕàÀ™ýëZ³Ö‰Ó¾ ½Ñ" -(2!Ÿ¡ªˆëôùÇ -V¨¾ñ≎̅ùÕàÀ™ýëZ“Ðåÿèåò?" -(2!½šòÐ䤿Œ -U…£²†µÜ‡‘_ùÕàÀ™ýëZñîôÒ‡¤´†z" -(2!é’¬¸ç¼‡Óª -K‘ã´¶üÅçúñ±Ë壆¨D”ÔÆ™Ž¤š¹»" -#(2ûÄߪæþªâÚ#Ië§ì·ºê•Æúñ±Ë壆¨DÈ‹ÄÂë¢Ä¯Ø" -#(2ª–¨Œ×ÿ¹¡k#JÙ¢Öúƒ†ðúñ±Ë壆¨Dáž‹â‘Ý»ä" -#(2÷‘–ä–Œ¿¯#JãýØåóªü)úñ±Ë壆¨D°ÔØùΣ‘¯" -#(2Ïä†÷ɯ#KËÅÝÇž¹Åãúñ±Ë壆¨D³Ö‰Ó¾ ½Ñ" -#(2Ÿ¡ªˆëôùÇ#Jɳ¸ó€„ø‘Ãúñ±Ë壆¨D“Ðåÿèåò?" -#(2½šòÐ䤿Œ#J•–ó«ðô¤Þïúñ±Ë壆¨DñîôÒ‡¤´†z" -#(2é’¬¸ç¼‡Óª#J•ºÔ¯•…Î’:›ºô¿Úó¾kŒÓÜ¢¾ÌÊ" -(2ûÄߪæþªâÚI»ÓòÓí‰àÖ -›ºô¿Úó¾kǮ›ŸÊŸäâ" -(2ª–¨Œ×ÿ¹¡kJÖÐük÷y›ºô¿Úó¾kÿ‚¾žÃ“’§õ" -(2÷‘–ä–Œ¿¯JøÿÊ“åøË¨›ºô¿Úó¾k˜ý˜‘¢ŒÐÀI" -(2Ïä†÷ɯKõß°—’ì¾×›ºô¿Úó¾kì‘áÒîŸÙÍá" -(2Ÿ¡ªˆëôùÇJà¸ëòä¥û݈›ºô¿Úó¾kôé‹¿¿¸ßÄ" -(2½šòÐ䤿ŒJУŒð†¬µÙ›ºô¿Úó¾k‰±Öйˆ–Ôõ" -(2é’¬¸ç¼‡ÓªK—ÍË é÷ÐM鮣öü©µŒÓÜ¢¾ÌÊ" -(2ûÄߪæþªâÚJÌŽë á«¡´{鮣öü©µÇ®Â›ŸÊŸäâ" -(2ª–¨Œ×ÿ¹¡kL˜†ŒÏØì¡‹³é®£öü©µÿ‚¾žÃ“’§õ" -(2÷‘–ä–Œ¿¯Ká°Ñàƒ½†¶ý鮣öü©µ˜ý˜‘¢ŒÐÀI" -(2Ïä†÷ɯKÛ”Ÿò§…ÂÁY鮣öü©µì‘áÒîŸÙÍá" -(2Ÿ¡ªˆëôùÇKϜݗÞç±ù¥é®£öü©µôé‹¿¿¸ßÄ" -(2½šòÐ䤿ŒKÚÖ•·‰–B鮣öü©µ‰±Öйˆ–Ôõ" -(2é’¬¸ç¼‡Óª,Ä®öèÀƒ‘âÌ¥•€†ÜÖ×AÐÙ•°‹ÁÈy" -(€õ»•÷¾ÓçȪŠéñðȃŹ~”‰àÉ®¶f"(2é’¬¸ç¼‡Óª2½šòÐ䤿Œ2Ÿ¡ªˆëôùÇLشͼá™þþæ¿É¶ˆœ‚æð±ûÄߪæþªâÚ" --(2ûÄߪæþªâÚ-JÉ¥˜‘Ûüˆ‹ò¿É¶ˆœ‚æð±ª–¨Œ×ÿ¹¡k" --(2ª–¨Œ×ÿ¹¡k-L¡©äÞ¨ô¶ø½¿É¶ˆœ‚æð±÷‘–ä–Œ¿¯" --(2÷‘–ä–Œ¿¯-L‡õÇþ¡¾ÚÈþ¿É¶ˆœ‚æð±Ïä†÷ɯ" --(2Ïä†÷ɯ-L—î«ãïÚ½û¬¿É¶ˆœ‚æð±Ÿ¡ªˆëôùÇ" --(2Ÿ¡ªˆëôùÇ-LñÍý‚ÿô¿É¶ˆœ‚æð±½šòÐ䤿Œ" --(2½šòÐ䤿Œ-K‹òʰ«»íè|¿É¶ˆœ‚æð±é’¬¸ç¼‡Óª" --(2é’¬¸ç¼‡Óª-"9äëæÚòœ§=̨…¥”ìãÏ‘N ÐÙ•°‹ÁÈy*‘N0@":šˆž‚ÎñÐ̨…¥”ìãÏ‘N ÐÙ•°‹ÁÈy*‘N0@":ù»Žôç¾ãˆÐ̨…¥”ìãÏ‘N ÐÙ•°‹ÁÈy*‘N0@"9›™›ŸÝ†—ãS̨…¥”ìãÏ‘N ÐÙ•°‹ÁÈy*‘N0@":‹ããùŸáŽôºßñ¼ûÌ•˜“N ÐÙ•°‹ÁÈy*“N0@":ç΀àë Œƒôºßñ¼ûÌ•˜“N ÐÙ•°‹ÁÈy*“N0@":™˜µøÖ²Äìõôºßñ¼ûÌ•˜“N ÐÙ•°‹ÁÈy*“N0@":¬Öœ¥¥ÃÆŒÑôºßñ¼ûÌ•˜“N ÐÙ•°‹ÁÈy*“N0@"5ôÇ™±¶Ï¢‘ò•™ÔéŠñ˜Èu›N ÐÙ•°‹ÁÈy*›N0@"4«÷µÿ󲂜e•™ÔéŠñ˜Èu›N ÐÙ•°‹ÁÈy*›N0@"4å¢úˆÑÙúý•™ÔéŠñ˜Èu›N ÐÙ•°‹ÁÈy*›N0@"+ž—Õõ‰î¶±~‡î‚ÒÔÊ´ËœN ÐÙ•°‹ÁÈy0@",‘ó‚³ïÙù¦‚‡î‚ÒÔÊ´ËœN ÐÙ•°‹ÁÈy0@"+òßù¥·«Äà;‡î‚ÒÔÊ´ËœN ÐÙ•°‹ÁÈy0@",ÓûÅñÒ¢ô§Â‡î‚ÒÔÊ´ËœN ÐÙ•°‹ÁÈy0@"+¨²°žò³ƒ¨R‡î‚ÒÔÊ´ËœN ÐÙ•°‹ÁÈy0@"+ÞìøݱþÖq‡î‚ÒÔÊ´ËœN ÐÙ•°‹ÁÈy0@",͉šæÜŽÄÀ‡î‚ÒÔÊ´ËœN ÐÙ•°‹ÁÈy0@"3Ä»±ê¾¸ô°˜þËÉÇåãÃ2 ÐÙ•°‹ÁÈy*0@"‰Ñ½Ëñɤ¡™a˜þËÉÇåãÃ2 ”‰àÉ®¶f*0:é’¬¸ç¼‡Óª:½šòÐ䤿Œ:Ÿ¡ªˆëôùÇ@":ÃêåÒýéó¦ï‘ŽÁ°×ËÇÅß–N ÐÙ•°‹ÁÈy*–N0@":Àš‚ÑÀí£Ï‹‘ŽÁ°×ËÇÅß–N ÐÙ•°‹ÁÈy*–N0@"9¾¢ðÛßÒëéø›²Ö·ÔÈšN ÐÙ•°‹ÁÈy*šN0@"9÷´ç鼯Š~½ ¤„¹®Ð§¨˜N ÐÙ•°‹ÁÈy*˜N0@2YÝæÆËò ‚ú‘»óÓ¤ãËÕ”ÔÆ™Ž¤š¹»"!ûÄߪæþªâÚ -(2 -82X°öòΪáŠþ„»óÓ¤ãËÕÈ‹ÄÂë¢Ä¯Ø" ª–¨Œ×ÿ¹¡k -(2 -82X°ç¦ùºŒó·0»óÓ¤ãËÕáž‹â‘Ý»ä"!÷‘–ä–Œ¿¯ -(2 -82Yôæï°Þ¼ ”»óÓ¤ãËÕ°ÔØùΣ‘¯"!Ïä†÷ɯ -(2 -82Y¡Î°Ãåüý»óÓ¤ãËÕ³Ö‰Ó¾ ½Ñ"!Ÿ¡ªˆëôùÇ -(2 -82XÕ¤ô®Ã£ÇÓÈ»óÓ¤ãËÕ“Ðåÿèåò?"!½šòÐ䤿Œ -(2 -82XÖÂªßøæÌÊ»óÓ¤ãËÕñîôÒ‡¤´†z"!é’¬¸ç¼‡Óª -(2 -825’ÌíûÍõ÷Æ»óÓ¤ãËÕ®Èùé€ù’±W(2 -82X²¹÷߆ŒÀ¶`»óÓ¤ãËÕ”ÔÆ™Ž¤š¹»"!ûÄߪæþªâÚ -(2 -82W÷ÞŸÉÔ ëÍd»óÓ¤ãËÕÈ‹ÄÂë¢Ä¯Ø" ª–¨Œ×ÿ¹¡k -(2 -82XäÁš´¤·ñì.»óÓ¤ãËÕáž‹â‘Ý»ä"!÷‘–ä–Œ¿¯ -(2 -82YîæÍçâ»óÓ¤ãËÕ°ÔØùΣ‘¯"!Ïä†÷ɯ -(2 -82Y˜²Ì´×³ìóÓ¤ãËÕ³Ö‰Ó¾ ½Ñ"!Ÿ¡ªˆëôùÇ -(2 -82W¾¯†‘§È“¿P»óÓ¤ãËÕ“Ðåÿèåò?"!½šòÐ䤿Œ -(2 -82XÂÀ¶òšË˜‚ð»óÓ¤ãËÕñîôÒ‡¤´†z"!é’¬¸ç¼‡Óª -(2 -82U—îïÛ²¸¸†µ»óÓ¤ãËÕ”ÔÆ™Ž¤š¹»"!ûÄߪæþªâÚ -(2 -82Tû¯ûû²¶ýñ§»óÓ¤ãËÕÈ‹ÄÂë¢Ä¯Ø" ª–¨Œ×ÿ¹¡k -(2 -82T†ñù™ŸÈ¨«»óÓ¤ãËÕáž‹â‘Ý»ä"!÷‘–ä–Œ¿¯ -(2 -82T҈πŸ¯‰ŠD»óÓ¤ãËÕ°ÔØùΣ‘¯"!Ïä†÷ɯ -(2 -82Uš¤Ë„阿ö†»óÓ¤ãËÕ³Ö‰Ó¾ ½Ñ"!Ÿ¡ªˆëôùÇ -(2 -82SË©ËÞ¯öÈÞ&»óÓ¤ãËÕ“Ðåÿèåò?"!½šòÐ䤿Œ -(2 -82TάóýᔺèÞ»óÓ¤ãËÕñîôÒ‡¤´†z"!é’¬¸ç¼‡Óª -(2 -82#àšÉ±ûÿ±»óÓ¤ãËÕ®Èùé€ù’±W(82#ÞžÞïƒ÷“…±œ–±ÃŸ€ÀâFÐÙ•°‹ÁÈy(82"䣞ƒƒ¿Èòrœ–±ÃŸ€ÀâFÐÙ•°‹ÁÈy(82*¦‚ó©ÂÊÓŒ3œ–±ÃŸ€ÀâFÐÙ•°‹ÁÈy(2%82*ÅМ½à®ÄŠœ–±ÃŸ€ÀâFÐÙ•°‹ÁÈy(2%82"‡è‘²†ÙÓÂ+œ–±ÃŸ€ÀâFÐÙ•°‹ÁÈy(82J›»ÜŽáø©ÔӈƄת¶£«ŒÓÜ¢¾ÌÊ"ûÄߪæþªâÚ'(2'82H®Ñä‚Òš„ÝqˆÆ„ת¶£«Ç®Â›ŸÊŸäâ"ª–¨Œ×ÿ¹¡k'(2'82JêýÂÄ…Œ™ÉˆƄת¶£«ÿ‚¾žÃ“’§õ"÷‘–ä–Œ¿¯'(2'82Iâ븛ÚðÖóˆÆ„×ª¶£«˜ý˜‘¢ŒÐÀI"Ïä†÷ɯ'(2'82Iÿ±å¨ ïÉKˆÆ„ת¶£«ì‘áÒîŸÙÍá"Ÿ¡ªˆëôùÇ'(2'82Hý©ëÞ¥é÷“\ˆÆ„ת¶£«ôé‹¿¿¸ßÄ"½šòÐ䤿Œ'(2'82JÁ°Õ즊ý…¯ˆÆ„ת¶£«‰±Öйˆ–Ôõ"é’¬¸ç¼‡Óª'(2'82ƒ˜Êã•ÿÊÙÚ·æúŸÌ½‚¡”‰àÉ®¶f"é’¬¸ç¼‡Óª"½šòÐ䤿Œ"Ÿ¡ªˆëôùÇ(282‚ÊùÑèÂóžàY·æúŸÌ½‚¡”‰àÉ®¶f"é’¬¸ç¼‡Óª"½šòÐ䤿Œ"Ÿ¡ªˆëôùÇ(282MÚËñ´ä‡¼Ä'ª ÿçû¤é刌ÓÜ¢¾ÌÊ"ûÄߪæþªâÚ(2 -82LŽÏ—ꤦݯ5ª ÿçû¤éåˆÇ®Â›ŸÊŸäâ"ª–¨Œ×ÿ¹¡k(2 -82Nø‚ÍÁœÆÚª ÿçû¤éåˆÿ‚¾žÃ“’§õ"÷‘–ä–Œ¿¯(2 -82LŸ¿ŽöåèÆ)ª ÿçû¤é刘ý˜‘¢ŒÐÀI"Ïä†÷ɯ(2 -82NþÝÒÿ§Ù¡±˜ª ÿçû¤éåˆì‘áÒîŸÙÍá"Ÿ¡ªˆëôùÇ(2 -82M³Ñ×饊ÿ¦ª ÿçû¤éåˆôé‹¿¿¸ßÄ"½šòÐ䤿Œ(2 -82M–ÎÜ‹ó¯Ë„ª ÿçû¤é刉±Öйˆ–Ôõ"é’¬¸ç¼‡Óª(2 -82ƒÍê´ýÞÁöª ÿçû¤é刔‰àÉ®¶f"é’¬¸ç¼‡Óª"½šòÐ䤿Œ"Ÿ¡ªˆëôùÇ(282,Ü›ä¤ö¸Åˆºª ÿçû¤éåˆÐÙ•°‹ÁÈy(282+Ù¼‡úýõ‰—êâÓ»ä¾ÙÙ9ÐÙ•°‹ÁÈy(282.ƒÚæË›Õ²“^âÓ»ä¾ÙÙ9ÐÙ•°‹ÁÈy(2 -82/òìšÀ·ÄŒÃ#óª·Žð–äÑŠÐÙ•°‹ÁÈy(2 -82+¢ØþËÄÝßíyóª·Žð–äÑŠÐÙ•°‹ÁÈy(282"йʢäÞ†#½³ úùš±KÐÙ•°‹ÁÈy(82I¾Œ…û±ªÓþν³ úùš±KŒÓÜ¢¾ÌÊ"ûÄߪæþªâÚ(282HÞûÅŠëŽî½³ úùš±KǮ›ŸÊŸäâ"ª–¨Œ×ÿ¹¡k(282HÚÇÍý›ôÍô_½³ úùš±Kÿ‚¾žÃ“’§õ"÷‘–ä–Œ¿¯(282Gá”ܰ§¼Üï{½³ úùš±K˜ý˜‘¢ŒÐÀI"Ïä†÷ɯ(282IŒú’ìÌšŒÿ½³ úùš±Kì‘áÒîŸÙÍá"Ÿ¡ªˆëôùÇ(282HðÒ¬¼ïˆâ½³ úùš±Kôé‹¿¿¸ßÄ"½šòÐ䤿Œ(282Hˆ¼ÂÁŒ‚ªŠE½³ úùš±K‰±Öйˆ–Ôõ"é’¬¸ç¼‡Óª(282 ðÚžºÖèà몖¨Œ×ÿ¹¡k(282"…ïèÙŸåóþÎûÄߪæþªâÚ(282!ÆÉ¨‡âž¯‰÷‘–ä–Œ¿¯(282"Õ™õê×Ì·›‹Ïä†÷ɯ(282!Þíó¾ó¿ÚªˆëôùÇ(282!ž¥ÌÔѾۛL½šòÐ䤿Œ(282!„±ÂææÄÁOé’¬¸ç¼‡Óª(282+¦‚ýª¬¢˜þǪ–¨Œ×ÿ¹¡kÐÙ•°‹ÁÈy(282+Ó®¿Œïɳ=ûÄߪæþªâÚÐÙ•°‹ÁÈy(282+ØŽ„Øø¨²c÷‘–ä–Œ¿¯ÐÙ•°‹ÁÈy(282,ýŒ®ê£ÀËŠÏä†÷ɯÐÙ•°‹ÁÈy(282+’ÐÝç’묩uŸ¡ªˆëôùÇÐÙ•°‹ÁÈy(282,ÌÅ„„ŒúÔÞ ½šòÐ䤿ŒÐÙ•°‹ÁÈy(282,ÿªØ¦ƒ®èý»é’¬¸ç¼‡ÓªÐÙ•°‹ÁÈy(282#©Ô†«¹ÛÈÊœä”Û›ÅËèÄÐÙ•°‹ÁÈy(82,‡¼ã™“ЧӜä”Û›ÅËèÄÐÙ•°‹ÁÈy(282#ëÜÄêÖª©ÞùÃýب°Ï=ÐÙ•°‹ÁÈy(82I¬Í˜ÝïÚÈéùÃýب°Ï=ŒÓÜ¢¾ÌÊ"ûÄߪæþªâÚ(282Hã;ܲÍË®øùÃýب°Ï=Ǯ›ŸÊŸäâ"ª–¨Œ×ÿ¹¡k(282IÌ™¹ý¢ÞúŒ…ùÃýب°Ï=ÿ‚¾žÃ“’§õ"÷‘–ä–Œ¿¯(282GÚ´¯Ðèà £YùÃýب°Ï=˜ý˜‘¢ŒÐÀI"Ïä†÷ɯ(282IƒÈæÑ€¸µ§µùÃýب°Ï=ì‘áÒîŸÙÍá"Ÿ¡ªˆëôùÇ(282Gׇã ÉÈùÃýب°Ï=ôé‹¿¿¸ßÄ"½šòÐ䤿Œ(282HÁµÏ›é¥¸ãUùÃýب°Ï=‰±Öйˆ–Ôõ"é’¬¸ç¼‡Óª(282"§±¿Œ·¾íé„ò݃‰…Ï™sÐÙ•°‹ÁÈy(82I®‰Å„ÿ··„ò݃‰…Ï™sŒÓÜ¢¾ÌÊ"ûÄߪæþªâÚ(282H°ªåÛòÿ™˜„ò݃‰…Ï™sǮ›ŸÊŸäâ"ª–¨Œ×ÿ¹¡k(282HÖïˆÇ÷ðî’ -„ò݃‰…Ï™sÿ‚¾žÃ“’§õ"÷‘–ä–Œ¿¯(282Gæü•о°‹„ò݃‰…Ï™s˜ý˜‘¢ŒÐÀI"Ïä†÷ɯ(282HáŒêË ûÜÝ„ò݃‰…Ï™sì‘áÒîŸÙÍá"Ÿ¡ªˆëôùÇ(282GµüŒºÊ¥„ò݃‰…Ï™sôé‹¿¿¸ßÄ"½šòÐ䤿Œ(282HÏææŸÞñ˜È'„ò݃‰…Ï™s‰±Öйˆ–Ôõ"é’¬¸ç¼‡Óª(282#䪧¸ëð»ÍÖ“®ß®þÏ¿ÐÙ•°‹ÁÈy(82HâÈòŸ‚Ϫ¶;“®ß®þÏ¿ŒÓÜ¢¾ÌÊ"ûÄߪæþªâÚ(282GÅöôŽåËà@“®ß®þϿǮ›ŸÊŸäâ"ª–¨Œ×ÿ¹¡k(282H±¤ñþÜÀ«‡-“®ß®þÏ¿ÿ‚¾žÃ“’§õ"÷‘–ä–Œ¿¯(282GЇ¶ÇéíËI“®ß®þÏ¿˜ý˜‘¢ŒÐÀI"Ïä†÷ɯ(282H¶ÛŽˆÊÜÇÔ -“®ß®þÏ¿ì‘áÒîŸÙÍá"Ÿ¡ªˆëôùÇ(282Gñå°ì’³¯Ç“®ß®þÏ¿ôé‹¿¿¸ßÄ"½šòÐ䤿Œ(282H¥×©ë¹ÖÕÅb“®ß®þÏ¿‰±Öйˆ–Ôõ"é’¬¸ç¼‡Óª(282#¼ù´–ºîª¡Ÿ¤øÝŽ£î“KÐÙ•°‹ÁÈy(82H…üÖ½ßÇЃqŸ¤øÝŽ£î“KŒÓÜ¢¾ÌÊ"ûÄߪæþªâÚ(282GÀê÷ÅÒ×´øŸ¤øÝŽ£î“KǮ›ŸÊŸäâ"ª–¨Œ×ÿ¹¡k(282Iô ¨øÍ¹€Ÿ¤øÝŽ£î“Kÿ‚¾žÃ“’§õ"÷‘–ä–Œ¿¯(282HÍÆýö®£ÏåËŸ¤øÝŽ£î“K˜ý˜‘¢ŒÐÀI"Ïä†÷ɯ(282H¿¾Š “ʽ៤øÝŽ£î“Kì‘áÒîŸÙÍá"Ÿ¡ªˆëôùÇ(282GºÉ¬¦“ÆçÜpŸ¤øÝŽ£î“Kôé‹¿¿¸ßÄ"½šòÐ䤿Œ(282H•úŸþî¯ø6Ÿ¤øÝŽ£î“K‰±Öйˆ–Ôõ"é’¬¸ç¼‡Óª(28:â鿨ŒÙœ®‘*:ÔØ’¯—꓌’ („:ðÿÈ€ÀíÐð© (¬:ì¹ó¢â¯¢ÚÌ (Ü:ôïÆ þ©ƒ„ø (À:õµ…çÈηŽB (¤
:¸Î½Èìù„ (:¶›èõÖð®÷m (è:¨ÆºÀõµÈï¹ (” -:ãï©Ê½˜š¡® (¼:¥Ã¯œí…â¥w (ø -:å½ÈÂÏò“Þó (ˆ:¶Æèܦ™¥ìß( -( -( -( -( -B.ª ÿçû¤éåˆ!ÂŒÝëð ¢Ý›ºô¿Úó¾k2 -B"󄽾؜ߞ}™ºŽÔ–í”"žÈ¶ž•ÞŠè( -B-âÓ»ä¾ÙÙ9!µêЊîÄa鮣öü©µ2 -B,·æúŸÌ½‚¡ûó⿼‘ÿÿƒ«ŸˆÇŽ¿¡ diff --git a/cmds/statsd/tools/dogfood/res/values/strings.xml b/cmds/statsd/tools/dogfood/res/values/strings.xml deleted file mode 100644 index 60948a181a1b..000000000000 --- a/cmds/statsd/tools/dogfood/res/values/strings.xml +++ /dev/null @@ -1,57 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** Copyright 2007, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> -<resources> - - <string name="app_name">Statsd Dogfood</string> - - <string name="statsd_running">Statsd Running</string> - <string name="statsd_not_running">Statsd NOT Running</string> - - <string name="push_config">Push baseline config</string> - <string name="set_receiver">Set pendingintent</string> - <string name="remove_receiver">Remove pendingintent</string> - - <string name="app_a_foreground">App A foreground</string> - <string name="app_b_foreground">App B foreground</string> - - - <string name="app_a_get_wl1">App A get wl_1</string> - <string name="app_a_release_wl1">App A release wl_1</string> - - <string name="app_a_get_wl2">App A get wl_2</string> - <string name="app_a_release_wl2">App A release wl_2</string> - - <string name="app_b_get_wl1">App B get wl_1</string> - <string name="app_b_release_wl1">App B release wl_1</string> - - <string name="app_b_get_wl2">App B get wl_2</string> - <string name="app_b_release_wl2">App B release wl_2</string> - - <string name="plug">Plug</string> - <string name="unplug">Unplug</string> - - <string name="screen_on">Screen On</string> - <string name="screen_off">Screen Off</string> - - <string name="custom_start">App hook start</string> - <string name="custom_stop">App hook stop</string> - - <string name="dump">DumpReport</string> - <string name="report_header">Report details</string> -</resources> diff --git a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java deleted file mode 100644 index b6b16e40a4b2..000000000000 --- a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.statsd.dogfood; - -import android.text.format.DateFormat; - -import com.android.os.StatsLog; - -import java.util.List; - -public class DisplayProtoUtils { - public static void displayLogReport(StringBuilder sb, StatsLog.ConfigMetricsReportList reports) { - sb.append("ConfigKey: "); - if (reports.hasConfigKey()) { - com.android.os.StatsLog.ConfigMetricsReportList.ConfigKey key = reports.getConfigKey(); - sb.append("\tuid: ").append(key.getUid()).append(" name: ").append(key.getId()) - .append("\n"); - } - - for (StatsLog.ConfigMetricsReport report : reports.getReportsList()) { - sb.append("StatsLogReport size: ").append(report.getMetricsCount()).append("\n"); - sb.append("Last report time:").append(getDateStr(report.getLastReportElapsedNanos())). - append("\n"); - sb.append("Current report time:").append(getDateStr(report.getCurrentReportElapsedNanos())). - append("\n"); - for (StatsLog.StatsLogReport log : report.getMetricsList()) { - sb.append("\n\n"); - sb.append("metric id: ").append(log.getMetricId()).append("\n"); - - switch (log.getDataCase()) { - case DURATION_METRICS: - sb.append("Duration metric data\n"); - displayDurationMetricData(sb, log); - break; - case EVENT_METRICS: - sb.append("Event metric data\n"); - displayEventMetricData(sb, log); - break; - case COUNT_METRICS: - sb.append("Count metric data\n"); - displayCountMetricData(sb, log); - break; - case GAUGE_METRICS: - sb.append("Gauge metric data\n"); - displayGaugeMetricData(sb, log); - break; - case VALUE_METRICS: - sb.append("Value metric data\n"); - displayValueMetricData(sb, log); - break; - case DATA_NOT_SET: - sb.append("No metric data\n"); - break; - } - } - } - } - - public static String getDateStr(long nanoSec) { - return DateFormat.format("dd/MM hh:mm:ss", nanoSec/1000000).toString(); - } - - private static void displayDimension(StringBuilder sb, StatsLog.DimensionsValue dimensionValue) { - sb.append(dimensionValue.getField()).append(":"); - if (dimensionValue.hasValueBool()) { - sb.append(dimensionValue.getValueBool()); - } else if (dimensionValue.hasValueFloat()) { - sb.append(dimensionValue.getValueFloat()); - } else if (dimensionValue.hasValueInt()) { - sb.append(dimensionValue.getValueInt()); - } else if (dimensionValue.hasValueStr()) { - sb.append(dimensionValue.getValueStr()); - } else if (dimensionValue.hasValueTuple()) { - sb.append("{"); - for (StatsLog.DimensionsValue child : - dimensionValue.getValueTuple().getDimensionsValueList()) { - displayDimension(sb, child); - } - sb.append("}"); - } - sb.append(" "); - } - - public static void displayDurationMetricData(StringBuilder sb, StatsLog.StatsLogReport log) { - StatsLog.StatsLogReport.DurationMetricDataWrapper durationMetricDataWrapper - = log.getDurationMetrics(); - sb.append("Dimension size: ").append(durationMetricDataWrapper.getDataCount()).append("\n"); - for (StatsLog.DurationMetricData duration : durationMetricDataWrapper.getDataList()) { - sb.append("dimension_in_what: "); - displayDimension(sb, duration.getDimensionsInWhat()); - sb.append("\n"); - if (duration.hasDimensionsInCondition()) { - sb.append("dimension_in_condition: "); - displayDimension(sb, duration.getDimensionsInCondition()); - sb.append("\n"); - } - - for (StatsLog.DurationBucketInfo info : duration.getBucketInfoList()) { - sb.append("\t[").append(getDateStr(info.getStartBucketElapsedNanos())).append("-") - .append(getDateStr(info.getEndBucketElapsedNanos())).append("] -> ") - .append(info.getDurationNanos()).append(" ns\n"); - } - } - } - - public static void displayEventMetricData(StringBuilder sb, StatsLog.StatsLogReport log) { - sb.append("Contains ").append(log.getEventMetrics().getDataCount()).append(" events\n"); - StatsLog.StatsLogReport.EventMetricDataWrapper eventMetricDataWrapper = - log.getEventMetrics(); - for (StatsLog.EventMetricData event : eventMetricDataWrapper.getDataList()) { - sb.append(getDateStr(event.getElapsedTimestampNanos())).append(": "); - sb.append(event.getAtom().getPushedCase().toString()).append("\n"); - } - } - - public static void displayCountMetricData(StringBuilder sb, StatsLog.StatsLogReport log) { - StatsLog.StatsLogReport.CountMetricDataWrapper countMetricDataWrapper - = log.getCountMetrics(); - sb.append("Dimension size: ").append(countMetricDataWrapper.getDataCount()).append("\n"); - for (StatsLog.CountMetricData count : countMetricDataWrapper.getDataList()) { - sb.append("dimension_in_what: "); - displayDimension(sb, count.getDimensionsInWhat()); - sb.append("\n"); - if (count.hasDimensionsInCondition()) { - sb.append("dimension_in_condition: "); - displayDimension(sb, count.getDimensionsInCondition()); - sb.append("\n"); - } - - for (StatsLog.CountBucketInfo info : count.getBucketInfoList()) { - sb.append("\t[").append(getDateStr(info.getStartBucketElapsedNanos())).append("-") - .append(getDateStr(info.getEndBucketElapsedNanos())).append("] -> ") - .append(info.getCount()).append("\n"); - } - } - } - - public static void displayGaugeMetricData(StringBuilder sb, StatsLog.StatsLogReport log) { - sb.append("Display me!"); - } - - public static void displayValueMetricData(StringBuilder sb, StatsLog.StatsLogReport log) { - sb.append("Display me!"); - } -} diff --git a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java deleted file mode 100644 index 4f4dd011e419..000000000000 --- a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java +++ /dev/null @@ -1,361 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.statsd.dogfood; - -import android.app.Activity; -import android.app.PendingIntent; -import android.app.IntentService; -import android.app.StatsManager; -import android.app.StatsManager.StatsUnavailableException; -import android.content.Context; -import android.content.Intent; -import android.content.res.Resources; -import android.os.Bundle; -import android.util.Log; -import android.util.StatsLog; -import android.view.View; -import android.widget.TextView; -import android.widget.Toast; -import android.os.IStatsManager; -import android.os.ServiceManager; - -import java.io.InputStream; - -import static com.android.statsd.dogfood.DisplayProtoUtils.displayLogReport; - -public class MainActivity extends Activity { - private final static String TAG = "StatsdDogfood"; - private final static long CONFIG_ID = 987654321; - - final int[] mUids = {11111111, 2222222}; - StatsManager mStatsManager; - TextView mReportText; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setContentView(R.layout.activity_main); - - findViewById(R.id.app_a_wake_lock_acquire1).setOnClickListener( - new View.OnClickListener() { - @Override - public void onClick(View view) { - onWakeLockAcquire(0, "wl_1"); - } - }); - - findViewById(R.id.app_b_wake_lock_acquire1).setOnClickListener( - new View.OnClickListener() { - @Override - public void onClick(View view) { - onWakeLockAcquire(1, "wl_1"); - } - }); - - findViewById(R.id.app_a_wake_lock_acquire2).setOnClickListener( - new View.OnClickListener() { - @Override - public void onClick(View view) { - onWakeLockAcquire(0, "wl_2"); - } - }); - - findViewById(R.id.app_b_wake_lock_acquire2).setOnClickListener( - new View.OnClickListener() { - @Override - public void onClick(View view) { - onWakeLockAcquire(1, "wl_2"); - } - }); - - findViewById(R.id.app_a_wake_lock_release1).setOnClickListener( - new View.OnClickListener() { - @Override - public void onClick(View view) { - onWakeLockRelease(0, "wl_1"); - } - }); - - - findViewById(R.id.app_b_wake_lock_release1).setOnClickListener( - new View.OnClickListener() { - @Override - public void onClick(View view) { - onWakeLockRelease(1, "wl_1"); - } - }); - - findViewById(R.id.app_a_wake_lock_release2).setOnClickListener( - new View.OnClickListener() { - @Override - public void onClick(View view) { - onWakeLockRelease(0, "wl_2"); - } - }); - - - findViewById(R.id.app_b_wake_lock_release2).setOnClickListener( - new View.OnClickListener() { - @Override - public void onClick(View view) { - onWakeLockRelease(1, "wl_2"); - } - }); - - - findViewById(R.id.plug).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - StatsLog.write(StatsLog.PLUGGED_STATE_CHANGED, - StatsLog.PLUGGED_STATE_CHANGED__STATE__BATTERY_PLUGGED_AC); - } - }); - - findViewById(R.id.unplug).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - StatsLog.write(StatsLog.PLUGGED_STATE_CHANGED, - StatsLog.PLUGGED_STATE_CHANGED__STATE__BATTERY_PLUGGED_NONE); - } - }); - - findViewById(R.id.screen_on).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - StatsLog.write(StatsLog.SCREEN_STATE_CHANGED, - StatsLog.SCREEN_STATE_CHANGED__STATE__DISPLAY_STATE_ON); - } - }); - - findViewById(R.id.screen_off).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - StatsLog.write(StatsLog.SCREEN_STATE_CHANGED, - StatsLog.SCREEN_STATE_CHANGED__STATE__DISPLAY_STATE_OFF); - } - }); - - findViewById(R.id.custom_start).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - StatsLog.logStart(8); - } - }); - - findViewById(R.id.custom_stop).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - StatsLog.logStop(8); - } - }); - - mReportText = (TextView) findViewById(R.id.report_text); - - findViewById(R.id.dump).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - if (!statsdRunning()) { - return; - } - if (mStatsManager != null) { - try { - byte[] data = mStatsManager.getReports(CONFIG_ID); - if (data != null) { - displayData(data); - return; - } - } catch (StatsUnavailableException e) { - Log.e(TAG, "Failed to get data from statsd", e); - } - mReportText.setText("Failed!"); - } - } - }); - - findViewById(R.id.push_config).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - try { - if (!statsdRunning()) { - return; - } - Resources res = getResources(); - InputStream inputStream = res.openRawResource(R.raw.statsd_baseline_config); - - byte[] config = new byte[inputStream.available()]; - inputStream.read(config); - if (mStatsManager != null) { - try { - mStatsManager.addConfig(CONFIG_ID, config); - Toast.makeText( - MainActivity.this, "Config pushed", Toast.LENGTH_LONG).show(); - } catch (StatsUnavailableException | IllegalArgumentException e) { - Toast.makeText(MainActivity.this, "Config push FAILED!", - Toast.LENGTH_LONG).show(); - } - } - } catch (Exception e) { - Toast.makeText(MainActivity.this, "failed to read config", Toast.LENGTH_LONG); - } - } - }); - - PendingIntent pi = PendingIntent.getService(this, 0, - new Intent(this, ReceiverIntentService.class), PendingIntent.FLAG_UPDATE_CURRENT); - findViewById(R.id.set_receiver).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - try { - if (!statsdRunning()) { - return; - } - if (mStatsManager != null) { - try { - mStatsManager.setFetchReportsOperation(pi, CONFIG_ID); - Toast.makeText(MainActivity.this, - "Receiver specified to pending intent", Toast.LENGTH_LONG) - .show(); - } catch (StatsUnavailableException e) { - Toast.makeText(MainActivity.this, "Statsd did not set receiver", - Toast.LENGTH_LONG) - .show(); - } - } - } catch (Exception e) { - Toast.makeText(MainActivity.this, "failed to set receiver", Toast.LENGTH_LONG); - } - } - }); - findViewById(R.id.remove_receiver).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - try { - if (!statsdRunning()) { - return; - } - if (mStatsManager != null) { - try { - mStatsManager.setFetchReportsOperation(null, CONFIG_ID); - Toast.makeText(MainActivity.this, "Receiver remove", Toast.LENGTH_LONG) - .show(); - } catch (StatsUnavailableException e) { - Toast.makeText(MainActivity.this, "Statsd did not remove receiver", - Toast.LENGTH_LONG) - .show(); - } - } - } catch (Exception e) { - Toast.makeText( - MainActivity.this, "failed to remove receiver", Toast.LENGTH_LONG); - } - } - }); - mStatsManager = (StatsManager) getSystemService("stats"); - } - - private boolean statsdRunning() { - if (IStatsManager.Stub.asInterface(ServiceManager.getService("stats")) == null) { - Log.d(TAG, "Statsd not running"); - Toast.makeText(MainActivity.this, "Statsd NOT running!", Toast.LENGTH_LONG).show(); - return false; - } - return true; - } - - @Override - public void onNewIntent(Intent intent) { - Log.d(TAG, "new intent: " + intent.getIntExtra("pkg", 0)); - int pkg = intent.getIntExtra("pkg", 0); - String name = intent.getStringExtra("name"); - if (intent.hasExtra("acquire")) { - onWakeLockAcquire(pkg, name); - } else if (intent.hasExtra("release")) { - onWakeLockRelease(pkg, name); - } - } - - private void displayData(byte[] data) { - com.android.os.StatsLog.ConfigMetricsReportList reports = null; - boolean good = false; - if (data != null) { - try { - reports = com.android.os.StatsLog.ConfigMetricsReportList.parseFrom(data); - good = true; - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - // display it in the text view. - } - } - int size = data == null ? 0 : data.length; - StringBuilder sb = new StringBuilder(); - sb.append(good ? "Proto parsing OK!" : "Proto parsing Error!"); - sb.append(" size:").append(size).append("\n"); - - if (good && reports != null) { - displayLogReport(sb, reports); - mReportText.setText(sb.toString()); - } - } - - - private void onWakeLockAcquire(int id, String name) { - if (id > 1) { - Log.d(TAG, "invalid pkg id"); - return; - } - int[] uids = new int[]{mUids[id]}; - String[] tags = new String[]{"acquire"}; - StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, uids, tags, - StatsLog.WAKELOCK_STATE_CHANGED__TYPE__PARTIAL_WAKE_LOCK, name, - StatsLog.WAKELOCK_STATE_CHANGED__STATE__ACQUIRE); - StringBuilder sb = new StringBuilder(); - sb.append("StagsLog.write(10, ").append(mUids[id]).append(", ").append(0) - .append(", ").append(name).append(", 1);"); - Toast.makeText(this, sb.toString(), Toast.LENGTH_LONG).show(); - } - - private void onWakeLockRelease(int id, String name) { - if (id > 1) { - Log.d(TAG, "invalid pkg id"); - return; - } - int[] uids = new int[]{mUids[id]}; - String[] tags = new String[]{"release"}; - StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, uids, tags, - StatsLog.WAKELOCK_STATE_CHANGED__TYPE__PARTIAL_WAKE_LOCK, name, - StatsLog.WAKELOCK_STATE_CHANGED__STATE__RELEASE); - StringBuilder sb = new StringBuilder(); - sb.append("StagsLog.write(10, ").append(mUids[id]).append(", ").append(0) - .append(", ").append(name).append(", 0);"); - Toast.makeText(this, sb.toString(), Toast.LENGTH_LONG).show(); - } - - public static class ReceiverIntentService extends IntentService { - public ReceiverIntentService() { - super("ReceiverIntentService"); - } - - /** - * The IntentService calls this method from the default worker thread with - * the intent that started the service. When this method returns, IntentService - * stops the service, as appropriate. - */ - @Override - protected void onHandleIntent(Intent intent) { - Log.i(TAG, "Received notification that we should call getData"); - } - } -} diff --git a/cmds/statsd/tools/loadtest/Android.bp b/cmds/statsd/tools/loadtest/Android.bp deleted file mode 100644 index bf87fc51dce1..000000000000 --- a/cmds/statsd/tools/loadtest/Android.bp +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (C) 2017 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// - -android_app { - name: "StatsdLoadtest", - platform_apis: true, - - srcs: ["src/**/*.java"], - - resource_dirs: ["res"], - static_libs: [ - "platformprotoslite", - "statsdprotolite", - ], - - certificate: "platform", - privileged: true, - dex_preopt: { - enabled: false, - }, - optimize: { - enabled: false, - }, -} diff --git a/cmds/statsd/tools/loadtest/AndroidManifest.xml b/cmds/statsd/tools/loadtest/AndroidManifest.xml deleted file mode 100644 index 2bf8ca95d846..000000000000 --- a/cmds/statsd/tools/loadtest/AndroidManifest.xml +++ /dev/null @@ -1,44 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** Copyright 2007, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.statsd.loadtest" - android:sharedUserId="android.uid.system" - android:versionCode="1" - android:versionName="1.0" > - - <uses-permission android:name="android.permission.DUMP" /> - <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> - <application - android:allowBackup="true" - android:icon="@drawable/ic_launcher" - android:label="@string/app_name" > - <activity - android:name=".LoadtestActivity" - android:label="@string/app_name" - android:launchMode="singleTop" > - <intent-filter> - <action android:name="android.intent.action.MAIN" /> - <category android:name="android.intent.category.LAUNCHER" /> - </intent-filter> - </activity> - <receiver android:name=".LoadtestActivity$PusherAlarmReceiver" /> - <receiver android:name=".LoadtestActivity$StopperAlarmReceiver"/> - <receiver android:name=".PerfData$PerfAlarmReceiver"/> - </application> -</manifest> diff --git a/cmds/statsd/tools/loadtest/res/drawable-hdpi/ic_launcher.png b/cmds/statsd/tools/loadtest/res/drawable-hdpi/ic_launcher.png Binary files differdeleted file mode 100644 index 55621cc1074f..000000000000 --- a/cmds/statsd/tools/loadtest/res/drawable-hdpi/ic_launcher.png +++ /dev/null diff --git a/cmds/statsd/tools/loadtest/res/drawable-mdpi/ic_launcher.png b/cmds/statsd/tools/loadtest/res/drawable-mdpi/ic_launcher.png Binary files differdeleted file mode 100644 index 11ec2068be19..000000000000 --- a/cmds/statsd/tools/loadtest/res/drawable-mdpi/ic_launcher.png +++ /dev/null diff --git a/cmds/statsd/tools/loadtest/res/drawable-xhdpi/ic_launcher.png b/cmds/statsd/tools/loadtest/res/drawable-xhdpi/ic_launcher.png Binary files differdeleted file mode 100644 index 7c02b784aa5d..000000000000 --- a/cmds/statsd/tools/loadtest/res/drawable-xhdpi/ic_launcher.png +++ /dev/null diff --git a/cmds/statsd/tools/loadtest/res/drawable-xxhdpi/ic_launcher.png b/cmds/statsd/tools/loadtest/res/drawable-xxhdpi/ic_launcher.png Binary files differdeleted file mode 100644 index 915d91441349..000000000000 --- a/cmds/statsd/tools/loadtest/res/drawable-xxhdpi/ic_launcher.png +++ /dev/null diff --git a/cmds/statsd/tools/loadtest/res/layout/activity_loadtest.xml b/cmds/statsd/tools/loadtest/res/layout/activity_loadtest.xml deleted file mode 100644 index d6f804734385..000000000000 --- a/cmds/statsd/tools/loadtest/res/layout/activity_loadtest.xml +++ /dev/null @@ -1,208 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** Copyright 2007, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> -<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="match_parent" > - - <LinearLayout - android:id="@+id/outside" - android:clickable="true" - android:focusable="true" - android:focusableInTouchMode="true" - android:gravity="center" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:layout_marginRight="10dp" - android:layout_marginLeft="10dp" - android:orientation="vertical"> - <requestFocus /> - - <LinearLayout - android:gravity="center" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="horizontal"> - <TextView - android:textSize="30dp" - android:layout_height="wrap_content" - android:layout_width="wrap_content" - android:text="@string/replication_label" /> - <EditText - android:id="@+id/replication" - android:inputType="number" - android:layout_weight="1" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:maxLength="4" - android:text="@integer/replication_default" - android:textSize="30dp"/> - </LinearLayout> - - <LinearLayout - android:gravity="center" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="horizontal"> - <TextView - android:textSize="30dp" - android:layout_height="wrap_content" - android:layout_width="wrap_content" - android:text="@string/bucket_label" /> - <Spinner - android:id="@+id/bucket_spinner" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:prompt="@string/bucket_label"/> - </LinearLayout> - - <LinearLayout - android:gravity="center" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="horizontal"> - <TextView - android:textSize="30dp" - android:layout_height="wrap_content" - android:layout_width="wrap_content" - android:text="@string/period_label" /> - <EditText - android:id="@+id/period" - android:inputType="number" - android:layout_weight="1" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:maxLength="3" - android:text="@integer/period_default" - android:textSize="30dp"/> - </LinearLayout> - - <LinearLayout - android:gravity="center" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="horizontal"> - <TextView - android:textSize="30dp" - android:layout_height="wrap_content" - android:layout_width="wrap_content" - android:text="@string/burst_label" /> - <EditText - android:id="@+id/burst" - android:inputType="number" - android:layout_weight="1" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:maxLength="4" - android:text="@integer/burst_default" - android:textSize="30dp"/> - </LinearLayout> - - <LinearLayout - android:gravity="center" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="horizontal"> - <TextView - android:textSize="30dp" - android:layout_height="wrap_content" - android:layout_width="wrap_content" - android:text="@string/duration_label" /> - <EditText - android:id="@+id/duration" - android:inputType="number" - android:layout_weight="1" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:maxLength="4" - android:text="@integer/duration_default" - android:textSize="30dp"/> - </LinearLayout> - <CheckBox - android:id="@+id/placebo" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/placebo" - android:checked="false" /> - - <LinearLayout - android:gravity="center" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="horizontal"> - <CheckBox - android:id="@+id/include_count" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/count" - android:checked="true"/> - <CheckBox - android:id="@+id/include_duration" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/duration" - android:checked="true"/> - <CheckBox - android:id="@+id/include_event" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/event" - android:checked="true"/> - <CheckBox - android:id="@+id/include_value" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/value" - android:checked="true"/> - <CheckBox - android:id="@+id/include_gauge" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/gauge" - android:checked="true"/> - </LinearLayout> - - <Space - android:layout_width="1dp" - android:layout_height="30dp"/> - - <Button - android:id="@+id/start_stop" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:background="#ffff0000" - android:text="@string/start" - android:textSize="50dp"/> - - <Space - android:layout_width="1dp" - android:layout_height="30dp"/> - - <Space - android:layout_width="1dp" - android:layout_height="30dp"/> - - <TextView - android:id="@+id/report_text" - android:gravity="center" - android:layout_width="match_parent" - android:layout_height="wrap_content" /> - - </LinearLayout> - -</ScrollView> diff --git a/cmds/statsd/tools/loadtest/res/layout/spinner_item.xml b/cmds/statsd/tools/loadtest/res/layout/spinner_item.xml deleted file mode 100644 index b03da06f7a77..000000000000 --- a/cmds/statsd/tools/loadtest/res/layout/spinner_item.xml +++ /dev/null @@ -1,10 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> - -<TextView - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:textSize="30dp" - android:gravity="left" - android:padding="5dip" - /> diff --git a/cmds/statsd/tools/loadtest/res/raw/loadtest_config b/cmds/statsd/tools/loadtest/res/raw/loadtest_config Binary files differdeleted file mode 100755 index 24221908cbeb..000000000000 --- a/cmds/statsd/tools/loadtest/res/raw/loadtest_config +++ /dev/null diff --git a/cmds/statsd/tools/loadtest/res/values/integers.xml b/cmds/statsd/tools/loadtest/res/values/integers.xml deleted file mode 100644 index c2407d3b85f2..000000000000 --- a/cmds/statsd/tools/loadtest/res/values/integers.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** Copyright 2007, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> -<resources> - <integer name="burst_default">1</integer> - <integer name="period_default">2</integer> - <integer name="replication_default">1</integer> - <integer name="duration_default">240</integer> -</resources> diff --git a/cmds/statsd/tools/loadtest/res/values/strings.xml b/cmds/statsd/tools/loadtest/res/values/strings.xml deleted file mode 100644 index e8ae3f82a7e3..000000000000 --- a/cmds/statsd/tools/loadtest/res/values/strings.xml +++ /dev/null @@ -1,36 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** Copyright 2007, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> -<resources> - <string name="app_name">Statsd Loadtest</string> - <string name="bucket_label">bucket size (mins): </string> - <string name="burst_label">burst: </string> - <string name="bucket_default">FIVE_MINUTES</string> - <string name="placebo">placebo</string> - <string name="period_label">logging period (secs): </string> - <string name="replication_label">metric replication: </string> - <string name="duration_label">test duration (mins): </string> - <string name="start">  Start  </string> - <string name="stop">  Stop  </string> - <string name="count"> count </string> - <string name="duration"> duration </string> - <string name="event"> event </string> - <string name="value"> value </string> - <string name="gauge"> gauge </string> - -</resources> diff --git a/cmds/statsd/tools/loadtest/run_loadtest.sh b/cmds/statsd/tools/loadtest/run_loadtest.sh deleted file mode 100755 index 3c93a0613183..000000000000 --- a/cmds/statsd/tools/loadtest/run_loadtest.sh +++ /dev/null @@ -1,99 +0,0 @@ -#!/bin/sh -# -# Script that measures statsd's PSS under an increasing number of metrics. - -# Globals. -pss="" -pid="" - -# Starts the loadtest. -start_loadtest() { - echo "Starting loadtest" - adb shell am start -n com.android.statsd.loadtest/.LoadtestActivity --es "type" "start" -} - -# Stops the loadtest. -stop_loadtest() { - echo "Stopping loadtest" - adb shell am start -n com.android.statsd.loadtest/.LoadtestActivity --es "type" "stop" -} - -# Sets the metrics replication. -# Arguments: -# $1: The replication factor. -set_replication() { - adb shell am start -n com.android.statsd.loadtest/.LoadtestActivity --es "type" "set_replication" --ei "replication" "${1}" - echo "Replication set to ${1}" -} - -# Reads statsd's pid and PSS. -update_pid_and_pss() { - # Command that reads the PSS for statsd. This also gives us its pid. - get_mem=$(adb shell dumpsys meminfo |grep statsd) - # Looks for statsd's pid. - regex="([0-9,]+)K: statsd \(pid ([0-9]+)\).*" - if [[ $get_mem =~ $regex ]]; then - pss=$(echo "${BASH_REMATCH[1]}" | tr -d , | sed 's/\.//g') - pid=$(echo "${BASH_REMATCH[2]}") - else - echo $cmd doesnt match $regex - fi -} - -# Kills statsd. -# Assumes the pid has been set. -kill_statsd() { - echo "Killing statsd (pid ${pid})" - adb shell kill -9 "${pid}" -} - -# Main loop. -main() { - start_time=$(date +%s) - values=() - stop_loadtest - - echo "" - echo "********************* NEW LOADTEST ************************" - update_pid_and_pss - for replication in 1 2 4 8 16 32 64 128 256 512 1024 2048 4096 - do - echo "**** Starting test at replication ${replication} ****" - - # (1) Restart statsd. This will ensure its state is empty. - kill_statsd - sleep 3 # wait a bit for it to restart - update_pid_and_pss - echo "Before the test, statsd's PSS is ${pss}" - - # (2) Set the replication. - set_replication "${replication}" - sleep 1 # wait a bit - - # (3) Start the loadtest. - start_loadtest - - # (4) Wait several seconds, then read the PSS. - sleep 100 && update_pid_and_pss - echo "During the test, statsd's PSS is ${pss}" - values+=(${pss}) - - echo "Values: ${values[@]}" - - # (5) Stop loadtest. - stop_loadtest - sleep 2 - - echo "" - done - - end_time=$(date +%s) - echo "Completed loadtest in $((${end_time} - ${start_time})) seconds." - - values_as_str=$(IFS=$'\n'; echo "${values[*]}") - echo "The PSS values are:" - echo "${values_as_str}" - echo "" -} - -main diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/BatteryDataRecorder.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/BatteryDataRecorder.java deleted file mode 100644 index bab0c1e3f540..000000000000 --- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/BatteryDataRecorder.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.statsd.loadtest; - -import android.annotation.Nullable; -import android.content.Context; -import android.util.Log; -import com.android.internal.os.StatsdConfigProto.TimeUnit; -import java.text.ParseException; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class BatteryDataRecorder extends PerfDataRecorder { - private static final String TAG = "loadtest.BatteryDataRecorder"; - private static final String DUMP_FILENAME = TAG + "_dump.tmp"; - - public BatteryDataRecorder(boolean placebo, int replication, TimeUnit bucket, long periodSecs, - int burst, boolean includeCountMetric, boolean includeDurationMetric, - boolean includeEventMetric, boolean includeValueMetric, boolean includeGaugeMetric) { - super(placebo, replication, bucket, periodSecs, burst, includeCountMetric, - includeDurationMetric, includeEventMetric, includeValueMetric, includeGaugeMetric); - } - - @Override - public void startRecording(Context context) { - // Reset batterystats. - runDumpsysStats(context, DUMP_FILENAME, "batterystats", "--reset"); - } - - @Override - public void onAlarm(Context context) { - // Nothing to do as for battery, the whole data is in the final dumpsys call. - } - - @Override - public void stopRecording(Context context) { - StringBuilder sb = new StringBuilder(); - // Don't use --checkin. - runDumpsysStats(context, DUMP_FILENAME, "batterystats"); - readDumpData(context, DUMP_FILENAME, new BatteryStatsParser(), sb); - writeData(context, "battery_", "time,battery_level", sb); - } -} diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/BatteryStatsParser.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/BatteryStatsParser.java deleted file mode 100644 index 203d97acefd8..000000000000 --- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/BatteryStatsParser.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.statsd.loadtest; - -import android.annotation.Nullable; -import android.util.Log; -import java.text.ParseException; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class BatteryStatsParser implements PerfParser { - - private static final Pattern LINE_PATTERN = - Pattern.compile("\\s*\\+*(\\S*)\\s\\(\\d+\\)\\s(\\d\\d\\d)\\s.*"); - private static final Pattern TIME_PATTERN = - Pattern.compile("(\\d+)?(h)?(\\d+)?(m)?(\\d+)?(s)?(\\d+)?(ms)?"); - private static final String TAG = "loadtest.BatteryStatsParser"; - - private boolean mHistoryStarted; - private boolean mHistoryEnded; - - public BatteryStatsParser() { - } - - @Override - @Nullable - public String parseLine(String line) { - if (mHistoryEnded) { - return null; - } - if (!mHistoryStarted) { - if (line.contains("Battery History")) { - mHistoryStarted = true; - } - return null; - } - if (line.isEmpty()) { - mHistoryEnded = true; - return null; - } - Matcher lineMatcher = LINE_PATTERN.matcher(line); - if (lineMatcher.find() && lineMatcher.group(1) != null && lineMatcher.group(2) != null) { - if (lineMatcher.group(1).equals("0")) { - return "0," + lineMatcher.group(2) + "\n"; - } else { - Matcher timeMatcher = TIME_PATTERN.matcher(lineMatcher.group(1)); - if (timeMatcher.find()) { - Long time = getTime(lineMatcher.group(1)); - if (time != null) { - return time + "," + lineMatcher.group(2) + "\n"; - } else { - return null; // bad time - } - } else { - return null; // bad or no time - } - } - } - return null; - } - - @Nullable - private Long getTime(String group) { - if ("0".equals(group)) { - return 0L; - } - Matcher timeMatcher = TIME_PATTERN.matcher(group); - if (!timeMatcher.find()) { - return null; - } - - // Get rid of "ms". - String[] matches = group.split("ms", -1); - if (matches.length > 1) { - group = matches[0]; - } - - long time = 0L; - matches = group.split("h"); - if (matches.length > 1) { - time += Long.parseLong(matches[0]) * 60 * 60 * 1000; // hours - group = matches[1]; - } - matches = group.split("m"); - if (matches.length > 1) { - time += Long.parseLong(matches[0]) * 60 * 1000; // minutes - group = matches[1]; - } - matches = group.split("s"); - if (matches.length > 1) { - time += Long.parseLong(matches[0]) * 1000; // seconds - group = matches[1]; - } - - if (!group.isEmpty()) { - time += Long.parseLong(group); // milliseconds - } - return time; - } -} diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ConfigFactory.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ConfigFactory.java deleted file mode 100644 index 2e0161be8096..000000000000 --- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ConfigFactory.java +++ /dev/null @@ -1,314 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.statsd.loadtest; - -import android.content.Context; -import android.content.res.Resources; -import android.util.Log; - -import com.android.internal.os.StatsdConfigProto.Predicate; -import com.android.internal.os.StatsdConfigProto.CountMetric; -import com.android.internal.os.StatsdConfigProto.DurationMetric; -import com.android.internal.os.StatsdConfigProto.MetricConditionLink; -import com.android.internal.os.StatsdConfigProto.EventMetric; -import com.android.internal.os.StatsdConfigProto.GaugeMetric; -import com.android.internal.os.StatsdConfigProto.ValueMetric; -import com.android.internal.os.StatsdConfigProto.FieldValueMatcher; -import com.android.internal.os.StatsdConfigProto.AtomMatcher; -import com.android.internal.os.StatsdConfigProto.SimplePredicate; -import com.android.internal.os.StatsdConfigProto.StatsdConfig; -import com.android.internal.os.StatsdConfigProto.TimeUnit; - -import java.io.InputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -/** - * Creates StatsdConfig protos for loadtesting. - */ -public class ConfigFactory { - public static class ConfigMetadata { - public final byte[] bytes; - public final int numMetrics; - - public ConfigMetadata(byte[] bytes, int numMetrics) { - this.bytes = bytes; - this.numMetrics = numMetrics; - } - } - - public static final long CONFIG_ID = 123456789; - - private static final String TAG = "loadtest.ConfigFactory"; - - private final StatsdConfig mTemplate; - - public ConfigFactory(Context context) { - // Read the config template from the resoures. - Resources res = context.getResources(); - byte[] template = null; - StatsdConfig templateProto = null; - try { - InputStream inputStream = res.openRawResource(R.raw.loadtest_config); - template = new byte[inputStream.available()]; - inputStream.read(template); - templateProto = StatsdConfig.parseFrom(template); - } catch (IOException e) { - Log.e(TAG, "Unable to read or parse loadtest config template. Using an empty config."); - } - mTemplate = templateProto == null ? StatsdConfig.newBuilder().build() : templateProto; - - Log.d(TAG, "Loadtest template config: " + mTemplate); - } - - /** - * Generates a config. - * - * All configs are based on the same template. - * That template is designed to make the most use of the set of atoms that {@code SequencePusher} - * pushes, and to exercise as many of the metrics features as possible. - * Furthermore, by passing a replication factor to this method, one can artificially inflate - * the number of metrics in the config. One can also adjust the bucket size for aggregate - * metrics. - * - * @param replication The number of times each metric is replicated in the config. - * If the config template has n metrics, the generated config will have n * replication - * ones - * @param bucketMillis The bucket size, in milliseconds, for aggregate metrics - * @param placebo If true, only return an empty config - * @return The serialized config and the number of metrics. - */ - public ConfigMetadata getConfig(int replication, TimeUnit bucket, boolean placebo, - boolean includeCount, boolean includeDuration, boolean includeEvent, - boolean includeValue, boolean includeGauge) { - StatsdConfig.Builder config = StatsdConfig.newBuilder() - .setId(CONFIG_ID); - if (placebo) { - replication = 0; // Config will be empty, aside from a name. - } - int numMetrics = 0; - for (int i = 0; i < replication; i++) { - // metrics - if (includeEvent) { - for (EventMetric metric : mTemplate.getEventMetricList()) { - addEventMetric(metric, i, config); - numMetrics++; - } - } - if (includeCount) { - for (CountMetric metric : mTemplate.getCountMetricList()) { - addCountMetric(metric, i, bucket, config); - numMetrics++; - } - } - if (includeDuration) { - for (DurationMetric metric : mTemplate.getDurationMetricList()) { - addDurationMetric(metric, i, bucket, config); - numMetrics++; - } - } - if (includeGauge) { - for (GaugeMetric metric : mTemplate.getGaugeMetricList()) { - addGaugeMetric(metric, i, bucket, config); - numMetrics++; - } - } - if (includeValue) { - for (ValueMetric metric : mTemplate.getValueMetricList()) { - addValueMetric(metric, i, bucket, config); - numMetrics++; - } - } - // predicates - for (Predicate predicate : mTemplate.getPredicateList()) { - addPredicate(predicate, i, config); - } - // matchers - for (AtomMatcher matcher : mTemplate.getAtomMatcherList()) { - addMatcher(matcher, i, config); - } - } - - Log.d(TAG, "Loadtest config is : " + config.build()); - Log.d(TAG, "Generated config has " + numMetrics + " metrics"); - - return new ConfigMetadata(config.build().toByteArray(), numMetrics); - } - - /** - * Creates {@link MetricConditionLink}s that are identical to the one passed to this method, - * except that the names are appended with the provided suffix. - */ - private List<MetricConditionLink> getLinks( - List<MetricConditionLink> links, int suffix) { - List<MetricConditionLink> newLinks = new ArrayList(); - for (MetricConditionLink link : links) { - newLinks.add(link.toBuilder() - .setCondition(link.getCondition() + suffix) - .build()); - } - return newLinks; - } - - /** - * Creates an {@link EventMetric} based on the template. Makes sure that all names are appended - * with the provided suffix. Then adds that metric to the config. - */ - private void addEventMetric(EventMetric template, int suffix, StatsdConfig.Builder config) { - EventMetric.Builder metric = template.toBuilder() - .setId(template.getId() + suffix) - .setWhat(template.getWhat() + suffix); - if (template.hasCondition()) { - metric.setCondition(template.getCondition() + suffix); - } - if (template.getLinksCount() > 0) { - List<MetricConditionLink> links = getLinks(template.getLinksList(), suffix); - metric.clearLinks(); - metric.addAllLinks(links); - } - config.addEventMetric(metric); - } - - /** - * Creates a {@link CountMetric} based on the template. Makes sure that all names are appended - * with the provided suffix, and overrides the bucket size. Then adds that metric to the config. - */ - private void addCountMetric(CountMetric template, int suffix, TimeUnit bucket, - StatsdConfig.Builder config) { - CountMetric.Builder metric = template.toBuilder() - .setId(template.getId() + suffix) - .setWhat(template.getWhat() + suffix); - if (template.hasCondition()) { - metric.setCondition(template.getCondition() + suffix); - } - if (template.getLinksCount() > 0) { - List<MetricConditionLink> links = getLinks(template.getLinksList(), suffix); - metric.clearLinks(); - metric.addAllLinks(links); - } - metric.setBucket(bucket); - config.addCountMetric(metric); - } - - /** - * Creates a {@link DurationMetric} based on the template. Makes sure that all names are appended - * with the provided suffix, and overrides the bucket size. Then adds that metric to the config. - */ - private void addDurationMetric(DurationMetric template, int suffix, TimeUnit bucket, - StatsdConfig.Builder config) { - DurationMetric.Builder metric = template.toBuilder() - .setId(template.getId() + suffix) - .setWhat(template.getWhat() + suffix); - if (template.hasCondition()) { - metric.setCondition(template.getCondition() + suffix); - } - if (template.getLinksCount() > 0) { - List<MetricConditionLink> links = getLinks(template.getLinksList(), suffix); - metric.clearLinks(); - metric.addAllLinks(links); - } - metric.setBucket(bucket); - config.addDurationMetric(metric); - } - - /** - * Creates a {@link GaugeMetric} based on the template. Makes sure that all names are appended - * with the provided suffix, and overrides the bucket size. Then adds that metric to the config. - */ - private void addGaugeMetric(GaugeMetric template, int suffix, TimeUnit bucket, - StatsdConfig.Builder config) { - GaugeMetric.Builder metric = template.toBuilder() - .setId(template.getId() + suffix) - .setWhat(template.getWhat() + suffix); - if (template.hasCondition()) { - metric.setCondition(template.getCondition() + suffix); - } - if (template.getLinksCount() > 0) { - List<MetricConditionLink> links = getLinks(template.getLinksList(), suffix); - metric.clearLinks(); - metric.addAllLinks(links); - } - metric.setBucket(bucket); - config.addGaugeMetric(metric); - } - - /** - * Creates a {@link ValueMetric} based on the template. Makes sure that all names are appended - * with the provided suffix, and overrides the bucket size. Then adds that metric to the config. - */ - private void addValueMetric(ValueMetric template, int suffix, TimeUnit bucket, - StatsdConfig.Builder config) { - ValueMetric.Builder metric = template.toBuilder() - .setId(template.getId() + suffix) - .setWhat(template.getWhat() + suffix); - if (template.hasCondition()) { - metric.setCondition(template.getCondition() + suffix); - } - if (template.getLinksCount() > 0) { - List<MetricConditionLink> links = getLinks(template.getLinksList(), suffix); - metric.clearLinks(); - metric.addAllLinks(links); - } - metric.setBucket(bucket); - config.addValueMetric(metric); - } - - /** - * Creates a {@link Predicate} based on the template. Makes sure that all names - * are appended with the provided suffix. Then adds that predicate to the config. - */ - private void addPredicate(Predicate template, int suffix, StatsdConfig.Builder config) { - Predicate.Builder predicate = template.toBuilder() - .setId(template.getId() + suffix); - if (template.hasCombination()) { - Predicate.Combination.Builder cb = template.getCombination().toBuilder() - .clearPredicate(); - for (long child : template.getCombination().getPredicateList()) { - cb.addPredicate(child + suffix); - } - predicate.setCombination(cb.build()); - } - if (template.hasSimplePredicate()) { - SimplePredicate.Builder sc = template.getSimplePredicate().toBuilder() - .setStart(template.getSimplePredicate().getStart() + suffix) - .setStop(template.getSimplePredicate().getStop() + suffix); - if (template.getSimplePredicate().hasStopAll()) { - sc.setStopAll(template.getSimplePredicate().getStopAll() + suffix); - } - predicate.setSimplePredicate(sc.build()); - } - config.addPredicate(predicate); - } - - /** - * Creates a {@link AtomMatcher} based on the template. Makes sure that all names - * are appended with the provided suffix. Then adds that matcher to the config. - */ - private void addMatcher(AtomMatcher template, int suffix, StatsdConfig.Builder config) { - AtomMatcher.Builder matcher = template.toBuilder() - .setId(template.getId() + suffix); - if (template.hasCombination()) { - AtomMatcher.Combination.Builder cb = template.getCombination().toBuilder() - .clearMatcher(); - for (long child : template.getCombination().getMatcherList()) { - cb.addMatcher(child + suffix); - } - matcher.setCombination(cb); - } - config.addAtomMatcher(matcher); - } -} diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/DisplayProtoUtils.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/DisplayProtoUtils.java deleted file mode 100644 index d55f3f31fd9f..000000000000 --- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/DisplayProtoUtils.java +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.statsd.loadtest; - -import android.text.format.DateFormat; - -import com.android.os.StatsLog; - -import java.util.List; - -public class DisplayProtoUtils { - private static final int MAX_NUM_METRICS_TO_DISPLAY = 10; - - public static void displayLogReport(StringBuilder sb, StatsLog.ConfigMetricsReportList reports) { - sb.append("******************** Report ********************\n"); - if (reports.hasConfigKey()) { - sb.append("ConfigKey: "); - com.android.os.StatsLog.ConfigMetricsReportList.ConfigKey key = reports.getConfigKey(); - sb.append("\tuid: ").append(key.getUid()).append(" id: ").append(key.getId()) - .append("\n"); - } - - int numMetrics = 0; - for (StatsLog.ConfigMetricsReport report : reports.getReportsList()) { - sb.append("StatsLogReport size: ").append(report.getMetricsCount()).append("\n"); - sb.append("Last report time:").append(getDateStr(report.getLastReportElapsedNanos())). - append("\n"); - sb.append("Current report time:").append(getDateStr(report.getCurrentReportElapsedNanos())). - append("\n"); - for (StatsLog.StatsLogReport log : report.getMetricsList()) { - numMetrics++; - if (numMetrics > MAX_NUM_METRICS_TO_DISPLAY) { - sb.append("... output truncated\n"); - sb.append("************************************************"); - return; - } - sb.append("\n"); - sb.append("metric id: ").append(log.getMetricId()).append("\n"); - - switch (log.getDataCase()) { - case DURATION_METRICS: - sb.append("Duration metric data\n"); - displayDurationMetricData(sb, log); - break; - case EVENT_METRICS: - sb.append("Event metric data\n"); - displayEventMetricData(sb, log); - break; - case COUNT_METRICS: - sb.append("Count metric data\n"); - displayCountMetricData(sb, log); - break; - case GAUGE_METRICS: - sb.append("Gauge metric data\n"); - displayGaugeMetricData(sb, log); - break; - case VALUE_METRICS: - sb.append("Value metric data\n"); - displayValueMetricData(sb, log); - break; - case DATA_NOT_SET: - sb.append("No metric data\n"); - break; - } - } - } - sb.append("************************************************"); - } - - public static String getDateStr(long nanoSec) { - return DateFormat.format("dd/MM hh:mm:ss", nanoSec/1000000).toString(); - } - - private static void displayDimension(StringBuilder sb, StatsLog.DimensionsValue dimensionValue) { - sb.append(dimensionValue.getField()).append(":"); - if (dimensionValue.hasValueBool()) { - sb.append(dimensionValue.getValueBool()); - } else if (dimensionValue.hasValueFloat()) { - sb.append(dimensionValue.getValueFloat()); - } else if (dimensionValue.hasValueInt()) { - sb.append(dimensionValue.getValueInt()); - } else if (dimensionValue.hasValueStr()) { - sb.append(dimensionValue.getValueStr()); - } else if (dimensionValue.hasValueTuple()) { - sb.append("{"); - for (StatsLog.DimensionsValue child : - dimensionValue.getValueTuple().getDimensionsValueList()) { - displayDimension(sb, child); - } - sb.append("}"); - } - sb.append(" "); - } - - public static void displayDurationMetricData(StringBuilder sb, StatsLog.StatsLogReport log) { - StatsLog.StatsLogReport.DurationMetricDataWrapper durationMetricDataWrapper - = log.getDurationMetrics(); - sb.append("Dimension size: ").append(durationMetricDataWrapper.getDataCount()).append("\n"); - for (StatsLog.DurationMetricData duration : durationMetricDataWrapper.getDataList()) { - sb.append("dimension_in_what: "); - displayDimension(sb, duration.getDimensionsInWhat()); - sb.append("\n"); - if (duration.hasDimensionsInCondition()) { - sb.append("dimension_in_condition: "); - displayDimension(sb, duration.getDimensionsInCondition()); - sb.append("\n"); - } - - for (StatsLog.DurationBucketInfo info : duration.getBucketInfoList()) { - sb.append("\t[").append(getDateStr(info.getStartBucketElapsedNanos())).append("-") - .append(getDateStr(info.getEndBucketElapsedNanos())).append("] -> ") - .append(info.getDurationNanos()).append(" ns\n"); - } - } - } - - public static void displayEventMetricData(StringBuilder sb, StatsLog.StatsLogReport log) { - sb.append("Contains ").append(log.getEventMetrics().getDataCount()).append(" events\n"); - StatsLog.StatsLogReport.EventMetricDataWrapper eventMetricDataWrapper = - log.getEventMetrics(); - for (StatsLog.EventMetricData event : eventMetricDataWrapper.getDataList()) { - sb.append(getDateStr(event.getElapsedTimestampNanos())).append(": "); - sb.append(event.getAtom().getPushedCase().toString()).append("\n"); - } - } - - public static void displayCountMetricData(StringBuilder sb, StatsLog.StatsLogReport log) { - StatsLog.StatsLogReport.CountMetricDataWrapper countMetricDataWrapper - = log.getCountMetrics(); - sb.append("Dimension size: ").append(countMetricDataWrapper.getDataCount()).append("\n"); - for (StatsLog.CountMetricData count : countMetricDataWrapper.getDataList()) { - sb.append("dimension_in_what: "); - displayDimension(sb, count.getDimensionsInWhat()); - sb.append("\n"); - if (count.hasDimensionsInCondition()) { - sb.append("dimension_in_condition: "); - displayDimension(sb, count.getDimensionsInCondition()); - sb.append("\n"); - } - - for (StatsLog.CountBucketInfo info : count.getBucketInfoList()) { - sb.append("\t[").append(getDateStr(info.getStartBucketElapsedNanos())).append("-") - .append(getDateStr(info.getEndBucketElapsedNanos())).append("] -> ") - .append(info.getCount()).append("\n"); - } - } - } - - public static void displayGaugeMetricData(StringBuilder sb, StatsLog.StatsLogReport log) { - sb.append("Display me!"); - } - - public static void displayValueMetricData(StringBuilder sb, StatsLog.StatsLogReport log) { - sb.append("Display me!"); - } -} diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java deleted file mode 100644 index 769f78c726e8..000000000000 --- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java +++ /dev/null @@ -1,756 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.statsd.loadtest; - -import android.annotation.Nullable; -import android.app.Activity; -import android.app.AlarmManager; -import android.app.PendingIntent; -import android.app.StatsManager; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.graphics.Color; -import android.os.Bundle; -import android.os.Handler; -import android.os.IStatsManager; -import android.os.PowerManager; -import android.os.PowerManager.WakeLock; -import android.os.ServiceManager; -import android.os.SystemClock; -import android.text.Editable; -import android.text.TextWatcher; -import android.util.Log; -import android.util.StatsLog; -import android.view.View; -import android.view.inputmethod.InputMethodManager; -import android.view.MotionEvent; -import android.view.View.OnFocusChangeListener; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.Button; -import android.widget.CheckBox; -import android.widget.EditText; -import android.widget.Spinner; -import android.widget.TextView; -import android.widget.Toast; - -import com.android.os.StatsLog.ConfigMetricsReport; -import com.android.os.StatsLog.ConfigMetricsReportList; -import com.android.os.StatsLog.StatsdStatsReport; -import com.android.internal.os.StatsdConfigProto.TimeUnit; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * Runs a load test for statsd. - * How it works: - * <ul> - * <li> Sets up and pushes a custom config with metrics that exercise a large swath of code paths. - * <li> Periodically logs certain atoms into logd. - * <li> Impact on battery can be printed to logcat, or a bug report can be filed and analyzed - * in battery Historian. - * </ul> - * The load depends on how demanding the config is, as well as how frequently atoms are pushsed - * to logd. Those are all controlled by 4 adjustable parameters: - * <ul> - * <li> The 'replication' parameter artificially multiplies the number of metrics in the config. - * <li> The bucket size controls the time-bucketing the aggregate metrics. - * <li> The period parameter controls how frequently atoms are pushed to logd. - * <li> The 'burst' parameter controls how many atoms are pushed at the same time (per period). - * </ul> - */ -public class LoadtestActivity extends Activity implements AdapterView.OnItemSelectedListener { - - private static final String TAG = "loadtest.LoadtestActivity"; - public static final String TYPE = "type"; - private static final String PUSH_ALARM = "push_alarm"; - public static final String PERF_ALARM = "perf_alarm"; - private static final String SET_REPLICATION = "set_replication"; - private static final String REPLICATION = "replication"; - private static final String START = "start"; - private static final String STOP = "stop"; - private static final Map<String, TimeUnit> TIME_UNIT_MAP = initializeTimeUnitMap(); - private static final List<String> TIME_UNIT_LABELS = initializeTimeUnitLabels(); - - public final static class PusherAlarmReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - Intent activityIntent = new Intent(context, LoadtestActivity.class); - activityIntent.putExtra(TYPE, PUSH_ALARM); - context.startActivity(activityIntent); - } - } - - public final static class StopperAlarmReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - Intent activityIntent = new Intent(context, LoadtestActivity.class); - activityIntent.putExtra(TYPE, STOP); - context.startActivity(activityIntent); - } - } - - private static Map<String, TimeUnit> initializeTimeUnitMap() { - Map<String, TimeUnit> labels = new HashMap(); - labels.put("1m", TimeUnit.ONE_MINUTE); - labels.put("5m", TimeUnit.FIVE_MINUTES); - labels.put("10m", TimeUnit.TEN_MINUTES); - labels.put("30m", TimeUnit.THIRTY_MINUTES); - labels.put("1h", TimeUnit.ONE_HOUR); - labels.put("3h", TimeUnit.THREE_HOURS); - labels.put("6h", TimeUnit.SIX_HOURS); - labels.put("12h", TimeUnit.TWELVE_HOURS); - labels.put("1d", TimeUnit.ONE_DAY); - labels.put("1s", TimeUnit.CTS); - return labels; - } - - private static List<String> initializeTimeUnitLabels() { - List<String> labels = new ArrayList(); - labels.add("1s"); - labels.add("1m"); - labels.add("5m"); - labels.add("10m"); - labels.add("30m"); - labels.add("1h"); - labels.add("3h"); - labels.add("6h"); - labels.add("12h"); - labels.add("1d"); - return labels; - } - - private AlarmManager mAlarmMgr; - - /** - * Used to periodically log atoms to logd. - */ - private PendingIntent mPushPendingIntent; - - /** - * Used to end the loadtest. - */ - private PendingIntent mStopPendingIntent; - - private Button mStartStop; - private EditText mReplicationText; - private Spinner mBucketSpinner; - private EditText mPeriodText; - private EditText mBurstText; - private EditText mDurationText; - private TextView mReportText; - private CheckBox mPlaceboCheckBox; - private CheckBox mCountMetricCheckBox; - private CheckBox mDurationMetricCheckBox; - private CheckBox mEventMetricCheckBox; - private CheckBox mValueMetricCheckBox; - private CheckBox mGaugeMetricCheckBox; - - /** - * When the load test started. - */ - private long mStartedTimeMillis; - - /** - * For measuring perf data. - */ - private PerfData mPerfData; - - /** - * For communicating with statsd. - */ - private StatsManager mStatsManager; - - private PowerManager mPowerManager; - private WakeLock mWakeLock; - - /** - * If true, we only measure the effect of the loadtest infrastructure. No atom are pushed and - * the configuration is empty. - */ - private boolean mPlacebo; - - /** - * Whether to include CountMetric in the config. - */ - private boolean mIncludeCountMetric; - - /** - * Whether to include DurationMetric in the config. - */ - private boolean mIncludeDurationMetric; - - /** - * Whether to include EventMetric in the config. - */ - private boolean mIncludeEventMetric; - - /** - * Whether to include ValueMetric in the config. - */ - private boolean mIncludeValueMetric; - - /** - * Whether to include GaugeMetric in the config. - */ - private boolean mIncludeGaugeMetric; - - /** - * The burst size. - */ - private int mBurst; - - /** - * The metrics replication. - */ - private int mReplication; - - /** - * The period, in seconds, at which batches of atoms are pushed. - */ - private long mPeriodSecs; - - /** - * The bucket size, in minutes, for aggregate metrics. - */ - private TimeUnit mBucket; - - /** - * The duration, in minutes, of the loadtest. - */ - private long mDurationMins; - - /** - * Whether the loadtest has started. - */ - private boolean mStarted = false; - - /** - * Orchestrates the logging of pushed events into logd. - */ - private SequencePusher mPusher; - - /** - * Generates statsd configs. - */ - private ConfigFactory mFactory; - - /** - * For intra-minute periods. - */ - private final Handler mHandler = new Handler(); - - /** - * Number of metrics in the current config. - */ - private int mNumMetrics; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - Log.d(TAG, "Starting loadtest Activity"); - - setContentView(R.layout.activity_loadtest); - mReportText = (TextView) findViewById(R.id.report_text); - initBurst(); - initReplication(); - initBucket(); - initPeriod(); - initDuration(); - initPlacebo(); - initMetricWhitelist(); - - // Hide the keyboard outside edit texts. - findViewById(R.id.outside).setOnTouchListener(new View.OnTouchListener() { - @Override - public boolean onTouch(View v, MotionEvent event) { - InputMethodManager imm = - (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); - if (getCurrentFocus() != null) { - imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0); - } - return true; - } - }); - - mStartStop = findViewById(R.id.start_stop); - mStartStop.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - if (mStarted) { - stopLoadtest(); - } else { - startLoadtest(); - } - } - }); - - mAlarmMgr = (AlarmManager) getSystemService(Context.ALARM_SERVICE); - mStatsManager = (StatsManager) getSystemService("stats"); - mPowerManager = (PowerManager) getSystemService(Context.POWER_SERVICE); - mFactory = new ConfigFactory(this); - stopLoadtest(); - mReportText.setText(""); - } - - @Override - public void onNewIntent(Intent intent) { - String type = intent.getStringExtra(TYPE); - if (type == null) { - return; - } - switch (type) { - case PERF_ALARM: - onPerfAlarm(); - break; - case PUSH_ALARM: - onAlarm(); - break; - case SET_REPLICATION: - if (intent.hasExtra(REPLICATION)) { - setReplication(intent.getIntExtra(REPLICATION, 0)); - } - break; - case START: - startLoadtest(); - break; - case STOP: - stopLoadtest(); - break; - default: - throw new IllegalArgumentException("Unknown type: " + type); - } - } - - @Override - public void onDestroy() { - Log.d(TAG, "Destroying"); - mPerfData.onDestroy(); - stopLoadtest(); - clearConfigs(); - super.onDestroy(); - } - - @Nullable - public StatsdStatsReport getMetadata() { - if (!statsdRunning()) { - return null; - } - if (mStatsManager != null) { - byte[] data; - try { - data = mStatsManager.getStatsMetadata(); - } catch (StatsManager.StatsUnavailableException e) { - Log.e(TAG, "Failed to get data from statsd", e); - return null; - } - if (data != null) { - StatsdStatsReport report = null; - boolean good = false; - try { - return StatsdStatsReport.parseFrom(data); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - Log.d(TAG, "Bad StatsdStatsReport"); - } - } - } - return null; - } - - @Nullable - public List<ConfigMetricsReport> getData() { - if (!statsdRunning()) { - return null; - } - if (mStatsManager != null) { - byte[] data; - try { - data = mStatsManager.getReports(ConfigFactory.CONFIG_ID); - } catch (StatsManager.StatsUnavailableException e) { - Log.e(TAG, "Failed to get data from statsd", e); - return null; - } - if (data != null) { - ConfigMetricsReportList reports = null; - try { - reports = ConfigMetricsReportList.parseFrom(data); - Log.d(TAG, "Num reports: " + reports.getReportsCount()); - StringBuilder sb = new StringBuilder(); - DisplayProtoUtils.displayLogReport(sb, reports); - Log.d(TAG, sb.toString()); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - Log.d(TAG, "Invalid data"); - } - if (reports != null) { - return reports.getReportsList(); - } - } - } - return null; - } - - @Override - public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { - String item = parent.getItemAtPosition(position).toString(); - - mBucket = TIME_UNIT_MAP.get(item); - } - - @Override - public void onNothingSelected(AdapterView<?> parent) { - // Another interface callback - } - - private void onPerfAlarm() { - if (mPerfData != null) { - mPerfData.onAlarm(this); - } - // Piggy-back on that alarm to show the elapsed time. - long elapsedTimeMins = (long) Math.floor( - (SystemClock.elapsedRealtime() - mStartedTimeMillis) / 60 / 1000); - mReportText.setText("Loadtest in progress.\n" - + "num metrics =" + mNumMetrics - + "\nElapsed time = " + elapsedTimeMins + " min(s)"); - } - - private void onAlarm() { - Log.d(TAG, "ON ALARM"); - - // Set the next task. - scheduleNext(); - - // Do the work. - mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "StatsdLoadTest"); - mWakeLock.acquire(); - if (mPusher != null) { - mPusher.next(); - } - mWakeLock.release(); - mWakeLock = null; - } - - /** - * Schedules the next cycle of pushing atoms into logd. - */ - private void scheduleNext() { - Intent intent = new Intent(this, PusherAlarmReceiver.class); - intent.putExtra(TYPE, PUSH_ALARM); - mPushPendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0); - long nextTime = SystemClock.elapsedRealtime() + mPeriodSecs * 1000; - mAlarmMgr.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextTime, mPushPendingIntent); - } - - private synchronized void startLoadtest() { - if (mStarted) { - return; - } - - // Clean up the state. - stopLoadtest(); - - // Prepare to push a sequence of atoms to logd. - mPusher = new SequencePusher(mBurst, mPlacebo); - - // Create a config and push it to statsd. - if (!setConfig(mFactory.getConfig(mReplication, mBucket, mPlacebo, - mIncludeCountMetric, mIncludeDurationMetric, mIncludeEventMetric, - mIncludeValueMetric, mIncludeGaugeMetric))) { - return; - } - - // Remember to stop in the future. - Intent intent = new Intent(this, StopperAlarmReceiver.class); - intent.putExtra(TYPE, STOP); - mStopPendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0); - long nextTime = SystemClock.elapsedRealtime() + mDurationMins * 60 * 1000; - mAlarmMgr.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextTime, mStopPendingIntent); - - // Log atoms. - scheduleNext(); - - // Start tracking performance. - mPerfData = new PerfData(this, mPlacebo, mReplication, mBucket, mPeriodSecs, mBurst, - mIncludeCountMetric, mIncludeDurationMetric, mIncludeEventMetric, mIncludeValueMetric, - mIncludeGaugeMetric); - mPerfData.startRecording(this); - - mReportText.setText("Loadtest in progress.\nnum metrics =" + mNumMetrics); - mStartedTimeMillis = SystemClock.elapsedRealtime(); - - updateStarted(true); - } - - private synchronized void stopLoadtest() { - if (mPushPendingIntent != null) { - Log.d(TAG, "Canceling pre-existing push alarm"); - mAlarmMgr.cancel(mPushPendingIntent); - mPushPendingIntent = null; - } - if (mStopPendingIntent != null) { - Log.d(TAG, "Canceling pre-existing stop alarm"); - mAlarmMgr.cancel(mStopPendingIntent); - mStopPendingIntent = null; - } - if (mWakeLock != null) { - mWakeLock.release(); - mWakeLock = null; - } - if (mPerfData != null) { - mPerfData.stopRecording(this); - mPerfData.onDestroy(); - mPerfData = null; - } - - // Obtain the latest data and display it. - getData(); - - long elapsedTimeMins = (long) Math.floor( - (SystemClock.elapsedRealtime() - mStartedTimeMillis) / 60 / 1000); - mReportText.setText("Loadtest ended. Elapsed time = " + elapsedTimeMins + " min(s)"); - clearConfigs(); - updateStarted(false); - } - - private synchronized void updateStarted(boolean started) { - mStarted = started; - mStartStop.setBackgroundColor(started ? - Color.parseColor("#FFFF0000") : Color.parseColor("#FF00FF00")); - mStartStop.setText(started ? getString(R.string.stop) : getString(R.string.start)); - updateControlsEnabled(); - } - - private void updateControlsEnabled() { - mBurstText.setEnabled(!mPlacebo && !mStarted); - mReplicationText.setEnabled(!mPlacebo && !mStarted); - mPeriodText.setEnabled(!mStarted); - mBucketSpinner.setEnabled(!mPlacebo && !mStarted); - mDurationText.setEnabled(!mStarted); - mPlaceboCheckBox.setEnabled(!mStarted); - - boolean enabled = !mStarted && !mPlaceboCheckBox.isChecked(); - mCountMetricCheckBox.setEnabled(enabled); - mDurationMetricCheckBox.setEnabled(enabled); - mEventMetricCheckBox.setEnabled(enabled); - mValueMetricCheckBox.setEnabled(enabled); - mGaugeMetricCheckBox.setEnabled(enabled); - } - - private boolean statsdRunning() { - if (IStatsManager.Stub.asInterface(ServiceManager.getService("stats")) == null) { - Log.d(TAG, "Statsd not running"); - Toast.makeText(LoadtestActivity.this, "Statsd NOT running!", Toast.LENGTH_LONG).show(); - return false; - } - return true; - } - - private int sanitizeInt(int val, int min, int max) { - if (val > max) { - val = max; - } else if (val < min) { - val = min; - } - return val; - } - - private void clearConfigs() { - // TODO: Clear all configs instead of specific ones. - if (mStatsManager != null) { - if (mStarted) { - try { - mStatsManager.removeConfig(ConfigFactory.CONFIG_ID); - Log.d(TAG, "Removed loadtest statsd configs."); - } catch (StatsManager.StatsUnavailableException e) { - Log.e(TAG, "Failed to remove loadtest configs.", e); - } - } - } - } - - private boolean setConfig(ConfigFactory.ConfigMetadata configData) { - if (mStatsManager != null) { - try { - mStatsManager.addConfig(ConfigFactory.CONFIG_ID, configData.bytes); - mNumMetrics = configData.numMetrics; - Log.d(TAG, "Config pushed to statsd"); - return true; - } catch (StatsManager.StatsUnavailableException | IllegalArgumentException e) { - Log.e(TAG, "Failed to push config to statsd", e); - } - } - return false; - } - - private synchronized void setReplication(int replication) { - if (mStarted) { - return; - } - mReplicationText.setText("" + replication); - } - - private synchronized void setPeriodSecs(long periodSecs) { - mPeriodSecs = periodSecs; - } - - private synchronized void setBurst(int burst) { - mBurst = burst; - } - - private synchronized void setDurationMins(long durationMins) { - mDurationMins = durationMins; - } - - - private void handleFocus(EditText editText) { - /* - editText.setOnFocusChangeListener(new OnFocusChangeListener() { - @Override - public void onFocusChange(View v, boolean hasFocus) { - if (!hasFocus && editText.getText().toString().isEmpty()) { - editText.setText("-1", TextView.BufferType.EDITABLE); - } - } - }); - */ - } - - private void initBurst() { - mBurst = getResources().getInteger(R.integer.burst_default); - mBurstText = (EditText) findViewById(R.id.burst); - mBurstText.addTextChangedListener(new NumericalWatcher(mBurstText, 0, 1000) { - @Override - public void onNewValue(int newValue) { - setBurst(newValue); - } - }); - handleFocus(mBurstText); - } - - private void initReplication() { - mReplication = getResources().getInteger(R.integer.replication_default); - mReplicationText = (EditText) findViewById(R.id.replication); - mReplicationText.addTextChangedListener(new NumericalWatcher(mReplicationText, 1, 4096) { - @Override - public void onNewValue(int newValue) { - mReplication = newValue; - } - }); - handleFocus(mReplicationText); - } - - private void initBucket() { - String defaultValue = getResources().getString(R.string.bucket_default); - mBucket = TimeUnit.valueOf(defaultValue); - mBucketSpinner = (Spinner) findViewById(R.id.bucket_spinner); - - ArrayAdapter<String> dataAdapter = new ArrayAdapter<String>( - this, R.layout.spinner_item, TIME_UNIT_LABELS); - - mBucketSpinner.setAdapter(dataAdapter); - mBucketSpinner.setOnItemSelectedListener(this); - - for (String label : TIME_UNIT_MAP.keySet()) { - if (defaultValue.equals(TIME_UNIT_MAP.get(label).toString())) { - mBucketSpinner.setSelection(dataAdapter.getPosition(label)); - } - } - } - - private void initPeriod() { - mPeriodSecs = getResources().getInteger(R.integer.period_default); - mPeriodText = (EditText) findViewById(R.id.period); - mPeriodText.addTextChangedListener(new NumericalWatcher(mPeriodText, 1, 60) { - @Override - public void onNewValue(int newValue) { - setPeriodSecs(newValue); - } - }); - handleFocus(mPeriodText); - } - - private void initDuration() { - mDurationMins = getResources().getInteger(R.integer.duration_default); - mDurationText = (EditText) findViewById(R.id.duration); - mDurationText.addTextChangedListener(new NumericalWatcher(mDurationText, 1, 24 * 60) { - @Override - public void onNewValue(int newValue) { - setDurationMins(newValue); - } - }); - handleFocus(mDurationText); - } - - private void initPlacebo() { - mPlaceboCheckBox = findViewById(R.id.placebo); - mPlacebo = false; - mPlaceboCheckBox.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - mPlacebo = mPlaceboCheckBox.isChecked(); - updateControlsEnabled(); - } - }); - } - - private void initMetricWhitelist() { - mCountMetricCheckBox = findViewById(R.id.include_count); - mCountMetricCheckBox.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - mIncludeCountMetric = mCountMetricCheckBox.isChecked(); - } - }); - mDurationMetricCheckBox = findViewById(R.id.include_duration); - mDurationMetricCheckBox.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - mIncludeDurationMetric = mDurationMetricCheckBox.isChecked(); - } - }); - mEventMetricCheckBox = findViewById(R.id.include_event); - mEventMetricCheckBox.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - mIncludeEventMetric = mEventMetricCheckBox.isChecked(); - } - }); - mValueMetricCheckBox = findViewById(R.id.include_value); - mValueMetricCheckBox.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - mIncludeValueMetric = mValueMetricCheckBox.isChecked(); - } - }); - mGaugeMetricCheckBox = findViewById(R.id.include_gauge); - mGaugeMetricCheckBox.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - mIncludeGaugeMetric = mGaugeMetricCheckBox.isChecked(); - } - }); - - mIncludeCountMetric = mCountMetricCheckBox.isChecked(); - mIncludeDurationMetric = mDurationMetricCheckBox.isChecked(); - mIncludeEventMetric = mEventMetricCheckBox.isChecked(); - mIncludeValueMetric = mValueMetricCheckBox.isChecked(); - mIncludeGaugeMetric = mGaugeMetricCheckBox.isChecked(); - } -} diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/MemInfoParser.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/MemInfoParser.java deleted file mode 100644 index 01eebf2ad1cf..000000000000 --- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/MemInfoParser.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.statsd.loadtest; - -import android.annotation.Nullable; -import android.os.SystemClock; -import android.util.Log; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** Parses PSS info from dumpsys meminfo */ -public class MemInfoParser implements PerfParser { - - private static final Pattern LINE_PATTERN = - Pattern.compile("\\s*(\\d*,*\\d*)K:\\s(\\S*)\\s\\.*"); - private static final String PSS_BY_PROCESS = "Total PSS by process:"; - private static final String TAG = "loadtest.MemInfoParser"; - - private boolean mPssStarted; - private boolean mPssEnded; - private final long mStartTimeMillis; - - public MemInfoParser(long startTimeMillis) { - mStartTimeMillis = startTimeMillis; - } - - @Override - @Nullable - public String parseLine(String line) { - if (mPssEnded) { - return null; - } - if (!mPssStarted) { - if (line.contains(PSS_BY_PROCESS)) { - mPssStarted = true; - } - return null; - } - if (line.isEmpty()) { - mPssEnded = true; - return null; - } - Matcher lineMatcher = LINE_PATTERN.matcher(line); - if (lineMatcher.find() && lineMatcher.group(1) != null && lineMatcher.group(2) != null) { - if (lineMatcher.group(2).equals("statsd")) { - long timeDeltaMillis = SystemClock.elapsedRealtime() - mStartTimeMillis; - return timeDeltaMillis + "," + convertToPss(lineMatcher.group(1)); - } - } - return null; - } - - private String convertToPss(String input) { - return input.replace(",", ""); - } -} diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/MemoryDataRecorder.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/MemoryDataRecorder.java deleted file mode 100644 index af7bd4d35966..000000000000 --- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/MemoryDataRecorder.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.statsd.loadtest; - -import android.content.Context; -import android.os.SystemClock; -import android.util.Log; -import com.android.internal.os.StatsdConfigProto.TimeUnit; - -public class MemoryDataRecorder extends PerfDataRecorder { - private static final String TAG = "loadtest.MemoryDataDataRecorder"; - private static final String DUMP_FILENAME = TAG + "_dump.tmp"; - - private long mStartTimeMillis; - private StringBuilder mSb; - - public MemoryDataRecorder(boolean placebo, int replication, TimeUnit bucket, long periodSecs, - int burst, boolean includeCountMetric, boolean includeDurationMetric, - boolean includeEventMetric, boolean includeValueMetric, boolean includeGaugeMetric) { - super(placebo, replication, bucket, periodSecs, burst, includeCountMetric, - includeDurationMetric, includeEventMetric, includeValueMetric, includeGaugeMetric); - } - - @Override - public void startRecording(Context context) { - mStartTimeMillis = SystemClock.elapsedRealtime(); - mSb = new StringBuilder(); - } - - @Override - public void onAlarm(Context context) { - runDumpsysStats(context, DUMP_FILENAME, "meminfo"); - readDumpData(context, DUMP_FILENAME, new MemInfoParser(mStartTimeMillis), mSb); - } - - @Override - public void stopRecording(Context context) { - writeData(context, "meminfo_", "time,pss", mSb); - } -} diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/NumericalWatcher.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/NumericalWatcher.java deleted file mode 100644 index 555e6dd2d99d..000000000000 --- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/NumericalWatcher.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.statsd.loadtest; - -import android.text.Editable; -import android.text.TextWatcher; -import android.util.Log; -import android.widget.TextView; - -public abstract class NumericalWatcher implements TextWatcher { - - private static final String TAG = "loadtest.NumericalWatcher"; - - private final TextView mTextView; - private final int mMin; - private final int mMax; - private int currentValue = -1; - - public NumericalWatcher(TextView textView, int min, int max) { - mTextView = textView; - mMin = min; - mMax = max; - } - - public abstract void onNewValue(int newValue); - - @Override - final public void afterTextChanged(Editable editable) { - String s = mTextView.getText().toString(); - if (s.isEmpty()) { - return; - } - int unsanitized = Integer.parseInt(s); - int newValue = sanitize(unsanitized); - if (currentValue != newValue || unsanitized != newValue) { - currentValue = newValue; - editable.clear(); - editable.append(newValue + ""); - } - onNewValue(newValue); - } - - @Override - final public void beforeTextChanged(CharSequence s, int start, int count, int after) {} - - @Override - final public void onTextChanged(CharSequence s, int start, int before, int count) {} - - private int sanitize(int val) { - if (val > mMax) { - val = mMax; - } else if (val < mMin) { - val = mMin; - } - return val; - } -} diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfData.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfData.java deleted file mode 100644 index 7a01adedfaa4..000000000000 --- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfData.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.statsd.loadtest; - -import com.android.internal.os.StatsdConfigProto.TimeUnit; - -import android.annotation.Nullable; -import android.app.AlarmManager; -import android.app.PendingIntent; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.os.SystemClock; -import android.util.Log; - -import java.util.HashSet; -import java.util.Set; - -/** Prints some information about the device via Dumpsys in order to evaluate health metrics. */ -public class PerfData extends PerfDataRecorder { - - private static final String TAG = "loadtest.PerfData"; - - /** Polling period for performance snapshots like memory. */ - private static final long POLLING_PERIOD_MILLIS = 1 * 60 * 1000; - - public final static class PerfAlarmReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - Intent activityIntent = new Intent(context, LoadtestActivity.class); - activityIntent.putExtra(LoadtestActivity.TYPE, LoadtestActivity.PERF_ALARM); - context.startActivity(activityIntent); - } - } - - private AlarmManager mAlarmMgr; - - /** Used to periodically poll some dumpsys data. */ - private PendingIntent mPendingIntent; - - private final Set<PerfDataRecorder> mRecorders; - - public PerfData(LoadtestActivity loadtestActivity, boolean placebo, int replication, - TimeUnit bucket, long periodSecs, int burst, boolean includeCountMetric, - boolean includeDurationMetric, boolean includeEventMetric, boolean includeValueMetric, - boolean includeGaugeMetric) { - super(placebo, replication, bucket, periodSecs, burst, includeCountMetric, - includeDurationMetric, includeEventMetric, includeValueMetric, includeGaugeMetric); - mRecorders = new HashSet(); - mRecorders.add(new BatteryDataRecorder(placebo, replication, bucket, periodSecs, burst, - includeCountMetric, includeDurationMetric, includeEventMetric, includeValueMetric, - includeGaugeMetric)); - mRecorders.add(new MemoryDataRecorder(placebo, replication, bucket, periodSecs, burst, - includeCountMetric, includeDurationMetric, includeEventMetric, includeValueMetric, - includeGaugeMetric)); - mRecorders.add(new StatsdStatsRecorder(loadtestActivity, placebo, replication, bucket, - periodSecs, burst, includeCountMetric, includeDurationMetric, includeEventMetric, - includeValueMetric, includeGaugeMetric)); - mRecorders.add(new ValidationRecorder(loadtestActivity, placebo, replication, bucket, - periodSecs, burst, includeCountMetric, includeDurationMetric, includeEventMetric, - includeValueMetric, includeGaugeMetric)); - mAlarmMgr = (AlarmManager) loadtestActivity.getSystemService(Context.ALARM_SERVICE); - } - - public void onDestroy() { - if (mPendingIntent != null) { - mAlarmMgr.cancel(mPendingIntent); - mPendingIntent = null; - } - } - - @Override - public void startRecording(Context context) { - Intent intent = new Intent(context, PerfAlarmReceiver.class); - intent.putExtra(LoadtestActivity.TYPE, LoadtestActivity.PERF_ALARM); - mPendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0); - mAlarmMgr.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, -1 /* now */, - POLLING_PERIOD_MILLIS, mPendingIntent); - - for (PerfDataRecorder recorder : mRecorders) { - recorder.startRecording(context); - } - } - - @Override - public void onAlarm(Context context) { - for (PerfDataRecorder recorder : mRecorders) { - recorder.onAlarm(context); - } - } - - @Override - public void stopRecording(Context context) { - if (mPendingIntent != null) { - mAlarmMgr.cancel(mPendingIntent); - mPendingIntent = null; - } - - for (PerfDataRecorder recorder : mRecorders) { - recorder.stopRecording(context); - } - } -} diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfDataRecorder.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfDataRecorder.java deleted file mode 100644 index 8613ac1c4796..000000000000 --- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfDataRecorder.java +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.statsd.loadtest; - -import android.annotation.Nullable; -import android.content.Context; -import android.os.Environment; -import android.util.Log; -import android.os.Debug; - -import com.android.internal.os.StatsdConfigProto.TimeUnit; -import java.io.BufferedReader; -import java.io.Closeable; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.FileWriter; -import java.io.InputStreamReader; -import java.io.IOException; -import java.text.SimpleDateFormat; -import java.util.Date; - -public abstract class PerfDataRecorder { - private static final String TAG = "loadtest.PerfDataRecorder"; - - protected final String mTimeAsString; - protected final String mColumnSuffix; - - protected PerfDataRecorder(boolean placebo, int replication, TimeUnit bucket, long periodSecs, - int burst, boolean includeCountMetric, boolean includeDurationMetric, - boolean includeEventMetric, boolean includeValueMetric, boolean includeGaugeMetric) { - mTimeAsString = new SimpleDateFormat("YYYY_MM_dd_HH_mm_ss").format(new Date()); - mColumnSuffix = getColumnSuffix(placebo, replication, bucket, periodSecs, burst, - includeCountMetric, includeDurationMetric, includeEventMetric, includeValueMetric, - includeGaugeMetric); - } - - /** Starts recording performance data. */ - public abstract void startRecording(Context context); - - /** Called periodically. For the recorder to sample data, if needed. */ - public abstract void onAlarm(Context context); - - /** Stops recording performance data, and writes it to disk. */ - public abstract void stopRecording(Context context); - - /** Runs the dumpsys command. */ - protected void runDumpsysStats(Context context, String dumpFilename, String cmd, - String... args) { - boolean success = false; - // Call dumpsys Dump statistics to a file. - FileOutputStream fo = null; - try { - fo = context.openFileOutput(dumpFilename, Context.MODE_PRIVATE); - if (!Debug.dumpService(cmd, fo.getFD(), args)) { - Log.w(TAG, "Dumpsys failed."); - } - success = true; - } catch (IOException | SecurityException | NullPointerException e) { - // SecurityException may occur when trying to dump multi-user info. - // NPE can occur during dumpService (root cause unknown). - throw new RuntimeException(e); - } finally { - closeQuietly(fo); - } - } - - /** - * Reads a text file and parses each line, one by one. The result of the parsing is stored - * in the passed {@link StringBuffer}. - */ - protected void readDumpData(Context context, String dumpFilename, PerfParser parser, - StringBuilder sb) { - FileInputStream fi = null; - BufferedReader br = null; - try { - fi = context.openFileInput(dumpFilename); - br = new BufferedReader(new InputStreamReader(fi)); - String line = br.readLine(); - while (line != null) { - String recordLine = parser.parseLine(line); - if (recordLine != null) { - sb.append(recordLine).append('\n'); - } - line = br.readLine(); - } - } catch (IOException e) { - throw new RuntimeException(e); - } finally { - closeQuietly(br); - } - } - - /** Writes CSV data to a file. */ - protected void writeData(Context context, String filePrefix, String columnPrefix, - StringBuilder sb) { - File dataFile = new File(getStorageDir(), filePrefix + mTimeAsString + ".csv"); - - FileWriter writer = null; - try { - writer = new FileWriter(dataFile); - writer.append(columnPrefix + mColumnSuffix + "\n"); - writer.append(sb.toString()); - writer.flush(); - Log.d(TAG, "Finished writing data at " + dataFile.getAbsolutePath()); - } catch (IOException e) { - throw new RuntimeException(e); - } finally { - closeQuietly(writer); - } - } - - /** Gets the suffix to use in the column name for perf data. */ - private String getColumnSuffix(boolean placebo, int replication, TimeUnit bucket, - long periodSecs, int burst, boolean includeCountMetric, boolean includeDurationMetric, - boolean includeEventMetric, boolean includeValueMetric, boolean includeGaugeMetric) { - if (placebo) { - return "_placebo_p=" + periodSecs; - } - StringBuilder sb = new StringBuilder() - .append("_r=" + replication) - .append("_bkt=" + bucket) - .append("_p=" + periodSecs) - .append("_bst=" + burst) - .append("_m="); - if (includeCountMetric) { - sb.append("c"); - } - if (includeEventMetric) { - sb.append("e"); - } - if (includeDurationMetric) { - sb.append("d"); - } - if (includeGaugeMetric) { - sb.append("g"); - } - if (includeValueMetric) { - sb.append("v"); - } - return sb.toString(); - } - - private File getStorageDir() { - File file = new File(Environment.getExternalStoragePublicDirectory( - Environment.DIRECTORY_DOCUMENTS), "loadtest/" + mTimeAsString); - if (!file.mkdirs()) { - Log.e(TAG, "Directory not created"); - } - return file; - } - - private void closeQuietly(@Nullable Closeable c) { - if (c != null) { - try { - c.close(); - } catch (IOException ignore) { - } - } - } -} diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfParser.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfParser.java deleted file mode 100644 index e000918fa0f7..000000000000 --- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/PerfParser.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.statsd.loadtest; - -import android.annotation.Nullable; - -public interface PerfParser { - - /** - * Parses one line of the dumpsys output, and returns a string to write to the data file, - * or null if no string should be written. - */ - @Nullable String parseLine(String line); -} diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/SequencePusher.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/SequencePusher.java deleted file mode 100644 index 5dcce9acb401..000000000000 --- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/SequencePusher.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.statsd.loadtest; - -import android.util.Log; -import android.util.StatsLog; - -import java.util.ArrayList; -import java.util.List; - -/** - * Manages the pushing of atoms into logd for loadtesting. - * We rely on small number of pushed atoms, and a config with metrics based on those atoms. - * The atoms are: - * <ul> - * <li> BatteryLevelChanged - For EventMetric, CountMetric and GaugeMetric (no dimensions). - * <li> BleScanResultReceived - For CountMetric and ValueMetric, sliced by uid. - * <li> ChargingStateChanged - For DurationMetric (no dimension). - * <li> GpsScanStateChanged - For DurationMetric, sliced by uid. - * <li> ScreenStateChanged - For Conditions with no dimensions. - * <li> AudioStateChanged - For Conditions with dimensions (uid). - * </ul> - * The sequence is played over and over at a given frequency. - */ -public class SequencePusher { - private static final String TAG = "SequencePusher"; - - /** Some atoms are pushed in burst of {@code mBurst} events. */ - private final int mBurst; - - /** If this is true, we don't log anything in logd. */ - private final boolean mPlacebo; - - /** Current state in the automaton. */ - private int mCursor = 0; - - public SequencePusher(int burst, boolean placebo) { - mBurst = burst; - mPlacebo = placebo; - } - - /** - * Pushes the next atom to logd. - * This follows a small automaton which makes the right events and conditions overlap: - * (0) Push a burst of BatteryLevelChanged atoms. - * (1) Push a burst of BleScanResultReceived atoms. - * (2) Push ChargingStateChanged with BATTERY_STATUS_CHARGING once. - * (3) Push a burst of GpsScanStateChanged atoms with ON, with a different uid each time. - * (4) Push ChargingStateChanged with BATTERY_STATUS_NOT_CHARGING once. - * (5) Push a burst GpsScanStateChanged atoms with OFF, with a different uid each time. - * (6) Push ScreenStateChanged with STATE_ON once. - * (7) Push a burst of AudioStateChanged with ON, with a different uid each time. - * (8) Repeat steps (0)-(5). - * (9) Push ScreenStateChanged with STATE_OFF once. - * (10) Push a burst of AudioStateChanged with OFF, with a different uid each time. - * and repeat. - */ - public void next() { - Log.d(TAG, "Next step: " + mCursor); - if (mPlacebo) { - return; - } - switch (mCursor) { - case 0: - case 8: - for (int i = 0; i < mBurst; i++) { - StatsLog.write(StatsLog.BATTERY_LEVEL_CHANGED, 50 + i /* battery_level */); - } - break; - case 1: - case 9: - for (int i = 0; i < mBurst; i++) { - StatsLog.write(StatsLog.BLE_SCAN_RESULT_RECEIVED, i /* uid */, - 100 /* num_of_results */); - } - break; - case 2: - case 10: - StatsLog.write(StatsLog.CHARGING_STATE_CHANGED, - StatsLog.CHARGING_STATE_CHANGED__STATE__BATTERY_STATUS_CHARGING - /* charging_state */); - break; - case 3: - case 11: - for (int i = 0; i < mBurst; i++) { - StatsLog.write(StatsLog.GPS_SCAN_STATE_CHANGED, i /* uid */, - StatsLog.GPS_SCAN_STATE_CHANGED__STATE__ON /* state */); - } - break; - case 4: - case 12: - StatsLog.write(StatsLog.CHARGING_STATE_CHANGED, - StatsLog.CHARGING_STATE_CHANGED__STATE__BATTERY_STATUS_NOT_CHARGING - /* charging_state */); - break; - case 5: - case 13: - for (int i = 0; i < mBurst; i++) { - StatsLog.write(StatsLog.GPS_SCAN_STATE_CHANGED, i /* uid */, - StatsLog.GPS_SCAN_STATE_CHANGED__STATE__OFF /* state */); - } - break; - case 6: - StatsLog.write(StatsLog.SCREEN_STATE_CHANGED, - StatsLog.SCREEN_STATE_CHANGED__STATE__DISPLAY_STATE_ON /* display_state */); - break; - case 7: - for (int i = 0; i < mBurst; i++) { - StatsLog.write(StatsLog.AUDIO_STATE_CHANGED, i /* uid */, - StatsLog.AUDIO_STATE_CHANGED__STATE__ON /* state */); - } - break; - case 14: - StatsLog.write(StatsLog.SCREEN_STATE_CHANGED, - StatsLog.SCREEN_STATE_CHANGED__STATE__DISPLAY_STATE_OFF /* display_state */); - break; - case 15: - for (int i = 0; i < mBurst; i++) { - StatsLog.write(StatsLog.AUDIO_STATE_CHANGED, i /* uid */, - StatsLog.AUDIO_STATE_CHANGED__STATE__OFF /* state */); - } - break; - default: - } - mCursor++; - if (mCursor > 15) { - mCursor = 0; - } - } - - /** - * Properly finishes in order to be close all conditions and durations. - */ - public void finish() { - // Screen goes back to off. This will ensure that conditions get back to false. - StatsLog.write(StatsLog.SCREEN_STATE_CHANGED, - StatsLog.SCREEN_STATE_CHANGED__STATE__DISPLAY_STATE_OFF /* display_state */); - for (int i = 0; i < mBurst; i++) { - StatsLog.write(StatsLog.AUDIO_STATE_CHANGED, i /* uid */, - StatsLog.AUDIO_STATE_CHANGED__STATE__OFF /* state */); - } - // Stop charging, to ensure the corresponding durations are closed. - StatsLog.write(StatsLog.CHARGING_STATE_CHANGED, - StatsLog.CHARGING_STATE_CHANGED__STATE__BATTERY_STATUS_NOT_CHARGING - /* charging_state */); - // Stop scanning GPS, to ensure the corresponding conditions get back to false. - for (int i = 0; i < mBurst; i++) { - StatsLog.write(StatsLog.GPS_SCAN_STATE_CHANGED, i /* uid */, - StatsLog.GPS_SCAN_STATE_CHANGED__STATE__OFF /* state */); - } - } -} diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/StatsdStatsRecorder.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/StatsdStatsRecorder.java deleted file mode 100644 index 3939e7e0b2fa..000000000000 --- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/StatsdStatsRecorder.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.statsd.loadtest; - -import android.content.Context; -import com.android.os.StatsLog.StatsdStatsReport; -import com.android.internal.os.StatsdConfigProto.TimeUnit; - -public class StatsdStatsRecorder extends PerfDataRecorder { - private static final String TAG = "loadtest.StatsdStatsRecorder"; - - private final LoadtestActivity mLoadtestActivity; - - public StatsdStatsRecorder(LoadtestActivity loadtestActivity, boolean placebo, int replication, - TimeUnit bucket, long periodSecs, int burst, boolean includeCountMetric, - boolean includeDurationMetric, boolean includeEventMetric, boolean includeValueMetric, - boolean includeGaugeMetric) { - super(placebo, replication, bucket, periodSecs, burst, includeCountMetric, - includeDurationMetric, includeEventMetric, includeValueMetric, includeGaugeMetric); - mLoadtestActivity = loadtestActivity; - } - - @Override - public void startRecording(Context context) { - // Nothing to do. - } - - @Override - public void onAlarm(Context context) { - // Nothing to do. - } - - @Override - public void stopRecording(Context context) { - StatsdStatsReport metadata = mLoadtestActivity.getMetadata(); - if (metadata != null) { - int numConfigs = metadata.getConfigStatsCount(); - StringBuilder sb = new StringBuilder(); - StatsdStatsReport.ConfigStats configStats = metadata.getConfigStats(numConfigs - 1); - sb.append("metric_count,") - .append(configStats.getMetricCount() + "\n") - .append("condition_count,") - .append(configStats.getConditionCount() + "\n") - .append("matcher_count,") - .append(configStats.getMatcherCount() + "\n"); - writeData(context, "statsdstats_", "stat,value", sb); - } - } -} diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ValidationRecorder.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ValidationRecorder.java deleted file mode 100644 index d9f0ca9d2461..000000000000 --- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ValidationRecorder.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.statsd.loadtest; - -import android.content.Context; -import android.util.Log; -import com.android.os.StatsLog.ConfigMetricsReport; -import com.android.os.StatsLog.EventMetricData; -import com.android.os.StatsLog.StatsLogReport; -import com.android.internal.os.StatsdConfigProto.TimeUnit; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Checks the correctness of the stats. - */ -public class ValidationRecorder extends PerfDataRecorder { - private static final String TAG = "loadtest.ValidationRecorder"; - - private final LoadtestActivity mLoadtestActivity; - - public ValidationRecorder(LoadtestActivity loadtestActivity, boolean placebo, int replication, - TimeUnit bucket, long periodSecs, int burst, boolean includeCountMetric, - boolean includeDurationMetric, boolean includeEventMetric, boolean includeValueMetric, - boolean includeGaugeMetric) { - super(placebo, replication, bucket, periodSecs, burst, includeCountMetric, - includeDurationMetric, includeEventMetric, includeValueMetric, includeGaugeMetric); - mLoadtestActivity = loadtestActivity; - } - - @Override - public void startRecording(Context context) { - // Nothing to do. - } - - @Override - public void onAlarm(Context context) { - validateData(); - } - - @Override - public void stopRecording(Context context) { - validateData(); - } - - private void validateData() { - // The code below is commented out because it calls getData, which has the side-effect - // of clearing statsd's data buffer. - /* - List<ConfigMetricsReport> reports = mLoadtestActivity.getData(); - if (reports != null) { - Log.d(TAG, "GOT DATA"); - for (ConfigMetricsReport report : reports) { - for (StatsLogReport logReport : report.getMetricsList()) { - if (!logReport.hasMetricId()) { - Log.e(TAG, "Metric missing name."); - } - } - } - } - */ - } - - private void validateEventBatteryLevelChanges(StatsLogReport logReport) { - Log.d(TAG, "Validating " + logReport.getMetricId()); - if (logReport.hasEventMetrics()) { - Log.d(TAG, "Num events captured: " + logReport.getEventMetrics().getDataCount()); - for (EventMetricData data : logReport.getEventMetrics().getDataList()) { - Log.d(TAG, " Event : " + data.getAtom()); - } - } else { - Log.d(TAG, "Metric is invalid"); - } - } - - private void validateEventBatteryLevelChangesWhileScreenIsOn(StatsLogReport logReport) { - Log.d(TAG, "Validating " + logReport.getMetricId()); - } -} diff --git a/config/boot-profile.txt b/config/boot-profile.txt new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/config/boot-profile.txt 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..ffba11520856 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. @@ -751,8 +777,8 @@ public abstract class AccessibilityService extends Service { callback, handler); mGestureStatusCallbackInfos.put(mGestureStatusCallbackSequence, callbackInfo); } - connection.sendGesture(mGestureStatusCallbackSequence, - new ParceledListSlice<>(steps)); + connection.dispatchGesture(mGestureStatusCallbackSequence, + new ParceledListSlice<>(steps), gesture.getDisplayId()); } } catch (RemoteException re) { throw new RuntimeException(re); diff --git a/core/java/android/accessibilityservice/GestureDescription.java b/core/java/android/accessibilityservice/GestureDescription.java index a3e7ad5da9b1..3b79d217c513 100644 --- a/core/java/android/accessibilityservice/GestureDescription.java +++ b/core/java/android/accessibilityservice/GestureDescription.java @@ -23,6 +23,7 @@ import android.graphics.PathMeasure; import android.graphics.RectF; import android.os.Parcel; import android.os.Parcelable; +import android.view.Display; import com.android.internal.util.Preconditions; @@ -33,7 +34,7 @@ import java.util.List; * Accessibility services with the * {@link android.R.styleable#AccessibilityService_canPerformGestures} property can dispatch * gestures. This class describes those gestures. Gestures are made up of one or more strokes. - * Gestures are immutable once built. + * Gestures are immutable once built and will be dispatched to the specified display. * <p> * Spatial dimensions throughout are in screen pixels. Time is measured in milliseconds. */ @@ -48,6 +49,7 @@ public final class GestureDescription { private final List<StrokeDescription> mStrokes = new ArrayList<>(); private final float[] mTempPos = new float[2]; + private final int mDisplayId; /** * Get the upper limit for the number of strokes a gesture may contain. @@ -67,10 +69,17 @@ public final class GestureDescription { return MAX_GESTURE_DURATION_MS; } - private GestureDescription() {} + private GestureDescription() { + this(new ArrayList<>()); + } private GestureDescription(List<StrokeDescription> strokes) { + this(strokes, Display.DEFAULT_DISPLAY); + } + + private GestureDescription(List<StrokeDescription> strokes, int displayId) { mStrokes.addAll(strokes); + mDisplayId = displayId; } /** @@ -94,6 +103,16 @@ public final class GestureDescription { } /** + * Returns the ID of the display this gesture is sent on, for use with + * {@link android.hardware.display.DisplayManager#getDisplay(int)}. + * + * @return The logical display id. + */ + public int getDisplayId() { + return mDisplayId; + } + + /** * Return the smallest key point (where a path starts or ends) that is at least a specified * offset * @param offset the minimum start time @@ -160,9 +179,10 @@ public final class GestureDescription { public static class Builder { private final List<StrokeDescription> mStrokes = new ArrayList<>(); + private int mDisplayId = Display.DEFAULT_DISPLAY; /** - * Add a stroke to the gesture description. Up to + * Adds a stroke to the gesture description. Up to * {@link GestureDescription#getMaxStrokeCount()} paths may be * added to a gesture, and the total gesture duration (earliest path start time to latest * path end time) may not exceed {@link GestureDescription#getMaxGestureDuration()}. @@ -187,11 +207,23 @@ public final class GestureDescription { return this; } + /** + * Sets the id of the display to dispatch gestures. + * + * @param displayId The logical display id + * + * @return this + */ + public @NonNull Builder setDisplayId(int displayId) { + mDisplayId = displayId; + return this; + } + public GestureDescription build() { if (mStrokes.size() == 0) { throw new IllegalStateException("Gestures must have at least one stroke"); } - return new GestureDescription(mStrokes); + return new GestureDescription(mStrokes, mDisplayId); } } diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl index 8c38fe4aaf6f..1ca07dd6ed23 100644 --- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl +++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl @@ -95,5 +95,9 @@ interface IAccessibilityServiceConnection { void sendGesture(int sequence, in ParceledListSlice gestureSteps); + void dispatchGesture(int sequence, in ParceledListSlice gestureSteps, int displayId); + boolean isFingerprintGestureDetectionAvailable(); + + IBinder getOverlayWindowToken(int displayid); } diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 91b98c71a613..cb99a3aa11c6 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -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/ActivityView.java b/core/java/android/app/ActivityView.java index 755f0476d9ba..415ec64dc126 100644 --- a/core/java/android/app/ActivityView.java +++ b/core/java/android/app/ActivityView.java @@ -386,9 +386,15 @@ public class ActivityView extends ViewGroup { // Also report this geometry information to InputMethodManagerService. // TODO(b/115693908): Unify this logic into the above WMS-based one. + // TODO(b/138175283): Address the location update when the host of this view is + // moving. final Matrix matrix = new Matrix(); + final int[] locationOnScreen = new int[2]; + getLocationOnScreen(locationOnScreen); + final int dx = locationOnScreen[0]; + final int dy = locationOnScreen[1]; matrix.set(getMatrix()); - matrix.postTranslate(x, y); + matrix.postTranslate(dx, dy); mContext.getSystemService(InputMethodManager.class) .reportActivityView(displayId, matrix); } 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/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl index 2957309094f6..47b784b3eaaf 100644 --- a/core/java/android/app/IActivityTaskManager.aidl +++ b/core/java/android/app/IActivityTaskManager.aidl @@ -153,6 +153,7 @@ interface IActivityTaskManager { void moveTaskToFront(in IApplicationThread app, in String callingPackage, int task, int flags, in Bundle options); int getTaskForActivity(in IBinder token, in boolean onlyRoot); + /** Finish all activities that were started for result from the specified activity. */ void finishSubActivity(in IBinder token, in String resultWho, int requestCode); ParceledListSlice getRecentTasks(int maxNum, int flags, int userId); boolean willActivityBeVisible(in IBinder token); diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index ccea964b1213..372eab259f4c 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -5437,10 +5437,11 @@ public class Notification implements Parcelable /** * Construct a RemoteViews for the display in public contexts like on the lockscreen. * + * @param isLowPriority is this notification low priority * @hide */ @UnsupportedAppUsage - public RemoteViews makePublicContentView() { + public RemoteViews makePublicContentView(boolean isLowPriority) { if (mN.publicVersion != null) { final Builder builder = recoverBuilder(mContext, mN.publicVersion); return builder.createContentView(); @@ -5467,7 +5468,11 @@ public class Notification implements Parcelable } mN.extras = publicExtras; RemoteViews view; - view = makeNotificationHeader(); + StandardTemplateParams params = mParams.reset().fillTextsFrom(this); + if (isLowPriority) { + params.forceDefaultColor(); + } + view = makeNotificationHeader(params); view.setBoolean(R.id.notification_header, "setExpandOnlyOnButton", true); mN.extras = savedBundle; mN.mLargeIcon = largeIcon; @@ -8101,8 +8106,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/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/bluetooth/BluetoothHeadsetClient.java b/core/java/android/bluetooth/BluetoothHeadsetClient.java index 05833b5f571d..5d00f09501b0 100644 --- a/core/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/core/java/android/bluetooth/BluetoothHeadsetClient.java @@ -126,6 +126,17 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { "android.bluetooth.headsetclient.profile.action.RESULT"; /** + * Intent that notifies about vendor specific event arrival. Events not defined in + * HFP spec will be matched with supported vendor event list and this intent will + * be broadcasted upon a match. Supported vendor events are of format of + * of "+eventCode" or "+eventCode=xxxx" or "+eventCode:=xxxx". + * Vendor event can be a response to an vendor specific command or unsolicited. + * + */ + public static final String ACTION_VENDOR_SPECIFIC_HEADSETCLIENT_EVENT = + "android.bluetooth.headsetclient.profile.action.VENDOR_SPECIFIC_EVENT"; + + /** * Intent that notifies about the number attached to the last voice tag * recorded on AG. * @@ -243,6 +254,28 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { public static final String EXTRA_CME_CODE = "android.bluetooth.headsetclient.extra.CME_CODE"; + /** + * Extra for VENDOR_SPECIFIC_HEADSETCLIENT_EVENT intent that + * indicates vendor ID. + */ + public static final String EXTRA_VENDOR_ID = + "android.bluetooth.headsetclient.extra.VENDOR_ID"; + + /** + * Extra for VENDOR_SPECIFIC_HEADSETCLIENT_EVENT intent that + * indicates vendor event code. + */ + public static final String EXTRA_VENDOR_EVENT_CODE = + "android.bluetooth.headsetclient.extra.VENDOR_EVENT_CODE"; + + /** + * Extra for VENDOR_SPECIFIC_HEADSETCLIENT_EVENT intent that + * contains full vendor event including event code and full arguments. + */ + public static final String EXTRA_VENDOR_EVENT_FULL_ARGS = + "android.bluetooth.headsetclient.extra.VENDOR_EVENT_FULL_ARGS"; + + /* Extras for AG_FEATURES, extras type is boolean */ // TODO verify if all of those are actually useful /** @@ -588,6 +621,31 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { } /** + * Send vendor specific AT command. + * + * @param device remote device + * @param vendorId vendor number by Bluetooth SIG + * @param atCommand command to be sent. It start with + prefix and only one command at one time. + * @return <code>true</code> if command has been issued successfully; <code>false</code> + * otherwise. + */ + public boolean sendVendorAtCommand(BluetoothDevice device, int vendorId, + String atCommand) { + if (DBG) log("sendVendorSpecificCommand()"); + final IBluetoothHeadsetClient service = + getService(); + if (service != null && isEnabled() && isValidDevice(device)) { + try { + return service.sendVendorAtCommand(device, vendorId, atCommand); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + } + } + if (service == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + + /** * Stops voice recognition. * * @param device remote device 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/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/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..895eba6cdec2 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -2044,6 +2044,30 @@ public abstract class PackageManager { /** * Feature for {@link #getSystemAvailableFeatures} and + * {@link #hasSystemFeature}: The device supports Open Mobile API capable UICC-based secure + * elements. + */ + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_SE_OMAPI_UICC = "android.hardware.se.omapi.uicc"; + + /** + * Feature for {@link #getSystemAvailableFeatures} and + * {@link #hasSystemFeature}: The device supports Open Mobile API capable eSE-based secure + * elements. + */ + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_SE_OMAPI_ESE = "android.hardware.se.omapi.ese"; + + /** + * Feature for {@link #getSystemAvailableFeatures} and + * {@link #hasSystemFeature}: The device supports Open Mobile API capable SD-based secure + * elements. + */ + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_SE_OMAPI_SD = "android.hardware.se.omapi.sd"; + + /** + * Feature for {@link #getSystemAvailableFeatures} and * {@link #hasSystemFeature}: The device supports the OpenGL ES * <a href="http://www.khronos.org/registry/gles/extensions/ANDROID/ANDROID_extension_pack_es31a.txt"> * Android Extension Pack</a>. @@ -3571,7 +3595,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 +3603,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 +3644,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 +3654,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 +3882,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 +3894,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 +3904,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 +3973,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 +4022,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 +4033,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 +4049,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 +4060,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 +4080,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 +4101,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 +4188,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 +4204,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 +4242,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 +4258,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 +4268,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 42f5e0faf64f..a4933ef1703f 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -66,6 +66,7 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; +import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.FileUtils; @@ -254,6 +255,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 { @@ -2443,6 +2446,8 @@ public class PackageParser { mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; return null; + } else if (tagName.equals("queries")) { + parseQueries(pkg, res, parser, flags, outError); } else { Slog.w(TAG, "Unknown element under <manifest>: " + parser.getName() + " at " + mArchiveSourcePath + " " @@ -3536,6 +3541,9 @@ public class PackageParser { owner.mRequiredAccountType = requiredAccountType; } + owner.mForceQueryable = + sa.getBoolean(R.styleable.AndroidManifestApplication_forceQueryable, false); + if (sa.getBoolean( com.android.internal.R.styleable.AndroidManifestApplication_debuggable, false)) { @@ -3951,7 +3959,6 @@ public class PackageParser { ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PROFILEABLE_BY_SHELL; } XmlUtils.skipCurrentTag(parser); - } else { if (!RIGID_PARSER) { Slog.w(TAG, "Unknown element under <application>: " + tagName @@ -3998,6 +4005,67 @@ public class PackageParser { return true; } + private boolean parseQueries(Package owner, Resources res, XmlResourceParser parser, int flags, + String[] outError) + throws IOException, XmlPullParserException { + + final int outerDepth = parser.getDepth(); + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG + || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + if (parser.getName().equals("intent")) { + QueriesIntentInfo intentInfo = new QueriesIntentInfo(); + if (!parseIntent(res, parser, true /*allowGlobs*/, true /*allowAutoVerify*/, + intentInfo, outError)) { + return false; + } + Intent intent = new Intent(); + if (intentInfo.countActions() != 1) { + outError[0] = "intent tags must contain exactly one action."; + return false; + } + intent.setAction(intentInfo.getAction(0)); + for (int i = 0, max = intentInfo.countCategories(); i < max; i++) { + intent.addCategory(intentInfo.getCategory(i)); + } + Uri data = null; + String dataType = null; + if (intentInfo.countDataTypes() > 1) { + outError[0] = "intent tag may have at most one data type."; + return false; + } + if (intentInfo.countDataSchemes() > 1) { + outError[0] = "intent tag may have at most one data scheme."; + return false; + } + if (intentInfo.countDataTypes() == 1) { + data = Uri.fromParts(intentInfo.getDataType(0), "", null); + } + if (intentInfo.countDataSchemes() == 1) { + dataType = intentInfo.getDataScheme(0); + } + intent.setDataAndType(data, dataType); + owner.mQueriesIntents = ArrayUtils.add(owner.mQueriesIntents, intent); + } else if (parser.getName().equals("package")) { + final TypedArray sa = res.obtainAttributes(parser, + com.android.internal.R.styleable.AndroidManifestQueriesPackage); + final String packageName = + sa.getString(R.styleable.AndroidManifestQueriesPackage_name); + if (TextUtils.isEmpty(packageName)) { + outError[0] = "Package name is missing from package tag."; + return false; + } + owner.mQueriesPackages = + ArrayUtils.add(owner.mQueriesPackages, packageName.intern()); + } + } + return true; + } + /** * Check if one of the IntentFilter as both actions DEFAULT / VIEW and a HTTP/HTTPS data URI */ @@ -6512,6 +6580,9 @@ public class PackageParser { // The major version code declared for this package. public int mVersionCodeMajor; + // Whether the package declares that it should be queryable by all normal apps on device. + public boolean mForceQueryable; + // Return long containing mVersionCode and mVersionCodeMajor. public long getLongVersionCode() { return PackageInfo.composeLongVersionCode(mVersionCodeMajor, mVersionCode); @@ -6617,6 +6688,9 @@ public class PackageParser { /** Whether or not the package is a stub and must be replaced by the full version. */ public boolean isStub; + public ArrayList<String> mQueriesPackages; + public ArrayList<Intent> mQueriesIntents; + @UnsupportedAppUsage public Package(String packageName) { this.packageName = packageName; @@ -7120,6 +7194,9 @@ public class PackageParser { use32bitAbi = (dest.readInt() == 1); restrictUpdateHash = dest.createByteArray(); visibleToInstantApps = dest.readInt() == 1; + mForceQueryable = dest.readBoolean(); + mQueriesIntents = dest.createTypedArrayList(Intent.CREATOR); + mQueriesPackages = dest.createStringArrayList(); } private static void internStringArrayList(List<String> list) { @@ -7245,6 +7322,9 @@ public class PackageParser { dest.writeInt(use32bitAbi ? 1 : 0); dest.writeByteArray(restrictUpdateHash); dest.writeInt(visibleToInstantApps ? 1 : 0); + dest.writeBoolean(mForceQueryable); + dest.writeTypedList(mQueriesIntents); + dest.writeList(mQueriesPackages); } @@ -8255,6 +8335,8 @@ public class PackageParser { } } + public static final class QueriesIntentInfo extends IntentInfo {} + public final static class ActivityIntentInfo extends IntentInfo { @UnsupportedAppUsage public Activity activity; 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/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/hardware/radio/ProgramList.java b/core/java/android/hardware/radio/ProgramList.java index 623d5ec434fe..f4fd1b6fb75f 100644 --- a/core/java/android/hardware/radio/ProgramList.java +++ b/core/java/android/hardware/radio/ProgramList.java @@ -369,6 +369,33 @@ public final class ProgramList implements AutoCloseable { public boolean areModificationsExcluded() { return mExcludeModifications; } + + @Override + public int hashCode() { + return Objects.hash(mIdentifierTypes, mIdentifiers, mIncludeCategories, + mExcludeModifications); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof Filter)) return false; + Filter other = (Filter) obj; + + if (mIncludeCategories != other.mIncludeCategories) return false; + if (mExcludeModifications != other.mExcludeModifications) return false; + if (!Objects.equals(mIdentifierTypes, other.mIdentifierTypes)) return false; + if (!Objects.equals(mIdentifiers, other.mIdentifiers)) return false; + return true; + } + + @Override + public String toString() { + return "Filter [mIdentifierTypes=" + mIdentifierTypes + + ", mIdentifiers=" + mIdentifiers + + ", mIncludeCategories=" + mIncludeCategories + + ", mExcludeModifications=" + mExcludeModifications + "]"; + } } /** @@ -436,5 +463,24 @@ public final class ProgramList implements AutoCloseable { public @NonNull Set<ProgramSelector.Identifier> getRemoved() { return mRemoved; } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof Chunk)) return false; + Chunk other = (Chunk) obj; + + if (mPurge != other.mPurge) return false; + if (mComplete != other.mComplete) return false; + if (!Objects.equals(mModified, other.mModified)) return false; + if (!Objects.equals(mRemoved, other.mRemoved)) return false; + return true; + } + + @Override + public String toString() { + return "Chunk [mPurge=" + mPurge + ", mComplete=" + mComplete + + ", mModified=" + mModified + ", mRemoved=" + mRemoved + "]"; + } } } diff --git a/core/java/android/hardware/radio/RadioMetadata.java b/core/java/android/hardware/radio/RadioMetadata.java index b60c13631edd..c135c8a73abc 100644 --- a/core/java/android/hardware/radio/RadioMetadata.java +++ b/core/java/android/hardware/radio/RadioMetadata.java @@ -258,6 +258,11 @@ public final class RadioMetadata implements Parcelable { private final Bundle mBundle; @Override + public int hashCode() { + return mBundle.hashCode(); + } + + @Override public boolean equals(Object obj) { if (this == obj) return true; if (!(obj instanceof RadioMetadata)) return false; diff --git a/core/java/android/os/SharedMemory.java b/core/java/android/os/SharedMemory.java index 57a88012a31a..3e2ba3d3115f 100644 --- a/core/java/android/os/SharedMemory.java +++ b/core/java/android/os/SharedMemory.java @@ -62,7 +62,7 @@ public final class SharedMemory implements Parcelable, Closeable { mMemoryRegistration = new MemoryRegistration(mSize); mCleaner = Cleaner.create(mFileDescriptor, - new Closer(mFileDescriptor, mMemoryRegistration)); + new Closer(mFileDescriptor.getInt$(), mMemoryRegistration)); } /** @@ -290,10 +290,10 @@ public final class SharedMemory implements Parcelable, Closeable { * Cleaner that closes the FD */ private static final class Closer implements Runnable { - private FileDescriptor mFd; + private int mFd; private MemoryRegistration mMemoryReference; - private Closer(FileDescriptor fd, MemoryRegistration memoryReference) { + private Closer(int fd, MemoryRegistration memoryReference) { mFd = fd; mMemoryReference = memoryReference; } @@ -301,7 +301,9 @@ public final class SharedMemory implements Parcelable, Closeable { @Override public void run() { try { - Os.close(mFd); + FileDescriptor fd = new FileDescriptor(); + fd.setInt$(mFd); + Os.close(fd); } catch (ErrnoException e) { /* swallow error */ } mMemoryReference.release(); mMemoryReference = null; 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/provider/CalendarContract.java b/core/java/android/provider/CalendarContract.java index 2f68eb475e7a..d862d6022154 100644 --- a/core/java/android/provider/CalendarContract.java +++ b/core/java/android/provider/CalendarContract.java @@ -40,7 +40,7 @@ import android.database.DatabaseUtils; import android.net.Uri; import android.os.RemoteException; import android.text.format.DateUtils; -import android.text.format.Time; +import android.text.format.TimeMigrationUtils; import android.util.Log; import com.android.internal.util.Preconditions; @@ -1680,7 +1680,7 @@ public final class CalendarContract { * <h3>Writing to Events</h3> There are further restrictions on all Updates * and Inserts in the Events table: * <ul> - * <li>If allDay is set to 1 eventTimezone must be {@link Time#TIMEZONE_UTC} + * <li>If allDay is set to 1 eventTimezone must be "UTC" * and the time must correspond to a midnight boundary.</li> * <li>Exceptions are not allowed to recur. If rrule or rdate is not empty, * original_id and original_sync_id must be empty.</li> @@ -2608,9 +2608,7 @@ public final class CalendarContract { @UnsupportedAppUsage public static void scheduleAlarm(Context context, AlarmManager manager, long alarmTime) { if (DEBUG) { - Time time = new Time(); - time.set(alarmTime); - String schedTime = time.format(" %a, %b %d, %Y %I:%M%P"); + String schedTime = TimeMigrationUtils.formatMillisWithFixedFormat(alarmTime); Log.d(TAG, "Schedule alarm at " + alarmTime + " " + schedTime); } 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/text/format/TimeFormatter.java b/core/java/android/text/format/TimeFormatter.java index 5a14092c95a8..f7fd89d7d819 100644 --- a/core/java/android/text/format/TimeFormatter.java +++ b/core/java/android/text/format/TimeFormatter.java @@ -26,6 +26,9 @@ import libcore.icu.LocaleData; import libcore.util.ZoneInfo; import java.nio.CharBuffer; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; import java.util.Formatter; import java.util.Locale; import java.util.TimeZone; @@ -86,6 +89,59 @@ class TimeFormatter { } /** + * The implementation of {@link TimeMigrationUtils#formatMillisWithFixedFormat(long)} for + * 2038-safe formatting with the pattern "%Y-%m-%d %H:%M:%S" and including the historic + * incorrect digit localization behavior. + */ + String formatMillisWithFixedFormat(long timeMillis) { + // This method is deliberately not a general purpose replacement for + // format(String, ZoneInfo.WallTime, ZoneInfo): It hard-codes the pattern used; many of the + // pattern characters supported by Time.format() have unusual behavior which would make + // using java.time.format or similar packages difficult. It would be a lot of work to share + // behavior and many internal Android usecases can be covered by this common pattern + // behavior. + + // No need to worry about overflow / underflow: long millis is representable by Instant and + // LocalDateTime with room to spare. + Instant instant = Instant.ofEpochMilli(timeMillis); + + // Date/times are calculated in the current system default time zone. + LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); + + // You'd think it would be as simple as: + // DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss", locale); + // return formatter.format(localDateTime); + // but we retain Time's behavior around digits. + + StringBuilder stringBuilder = new StringBuilder(19); + + // This effectively uses the US locale because number localization is handled separately + // (see below). + stringBuilder.append(localDateTime.getYear()); + stringBuilder.append('-'); + append2DigitNumber(stringBuilder, localDateTime.getMonthValue()); + stringBuilder.append('-'); + append2DigitNumber(stringBuilder, localDateTime.getDayOfMonth()); + stringBuilder.append(' '); + append2DigitNumber(stringBuilder, localDateTime.getHour()); + stringBuilder.append(':'); + append2DigitNumber(stringBuilder, localDateTime.getMinute()); + stringBuilder.append(':'); + append2DigitNumber(stringBuilder, localDateTime.getSecond()); + + String result = stringBuilder.toString(); + return localizeDigits(result); + } + + /** Zero-pads value as needed to achieve a 2-digit number. */ + private static void append2DigitNumber(StringBuilder builder, int value) { + if (value < 10) { + builder.append('0'); + } + builder.append(value); + } + + /** * Format the specified {@code wallTime} using {@code pattern}. The output is returned. */ public String format(String pattern, ZoneInfo.WallTime wallTime, ZoneInfo zoneInfo) { @@ -99,12 +155,9 @@ class TimeFormatter { formatInternal(pattern, wallTime, zoneInfo); String result = stringBuilder.toString(); - // This behavior is the source of a bug since some formats are defined as being - // in ASCII and not localized. - if (localeData.zeroDigit != '0') { - result = localizeDigits(result); - } - return result; + // The localizeDigits() behavior is the source of a bug since some formats are defined + // as being in ASCII and not localized. + return localizeDigits(result); } finally { outputBuilder = null; numberFormatter = null; @@ -112,6 +165,10 @@ class TimeFormatter { } private String localizeDigits(String s) { + if (localeData.zeroDigit == '0') { + return s; + } + int length = s.length(); int offsetToLocalizedDigits = localeData.zeroDigit - '0'; StringBuilder result = new StringBuilder(length); diff --git a/core/java/android/text/format/TimeMigrationUtils.java b/core/java/android/text/format/TimeMigrationUtils.java new file mode 100644 index 000000000000..17bac8d67b26 --- /dev/null +++ b/core/java/android/text/format/TimeMigrationUtils.java @@ -0,0 +1,40 @@ +/* + * 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.text.format; + +/** + * Logic to ease migration away from {@link Time} in Android internal code. {@link Time} is + * afflicted by the Y2038 issue and deprecated. The methods here are intended to allow minimal + * changes to classes that use {@link Time} for common behavior. + * + * @hide + */ +public class TimeMigrationUtils { + + private TimeMigrationUtils() {} + + /** + * A Y2038-safe replacement for various users of the {@link Time#format(String)} with the + * pattern "%Y-%m-%d %H:%M:%S". Note, this method retains the unusual localization behavior + * originally implemented by Time, which can lead to non-latin numbers being produced if the + * default locale does not use latin numbers. + */ + public static String formatMillisWithFixedFormat(long timeMillis) { + // Delegate to TimeFormatter so that the unusual localization / threading behavior can be + // reused. + return new TimeFormatter().formatMillisWithFixedFormat(timeMillis); + } +} diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java index 5a9ab3811498..6aef5a5cf2a5 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -40,6 +40,7 @@ public class FeatureFlagUtils { public static final String HEARING_AID_SETTINGS = "settings_bluetooth_hearing_aid"; public static final String SCREENRECORD_LONG_PRESS = "settings_screenrecord_long_press"; public static final String DYNAMIC_SYSTEM = "settings_dynamic_system"; + public static final String SETTINGS_WIFITRACKER2 = "settings_wifitracker2"; private static final Map<String, String> DEFAULT_FLAGS; @@ -56,6 +57,7 @@ public class FeatureFlagUtils { DEFAULT_FLAGS.put(SCREENRECORD_LONG_PRESS, "false"); DEFAULT_FLAGS.put("settings_wifi_details_datausage_header", "false"); DEFAULT_FLAGS.put("settings_skip_direction_mutable", "true"); + DEFAULT_FLAGS.put(SETTINGS_WIFITRACKER2, "false"); } /** diff --git a/core/java/android/view/IRecentsAnimationController.aidl b/core/java/android/view/IRecentsAnimationController.aidl index 956161acd762..955be8d40c47 100644 --- a/core/java/android/view/IRecentsAnimationController.aidl +++ b/core/java/android/view/IRecentsAnimationController.aidl @@ -77,6 +77,21 @@ interface IRecentsAnimationController { void hideCurrentInputMethod(); /** + * This call is deprecated, use #setDeferCancelUntilNextTransition() instead + * TODO(138144750): Remove this method once there are no callers + * @deprecated + */ + void setCancelWithDeferredScreenshot(boolean screenshot); + + /** + * Clean up the screenshot of previous task which was created during recents animation that + * was cancelled by a stack order change. + * + * @see {@link IRecentsAnimationRunner#onAnimationCanceled} + */ + void cleanupScreenshot(); + + /** * Set a state for controller whether would like to cancel recents animations with deferred * task screenshot presentation. * @@ -86,22 +101,23 @@ interface IRecentsAnimationController { * screenshot, so that Launcher can still control the leash lifecycle & make the next app * transition animate smoothly without flickering. * - * @param screenshot When set {@code true}, means recents animation will be canceled when the - * next app launch. System will take previous task's screenshot when the next - * app transition starting, and skip previous task's animation. - * Set {@code false} means will not take screenshot & skip animation - * for previous task. + * @param defer When set {@code true}, means that the recents animation will defer canceling the + * animation when a stack order change is triggered until the subsequent app + * transition start and skip previous task's animation. + * When set to {@code false}, means that the recents animation will be canceled + * immediately when the stack order changes. + * @param screenshot When set {@code true}, means that the system will take previous task's + * screenshot and replace the contents of the leash with it when the next app + * transition starting. The runner must call #cleanupScreenshot() to end the + * recents animation. + * When set to {@code false}, means that the system will simply wait for the + * next app transition start to immediately cancel the recents animation. This + * can be useful when you want an immediate transition into a state where the + * task is shown in the home/recents activity (without waiting for a + * screenshot). * * @see #cleanupScreenshot() * @see IRecentsAnimationRunner#onCancelled */ - void setCancelWithDeferredScreenshot(boolean screenshot); - - /** - * Clean up the screenshot of previous task which was created during recents animation that - * was cancelled by a stack order change. - * - * @see {@link IRecentsAnimationRunner#onAnimationCanceled} - */ - void cleanupScreenshot(); + void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot); } diff --git a/core/java/android/view/IRecentsAnimationRunner.aidl b/core/java/android/view/IRecentsAnimationRunner.aidl index 9c652a8d990b..6cda60c80b7d 100644 --- a/core/java/android/view/IRecentsAnimationRunner.aidl +++ b/core/java/android/view/IRecentsAnimationRunner.aidl @@ -36,7 +36,7 @@ oneway interface IRecentsAnimationRunner { * @param deferredWithScreenshot If set to {@code true}, the contents of the task will be * replaced with a screenshot, such that the runner's leash is * still active. As soon as the runner doesn't need the leash - * anymore, it can call + * anymore, it must call * {@link IRecentsAnimationController#cleanupScreenshot). * * @see {@link RecentsAnimationController#cleanupScreenshot} diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 9cc6b287431e..1a6689838545 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -96,7 +96,7 @@ import java.util.concurrent.locks.ReentrantLock; * artifacts may occur on previous versions of the platform when its window is * positioned asynchronously.</p> */ -public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallback { +public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCallback { private static final String TAG = "SurfaceView"; private static final boolean DEBUG = false; private static final boolean DEBUG_POSITION = false; @@ -227,9 +227,7 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb mRequestedVisible = mViewVisibility && mWindowVisibility && !mWindowStopped; } - /** @hide */ - @Override - public void windowStopped(boolean stopped) { + private void setWindowStopped(boolean stopped) { mWindowStopped = stopped; updateRequestedVisibility(); updateSurface(); @@ -239,7 +237,7 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb protected void onAttachedToWindow() { super.onAttachedToWindow(); - getViewRootImpl().addWindowStoppedCallback(this); + getViewRootImpl().addSurfaceChangedCallback(this); mWindowStopped = false; mViewVisibility = getVisibility() == VISIBLE; @@ -459,7 +457,7 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb // the lifecycle. Instead of attaching it to a view, he/she can just pass // the SurfaceHolder forward, most live wallpapers do it. if (viewRoot != null) { - viewRoot.removeWindowStoppedCallback(this); + viewRoot.removeSurfaceChangedCallback(this); } mAttachedToWindow = false; @@ -1349,4 +1347,33 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb public SurfaceControl getSurfaceControl() { return mSurfaceControl; } + + /** + * Set window stopped to false and update surface visibility when ViewRootImpl surface is + * created. + * @hide + */ + @Override + public void surfaceCreated(SurfaceControl.Transaction t) { + setWindowStopped(false); + } + + /** + * Set window stopped to true and update surface visibility when ViewRootImpl surface is + * destroyed. + * @hide + */ + @Override + public void surfaceDestroyed() { + setWindowStopped(true); + } + + /** + * Called when a valid ViewRootImpl surface is replaced by another valid surface. + * @hide + */ + @Override + public void surfaceReplaced(SurfaceControl.Transaction t) { + + } } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 613ddcf577ed..354cc9645635 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -478,6 +478,12 @@ public final class ViewRootImpl implements ViewParent, private final SurfaceControl mSurfaceControl = new SurfaceControl(); /** + * Transaction object that can be used to synchronize child SurfaceControl changes with + * ViewRootImpl SurfaceControl changes by the server. The object is passed along with + * the SurfaceChangedCallback. + */ + private final Transaction mSurfaceChangedTransaction = new Transaction(); + /** * Child surface of {@code mSurface} with the same bounds as its parent, and crop bounds * are set to the parent's bounds adjusted for surface insets. This surface is created when * {@link ViewRootImpl#createBoundsSurface(int)} is called. @@ -1541,19 +1547,6 @@ public final class ViewRootImpl implements ViewParent, mIsAmbientMode = ambient; } - interface WindowStoppedCallback { - public void windowStopped(boolean stopped); - } - private final ArrayList<WindowStoppedCallback> mWindowStoppedCallbacks = new ArrayList<>(); - - void addWindowStoppedCallback(WindowStoppedCallback c) { - mWindowStoppedCallbacks.add(c); - } - - void removeWindowStoppedCallback(WindowStoppedCallback c) { - mWindowStoppedCallbacks.remove(c); - } - void setWindowStopped(boolean stopped) { checkThread(); if (mStopped != stopped) { @@ -1570,14 +1563,11 @@ public final class ViewRootImpl implements ViewParent, if (renderer != null) { renderer.destroyHardwareResources(mView); } - } - - for (int i = 0; i < mWindowStoppedCallbacks.size(); i++) { - mWindowStoppedCallbacks.get(i).windowStopped(stopped); - } - if (mStopped) { - if (mSurfaceHolder != null && mSurface.isValid()) { + if (mSurface.isValid()) { + if (mSurfaceHolder != null) { + notifyHolderSurfaceDestroyed(); + } notifySurfaceDestroyed(); } destroySurface(); @@ -1585,6 +1575,46 @@ public final class ViewRootImpl implements ViewParent, } } + /** Register callbacks to be notified when the ViewRootImpl surface changes. */ + interface SurfaceChangedCallback { + void surfaceCreated(Transaction t); + void surfaceReplaced(Transaction t); + void surfaceDestroyed(); + } + + private final ArrayList<SurfaceChangedCallback> mSurfaceChangedCallbacks = new ArrayList<>(); + void addSurfaceChangedCallback(SurfaceChangedCallback c) { + mSurfaceChangedCallbacks.add(c); + } + + void removeSurfaceChangedCallback(SurfaceChangedCallback c) { + mSurfaceChangedCallbacks.remove(c); + } + + private void notifySurfaceCreated() { + for (int i = 0; i < mSurfaceChangedCallbacks.size(); i++) { + mSurfaceChangedCallbacks.get(i).surfaceCreated(mSurfaceChangedTransaction); + } + } + + /** + * Notify listeners when the ViewRootImpl surface has been replaced. This callback will not be + * called if a new surface is created, only if the valid surface has been replaced with another + * valid surface. + */ + private void notifySurfaceReplaced() { + for (int i = 0; i < mSurfaceChangedCallbacks.size(); i++) { + mSurfaceChangedCallbacks.get(i).surfaceReplaced(mSurfaceChangedTransaction); + } + } + + private void notifySurfaceDestroyed() { + for (int i = 0; i < mSurfaceChangedCallbacks.size(); i++) { + mSurfaceChangedCallbacks.get(i).surfaceDestroyed(); + } + } + + /** * Creates a surface as a child of {@code mSurface} with the same bounds as its parent and * crop bounds set to the parent's bounds adjusted for surface insets. @@ -1984,7 +2014,7 @@ public final class ViewRootImpl implements ViewParent, mIsInTraversal = true; mWillDrawSoon = true; boolean windowSizeMayChange = false; - boolean surfaceChanged = false; + final boolean windowAttributesChanged = mWindowAttributesChanged; WindowManager.LayoutParams lp = mWindowAttributes; int desiredWindowWidth; @@ -2003,7 +2033,6 @@ public final class ViewRootImpl implements ViewParent, WindowManager.LayoutParams params = null; if (mWindowAttributesChanged) { mWindowAttributesChanged = false; - surfaceChanged = true; params = lp; } CompatibilityInfo compatibilityInfo = @@ -2244,6 +2273,10 @@ public final class ViewRootImpl implements ViewParent, final boolean isViewVisible = viewVisibility == View.VISIBLE; final boolean windowRelayoutWasForced = mForceNextWindowRelayout; boolean surfaceSizeChanged = false; + boolean surfaceCreated = false; + boolean surfaceDestroyed = false; + /* True if surface generation id changes. */ + boolean surfaceReplaced = false; if (mFirst || windowShouldResize || insetsChanged || viewVisibilityChanged || params != null || mForceNextWindowRelayout) { @@ -2325,10 +2358,14 @@ public final class ViewRootImpl implements ViewParent, final boolean outsetsChanged = !mPendingOutsets.equals(mAttachInfo.mOutsets); surfaceSizeChanged = (relayoutResult & WindowManagerGlobal.RELAYOUT_RES_SURFACE_RESIZED) != 0; - surfaceChanged |= surfaceSizeChanged; final boolean alwaysConsumeSystemBarsChanged = mPendingAlwaysConsumeSystemBars != mAttachInfo.mAlwaysConsumeSystemBars; final boolean colorModeChanged = hasColorModeChanged(lp.getColorMode()); + surfaceCreated = !hadSurface && mSurface.isValid(); + surfaceDestroyed = hadSurface && !mSurface.isValid(); + surfaceReplaced = (surfaceGenerationId != mSurface.getGenerationId()) + && mSurface.isValid(); + if (contentInsetsChanged) { mAttachInfo.mContentInsets.set(mPendingContentInsets); if (DEBUG_LAYOUT) Log.v(mTag, "Content insets changing to: " @@ -2383,33 +2420,32 @@ public final class ViewRootImpl implements ViewParent, lp.getColorMode() == ActivityInfo.COLOR_MODE_WIDE_COLOR_GAMUT); } - if (!hadSurface) { - if (mSurface.isValid()) { - // If we are creating a new surface, then we need to - // completely redraw it. - mFullRedrawNeeded = true; - mPreviousTransparentRegion.setEmpty(); + if (surfaceCreated) { + // If we are creating a new surface, then we need to + // completely redraw it. + mFullRedrawNeeded = true; + mPreviousTransparentRegion.setEmpty(); - // Only initialize up-front if transparent regions are not - // requested, otherwise defer to see if the entire window - // will be transparent - if (mAttachInfo.mThreadedRenderer != null) { - try { - hwInitialized = mAttachInfo.mThreadedRenderer.initialize( - mSurface); - if (hwInitialized && (host.mPrivateFlags - & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) == 0) { - // Don't pre-allocate if transparent regions - // are requested as they may not be needed - mAttachInfo.mThreadedRenderer.allocateBuffers(); - } - } catch (OutOfResourcesException e) { - handleOutOfResourcesException(e); - return; + // Only initialize up-front if transparent regions are not + // requested, otherwise defer to see if the entire window + // will be transparent + if (mAttachInfo.mThreadedRenderer != null) { + try { + hwInitialized = mAttachInfo.mThreadedRenderer.initialize( + mSurface); + if (hwInitialized && (host.mPrivateFlags + & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) == 0) { + // Don't pre-allocate if transparent regions + // are requested as they may not be needed + mAttachInfo.mThreadedRenderer.allocateBuffers(); } + } catch (OutOfResourcesException e) { + handleOutOfResourcesException(e); + return; } } - } else if (!mSurface.isValid()) { + notifySurfaceCreated(); + } else if (surfaceDestroyed) { // If the surface has been removed, then reset the scroll // positions. if (mLastScrolledFocus != null) { @@ -2427,10 +2463,12 @@ public final class ViewRootImpl implements ViewParent, mAttachInfo.mThreadedRenderer.isEnabled()) { mAttachInfo.mThreadedRenderer.destroy(); } - } else if ((surfaceGenerationId != mSurface.getGenerationId() + notifySurfaceDestroyed(); + } else if ((surfaceReplaced || surfaceSizeChanged || windowRelayoutWasForced || colorModeChanged) && mSurfaceHolder == null - && mAttachInfo.mThreadedRenderer != null) { + && mAttachInfo.mThreadedRenderer != null + && mSurface.isValid()) { mFullRedrawNeeded = true; try { // Need to do updateSurface (which leads to CanvasContext::setSurface and @@ -2448,6 +2486,10 @@ public final class ViewRootImpl implements ViewParent, } } + if (!surfaceCreated && surfaceReplaced) { + notifySurfaceReplaced(); + } + final boolean freeformResizing = (relayoutResult & WindowManagerGlobal.RELAYOUT_RES_DRAG_RESIZING_FREEFORM) != 0; final boolean dockedResizing = (relayoutResult @@ -2503,31 +2545,32 @@ public final class ViewRootImpl implements ViewParent, } mSurfaceHolder.setSurfaceFrameSize(mWidth, mHeight); mSurfaceHolder.mSurfaceLock.unlock(); - if (mSurface.isValid()) { - if (!hadSurface) { - mSurfaceHolder.ungetCallbacks(); - - mIsCreating = true; - SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); - if (callbacks != null) { - for (SurfaceHolder.Callback c : callbacks) { - c.surfaceCreated(mSurfaceHolder); - } + if (surfaceCreated) { + mSurfaceHolder.ungetCallbacks(); + + mIsCreating = true; + SurfaceHolder.Callback[] callbacks = mSurfaceHolder.getCallbacks(); + if (callbacks != null) { + for (SurfaceHolder.Callback c : callbacks) { + c.surfaceCreated(mSurfaceHolder); } - surfaceChanged = true; } - if (surfaceChanged || surfaceGenerationId != mSurface.getGenerationId()) { - SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks(); - if (callbacks != null) { - for (SurfaceHolder.Callback c : callbacks) { - c.surfaceChanged(mSurfaceHolder, lp.format, - mWidth, mHeight); - } + } + + if ((surfaceCreated || surfaceReplaced || surfaceSizeChanged + || windowAttributesChanged) && mSurface.isValid()) { + SurfaceHolder.Callback[] callbacks = mSurfaceHolder.getCallbacks(); + if (callbacks != null) { + for (SurfaceHolder.Callback c : callbacks) { + c.surfaceChanged(mSurfaceHolder, lp.format, + mWidth, mHeight); } } mIsCreating = false; - } else if (hadSurface) { - notifySurfaceDestroyed(); + } + + if (surfaceDestroyed) { + notifyHolderSurfaceDestroyed(); mSurfaceHolder.mSurfaceLock.lock(); try { mSurfaceHolder.mSurface = new Surface(); @@ -2841,7 +2884,7 @@ public final class ViewRootImpl implements ViewParent, } } - private void notifySurfaceDestroyed() { + private void notifyHolderSurfaceDestroyed() { mSurfaceHolder.ungetCallbacks(); SurfaceHolder.Callback[] callbacks = mSurfaceHolder.getCallbacks(); if (callbacks != null) { @@ -3447,7 +3490,7 @@ public final class ViewRootImpl implements ViewParent, private void reportDrawFinished() { try { mDrawsNeededToReport = 0; - mWindowSession.finishDrawing(mWindow, null /* postDrawTransaction */); + mWindowSession.finishDrawing(mWindow, mSurfaceChangedTransaction); } catch (RemoteException e) { // Have fun! } 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/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/android/widget/TabWidget.java b/core/java/android/widget/TabWidget.java index 49a0f39b3bad..4c67b080252a 100644 --- a/core/java/android/widget/TabWidget.java +++ b/core/java/android/widget/TabWidget.java @@ -433,7 +433,7 @@ public class TabWidget extends LinearLayout implements OnFocusChangeListener { * to the next tabbed view, in this example). * <p> * To move both the focus AND the selected tab at once, please use - * {@link #setCurrentTab}. Normally, the view logic takes care of + * {@link #focusCurrentTab}. Normally, the view logic takes care of * adjusting the focus, so unless you're circumventing the UI, * you'll probably just focus your interest here. * diff --git a/core/java/android/widget/ViewAnimator.java b/core/java/android/widget/ViewAnimator.java index 80ea363a6ad6..d36f3434e8c8 100644 --- a/core/java/android/widget/ViewAnimator.java +++ b/core/java/android/widget/ViewAnimator.java @@ -57,6 +57,9 @@ public class ViewAnimator extends FrameLayout { super(context, attrs); TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.ViewAnimator); + saveAttributeDataForStyleable(context, com.android.internal.R.styleable.ViewAnimator, + attrs, a, 0, 0); + int resource = a.getResourceId(com.android.internal.R.styleable.ViewAnimator_inAnimation, 0); if (resource > 0) { setInAnimation(context, resource); @@ -90,6 +93,8 @@ public class ViewAnimator extends FrameLayout { // attribute to override. final TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.FrameLayout); + saveAttributeDataForStyleable(context, com.android.internal.R.styleable.FrameLayout, + attrs, a, 0, 0); final boolean measureAllChildren = a.getBoolean( com.android.internal.R.styleable.FrameLayout_measureAllChildren, true); setMeasureAllChildren(measureAllChildren); 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/KernelWakelockReader.java b/core/java/com/android/internal/os/KernelWakelockReader.java index 3ac58e018a45..d53fadacb609 100644 --- a/core/java/com/android/internal/os/KernelWakelockReader.java +++ b/core/java/com/android/internal/os/KernelWakelockReader.java @@ -27,6 +27,7 @@ import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; +import java.io.File; import java.io.FileInputStream; import java.util.Iterator; @@ -38,6 +39,7 @@ public class KernelWakelockReader { private static int sKernelWakelockUpdateVersion = 0; private static final String sWakelockFile = "/proc/wakelocks"; private static final String sWakeupSourceFile = "/d/wakeup_sources"; + private static final String sSysClassWakeupDir = "/sys/class/wakeup"; private static final int[] PROC_WAKELOCKS_FORMAT = new int[] { Process.PROC_TAB_TERM|Process.PROC_OUT_STRING| // 0: name @@ -71,99 +73,108 @@ public class KernelWakelockReader { * @return the updated data. */ public final KernelWakelockStats readKernelWakelockStats(KernelWakelockStats staleStats) { - byte[] buffer = new byte[32*1024]; - int len = 0; - boolean wakeup_sources; - final long startTime = SystemClock.uptimeMillis(); + boolean useSystemSuspend = (new File(sSysClassWakeupDir)).exists(); - final int oldMask = StrictMode.allowThreadDiskReadsMask(); - try { - FileInputStream is; - try { - is = new FileInputStream(sWakelockFile); - wakeup_sources = false; - } catch (java.io.FileNotFoundException e) { + if (useSystemSuspend) { + WakeLockInfo[] wlStats = null; + if (mSuspendControlService == null) { try { - is = new FileInputStream(sWakeupSourceFile); - wakeup_sources = true; - } catch (java.io.FileNotFoundException e2) { - Slog.wtf(TAG, "neither " + sWakelockFile + " nor " + - sWakeupSourceFile + " exists"); + mSuspendControlService = ISuspendControlService.Stub.asInterface( + ServiceManager.getServiceOrThrow("suspend_control")); + } catch (ServiceNotFoundException e) { + Slog.wtf(TAG, "Required service suspend_control not available", e); return null; } } - int cnt; - while ((cnt = is.read(buffer, len, buffer.length - len)) > 0) { - len += cnt; + try { + wlStats = mSuspendControlService.getWakeLockStats(); + updateVersion(staleStats); + updateWakelockStats(wlStats, staleStats); + } catch (RemoteException e) { + Slog.wtf(TAG, "Failed to obtain wakelock stats from ISuspendControlService", e); + return null; } - is.close(); - } catch (java.io.IOException e) { - Slog.wtf(TAG, "failed to read kernel wakelocks", e); - return null; - } finally { - StrictMode.setThreadPolicyMask(oldMask); - } + return removeOldStats(staleStats); - final long readTime = SystemClock.uptimeMillis() - startTime; - if (readTime > 100) { - Slog.w(TAG, "Reading wakelock stats took " + readTime + "ms"); - } + } else { + byte[] buffer = new byte[32*1024]; + int len = 0; + boolean wakeup_sources; + final long startTime = SystemClock.uptimeMillis(); - if (len > 0) { - if (len >= buffer.length) { - Slog.wtf(TAG, "Kernel wake locks exceeded buffer size " + buffer.length); - } - int i; - for (i=0; i<len; i++) { - if (buffer[i] == '\0') { - len = i; - break; + final int oldMask = StrictMode.allowThreadDiskReadsMask(); + try { + FileInputStream is; + try { + is = new FileInputStream(sWakelockFile); + wakeup_sources = false; + } catch (java.io.FileNotFoundException e) { + try { + is = new FileInputStream(sWakeupSourceFile); + wakeup_sources = true; + } catch (java.io.FileNotFoundException e2) { + Slog.wtf(TAG, "neither " + sWakelockFile + " nor " + + sWakeupSourceFile + " exists"); + return null; + } } - } - } - updateVersion(staleStats); + int cnt; + while ((cnt = is.read(buffer, len, buffer.length - len)) > 0) { + len += cnt; + } - parseProcWakelocks(buffer, len, wakeup_sources, staleStats); + is.close(); + } catch (java.io.IOException e) { + Slog.wtf(TAG, "failed to read kernel wakelocks", e); + return null; + } finally { + StrictMode.setThreadPolicyMask(oldMask); + } - if (mSuspendControlService == null) { - try { - mSuspendControlService = ISuspendControlService.Stub.asInterface( - ServiceManager.getServiceOrThrow("suspend_control")); - } catch (ServiceNotFoundException e) { - Slog.wtf(TAG, "Required service suspend_control not available", e); + final long readTime = SystemClock.uptimeMillis() - startTime; + if (readTime > 100) { + Slog.w(TAG, "Reading wakelock stats took " + readTime + "ms"); } - } - try { - WakeLockInfo[] wlStats = mSuspendControlService.getWakeLockStats(); - getNativeWakelockStats(wlStats, staleStats); - } catch (RemoteException e) { - Slog.wtf(TAG, "Failed to obtain wakelock stats from ISuspendControlService", e); - } + if (len > 0) { + if (len >= buffer.length) { + Slog.wtf(TAG, "Kernel wake locks exceeded buffer size " + buffer.length); + } + int i; + for (i=0; i<len; i++) { + if (buffer[i] == '\0') { + len = i; + break; + } + } + } - return removeOldStats(staleStats); + updateVersion(staleStats); + parseProcWakelocks(buffer, len, wakeup_sources, staleStats); + return removeOldStats(staleStats); + } } /** - * Reads native wakelock stats from SystemSuspend and updates staleStats with the new - * information. + * Updates statleStats with stats from SystemSuspend. * @param staleStats Existing object to update. * @return the updated stats. */ @VisibleForTesting - public KernelWakelockStats getNativeWakelockStats(WakeLockInfo[] wlStats, + public KernelWakelockStats updateWakelockStats(WakeLockInfo[] wlStats, final KernelWakelockStats staleStats) { for (WakeLockInfo info : wlStats) { if (!staleStats.containsKey(info.name)) { staleStats.put(info.name, new KernelWakelockStats.Entry((int) info.activeCount, - info.totalTime, sKernelWakelockUpdateVersion)); + info.totalTime * 1000 /* ms to us */, sKernelWakelockUpdateVersion)); } else { KernelWakelockStats.Entry kwlStats = staleStats.get(info.name); kwlStats.mCount = (int) info.activeCount; - kwlStats.mTotalTime = info.totalTime; + // Convert milliseconds to microseconds + kwlStats.mTotalTime = info.totalTime * 1000; kwlStats.mVersion = sKernelWakelockUpdateVersion; } } diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java index 480ff4d528f0..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(); @@ -2003,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/jni/android/graphics/Picture.cpp b/core/jni/android/graphics/Picture.cpp index d29857d0cf12..d1b952130e88 100644 --- a/core/jni/android/graphics/Picture.cpp +++ b/core/jni/android/graphics/Picture.cpp @@ -100,8 +100,9 @@ void Picture::draw(Canvas* canvas) { this->endRecording(); SkASSERT(NULL != mPicture.get()); } - if (NULL != mPicture.get()) { - mPicture->playback(canvas->asSkCanvas()); + + if (mPicture) { + canvas->drawPicture(*mPicture); } } diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp index c3e7a36bef78..13e1dfa00160 100644 --- a/core/jni/android_os_Debug.cpp +++ b/core/jni/android_os_Debug.cpp @@ -259,6 +259,8 @@ static void load_maps(int pid, stats_t* stats, bool* foundSwapPss) which_heap = HEAP_NATIVE; } else if (base::StartsWith(name, "[stack")) { which_heap = HEAP_STACK; + } else if (base::StartsWith(name, "[anon:stack_and_tls:")) { + which_heap = HEAP_STACK; } else if (base::EndsWith(name, ".so")) { which_heap = HEAP_SO; is_swappable = true; diff --git a/core/proto/OWNERS b/core/proto/OWNERS index a3d4798647be..71e386011253 100644 --- a/core/proto/OWNERS +++ b/core/proto/OWNERS @@ -7,7 +7,7 @@ yaochen@google.com yro@google.com # Settings UI -per-file settings_enums.proto=zhfan@google.com +per-file settings_enums.proto=tmfang@google.com # Frameworks ogunwale@google.com diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto index c023438eecc2..3323095a6244 100644 --- a/core/proto/android/app/settings_enums.proto +++ b/core/proto/android/app/settings_enums.proto @@ -2401,4 +2401,11 @@ enum PageId { // OS: Q // Note: Gear icon is shown next to gesture navigation preference and opens sensitivity dialog SETTINGS_GESTURE_NAV_BACK_SENSITIVITY_DLG = 1748; + + // ---- End Q Constants, all Q constants go above this line ---- + // OPEN: Settings > Network & Internet > Wi-Fi > Click new network + // CATEGORY: SETTINGS + // OS: R + SETTINGS_WIFI_CONFIGURE_NETWORK = 1800; + } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index ebf5b9306c4a..aa440d348a57 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -727,7 +727,7 @@ <p>Protection level: dangerous <p> This is a hard restricted permission which cannot be held by an app until - the installer on record did not whitelist the permission. For more details see + the installer on record whitelists the permission. For more details see {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}. --> <permission android:name="android.permission.SEND_SMS" @@ -741,7 +741,7 @@ <p>Protection level: dangerous <p> This is a hard restricted permission which cannot be held by an app until - the installer on record did not whitelist the permission. For more details see + the installer on record whitelists the permission. For more details see {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}. --> <permission android:name="android.permission.RECEIVE_SMS" @@ -755,7 +755,7 @@ <p>Protection level: dangerous <p> This is a hard restricted permission which cannot be held by an app until - the installer on record did not whitelist the permission. For more details see + the installer on record whitelists the permission. For more details see {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}. --> <permission android:name="android.permission.READ_SMS" @@ -769,7 +769,7 @@ <p>Protection level: dangerous <p> This is a hard restricted permission which cannot be held by an app until - the installer on record did not whitelist the permission. For more details see + the installer on record whitelists the permission. For more details see {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}. --> <permission android:name="android.permission.RECEIVE_WAP_PUSH" @@ -783,7 +783,7 @@ <p>Protection level: dangerous <p> This is a hard restricted permission which cannot be held by an app until - the installer on record did not whitelist the permission. For more details see + the installer on record whitelists the permission. For more details see {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}. --> <permission android:name="android.permission.RECEIVE_MMS" @@ -806,7 +806,7 @@ <p>Protection level: dangerous <p> This is a hard restricted permission which cannot be held by an app until - the installer on record did not whitelist the permission. For more details see + the installer on record whitelists the permission. For more details see {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}. @hide Pending API council approval --> @@ -852,7 +852,7 @@ targetSdkVersion}</a> is 4 or higher. <p> This is a soft restricted permission which cannot be held by an app it its - full form until the installer on record did not whitelist the permission. + full form until the installer on record whitelists the permission. Specifically, if the permission is whitelisted the holder app can access external storage and the visual and aural media collections while if the permission is not whitelisted the holder app can only access to the visual @@ -860,7 +860,7 @@ meaning that the whitelist state can be specified only at install time and cannot change until the app is installed. For more details see {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}. - --> + <p>Protection level: dangerous --> <permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_sdcardRead" @@ -881,8 +881,9 @@ read/write files in your application-specific directories returned by {@link android.content.Context#getExternalFilesDir} and {@link android.content.Context#getExternalCacheDir}. - <p>Is this permission is not whitelisted for an app that targets an API level before + <p>If this permission is not whitelisted for an app that targets an API level before {@link android.os.Build.VERSION_CODES#Q} this permission cannot be granted to apps.</p> + <p>Protection level: dangerous</p> --> <permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:permissionGroup="android.permission-group.UNDEFINED" @@ -892,7 +893,8 @@ android:protectionLevel="dangerous" /> <!-- Allows an application to access any geographic locations persisted in the - user's shared collection. --> + user's shared collection. + <p>Protection level: dangerous --> <permission android:name="android.permission.ACCESS_MEDIA_LOCATION" android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_mediaLocation" @@ -949,7 +951,7 @@ <p>Protection level: dangerous <p> This is a hard restricted permission which cannot be held by an app until - the installer on record did not whitelist the permission. For more details see + the installer on record whitelists the permission. For more details see {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}. --> <permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" @@ -995,7 +997,7 @@ <p>Protection level: dangerous <p> This is a hard restricted permission which cannot be held by an app until - the installer on record did not whitelist the permission. For more details see + the installer on record whitelists the permission. For more details see {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}. --> <permission android:name="android.permission.READ_CALL_LOG" @@ -1019,7 +1021,7 @@ <p>Protection level: dangerous <p> This is a hard restricted permission which cannot be held by an app until - the installer on record did not whitelist the permission. For more details see + the installer on record whitelists the permission. For more details see {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}. --> <permission android:name="android.permission.WRITE_CALL_LOG" @@ -1035,7 +1037,7 @@ <p>Protection level: dangerous <p> This is a hard restricted permission which cannot be held by an app until - the installer on record did not whitelist the permission. For more details see + the installer on record whitelists the permission. For more details see {@link android.content.pm.PackageInstaller.SessionParams#setWhitelistedRestrictedPermissions(Set)}. @deprecated Applications should use {@link android.telecom.CallRedirectionService} instead @@ -1694,7 +1696,7 @@ <!-- Allows applications to pair bluetooth devices without user interaction, and to allow or disallow phonebook access or message access. - This is not available to third party applications. --> + <p>Not for use by third-party applications. --> <permission android:name="android.permission.BLUETOOTH_PRIVILEGED" android:protectionLevel="signature|privileged" /> @@ -2544,7 +2546,8 @@ android:protectionLevel="signature" /> <!-- Allows an application to modify the current configuration, such - as locale. --> + as locale. + <p>Protection level: signature|privileged|development --> <permission android:name="android.permission.CHANGE_CONFIGURATION" android:protectionLevel="signature|privileged|development" /> @@ -2843,7 +2846,8 @@ <!-- ==================================== --> <eat-comment /> - <!-- Allows access to the list of accounts in the Accounts Service. --> + <!-- Allows access to the list of accounts in the Accounts Service. + <p>Protection level: signature|privileged --> <permission android:name="android.permission.GET_ACCOUNTS_PRIVILEGED" android:protectionLevel="signature|privileged" /> @@ -3429,7 +3433,8 @@ android:protectionLevel="signature" /> <!-- Old permission for deleting an app's cache files, no longer used, - but signals for us to quietly ignore calls instead of throwing an exception. --> + but signals for us to quietly ignore calls instead of throwing an exception. + <p>Protection level: signature|privileged --> <permission android:name="android.permission.DELETE_CACHE_FILES" android:protectionLevel="signature|privileged" /> @@ -3792,7 +3797,8 @@ <!-- Allows an application to collect component usage statistics <p>Declaring the permission implies intention to use the API and the user of the - device can grant permission through the Settings application. --> + device can grant permission through the Settings application. + <p>Protection level: signature|privileged|development|appop --> <permission android:name="android.permission.PACKAGE_USAGE_STATS" android:protectionLevel="signature|privileged|development|appop" /> <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" /> @@ -3815,14 +3821,14 @@ <!-- Permission an application must hold in order to use {@link android.provider.Settings#ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS}. - This is a normal permission: an app requesting it will always be granted the - permission, without the user needing to approve or see it. --> + <p>Protection level: normal --> <permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" android:label="@string/permlab_requestIgnoreBatteryOptimizations" android:description="@string/permdesc_requestIgnoreBatteryOptimizations" android:protectionLevel="normal" /> - <!-- Allows an application to collect battery statistics --> + <!-- Allows an application to collect battery statistics + <p>Protection level: signature|privileged|development --> <permission android:name="android.permission.BATTERY_STATS" android:protectionLevel="signature|privileged|development" /> @@ -3852,7 +3858,8 @@ android:protectionLevel="signature" /> <!-- Must be required by a {@link android.widget.RemoteViewsService}, - to ensure that only the system can bind to it. --> + to ensure that only the system can bind to it. + <p>Protection level: signature|privileged --> <permission android:name="android.permission.BIND_REMOTEVIEWS" android:protectionLevel="signature|privileged" /> @@ -3894,7 +3901,8 @@ to the path in the provider where global search queries are performed. This permission can not be held by regular applications; it is used by applications to protect themselves from everyone else - besides global search. --> + besides global search. + <p>Protection level: signature|privileged --> <permission android:name="android.permission.GLOBAL_SEARCH" android:protectionLevel="signature|privileged" /> @@ -4433,7 +4441,8 @@ <permission android:name="android.permission.MODIFY_THEME_OVERLAY" android:protectionLevel="signature" /> - <!-- Allows an instant app to create foreground services. --> + <!-- Allows an instant app to create foreground services. + <p>Protection level: signature|development|instant|appop --> <permission android:name="android.permission.INSTANT_APP_FOREGROUND_SERVICE" android:protectionLevel="signature|development|instant|appop" /> @@ -4503,7 +4512,8 @@ <permission android:name="android.permission.MONITOR_DEFAULT_SMS_PACKAGE" android:protectionLevel="signature|privileged" /> - <!-- A subclass of {@link android.service.carrier.CarrierMessagingClientService} must be protected with this permission. --> + <!-- A subclass of {@link android.service.carrier.CarrierMessagingClientService} must be protected with this permission. + <p>Protection level: signature --> <permission android:name="android.permission.BIND_CARRIER_MESSAGING_CLIENT_SERVICE" android:protectionLevel="signature" /> @@ -4535,13 +4545,15 @@ <permission android:name="android.permission.GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS" android:protectionLevel="signature" /> - <!-- Allows financial apps to read filtered sms messages. --> + <!-- Allows financial apps to read filtered sms messages. + Protection level: signature|appop --> <permission android:name="android.permission.SMS_FINANCIAL_TRANSACTIONS" android:protectionLevel="signature|appop" /> <!-- Required for apps targeting {@link android.os.Build.VERSION_CODES#Q} that want to use {@link android.app.Notification.Builder#setFullScreenIntent notification full screen - intents}. --> + intents}. + <p>Protection level: normal --> <permission android:name="android.permission.USE_FULL_SCREEN_INTENT" android:protectionLevel="normal" /> @@ -4598,6 +4610,7 @@ android:supportsRtl="true" android:theme="@style/Theme.DeviceDefault.Light.DarkActionBar" android:defaultToDeviceProtectedStorage="true" + android:forceQueryable="true" android:directBootAware="true"> <activity android:name="com.android.internal.app.ChooserActivity" android:theme="@style/Theme.DeviceDefault.Resolver" @@ -4981,6 +4994,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..3ea8a778ba9b 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 @@ -1739,6 +1743,13 @@ - {@code true} for apps with targetSdkVersion < 29. --> <attr name="requestLegacyExternalStorage" format="boolean" /> + + <!-- If {@code true} this app declares that it should be visible to all other apps on + device, regardless of what they declare via the {@code queries} tags in their + manifest. + + The default value is {@code false}. --> + <attr name="forceQueryable" format="boolean" /> </declare-styleable> <!-- The <code>permission</code> tag declares a security permission that can be used to control access from other packages to specific components or @@ -1973,6 +1984,12 @@ <attr name="name" /> </declare-styleable> + <declare-styleable name="AndroidManifestQueries" parent="AndroidManifest" /> + <declare-styleable name="AndroidManifestQueriesPackage" parent="AndroidManifestQueries"> + <attr name="name" /> + </declare-styleable> + <declare-styleable name="AndroidManifestQueriesIntent" parent="AndroidManifestQueries" /> + <!-- The <code>static-library</code> tag declares that this apk is providing itself as a static shared library for other applications to use. Any app can declare such @@ -2473,6 +2490,7 @@ <!-- High dynamic range color mode. --> <enum name="hdr" value="2" /> </attr> + <attr name="forceQueryable" format="boolean" /> </declare-styleable> <!-- The <code>activity-alias</code> tag declares a new diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 3b1275354d1f..dce4bf3f1ed4 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1700,6 +1700,12 @@ <!-- Add packages here --> </string-array> + <string-array name="config_forceQueryablePackages" translatable="false"> + <item>com.android.settings</item> + <!-- Add packages here --> + </string-array> + + <!-- Component name of the default wallpaper. This will be ImageWallpaper if not specified --> <string name="default_wallpaper_component" translatable="false">@null</string> 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 c49092effd9b..123deeb421b7 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -786,6 +786,7 @@ <java-symbol type="string" name="widget_default_class_name" /> <java-symbol type="string" name="emergency_calls_only" /> <java-symbol type="array" name="config_ephemeralResolverPackage" /> + <java-symbol type="array" name="config_forceQueryablePackages" /> <java-symbol type="string" name="eventTypeAnniversary" /> <java-symbol type="string" name="eventTypeBirthday" /> <java-symbol type="string" name="eventTypeCustom" /> @@ -3802,6 +3803,7 @@ <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/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ProgramInfoCacheTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ProgramInfoCacheTest.java index b2254c5fe59e..eadf226b01ce 100644 --- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ProgramInfoCacheTest.java +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ProgramInfoCacheTest.java @@ -17,12 +17,10 @@ package com.android.server.broadcastradio.hal2; import static org.junit.Assert.*; -import android.hardware.broadcastradio.V2_0.ProgramInfo; import android.hardware.broadcastradio.V2_0.ProgramListChunk; import android.hardware.radio.ProgramList; import android.hardware.radio.ProgramSelector; import android.hardware.radio.RadioManager; -import android.hardware.radio.RadioMetadata; import android.test.suitebuilder.annotation.MediumTest; import androidx.test.runner.AndroidJUnit4; @@ -30,7 +28,6 @@ import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; -import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -45,55 +42,58 @@ public class ProgramInfoCacheTest { private final ProgramSelector.Identifier mAmFmIdentifier = new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY, 88500); - private final RadioManager.ProgramInfo mAmFmInfo = makeProgramInfo( + private final RadioManager.ProgramInfo mAmFmInfo = TestUtils.makeProgramInfo( ProgramSelector.PROGRAM_TYPE_FM, mAmFmIdentifier, 0); private final ProgramSelector.Identifier mRdsIdentifier = new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_RDS_PI, 15019); - private final RadioManager.ProgramInfo mRdsInfo = makeProgramInfo( + private final RadioManager.ProgramInfo mRdsInfo = TestUtils.makeProgramInfo( ProgramSelector.PROGRAM_TYPE_FM, mRdsIdentifier, 0); private final ProgramSelector.Identifier mDabEnsembleIdentifier = new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_DAB_ENSEMBLE, 1337); - private final RadioManager.ProgramInfo mDabEnsembleInfo = makeProgramInfo( + private final RadioManager.ProgramInfo mDabEnsembleInfo = TestUtils.makeProgramInfo( ProgramSelector.PROGRAM_TYPE_DAB, mDabEnsembleIdentifier, 0); private final ProgramSelector.Identifier mVendorCustomIdentifier = new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_VENDOR_START, 9001); - private final RadioManager.ProgramInfo mVendorCustomInfo = makeProgramInfo( + private final RadioManager.ProgramInfo mVendorCustomInfo = TestUtils.makeProgramInfo( ProgramSelector.PROGRAM_TYPE_VENDOR_START, mVendorCustomIdentifier, 0); // HAL-side ProgramInfoCache containing all of the above ProgramInfos. - private final ProgramInfoCache mAllProgramInfos = new ProgramInfoCache(null, mAmFmInfo, + private final ProgramInfoCache mAllProgramInfos = new ProgramInfoCache(null, true, mAmFmInfo, mRdsInfo, mDabEnsembleInfo, mVendorCustomInfo); @Test public void testUpdateFromHal() { - // First test a purging chunk. - ProgramInfoCache cache = new ProgramInfoCache(null, mAmFmInfo); + // First test updating an incomplete cache with a purging, complete chunk. + ProgramInfoCache cache = new ProgramInfoCache(null, false, mAmFmInfo); ProgramListChunk chunk = new ProgramListChunk(); chunk.purge = true; - chunk.modified.add(programInfoToHal(mRdsInfo)); - chunk.modified.add(programInfoToHal(mDabEnsembleInfo)); - cache.updateFromHalProgramListChunk(chunk); chunk.complete = true; + chunk.modified.add(TestUtils.programInfoToHal(mRdsInfo)); + chunk.modified.add(TestUtils.programInfoToHal(mDabEnsembleInfo)); + cache.updateFromHalProgramListChunk(chunk); assertTrue(cache.programInfosAreExactly(mRdsInfo, mDabEnsembleInfo)); + assertTrue(cache.isComplete()); - // Then test a non-purging chunk. + // Then test a non-purging, incomplete chunk. chunk.purge = false; + chunk.complete = false; chunk.modified.clear(); - RadioManager.ProgramInfo updatedRdsInfo = makeProgramInfo(ProgramSelector.PROGRAM_TYPE_FM, - mRdsIdentifier, 1); - chunk.modified.add(programInfoToHal(updatedRdsInfo)); - chunk.modified.add(programInfoToHal(mVendorCustomInfo)); + RadioManager.ProgramInfo updatedRdsInfo = TestUtils.makeProgramInfo( + ProgramSelector.PROGRAM_TYPE_FM, mRdsIdentifier, 1); + chunk.modified.add(TestUtils.programInfoToHal(updatedRdsInfo)); + chunk.modified.add(TestUtils.programInfoToHal(mVendorCustomInfo)); chunk.removed.add(Convert.programIdentifierToHal(mDabEnsembleIdentifier)); cache.updateFromHalProgramListChunk(chunk); assertTrue(cache.programInfosAreExactly(updatedRdsInfo, mVendorCustomInfo)); + assertFalse(cache.isComplete()); } @Test public void testNullFilter() { - ProgramInfoCache cache = new ProgramInfoCache(null); + ProgramInfoCache cache = new ProgramInfoCache(null, true); cache.filterAndUpdateFrom(mAllProgramInfos, false); assertTrue(cache.programInfosAreExactly(mAmFmInfo, mRdsInfo, mDabEnsembleInfo, mVendorCustomInfo)); @@ -140,11 +140,11 @@ public class ProgramInfoCacheTest { @Test public void testPurgeUpdateChunks() { - ProgramInfoCache cache = new ProgramInfoCache(null, mAmFmInfo); + ProgramInfoCache cache = new ProgramInfoCache(null, false, mAmFmInfo); List<ProgramList.Chunk> chunks = cache.filterAndUpdateFromInternal(mAllProgramInfos, true, 3, 3); assertEquals(2, chunks.size()); - verifyChunkListFlags(chunks, true); + verifyChunkListFlags(chunks, true, true); verifyChunkListModified(chunks, 3, mAmFmInfo, mRdsInfo, mDabEnsembleInfo, mVendorCustomInfo); verifyChunkListRemoved(chunks, 0); @@ -154,23 +154,26 @@ public class ProgramInfoCacheTest { public void testDeltaUpdateChunksModificationsIncluded() { // Create a cache with a filter that allows modifications, and set its contents to // mAmFmInfo, mRdsInfo, mDabEnsembleInfo, and mVendorCustomInfo. - ProgramInfoCache cache = new ProgramInfoCache(null, mAmFmInfo, mRdsInfo, mDabEnsembleInfo, - mVendorCustomInfo); + ProgramInfoCache cache = new ProgramInfoCache(null, true, mAmFmInfo, mRdsInfo, + mDabEnsembleInfo, mVendorCustomInfo); // Create a HAL cache that: + // - Is complete. // - Retains mAmFmInfo. // - Replaces mRdsInfo with updatedRdsInfo. // - Drops mDabEnsembleInfo and mVendorCustomInfo. // - Introduces a new SXM info. - RadioManager.ProgramInfo updatedRdsInfo = makeProgramInfo(ProgramSelector.PROGRAM_TYPE_FM, - mRdsIdentifier, 1); - RadioManager.ProgramInfo newSxmInfo = makeProgramInfo(ProgramSelector.PROGRAM_TYPE_SXM, + RadioManager.ProgramInfo updatedRdsInfo = TestUtils.makeProgramInfo( + ProgramSelector.PROGRAM_TYPE_FM, mRdsIdentifier, 1); + RadioManager.ProgramInfo newSxmInfo = TestUtils.makeProgramInfo( + ProgramSelector.PROGRAM_TYPE_SXM, new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_SXM_CHANNEL, 12345), 0); - ProgramInfoCache halCache = new ProgramInfoCache(null, mAmFmInfo, updatedRdsInfo, + ProgramInfoCache halCache = new ProgramInfoCache(null, true, mAmFmInfo, updatedRdsInfo, newSxmInfo); // Update the cache and verify: + // - The final chunk's complete flag is set. // - mAmFmInfo is retained and not reported in the chunks. // - updatedRdsInfo should appear as an update to mRdsInfo. // - newSxmInfo should appear as a new entry. @@ -178,7 +181,7 @@ public class ProgramInfoCacheTest { List<ProgramList.Chunk> chunks = cache.filterAndUpdateFromInternal(halCache, false, 5, 1); assertTrue(cache.programInfosAreExactly(mAmFmInfo, updatedRdsInfo, newSxmInfo)); assertEquals(2, chunks.size()); - verifyChunkListFlags(chunks, false); + verifyChunkListFlags(chunks, false, true); verifyChunkListModified(chunks, 5, updatedRdsInfo, newSxmInfo); verifyChunkListRemoved(chunks, 1, mDabEnsembleIdentifier, mVendorCustomIdentifier); } @@ -188,63 +191,50 @@ public class ProgramInfoCacheTest { // Create a cache with a filter that excludes modifications, and set its contents to // mAmFmInfo, mRdsInfo, mDabEnsembleInfo, and mVendorCustomInfo. ProgramInfoCache cache = new ProgramInfoCache(new ProgramList.Filter(new HashSet<Integer>(), - new HashSet<ProgramSelector.Identifier>(), true, true), mAmFmInfo, mRdsInfo, + new HashSet<ProgramSelector.Identifier>(), true, true), true, mAmFmInfo, mRdsInfo, mDabEnsembleInfo, mVendorCustomInfo); // Create a HAL cache that: + // - Is incomplete. // - Retains mAmFmInfo. // - Replaces mRdsInfo with updatedRdsInfo. // - Drops mDabEnsembleInfo and mVendorCustomInfo. // - Introduces a new SXM info. - RadioManager.ProgramInfo updatedRdsInfo = makeProgramInfo(ProgramSelector.PROGRAM_TYPE_FM, - mRdsIdentifier, 1); - RadioManager.ProgramInfo newSxmInfo = makeProgramInfo(ProgramSelector.PROGRAM_TYPE_SXM, + RadioManager.ProgramInfo updatedRdsInfo = TestUtils.makeProgramInfo( + ProgramSelector.PROGRAM_TYPE_FM, mRdsIdentifier, 1); + RadioManager.ProgramInfo newSxmInfo = TestUtils.makeProgramInfo( + ProgramSelector.PROGRAM_TYPE_SXM, new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_SXM_CHANNEL, 12345), 0); - ProgramInfoCache halCache = new ProgramInfoCache(null, mAmFmInfo, updatedRdsInfo, + ProgramInfoCache halCache = new ProgramInfoCache(null, false, mAmFmInfo, updatedRdsInfo, newSxmInfo); // Update the cache and verify: + // - All complete flags are false. // - mAmFmInfo and mRdsInfo are retained and not reported in the chunks. // - newSxmInfo should appear as a new entry. // - mDabEnsembleInfo and mVendorCustomInfo should be reported as removed. List<ProgramList.Chunk> chunks = cache.filterAndUpdateFromInternal(halCache, false, 5, 1); assertTrue(cache.programInfosAreExactly(mAmFmInfo, mRdsInfo, newSxmInfo)); assertEquals(2, chunks.size()); - verifyChunkListFlags(chunks, false); + verifyChunkListFlags(chunks, false, false); verifyChunkListModified(chunks, 5, newSxmInfo); verifyChunkListRemoved(chunks, 1, mDabEnsembleIdentifier, mVendorCustomIdentifier); } - private static RadioManager.ProgramInfo makeProgramInfo(int programType, - ProgramSelector.Identifier identifier, int signalQuality) { - // Note: If you set new fields, check if programInfoToHal() needs to be updated as well. - return new RadioManager.ProgramInfo(new ProgramSelector(programType, identifier, null, - null), null, null, null, 0, signalQuality, new RadioMetadata.Builder().build(), - new HashMap<String, String>()); - } - - private static ProgramInfo programInfoToHal(RadioManager.ProgramInfo info) { - // Note that because Convert does not by design provide functions for all conversions, this - // function only copies fields that are set by makeProgramInfo(). - ProgramInfo hwInfo = new ProgramInfo(); - hwInfo.selector = Convert.programSelectorToHal(info.getSelector()); - hwInfo.signalQuality = info.getSignalStrength(); - return hwInfo; - } - // Verifies that: // - The first chunk's purge flag matches expectPurge. - // - The last chunk's complete flag is set. + // - The last chunk's complete flag matches expectComplete. // - All other flags are false. - private static void verifyChunkListFlags(List<ProgramList.Chunk> chunks, boolean expectPurge) { + private static void verifyChunkListFlags(List<ProgramList.Chunk> chunks, boolean expectPurge, + boolean expectComplete) { if (chunks.isEmpty()) { return; } for (int i = 0; i < chunks.size(); i++) { ProgramList.Chunk chunk = chunks.get(i); assertEquals(i == 0 && expectPurge, chunk.isPurge()); - assertEquals(i == chunks.size() - 1, chunk.isComplete()); + assertEquals(i == chunks.size() - 1 && expectComplete, chunk.isComplete()); } } diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java new file mode 100644 index 000000000000..f9e37981fa6f --- /dev/null +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java @@ -0,0 +1,353 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.broadcastradio.hal2; + +import static org.junit.Assert.*; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.argThat; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.hardware.broadcastradio.V2_0.IBroadcastRadio; +import android.hardware.broadcastradio.V2_0.ITunerCallback; +import android.hardware.broadcastradio.V2_0.ITunerSession; +import android.hardware.broadcastradio.V2_0.ProgramFilter; +import android.hardware.broadcastradio.V2_0.ProgramListChunk; +import android.hardware.broadcastradio.V2_0.Result; +import android.hardware.radio.ProgramList; +import android.hardware.radio.ProgramSelector; +import android.hardware.radio.RadioManager; +import android.os.RemoteException; +import android.test.suitebuilder.annotation.MediumTest; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentMatcher; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.stubbing.Answer; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Tests for v2 HAL RadioModule. + */ +@RunWith(AndroidJUnit4.class) +@MediumTest +public class StartProgramListUpdatesFanoutTest { + private static final String TAG = "BroadcastRadioTests.hal2.StartProgramListUpdatesFanout"; + + // Mocks + @Mock IBroadcastRadio mBroadcastRadioMock; + @Mock ITunerSession mHalTunerSessionMock; + private android.hardware.radio.ITunerCallback[] mAidlTunerCallbackMocks; + + // RadioModule under test + private RadioModule mRadioModule; + + // Objects created by mRadioModule + private ITunerCallback mHalTunerCallback; + private TunerSession[] mTunerSessions; + + // Data objects used during tests + private final ProgramSelector.Identifier mAmFmIdentifier = + new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY, 88500); + private final RadioManager.ProgramInfo mAmFmInfo = TestUtils.makeProgramInfo( + ProgramSelector.PROGRAM_TYPE_FM, mAmFmIdentifier, 0); + private final RadioManager.ProgramInfo mModifiedAmFmInfo = TestUtils.makeProgramInfo( + ProgramSelector.PROGRAM_TYPE_FM, mAmFmIdentifier, 1); + + private final ProgramSelector.Identifier mRdsIdentifier = + new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_RDS_PI, 15019); + private final RadioManager.ProgramInfo mRdsInfo = TestUtils.makeProgramInfo( + ProgramSelector.PROGRAM_TYPE_FM, mRdsIdentifier, 0); + + private final ProgramSelector.Identifier mDabEnsembleIdentifier = + new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_DAB_ENSEMBLE, 1337); + private final RadioManager.ProgramInfo mDabEnsembleInfo = TestUtils.makeProgramInfo( + ProgramSelector.PROGRAM_TYPE_DAB, mDabEnsembleIdentifier, 0); + + @Before + public void setup() throws RemoteException { + MockitoAnnotations.initMocks(this); + + mRadioModule = new RadioModule(mBroadcastRadioMock, new RadioManager.ModuleProperties(0, "", + 0, "", "", "", "", 0, 0, false, false, null, false, new int[] {}, new int[] {}, + null, null)); + + doAnswer((Answer) invocation -> { + mHalTunerCallback = (ITunerCallback) invocation.getArguments()[0]; + IBroadcastRadio.openSessionCallback cb = (IBroadcastRadio.openSessionCallback) + invocation.getArguments()[1]; + cb.onValues(Result.OK, mHalTunerSessionMock); + return null; + }).when(mBroadcastRadioMock).openSession(any(), any()); + when(mHalTunerSessionMock.startProgramListUpdates(any())).thenReturn(Result.OK); + } + + @Test + public void testFanout() throws RemoteException { + // Open 3 clients that will all use the same filter, and start updates on two of them for + // now. The HAL TunerSession should only see 1 filter update. + openAidlClients(3); + ProgramList.Filter aidlFilter = new ProgramList.Filter(new HashSet<Integer>(), + new HashSet<ProgramSelector.Identifier>(), true, false); + ProgramFilter halFilter = Convert.programFilterToHal(aidlFilter); + for (int i = 0; i < 2; i++) { + mTunerSessions[i].startProgramListUpdates(aidlFilter); + } + verify(mHalTunerSessionMock, times(1)).startProgramListUpdates(halFilter); + + // Initiate a program list update from the HAL side and verify both connected AIDL clients + // receive the update. + updateHalProgramInfo(true, Arrays.asList(mAmFmInfo, mRdsInfo), null); + for (int i = 0; i < 2; i++) { + verifyAidlClientReceivedChunk(mAidlTunerCallbackMocks[i], true, Arrays.asList( + mAmFmInfo, mRdsInfo), null); + } + + // Repeat with a non-purging update. + updateHalProgramInfo(false, Arrays.asList(mModifiedAmFmInfo), + Arrays.asList(mRdsIdentifier)); + for (int i = 0; i < 2; i++) { + verifyAidlClientReceivedChunk(mAidlTunerCallbackMocks[i], false, + Arrays.asList(mModifiedAmFmInfo), Arrays.asList(mRdsIdentifier)); + } + + // Now start updates on the 3rd client. Verify the HAL function has not been called again + // and client receives the appropriate update. + mTunerSessions[2].startProgramListUpdates(aidlFilter); + verify(mHalTunerSessionMock, times(1)).startProgramListUpdates(any()); + verifyAidlClientReceivedChunk(mAidlTunerCallbackMocks[2], true, + Arrays.asList(mModifiedAmFmInfo), null); + } + + @Test + public void testFiltering() throws RemoteException { + // Open 4 clients that will use the following filters: + // [0]: ID mRdsIdentifier, modifications excluded + // [1]: No categories, modifications excluded + // [2]: Type IDENTIFIER_TYPE_AMFM_FREQUENCY, modifications excluded + // [3]: Type IDENTIFIER_TYPE_AMFM_FREQUENCY, modifications included + openAidlClients(4); + ProgramList.Filter idFilter = new ProgramList.Filter(new HashSet<Integer>(), + new HashSet<ProgramSelector.Identifier>(Arrays.asList(mRdsIdentifier)), true, true); + ProgramList.Filter categoryFilter = new ProgramList.Filter(new HashSet<Integer>(), + new HashSet<ProgramSelector.Identifier>(), false, true); + ProgramList.Filter typeFilterWithoutModifications = new ProgramList.Filter( + new HashSet<Integer>(Arrays.asList(ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY)), + new HashSet<ProgramSelector.Identifier>(), true, true); + ProgramList.Filter typeFilterWithModifications = new ProgramList.Filter( + new HashSet<Integer>(Arrays.asList(ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY)), + new HashSet<ProgramSelector.Identifier>(), true, false); + + // Start updates on the clients in order. The HAL filter should get updated after each + // client except [2]. + mTunerSessions[0].startProgramListUpdates(idFilter); + ProgramFilter halFilter = Convert.programFilterToHal(idFilter); + verify(mHalTunerSessionMock, times(1)).startProgramListUpdates(halFilter); + + mTunerSessions[1].startProgramListUpdates(categoryFilter); + halFilter.identifiers.clear(); + verify(mHalTunerSessionMock, times(1)).startProgramListUpdates(halFilter); + + mTunerSessions[2].startProgramListUpdates(typeFilterWithoutModifications); + verify(mHalTunerSessionMock, times(2)).startProgramListUpdates(any()); + + mTunerSessions[3].startProgramListUpdates(typeFilterWithModifications); + halFilter.excludeModifications = false; + verify(mHalTunerSessionMock, times(1)).startProgramListUpdates(halFilter); + + // Adding mRdsInfo should update clients [0] and [1]. + updateHalProgramInfo(false, Arrays.asList(mRdsInfo), null); + verifyAidlClientReceivedChunk(mAidlTunerCallbackMocks[0], false, Arrays.asList(mRdsInfo), + null); + verifyAidlClientReceivedChunk(mAidlTunerCallbackMocks[1], false, Arrays.asList(mRdsInfo), + null); + + // Adding mAmFmInfo should update clients [1], [2], and [3]. + updateHalProgramInfo(false, Arrays.asList(mAmFmInfo), null); + verifyAidlClientReceivedChunk(mAidlTunerCallbackMocks[1], false, Arrays.asList(mAmFmInfo), + null); + verifyAidlClientReceivedChunk(mAidlTunerCallbackMocks[2], false, Arrays.asList(mAmFmInfo), + null); + verifyAidlClientReceivedChunk(mAidlTunerCallbackMocks[3], false, Arrays.asList(mAmFmInfo), + null); + + // Modifying mAmFmInfo to mModifiedAmFmInfo should update only [3]. + updateHalProgramInfo(false, Arrays.asList(mModifiedAmFmInfo), null); + verifyAidlClientReceivedChunk(mAidlTunerCallbackMocks[3], false, + Arrays.asList(mModifiedAmFmInfo), null); + + // Adding mDabEnsembleInfo should not update any client. + updateHalProgramInfo(false, Arrays.asList(mDabEnsembleInfo), null); + verify(mAidlTunerCallbackMocks[0], times(1)).onProgramListUpdated(any()); + verify(mAidlTunerCallbackMocks[1], times(2)).onProgramListUpdated(any()); + verify(mAidlTunerCallbackMocks[2], times(1)).onProgramListUpdated(any()); + verify(mAidlTunerCallbackMocks[3], times(2)).onProgramListUpdated(any()); + } + + @Test + public void testClientClosing() throws RemoteException { + // Open 2 clients that use different filters that are both sensitive to mAmFmIdentifier. + openAidlClients(2); + ProgramList.Filter idFilter = new ProgramList.Filter(new HashSet<Integer>(), + new HashSet<ProgramSelector.Identifier>(Arrays.asList(mAmFmIdentifier)), true, + false); + ProgramList.Filter typeFilter = new ProgramList.Filter( + new HashSet<Integer>(Arrays.asList(ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY)), + new HashSet<ProgramSelector.Identifier>(), true, false); + + // Start updates on the clients, and verify the HAL filter is updated after each one. + mTunerSessions[0].startProgramListUpdates(idFilter); + ProgramFilter halFilter = Convert.programFilterToHal(idFilter); + verify(mHalTunerSessionMock, times(1)).startProgramListUpdates(halFilter); + + mTunerSessions[1].startProgramListUpdates(typeFilter); + halFilter.identifiers.clear(); + verify(mHalTunerSessionMock, times(1)).startProgramListUpdates(halFilter); + + // Update the HAL with mAmFmInfo, and verify both clients are updated. + updateHalProgramInfo(true, Arrays.asList(mAmFmInfo), null); + verifyAidlClientReceivedChunk(mAidlTunerCallbackMocks[0], true, Arrays.asList(mAmFmInfo), + null); + verifyAidlClientReceivedChunk(mAidlTunerCallbackMocks[1], true, Arrays.asList(mAmFmInfo), + null); + + // Stop updates on the first client and verify the HAL filter is updated. + mTunerSessions[0].stopProgramListUpdates(); + verify(mHalTunerSessionMock, times(1)).startProgramListUpdates(Convert.programFilterToHal( + typeFilter)); + + // Update the HAL with mModifiedAmFmInfo, and verify only the remaining client is updated. + updateHalProgramInfo(true, Arrays.asList(mModifiedAmFmInfo), null); + verify(mAidlTunerCallbackMocks[0], times(1)).onProgramListUpdated(any()); + verifyAidlClientReceivedChunk(mAidlTunerCallbackMocks[1], true, + Arrays.asList(mModifiedAmFmInfo), null); + + // Close the other client without explicitly stopping updates, and verify HAL updates are + // stopped as well. + mTunerSessions[1].close(); + verify(mHalTunerSessionMock).stopProgramListUpdates(); + } + + private void openAidlClients(int numClients) throws RemoteException { + mAidlTunerCallbackMocks = new android.hardware.radio.ITunerCallback[numClients]; + mTunerSessions = new TunerSession[numClients]; + for (int i = 0; i < numClients; i++) { + mAidlTunerCallbackMocks[i] = mock(android.hardware.radio.ITunerCallback.class); + mTunerSessions[i] = mRadioModule.openSession(mAidlTunerCallbackMocks[i]); + } + } + + private void updateHalProgramInfo(boolean purge, List<RadioManager.ProgramInfo> modified, + List<ProgramSelector.Identifier> removed) throws RemoteException { + ProgramListChunk programListChunk = new ProgramListChunk(); + programListChunk.purge = purge; + programListChunk.complete = true; + if (modified != null) { + for (RadioManager.ProgramInfo mod : modified) { + programListChunk.modified.add(TestUtils.programInfoToHal(mod)); + } + } + if (removed != null) { + for (ProgramSelector.Identifier id : removed) { + programListChunk.removed.add(Convert.programIdentifierToHal(id)); + } + } + mHalTunerCallback.onProgramListUpdated(programListChunk); + } + + private void verifyAidlClientReceivedChunk(android.hardware.radio.ITunerCallback clientMock, + boolean purge, List<RadioManager.ProgramInfo> modified, + List<ProgramSelector.Identifier> removed) throws RemoteException { + HashSet<RadioManager.ProgramInfo> modifiedSet = new HashSet<>(); + if (modified != null) { + modifiedSet.addAll(modified); + } + HashSet<ProgramSelector.Identifier> removedSet = new HashSet<>(); + if (removed != null) { + removedSet.addAll(removed); + } + ProgramList.Chunk expectedChunk = new ProgramList.Chunk(purge, true, modifiedSet, + removedSet); + verify(clientMock).onProgramListUpdated(argThat(new ChunkMatcher(expectedChunk))); + } + + // TODO(b/130750904): Remove this class and replace "argThat(new ChunkMatcher(chunk))" with + // "eq(chunk)". + // + // Ideally, this class wouldn't exist, but currently RadioManager.ProgramInfo#hashCode() can + // return different values for objects that satisfy ProgramInfo#equals(). As a short term + // workaround, this class performs the O(N^2) comparison between the Chunks' mModified sets. + // + // To test if ProgramInfo#hashCode() has been fixed, remove commenting from + // testProgramInfoHashCode() below. + private class ChunkMatcher implements ArgumentMatcher<ProgramList.Chunk> { + private final ProgramList.Chunk mExpected; + + ChunkMatcher(ProgramList.Chunk expected) { + mExpected = expected; + } + + @Override + public boolean matches(ProgramList.Chunk actual) { + if ((mExpected.isPurge() != actual.isPurge()) + || (mExpected.isComplete() != actual.isComplete()) + || (!mExpected.getRemoved().equals(actual.getRemoved()))) { + return false; + } + Set<RadioManager.ProgramInfo> expectedModified = mExpected.getModified(); + Set<RadioManager.ProgramInfo> actualModified = new HashSet<>(actual.getModified()); + if (expectedModified.size() != actualModified.size()) { + return false; + } + for (RadioManager.ProgramInfo expectedInfo : expectedModified) { + boolean found = false; + for (RadioManager.ProgramInfo actualInfo : actualModified) { + if (expectedInfo.equals(actualInfo)) { + found = true; + actualModified.remove(actualInfo); + break; + } + } + if (!found) { + return false; + } + } + return true; + } + } + + // @Test + // public void testProgramInfoHashCode() { + // RadioManager.ProgramInfo info1 = TestUtils.makeProgramInfo( + // ProgramSelector.PROGRAM_TYPE_FM, mAmFmIdentifier, 0); + // RadioManager.ProgramInfo info2 = TestUtils.makeProgramInfo( + // ProgramSelector.PROGRAM_TYPE_FM, mAmFmIdentifier, 0); + // assertEquals(info1, info2); + // assertEquals(info1.hashCode(), info2.hashCode()); + // } +} diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TestUtils.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TestUtils.java new file mode 100644 index 000000000000..4944803eaafe --- /dev/null +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TestUtils.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.broadcastradio.hal2; + +import android.hardware.broadcastradio.V2_0.ProgramInfo; +import android.hardware.radio.ProgramSelector; +import android.hardware.radio.RadioManager; +import android.hardware.radio.RadioMetadata; + +import java.util.HashMap; + +final class TestUtils { + static RadioManager.ProgramInfo makeProgramInfo(int programType, + ProgramSelector.Identifier identifier, int signalQuality) { + // Note: If you set new fields, check if programInfoToHal() needs to be updated as well. + return new RadioManager.ProgramInfo(new ProgramSelector(programType, identifier, null, + null), null, null, null, 0, signalQuality, new RadioMetadata.Builder().build(), + new HashMap<String, String>()); + } + + static ProgramInfo programInfoToHal(RadioManager.ProgramInfo info) { + // Note that because Convert does not by design provide functions for all conversions, this + // function only copies fields that are set by makeProgramInfo(). + ProgramInfo hwInfo = new ProgramInfo(); + hwInfo.selector = Convert.programSelectorToHal(info.getSelector()); + hwInfo.signalQuality = info.getSignalStrength(); + return hwInfo; + } +} 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/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/text/format/TimeMigrationUtilsTest.java b/core/tests/coretests/src/android/text/format/TimeMigrationUtilsTest.java new file mode 100644 index 000000000000..b605520c659d --- /dev/null +++ b/core/tests/coretests/src/android/text/format/TimeMigrationUtilsTest.java @@ -0,0 +1,120 @@ +/* + * 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.text.format; + +import static org.junit.Assert.assertEquals; + +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Locale; +import java.util.TimeZone; + +@Presubmit +@SmallTest +@RunWith(AndroidJUnit4.class) +public class TimeMigrationUtilsTest { + + private static final int ONE_DAY_IN_SECONDS = 24 * 60 * 60; + + private Locale mDefaultLocale; + private TimeZone mDefaultTimeZone; + + @Before + public void setUp() { + mDefaultLocale = Locale.getDefault(); + mDefaultTimeZone = TimeZone.getDefault(); + } + + @After + public void tearDown() { + Locale.setDefault(mDefaultLocale); + TimeZone.setDefault(mDefaultTimeZone); + } + + @Test + public void formatMillisWithFixedFormat_fixes2038Issue() { + Locale.setDefault(Locale.UK); + TimeZone.setDefault(TimeZone.getTimeZone("UTC")); + + // The following cannot be represented properly using Time because they are outside of the + // supported range. + long y2038Issue1 = (((long) Integer.MIN_VALUE) - ONE_DAY_IN_SECONDS) * 1000L; + assertEquals( + "1901-12-12 20:45:52", TimeMigrationUtils.formatMillisWithFixedFormat(y2038Issue1)); + long y2038Issue2 = (((long) Integer.MAX_VALUE) + ONE_DAY_IN_SECONDS) * 1000L; + assertEquals( + "2038-01-20 03:14:07", TimeMigrationUtils.formatMillisWithFixedFormat(y2038Issue2)); + } + + /** + * Compares TimeMigrationUtils.formatSimpleDateTime() with the code it is replacing. + */ + @Test + public void formatMillisAsDateTime_matchesOldBehavior() { + // A selection of interesting locales. + Locale[] locales = new Locale[] { + Locale.US, + Locale.UK, + Locale.FRANCE, + Locale.JAPAN, + Locale.CHINA, + // Android supports RTL locales like arabic and arabic with latin numbers. + Locale.forLanguageTag("ar-AE"), + Locale.forLanguageTag("ar-AE-u-nu-latn"), + }; + // A selection of interesting time zones. + String[] timeZoneIds = new String[] { + "UTC", "Europe/London", "America/New_York", "America/Los_Angeles", "Asia/Shanghai", + }; + // Some arbitrary times when the two formatters should agree. + long[] timesMillis = new long[] { + System.currentTimeMillis(), + 0, + // The Time class only works in 32-bit range, the replacement works beyond that. To + // avoid messing around with offsets and complicating the test, below there are a + // day after / before the known limits. + (Integer.MIN_VALUE + ONE_DAY_IN_SECONDS) * 1000L, + (Integer.MAX_VALUE - ONE_DAY_IN_SECONDS) * 1000L, + }; + + for (Locale locale : locales) { + Locale.setDefault(locale); + for (String timeZoneId : timeZoneIds) { + TimeZone timeZone = TimeZone.getTimeZone(timeZoneId); + TimeZone.setDefault(timeZone); + for (long timeMillis : timesMillis) { + Time time = new Time(); + time.set(timeMillis); + String oldResult = time.format("%Y-%m-%d %H:%M:%S"); + String newResult = TimeMigrationUtils.formatMillisWithFixedFormat(timeMillis); + assertEquals( + "locale=" + locale + ", timeZoneId=" + timeZoneId + + ", timeMillis=" + timeMillis, + oldResult, newResult); + } + } + } + } +} diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java index 683d16b1dea1..682416c58c72 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; @@ -131,7 +132,13 @@ public class AccessibilityServiceConnectionImpl extends IAccessibilityServiceCon public void sendGesture(int sequence, ParceledListSlice gestureSteps) {} + public void dispatchGesture(int sequence, ParceledListSlice gestureSteps, int displayId) {} + public boolean isFingerprintGestureDetectionAvailable() { return false; } + + public IBinder getOverlayWindowToken(int displayId) { + return null; + } } diff --git a/core/tests/coretests/src/com/android/internal/os/KernelWakelockReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelWakelockReaderTest.java index 008085e38dbf..a93573724ba1 100644 --- a/core/tests/coretests/src/com/android/internal/os/KernelWakelockReaderTest.java +++ b/core/tests/coretests/src/com/android/internal/os/KernelWakelockReaderTest.java @@ -68,12 +68,7 @@ public class KernelWakelockReaderTest extends TestCase { private WakeLockInfo createWakeLockInfo(String name, int activeCount, long totalTime) { WakeLockInfo info = new WakeLockInfo(); info.name = name; - info.pid = 1; info.activeCount = activeCount; - info.isActive = true; - info.activeSince = 0; - info.lastChange = 0; - info.maxTime = 0; info.totalTime = totalTime; return info; } @@ -89,7 +84,7 @@ public class KernelWakelockReaderTest extends TestCase { byte[] buffer, WakeLockInfo[] wlStats) { mReader.updateVersion(staleStats); mReader.parseProcWakelocks(buffer, buffer.length, true, staleStats); - mReader.getNativeWakelockStats(wlStats, staleStats); + mReader.updateWakelockStats(wlStats, staleStats); return mReader.removeOldStats(staleStats); } @@ -101,7 +96,7 @@ public class KernelWakelockReaderTest extends TestCase { mReader = new KernelWakelockReader(); } -// ------------------------- Kernel Wakelock Stats Test ------------------------ +// ------------------------- Legacy Wakelock Stats Test ------------------------ @SmallTest public void testParseEmptyFile() throws Exception { KernelWakelockStats staleStats = mReader.parseProcWakelocks(new byte[0], 0, true, @@ -196,10 +191,10 @@ public class KernelWakelockReaderTest extends TestCase { assertFalse(staleStats.containsKey("Fakelock")); } -// -------------------- Native (SystemSuspend) Wakelock Stats Test ------------------- +// -------------------- SystemSuspend Wakelock Stats Test ------------------- @SmallTest public void testEmptyWakeLockInfoList() { - KernelWakelockStats staleStats = mReader.getNativeWakelockStats(new WakeLockInfo[0], + KernelWakelockStats staleStats = mReader.updateWakelockStats(new WakeLockInfo[0], new KernelWakelockStats()); assertTrue(staleStats.isEmpty()); @@ -208,9 +203,9 @@ public class KernelWakelockReaderTest extends TestCase { @SmallTest public void testOneWakeLockInfo() { WakeLockInfo[] wlStats = new WakeLockInfo[1]; - wlStats[0] = createWakeLockInfo("WakeLock", 20, 10000); + wlStats[0] = createWakeLockInfo("WakeLock", 20, 1000); // Milliseconds - KernelWakelockStats staleStats = mReader.getNativeWakelockStats(wlStats, + KernelWakelockStats staleStats = mReader.updateWakelockStats(wlStats, new KernelWakelockStats()); assertEquals(1, staleStats.size()); @@ -219,16 +214,16 @@ public class KernelWakelockReaderTest extends TestCase { KernelWakelockStats.Entry entry = staleStats.get("WakeLock"); assertEquals(20, entry.mCount); - assertEquals(10000, entry.mTotalTime); + assertEquals(1000 * 1000, entry.mTotalTime); // Microseconds } @SmallTest public void testTwoWakeLockInfos() { WakeLockInfo[] wlStats = new WakeLockInfo[2]; - wlStats[0] = createWakeLockInfo("WakeLock1", 10, 1000); - wlStats[1] = createWakeLockInfo("WakeLock2", 20, 2000); + wlStats[0] = createWakeLockInfo("WakeLock1", 10, 1000); // Milliseconds + wlStats[1] = createWakeLockInfo("WakeLock2", 20, 2000); // Milliseconds - KernelWakelockStats staleStats = mReader.getNativeWakelockStats(wlStats, + KernelWakelockStats staleStats = mReader.updateWakelockStats(wlStats, new KernelWakelockStats()); assertEquals(2, staleStats.size()); @@ -238,17 +233,17 @@ public class KernelWakelockReaderTest extends TestCase { KernelWakelockStats.Entry entry1 = staleStats.get("WakeLock1"); assertEquals(10, entry1.mCount); - assertEquals(1000, entry1.mTotalTime); + assertEquals(1000 * 1000, entry1.mTotalTime); // Microseconds KernelWakelockStats.Entry entry2 = staleStats.get("WakeLock2"); assertEquals(20, entry2.mCount); - assertEquals(2000, entry2.mTotalTime); + assertEquals(2000 * 1000, entry2.mTotalTime); // Microseconds } @SmallTest public void testWakeLockInfosBecomeStale() { WakeLockInfo[] wlStats = new WakeLockInfo[1]; - wlStats[0] = createWakeLockInfo("WakeLock1", 10, 1000); + wlStats[0] = createWakeLockInfo("WakeLock1", 10, 1000); // Milliseconds KernelWakelockStats staleStats = new KernelWakelockStats(); @@ -259,9 +254,9 @@ public class KernelWakelockReaderTest extends TestCase { assertTrue(staleStats.containsKey("WakeLock1")); KernelWakelockStats.Entry entry = staleStats.get("WakeLock1"); assertEquals(10, entry.mCount); - assertEquals(1000, entry.mTotalTime); + assertEquals(1000 * 1000, entry.mTotalTime); // Microseconds - wlStats[0] = createWakeLockInfo("WakeLock2", 20, 2000); + wlStats[0] = createWakeLockInfo("WakeLock2", 20, 2000); // Milliseconds readKernelWakelockStats(staleStats, new byte[0], wlStats); @@ -271,146 +266,6 @@ public class KernelWakelockReaderTest extends TestCase { assertTrue(staleStats.containsKey("WakeLock2")); entry = staleStats.get("WakeLock2"); assertEquals(20, entry.mCount); - assertEquals(2000, entry.mTotalTime); - } - -// -------------------- Aggregate Wakelock Stats Tests -------------------- - @SmallTest - public void testAggregateStatsEmpty() throws Exception { - KernelWakelockStats staleStats = new KernelWakelockStats(); - - byte[] buffer = new byte[0]; - WakeLockInfo[] wlStats = new WakeLockInfo[0]; - - readKernelWakelockStats(staleStats, buffer, wlStats); - - assertTrue(staleStats.isEmpty()); - } - - @SmallTest - public void testAggregateStatsNoNativeWakelocks() throws Exception { - KernelWakelockStats staleStats = new KernelWakelockStats(); - - byte[] buffer = new ProcFileBuilder() - .addLine("Wakelock", 34, 123) // Milliseconds - .getBytes(); - WakeLockInfo[] wlStats = new WakeLockInfo[0]; - - readKernelWakelockStats(staleStats, buffer, wlStats); - - assertEquals(1, staleStats.size()); - - assertTrue(staleStats.containsKey("Wakelock")); - - KernelWakelockStats.Entry entry = staleStats.get("Wakelock"); - assertEquals(34, entry.mCount); - assertEquals(1000 * 123, entry.mTotalTime); // Microseconds - } - - @SmallTest - public void testAggregateStatsNoKernelWakelocks() throws Exception { - KernelWakelockStats staleStats = new KernelWakelockStats(); - - byte[] buffer = new byte[0]; - WakeLockInfo[] wlStats = new WakeLockInfo[1]; - wlStats[0] = createWakeLockInfo("WakeLock", 10, 1000); - - readKernelWakelockStats(staleStats, buffer, wlStats); - - assertEquals(1, staleStats.size()); - - assertTrue(staleStats.containsKey("WakeLock")); - - KernelWakelockStats.Entry entry = staleStats.get("WakeLock"); - assertEquals(10, entry.mCount); - assertEquals(1000, entry.mTotalTime); - } - - @SmallTest - public void testAggregateStatsBothKernelAndNativeWakelocks() throws Exception { - KernelWakelockStats staleStats = new KernelWakelockStats(); - - byte[] buffer = new ProcFileBuilder() - .addLine("WakeLock1", 34, 123) // Milliseconds - .getBytes(); - WakeLockInfo[] wlStats = new WakeLockInfo[1]; - wlStats[0] = createWakeLockInfo("WakeLock2", 10, 1000); - - readKernelWakelockStats(staleStats, buffer, wlStats); - - assertEquals(2, staleStats.size()); - - assertTrue(staleStats.containsKey("WakeLock1")); - KernelWakelockStats.Entry entry1 = staleStats.get("WakeLock1"); - assertEquals(34, entry1.mCount); - assertEquals(123 * 1000, entry1.mTotalTime); // Microseconds - - assertTrue(staleStats.containsKey("WakeLock2")); - KernelWakelockStats.Entry entry2 = staleStats.get("WakeLock2"); - assertEquals(10, entry2.mCount); - assertEquals(1000, entry2.mTotalTime); - } - - @SmallTest - public void testAggregateStatsUpdate() throws Exception { - KernelWakelockStats staleStats = new KernelWakelockStats(); - - byte[] buffer = new ProcFileBuilder() - .addLine("WakeLock1", 34, 123) // Milliseconds - .addLine("WakeLock2", 46, 345) // Milliseconds - .getBytes(); - WakeLockInfo[] wlStats = new WakeLockInfo[2]; - wlStats[0] = createWakeLockInfo("WakeLock3", 10, 1000); - wlStats[1] = createWakeLockInfo("WakeLock4", 20, 2000); - - readKernelWakelockStats(staleStats, buffer, wlStats); - - assertEquals(4, staleStats.size()); - - assertTrue(staleStats.containsKey("WakeLock1")); - assertTrue(staleStats.containsKey("WakeLock2")); - assertTrue(staleStats.containsKey("WakeLock3")); - assertTrue(staleStats.containsKey("WakeLock4")); - - KernelWakelockStats.Entry entry1 = staleStats.get("WakeLock1"); - assertEquals(34, entry1.mCount); - assertEquals(123 * 1000, entry1.mTotalTime); // Microseconds - - KernelWakelockStats.Entry entry2 = staleStats.get("WakeLock2"); - assertEquals(46, entry2.mCount); - assertEquals(345 * 1000, entry2.mTotalTime); // Microseconds - - KernelWakelockStats.Entry entry3 = staleStats.get("WakeLock3"); - assertEquals(10, entry3.mCount); - assertEquals(1000, entry3.mTotalTime); - - KernelWakelockStats.Entry entry4 = staleStats.get("WakeLock4"); - assertEquals(20, entry4.mCount); - assertEquals(2000, entry4.mTotalTime); - - buffer = new ProcFileBuilder() - .addLine("WakeLock1", 45, 789) // Milliseconds - .addLine("WakeLock1", 56, 123) // Milliseconds - .getBytes(); - wlStats = new WakeLockInfo[1]; - wlStats[0] = createWakeLockInfo("WakeLock4", 40, 4000); - - readKernelWakelockStats(staleStats, buffer, wlStats); - - assertEquals(2, staleStats.size()); - - assertTrue(staleStats.containsKey("WakeLock1")); - assertTrue(staleStats.containsKey("WakeLock4")); - - assertFalse(staleStats.containsKey("WakeLock2")); - assertFalse(staleStats.containsKey("WakeLock3")); - - entry1 = staleStats.get("WakeLock1"); - assertEquals(45 + 56, entry1.mCount); - assertEquals((789 + 123) * 1000, entry1.mTotalTime); // Microseconds - - entry2 = staleStats.get("WakeLock4"); - assertEquals(40, entry2.mCount); - assertEquals(4000, entry4.mTotalTime); + assertEquals(2000 * 1000, entry.mTotalTime); // Micro seconds } } diff --git a/core/tests/coretests/src/com/android/internal/policy/DecorViewTest.java b/core/tests/coretests/src/com/android/internal/policy/DecorViewTest.java index f66717e9066c..62e4efe7a543 100644 --- a/core/tests/coretests/src/com/android/internal/policy/DecorViewTest.java +++ b/core/tests/coretests/src/com/android/internal/policy/DecorViewTest.java @@ -76,4 +76,13 @@ public class DecorViewTest { 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/graphics/java/android/graphics/RenderNode.java b/graphics/java/android/graphics/RenderNode.java index ae7fe6c46f2f..0e635c774c84 100644 --- a/graphics/java/android/graphics/RenderNode.java +++ b/graphics/java/android/graphics/RenderNode.java @@ -63,7 +63,7 @@ import java.lang.annotation.RetentionPolicy; * <h3>Creating a RenderNode</h3> * <pre class="prettyprint"> * RenderNode renderNode = new RenderNode("myRenderNode"); - * renderNode.setLeftTopRightBottom(0, 0, 50, 50); // Set the size to 50x50 + * renderNode.setPosition(0, 0, 50, 50); // Set the size to 50x50 * RecordingCanvas canvas = renderNode.beginRecording(); * try { * // Draw with the canvas @@ -104,7 +104,7 @@ import java.lang.annotation.RetentionPolicy; * <pre class="prettyprint"> * private void createDisplayList() { * mRenderNode = new RenderNode("MyRenderNode"); - * mRenderNode.setLeftTopRightBottom(0, 0, width, height); + * mRenderNode.setPosition(0, 0, width, height); * RecordingCanvas canvas = mRenderNode.beginRecording(); * try { * for (Bitmap b : mBitmaps) { diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index 6ea6af8f2935..f01b1bfa780d 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -778,6 +778,13 @@ void SkiaCanvas::drawCircle(uirenderer::CanvasPropertyPrimitive* x, mCanvas->drawDrawable(drawable.get()); } +void SkiaCanvas::drawPicture(const SkPicture& picture) { + // TODO: Change to mCanvas->drawPicture()? SkCanvas::drawPicture seems to be + // where the logic is for playback vs. ref picture. Using picture.playback here + // to stay behavior-identical for now, but should revisit this at some point. + picture.playback(mCanvas); +} + // ---------------------------------------------------------------------------- // Canvas draw operations: View System // ---------------------------------------------------------------------------- diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h index 05a6d0dda42d..799a89158298 100644 --- a/libs/hwui/SkiaCanvas.h +++ b/libs/hwui/SkiaCanvas.h @@ -46,8 +46,6 @@ public: virtual ~SkiaCanvas(); - virtual SkCanvas* asSkCanvas() override { return mCanvas; } - virtual void resetRecording(int width, int height, uirenderer::RenderNode* renderNode) override { LOG_ALWAYS_FATAL("SkiaCanvas cannot be reset as a recording canvas"); @@ -155,9 +153,11 @@ public: virtual void drawRenderNode(uirenderer::RenderNode* renderNode) override; virtual void callDrawGLFunction(Functor* functor, uirenderer::GlFunctorLifecycleListener* listener) override; + virtual void drawPicture(const SkPicture& picture) override; protected: SkiaCanvas(); + SkCanvas* asSkCanvas() { return mCanvas; } void reset(SkCanvas* skiaCanvas); void drawDrawable(SkDrawable* drawable) { mCanvas->drawDrawable(drawable); } diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h index ac8db216b059..ee4fa1d689ef 100644 --- a/libs/hwui/hwui/Canvas.h +++ b/libs/hwui/hwui/Canvas.h @@ -131,20 +131,6 @@ public: */ static void setCompatibilityVersion(int apiLevel); - /** - * Provides a Skia SkCanvas interface that acts as a proxy to this Canvas. - * It is useful for testing and clients (e.g. Picture/Movie) that expect to - * draw their contents into an SkCanvas. - * - * The SkCanvas returned is *only* valid until another Canvas call is made - * that would change state (e.g. matrix or clip). Clients of asSkCanvas() - * are responsible for *not* persisting this pointer. - * - * Further, the returned SkCanvas should NOT be unref'd and is valid until - * this canvas is destroyed or a new bitmap is set. - */ - virtual SkCanvas* asSkCanvas() = 0; - virtual void setBitmap(const SkBitmap& bitmap) = 0; virtual bool isOpaque() = 0; @@ -264,6 +250,7 @@ public: const SkPaint* paint) = 0; virtual double drawAnimatedImage(AnimatedImageDrawable* imgDrawable) = 0; + virtual void drawPicture(const SkPicture& picture) = 0; /** * Specifies if the positions passed to ::drawText are absolute or relative 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/tests/unit/SkiaCanvasTests.cpp b/libs/hwui/tests/unit/SkiaCanvasTests.cpp index f6178aff0c2e..2ed1b25efc71 100644 --- a/libs/hwui/tests/unit/SkiaCanvasTests.cpp +++ b/libs/hwui/tests/unit/SkiaCanvasTests.cpp @@ -78,7 +78,7 @@ TEST(SkiaCanvas, colorSpaceXform) { sk_sp<SkPicture> picture = recorder.finishRecordingAsPicture(); // Playback to a software sRGB canvas. The result should be fully red. - canvas.asSkCanvas()->drawPicture(picture); + canvas.drawPicture(*picture); ASSERT_EQ(0xFF0000FF, *skBitmap.getAddr32(0, 0)); } 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/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 bc3de7484bf9..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} 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/native/midi/amidi.cpp b/media/native/midi/amidi.cpp index 1e9a194d76c8..357bb5e50eee 100644 --- a/media/native/midi/amidi.cpp +++ b/media/native/midi/amidi.cpp @@ -122,7 +122,7 @@ static std::mutex openMutex; // Ensure that the device can be connected just onc * - AMEDIA_ERROR_INVALID_PARAMETER * AMEDIA_ERROR_UNKNOWN */ -static media_status_t AMIDI_API AMIDI_getDeviceInfo(const AMidiDevice *device, +static media_status_t AMIDI_getDeviceInfo(const AMidiDevice *device, AMidiDeviceInfo *outDeviceInfoPtr) { if (device == nullptr) { return AMEDIA_ERROR_INVALID_PARAMETER; @@ -142,7 +142,7 @@ static media_status_t AMIDI_API AMIDI_getDeviceInfo(const AMidiDevice *device, return AMEDIA_OK; } -media_status_t AMIDI_API AMidiDevice_fromJava(JNIEnv *env, jobject j_midiDeviceObj, +media_status_t AMidiDevice_fromJava(JNIEnv *env, jobject j_midiDeviceObj, AMidiDevice** devicePtrPtr) { if (j_midiDeviceObj == nullptr) { @@ -188,7 +188,7 @@ media_status_t AMIDI_API AMidiDevice_fromJava(JNIEnv *env, jobject j_midiDeviceO return AMEDIA_OK; } -media_status_t AMIDI_API AMidiDevice_release(const AMidiDevice *device) +media_status_t AMidiDevice_release(const AMidiDevice *device) { if (device == nullptr || device->midiDeviceObj == nullptr) { return AMEDIA_ERROR_INVALID_PARAMETER; @@ -217,21 +217,21 @@ media_status_t AMIDI_API AMidiDevice_release(const AMidiDevice *device) return AMEDIA_OK; } -int32_t AMIDI_API AMidiDevice_getType(const AMidiDevice *device) { +int32_t AMidiDevice_getType(const AMidiDevice *device) { if (device == nullptr) { return AMEDIA_ERROR_INVALID_PARAMETER; } return device->deviceInfo.type; } -ssize_t AMIDI_API AMidiDevice_getNumInputPorts(const AMidiDevice *device) { +ssize_t AMidiDevice_getNumInputPorts(const AMidiDevice *device) { if (device == nullptr) { return AMEDIA_ERROR_INVALID_PARAMETER; } return device->deviceInfo.inputPortCount; } -ssize_t AMIDI_API AMidiDevice_getNumOutputPorts(const AMidiDevice *device) { +ssize_t AMidiDevice_getNumOutputPorts(const AMidiDevice *device) { if (device == nullptr) { return AMEDIA_ERROR_INVALID_PARAMETER; } @@ -291,7 +291,7 @@ static void AMIDI_closePort(AMIDI_Port *port) { /* * Output (receiving) API */ -media_status_t AMIDI_API AMidiOutputPort_open(const AMidiDevice *device, int32_t portNumber, +media_status_t AMidiOutputPort_open(const AMidiDevice *device, int32_t portNumber, AMidiOutputPort **outOutputPortPtr) { return AMIDI_openPort(device, portNumber, PORTTYPE_OUTPUT, (AMIDI_Port**)outOutputPortPtr); } @@ -350,7 +350,7 @@ private: AMIDI_Port *mPort; }; -ssize_t AMIDI_API AMidiOutputPort_receive(const AMidiOutputPort *outputPort, int32_t *opcodePtr, +ssize_t AMidiOutputPort_receive(const AMidiOutputPort *outputPort, int32_t *opcodePtr, uint8_t *buffer, size_t maxBytes, size_t* numBytesReceivedPtr, int64_t *timestampPtr) { if (outputPort == nullptr || buffer == nullptr) { @@ -361,19 +361,19 @@ ssize_t AMIDI_API AMidiOutputPort_receive(const AMidiOutputPort *outputPort, int numBytesReceivedPtr, timestampPtr); } -void AMIDI_API AMidiOutputPort_close(const AMidiOutputPort *outputPort) { +void AMidiOutputPort_close(const AMidiOutputPort *outputPort) { AMIDI_closePort((AMIDI_Port*)outputPort); } /* * Input (sending) API */ -media_status_t AMIDI_API AMidiInputPort_open(const AMidiDevice *device, int32_t portNumber, +media_status_t AMidiInputPort_open(const AMidiDevice *device, int32_t portNumber, AMidiInputPort **outInputPortPtr) { return AMIDI_openPort(device, portNumber, PORTTYPE_INPUT, (AMIDI_Port**)outInputPortPtr); } -void AMIDI_API AMidiInputPort_close(const AMidiInputPort *inputPort) { +void AMidiInputPort_close(const AMidiInputPort *inputPort) { AMIDI_closePort((AMIDI_Port*)inputPort); } @@ -386,12 +386,12 @@ static ssize_t AMIDI_makeSendBuffer( return numBytes + AMIDI_PACKET_OVERHEAD; } -ssize_t AMIDI_API AMidiInputPort_send(const AMidiInputPort *inputPort, const uint8_t *buffer, +ssize_t AMidiInputPort_send(const AMidiInputPort *inputPort, const uint8_t *buffer, size_t numBytes) { return AMidiInputPort_sendWithTimestamp(inputPort, buffer, numBytes, 0); } -ssize_t AMIDI_API AMidiInputPort_sendWithTimestamp(const AMidiInputPort *inputPort, +ssize_t AMidiInputPort_sendWithTimestamp(const AMidiInputPort *inputPort, const uint8_t *data, size_t numBytes, int64_t timestamp) { if (inputPort == nullptr || data == nullptr) { return AMEDIA_ERROR_INVALID_PARAMETER; @@ -423,7 +423,7 @@ ssize_t AMIDI_API AMidiInputPort_sendWithTimestamp(const AMidiInputPort *inputPo return numSent; } -media_status_t AMIDI_API AMidiInputPort_sendFlush(const AMidiInputPort *inputPort) { +media_status_t AMidiInputPort_sendFlush(const AMidiInputPort *inputPort) { if (inputPort == nullptr) { return AMEDIA_ERROR_INVALID_PARAMETER; } diff --git a/media/native/midi/include/amidi/AMidi.h b/media/native/midi/include/amidi/AMidi.h index cbe410f46af9..76dec0f7e723 100644 --- a/media/native/midi/include/amidi/AMidi.h +++ b/media/native/midi/include/amidi/AMidi.h @@ -41,8 +41,6 @@ typedef struct AMidiDevice AMidiDevice; typedef struct AMidiInputPort AMidiInputPort; typedef struct AMidiOutputPort AMidiOutputPort; -#define AMIDI_API __attribute__((visibility("default"))) - /* * Message Op Codes. Used to parse MIDI data packets */ @@ -61,6 +59,8 @@ enum { AMIDI_DEVICE_TYPE_BLUETOOTH = 3 /* A MIDI device connected via BlueTooth */ }; +#if __ANDROID_API__ >= 29 + /* * Device API */ @@ -78,7 +78,7 @@ enum { * is null or already connected to a native AMidiDevice * @see AMEDIA_ERROR_UNKNOWN - an unknown error occurred. */ -media_status_t AMIDI_API AMidiDevice_fromJava( +media_status_t AMidiDevice_fromJava( JNIEnv *env, jobject midiDeviceObj, AMidiDevice **outDevicePtrPtr) __INTRODUCED_IN(29); /** @@ -93,7 +93,7 @@ media_status_t AMIDI_API AMidiDevice_fromJava( * @see AMEDIA_ERROR_INVALID_OBJECT - the JNI interface initialization to the associated java MidiDevice failed. * @see AMEDIA_ERROR_UNKNOWN - couldn't retrieve the device info. */ -media_status_t AMIDI_API AMidiDevice_release(const AMidiDevice *midiDevice) __INTRODUCED_IN(29); +media_status_t AMidiDevice_release(const AMidiDevice *midiDevice) __INTRODUCED_IN(29); /** * Gets the MIDI device type. @@ -108,7 +108,7 @@ media_status_t AMIDI_API AMidiDevice_release(const AMidiDevice *midiDevice) __IN * @see AMEDIA_ERROR_INVALID_PARAMETER - the device parameter is NULL. * @see AMEDIA_ERROR_UNKNOWN - Unknown error. */ -int32_t AMIDI_API AMidiDevice_getType(const AMidiDevice *device) __INTRODUCED_IN(29); +int32_t AMidiDevice_getType(const AMidiDevice *device) __INTRODUCED_IN(29); /** * Gets the number of input (sending) ports available on the specified MIDI device. @@ -120,7 +120,7 @@ int32_t AMIDI_API AMidiDevice_getType(const AMidiDevice *device) __INTRODUCED_IN * @see AMEDIA_ERROR_INVALID_PARAMETER - the device parameter is NULL. * @see AMEDIA_ERROR_UNKNOWN - couldn't retrieve the device info. */ -ssize_t AMIDI_API AMidiDevice_getNumInputPorts(const AMidiDevice *device) __INTRODUCED_IN(29); +ssize_t AMidiDevice_getNumInputPorts(const AMidiDevice *device) __INTRODUCED_IN(29); /** * Gets the number of output (receiving) ports available on the specified MIDI device. @@ -132,7 +132,7 @@ ssize_t AMIDI_API AMidiDevice_getNumInputPorts(const AMidiDevice *device) __INTR * @see AMEDIA_ERROR_INVALID_PARAMETER - the device parameter is NULL. * @see AMEDIA_ERROR_UNKNOWN - couldn't retrieve the device info. */ -ssize_t AMIDI_API AMidiDevice_getNumOutputPorts(const AMidiDevice *device) __INTRODUCED_IN(29); +ssize_t AMidiDevice_getNumOutputPorts(const AMidiDevice *device) __INTRODUCED_IN(29); /* * API for receiving data from the Output port of a device. @@ -150,7 +150,7 @@ ssize_t AMIDI_API AMidiDevice_getNumOutputPorts(const AMidiDevice *device) __INT * @return AMEDIA_OK, or a negative error code: * @see AMEDIA_ERROR_UNKNOWN - Unknown Error. */ -media_status_t AMIDI_API AMidiOutputPort_open(const AMidiDevice *device, int32_t portNumber, +media_status_t AMidiOutputPort_open(const AMidiDevice *device, int32_t portNumber, AMidiOutputPort **outOutputPortPtr) __INTRODUCED_IN(29); /** @@ -158,7 +158,7 @@ media_status_t AMIDI_API AMidiOutputPort_open(const AMidiDevice *device, int32_t * * @param outputPort The native API port identifier of the port. */ -void AMIDI_API AMidiOutputPort_close(const AMidiOutputPort *outputPort) __INTRODUCED_IN(29); +void AMidiOutputPort_close(const AMidiOutputPort *outputPort) __INTRODUCED_IN(29); /** * Receives the next pending MIDI message. To retrieve all pending messages, the client should @@ -178,7 +178,7 @@ void AMIDI_API AMidiOutputPort_close(const AMidiOutputPort *outputPort) __INTROD * @return the number of messages received (either 0 or 1), or a negative error code: * @see AMEDIA_ERROR_UNKNOWN - Unknown Error. */ -ssize_t AMIDI_API AMidiOutputPort_receive(const AMidiOutputPort *outputPort, int32_t *opcodePtr, +ssize_t AMidiOutputPort_receive(const AMidiOutputPort *outputPort, int32_t *opcodePtr, uint8_t *buffer, size_t maxBytes, size_t* numBytesReceivedPtr, int64_t *outTimestampPtr) __INTRODUCED_IN(29); /* @@ -197,7 +197,7 @@ ssize_t AMIDI_API AMidiOutputPort_receive(const AMidiOutputPort *outputPort, int * @return AMEDIA_OK, or a negative error code: * @see AMEDIA_ERROR_UNKNOWN - Unknown Error. */ -media_status_t AMIDI_API AMidiInputPort_open(const AMidiDevice *device, int32_t portNumber, +media_status_t AMidiInputPort_open(const AMidiDevice *device, int32_t portNumber, AMidiInputPort **outInputPortPtr) __INTRODUCED_IN(29); /** @@ -210,7 +210,7 @@ media_status_t AMIDI_API AMidiInputPort_open(const AMidiDevice *device, int32_t * @return The number of bytes sent, which could be less than specified or a negative error code: * @see AMEDIA_ERROR_INVALID_PARAMETER - The specified port was NULL, the specified buffer was NULL. */ -ssize_t AMIDI_API AMidiInputPort_send(const AMidiInputPort *inputPort, const uint8_t *buffer, +ssize_t AMidiInputPort_send(const AMidiInputPort *inputPort, const uint8_t *buffer, size_t numBytes) __INTRODUCED_IN(29); /** @@ -224,7 +224,7 @@ ssize_t AMIDI_API AMidiInputPort_send(const AMidiInputPort *inputPort, const uin * @return The number of bytes sent, which could be less than specified or a negative error code: * @see AMEDIA_ERROR_INVALID_PARAMETER - The specified port was NULL, the specified buffer was NULL. */ -ssize_t AMIDI_API AMidiInputPort_sendWithTimestamp(const AMidiInputPort *inputPort, +ssize_t AMidiInputPort_sendWithTimestamp(const AMidiInputPort *inputPort, const uint8_t *buffer, size_t numBytes, int64_t timestamp) __INTRODUCED_IN(29); /** @@ -238,14 +238,16 @@ ssize_t AMIDI_API AMidiInputPort_sendWithTimestamp(const AMidiInputPort *inputPo * @see AMEDIA_ERROR_UNSUPPORTED - The FLUSH command couldn't * be sent. */ -media_status_t AMIDI_API AMidiInputPort_sendFlush(const AMidiInputPort *inputPort) __INTRODUCED_IN(29); +media_status_t AMidiInputPort_sendFlush(const AMidiInputPort *inputPort) __INTRODUCED_IN(29); /** * Closes the input port. * * @param inputPort Identifies the input (sending) port to close. */ -void AMIDI_API AMidiInputPort_close(const AMidiInputPort *inputPort) __INTRODUCED_IN(29); +void AMidiInputPort_close(const AMidiInputPort *inputPort) __INTRODUCED_IN(29); + +#endif /* __ANDROID_API__ >= 29 */ #ifdef __cplusplus } 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/CtsShim/build/Android.mk b/packages/CtsShim/build/Android.mk index 03eb0d9aad5a..0ef4654d72e7 100644 --- a/packages/CtsShim/build/Android.mk +++ b/packages/CtsShim/build/Android.mk @@ -65,6 +65,9 @@ LOCAL_FULL_MANIFEST_FILE := $(gen) LOCAL_MULTILIB := both LOCAL_JNI_SHARED_LIBRARIES := libshim_jni +# Explicitly uncompress native libs rather than letting the build system doing it and destroy the +# v2/v3 signature. +LOCAL_USE_EMBEDDED_NATIVE_LIBS := true LOCAL_USE_AAPT2 := true 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/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java index 9d398b5b69a1..7d56868d0ce8 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; @@ -56,6 +56,7 @@ import java.time.DateTimeException; import java.util.Arrays; import java.util.HashSet; import java.util.Map; +import java.util.Set; import java.util.zip.CRC32; /** @@ -241,6 +242,8 @@ public class SettingsBackupAgent extends BackupAgentHelper { HashSet<String> movedToGlobal = new HashSet<String>(); Settings.System.getMovedToGlobalSettings(movedToGlobal); Settings.Secure.getMovedToGlobalSettings(movedToGlobal); + Set<String> movedToSecure = getMovedToSecureSettings(); + byte[] restoredWifiSupplicantData = null; byte[] restoredWifiIpConfigData = null; @@ -259,16 +262,17 @@ public class SettingsBackupAgent extends BackupAgentHelper { switch (key) { case KEY_SYSTEM : - restoreSettings(data, Settings.System.CONTENT_URI, movedToGlobal); + restoreSettings(data, Settings.System.CONTENT_URI, movedToGlobal, + movedToSecure); mSettingsHelper.applyAudioSettings(); break; case KEY_SECURE : - restoreSettings(data, Settings.Secure.CONTENT_URI, movedToGlobal); + restoreSettings(data, Settings.Secure.CONTENT_URI, movedToGlobal, null); break; case KEY_GLOBAL : - restoreSettings(data, Settings.Global.CONTENT_URI, null); + restoreSettings(data, Settings.Global.CONTENT_URI, null, movedToSecure); break; case KEY_WIFI_SUPPLICANT : @@ -347,20 +351,22 @@ public class SettingsBackupAgent extends BackupAgentHelper { HashSet<String> movedToGlobal = new HashSet<String>(); Settings.System.getMovedToGlobalSettings(movedToGlobal); Settings.Secure.getMovedToGlobalSettings(movedToGlobal); + Set<String> movedToSecure = getMovedToSecureSettings(); // system settings data first int nBytes = in.readInt(); if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of settings data"); byte[] buffer = new byte[nBytes]; in.readFully(buffer, 0, nBytes); - restoreSettings(buffer, nBytes, Settings.System.CONTENT_URI, movedToGlobal); + restoreSettings(buffer, nBytes, Settings.System.CONTENT_URI, movedToGlobal, + movedToSecure); // secure settings nBytes = in.readInt(); if (DEBUG_BACKUP) Log.d(TAG, nBytes + " bytes of secure settings data"); if (nBytes > buffer.length) buffer = new byte[nBytes]; in.readFully(buffer, 0, nBytes); - restoreSettings(buffer, nBytes, Settings.Secure.CONTENT_URI, movedToGlobal); + restoreSettings(buffer, nBytes, Settings.Secure.CONTENT_URI, movedToGlobal, null); // Global only if sufficiently new if (version >= FULL_BACKUP_ADDED_GLOBAL) { @@ -369,7 +375,8 @@ public class SettingsBackupAgent extends BackupAgentHelper { if (nBytes > buffer.length) buffer = new byte[nBytes]; in.readFully(buffer, 0, nBytes); movedToGlobal.clear(); // no redirection; this *is* the global namespace - restoreSettings(buffer, nBytes, Settings.Global.CONTENT_URI, movedToGlobal); + restoreSettings(buffer, nBytes, Settings.Global.CONTENT_URI, movedToGlobal, + movedToSecure); } // locale @@ -440,6 +447,13 @@ public class SettingsBackupAgent extends BackupAgentHelper { } } + private Set<String> getMovedToSecureSettings() { + Set<String> movedToSecureSettings = new HashSet<>(); + Settings.Global.getMovedToSecureSettings(movedToSecureSettings); + Settings.System.getMovedToSecureSettings(movedToSecureSettings); + return movedToSecureSettings; + } + private long[] readOldChecksums(ParcelFileDescriptor oldState) throws IOException { long[] stateChecksums = new long[STATE_SIZE]; @@ -564,7 +578,7 @@ public class SettingsBackupAgent extends BackupAgentHelper { } private void restoreSettings(BackupDataInput data, Uri contentUri, - HashSet<String> movedToGlobal) { + HashSet<String> movedToGlobal, Set<String> movedToSecure) { byte[] settings = new byte[data.getDataSize()]; try { data.readEntityData(settings, 0, settings.length); @@ -572,11 +586,11 @@ public class SettingsBackupAgent extends BackupAgentHelper { Log.e(TAG, "Couldn't read entity data"); return; } - restoreSettings(settings, settings.length, contentUri, movedToGlobal); + restoreSettings(settings, settings.length, contentUri, movedToGlobal, movedToSecure); } private void restoreSettings(byte[] settings, int bytes, Uri contentUri, - HashSet<String> movedToGlobal) { + HashSet<String> movedToGlobal, Set<String> movedToSecure) { if (DEBUG) { Log.i(TAG, "restoreSettings: " + contentUri); } @@ -586,15 +600,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 { @@ -651,9 +665,14 @@ public class SettingsBackupAgent extends BackupAgentHelper { continue; } - final Uri destination = (movedToGlobal != null && movedToGlobal.contains(key)) - ? Settings.Global.CONTENT_URI - : contentUri; + final Uri destination; + if (movedToGlobal != null && movedToGlobal.contains(key)) { + destination = Settings.Global.CONTENT_URI; + } else if (movedToSecure != null && movedToSecure.contains(key)) { + destination = Settings.Secure.CONTENT_URI; + } else { + destination = contentUri; + } settingsHelper.restoreValue(this, cr, contentValues, destination, key, value, mRestoredFromSdkInt); @@ -672,18 +691,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/ExamplePlugin/Android.bp b/packages/SystemUI/plugin/ExamplePlugin/Android.bp index a0eaf14f4a06..c6c80f3780a3 100644 --- a/packages/SystemUI/plugin/ExamplePlugin/Android.bp +++ b/packages/SystemUI/plugin/ExamplePlugin/Android.bp @@ -11,4 +11,5 @@ android_app { srcs: ["src/**/*.java"], + platform_apis: true, } 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/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/plugins/PluginManagerImpl.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java index 53403aa4dbf1..a7e3c59ce72e 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java @@ -171,15 +171,21 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage PluginInstanceManager p = mFactory.createPluginInstanceManager(mContext, action, listener, allowMultiple, mLooper, cls, this); p.loadAll(); - mPluginMap.put(listener, p); + synchronized (this) { + mPluginMap.put(listener, p); + } startListening(); } public void removePluginListener(PluginListener<?> listener) { - if (!mPluginMap.containsKey(listener)) return; - mPluginMap.remove(listener).destroy(); - if (mPluginMap.size() == 0) { - stopListening(); + synchronized (this) { + if (!mPluginMap.containsKey(listener)) { + return; + } + mPluginMap.remove(listener).destroy(); + if (mPluginMap.size() == 0) { + stopListening(); + } } } @@ -208,8 +214,10 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage @Override public void onReceive(Context context, Intent intent) { if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) { - for (PluginInstanceManager manager : mPluginMap.values()) { - manager.loadAll(); + synchronized (this) { + for (PluginInstanceManager manager : mPluginMap.values()) { + manager.loadAll(); + } } } else if (DISABLE_PLUGIN.equals(intent.getAction())) { Uri uri = intent.getData(); @@ -274,13 +282,15 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage getPluginEnabler().setEnabled(componentName); } } - if (!Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) { - for (PluginInstanceManager manager : mPluginMap.values()) { - manager.onPackageChange(pkg); - } - } else { - for (PluginInstanceManager manager : mPluginMap.values()) { - manager.onPackageRemoved(pkg); + synchronized (this) { + if (!Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) { + for (PluginInstanceManager manager : mPluginMap.values()) { + manager.onPackageChange(pkg); + } + } else { + for (PluginInstanceManager manager : mPluginMap.values()) { + manager.onPackageRemoved(pkg); + } } } } @@ -322,9 +332,11 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage } public <T> boolean dependsOn(Plugin p, Class<T> cls) { - for (int i = 0; i < mPluginMap.size(); i++) { - if (mPluginMap.valueAt(i).dependsOn(p, cls)) { - return true; + synchronized (this) { + for (int i = 0; i < mPluginMap.size(); i++) { + if (mPluginMap.valueAt(i).dependsOn(p, cls)) { + return true; + } } } return false; @@ -335,10 +347,12 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage } public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - pw.println(String.format(" plugin map (%d):", mPluginMap.size())); - for (PluginListener listener: mPluginMap.keySet()) { - pw.println(String.format(" %s -> %s", - listener, mPluginMap.get(listener))); + synchronized (this) { + pw.println(String.format(" plugin map (%d):", mPluginMap.size())); + for (PluginListener listener : mPluginMap.keySet()) { + pw.println(String.format(" %s -> %s", + listener, mPluginMap.get(listener))); + } } } @@ -418,8 +432,10 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage // We couldn't find any plugins involved in this crash, just to be safe // disable all the plugins, so we can be sure that SysUI is running as // best as possible. - for (PluginInstanceManager manager : mPluginMap.values()) { - disabledAny |= manager.disableAll(); + synchronized (this) { + for (PluginInstanceManager manager : mPluginMap.values()) { + disabledAny |= manager.disableAll(); + } } } if (disabledAny) { @@ -433,9 +449,11 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage private boolean checkStack(Throwable throwable) { if (throwable == null) return false; boolean disabledAny = false; - for (StackTraceElement element : throwable.getStackTrace()) { - for (PluginInstanceManager manager : mPluginMap.values()) { - disabledAny |= manager.checkAndDisable(element.getClassName()); + synchronized (this) { + for (StackTraceElement element : throwable.getStackTrace()) { + for (PluginInstanceManager manager : mPluginMap.values()) { + disabledAny |= manager.checkAndDisable(element.getClassName()); + } } } return disabledAny | checkStack(throwable.getCause()); 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/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java index cc7863c0113b..2a890fe20d8a 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java @@ -80,6 +80,8 @@ public class QuickStepContract { public static final int SYSUI_STATE_HOME_DISABLED = 1 << 8; // The keyguard is showing, but occluded public static final int SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED = 1 << 9; + // The search feature is disabled (either by SUW/SysUI/device policy) + public static final int SYSUI_STATE_SEARCH_DISABLED = 1 << 10; @Retention(RetentionPolicy.SOURCE) @IntDef({SYSUI_STATE_SCREEN_PINNING, @@ -91,7 +93,8 @@ public class QuickStepContract { SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING, SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED, SYSUI_STATE_OVERVIEW_DISABLED, - SYSUI_STATE_HOME_DISABLED + SYSUI_STATE_HOME_DISABLED, + SYSUI_STATE_SEARCH_DISABLED }) public @interface SystemUiStateFlags {} @@ -100,6 +103,7 @@ public class QuickStepContract { str.add((flags & SYSUI_STATE_SCREEN_PINNING) != 0 ? "screen_pinned" : ""); str.add((flags & SYSUI_STATE_OVERVIEW_DISABLED) != 0 ? "overview_disabled" : ""); str.add((flags & SYSUI_STATE_HOME_DISABLED) != 0 ? "home_disabled" : ""); + str.add((flags & SYSUI_STATE_SEARCH_DISABLED) != 0 ? "search_disabled" : ""); str.add((flags & SYSUI_STATE_NAV_BAR_HIDDEN) != 0 ? "navbar_hidden" : ""); str.add((flags & SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED) != 0 ? "notif_visible" : ""); str.add((flags & SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING) != 0 ? "keygrd_visible" : ""); @@ -150,10 +154,11 @@ public class QuickStepContract { * disabled. */ public static boolean isAssistantGestureDisabled(int sysuiStateFlags) { - // Disable when in screen pinning, immersive, the bouncer is showing + // Disable when in screen pinning, immersive, the bouncer is showing, or search is disabled int disableFlags = SYSUI_STATE_SCREEN_PINNING | SYSUI_STATE_NAV_BAR_HIDDEN - | SYSUI_STATE_BOUNCER_SHOWING; + | SYSUI_STATE_BOUNCER_SHOWING + | SYSUI_STATE_SEARCH_DISABLED; if ((sysuiStateFlags & disableFlags) != 0) { return true; } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java index d2fe5cd9ef64..2ef042210e67 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java @@ -91,6 +91,7 @@ public class RecentsAnimationControllerCompat { } } + @Deprecated public void setCancelWithDeferredScreenshot(boolean screenshot) { try { mAnimationController.setCancelWithDeferredScreenshot(screenshot); @@ -99,6 +100,14 @@ public class RecentsAnimationControllerCompat { } } + public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) { + try { + mAnimationController.setDeferCancelUntilNextTransition(defer, screenshot); + } catch (RemoteException e) { + Log.e(TAG, "Failed to set deferred cancel with screenshot", e); + } + } + public void cleanupScreenshot() { try { mAnimationController.cleanupScreenshot(); diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentTaskInfoCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskInfoCompat.java index a5299038d3a9..326c2aa37175 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentTaskInfoCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskInfoCompat.java @@ -17,29 +17,32 @@ package com.android.systemui.shared.system; import android.app.ActivityManager; +import android.app.TaskInfo; import android.content.ComponentName; -public class RecentTaskInfoCompat { +public class TaskInfoCompat { - private ActivityManager.RecentTaskInfo mInfo; + public static int getUserId(TaskInfo info) { + return info.userId; + } - public RecentTaskInfoCompat(ActivityManager.RecentTaskInfo info) { - mInfo = info; + public static int getActivityType(TaskInfo info) { + return info.configuration.windowConfiguration.getActivityType(); } - public int getUserId() { - return mInfo.userId; + public static int getWindowingMode(TaskInfo info) { + return info.configuration.windowConfiguration.getWindowingMode(); } - public boolean supportsSplitScreenMultiWindow() { - return mInfo.supportsSplitScreenMultiWindow; + public static boolean supportsSplitScreenMultiWindow(TaskInfo info) { + return info.supportsSplitScreenMultiWindow; } - public ComponentName getTopActivity() { - return mInfo.topActivity; + public static ComponentName getTopActivity(TaskInfo info) { + return info.topActivity; } - public ActivityManager.TaskDescription getTaskDescription() { - return mInfo.taskDescription; + public static ActivityManager.TaskDescription getTaskDescription(TaskInfo info) { + return info.taskDescription; } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java index 2483192eb04e..5097216a7405 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java @@ -584,6 +584,9 @@ public class KeyguardClockSwitch extends RelativeLayout { @Override public Animator onAppear(ViewGroup sceneRoot, View view, TransitionValues startValues, TransitionValues endValues) { + if (!sceneRoot.isShown()) { + return null; + } final float cutoff = mCutoff; final int startVisibility = View.INVISIBLE; final int endVisibility = (int) endValues.values.get(PROPNAME_VISIBILITY); @@ -596,6 +599,9 @@ public class KeyguardClockSwitch extends RelativeLayout { @Override public Animator onDisappear(ViewGroup sceneRoot, View view, TransitionValues startValues, TransitionValues endValues) { + if (!sceneRoot.isShown()) { + return null; + } final float cutoff = 1f - mCutoff; final int startVisibility = View.VISIBLE; final int endVisibility = (int) endValues.values.get(PROPNAME_VISIBILITY); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java index c7c648cd023f..8059dcf33df7 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java @@ -269,7 +269,7 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe */ private void updateBiometricRetry() { SecurityMode securityMode = getSecurityMode(); - mSwipeUpToRetry = mUnlockMethodCache.isUnlockingWithFacePossible() + mSwipeUpToRetry = mUnlockMethodCache.isFaceAuthEnabled() && securityMode != SecurityMode.SimPin && securityMode != SecurityMode.SimPuk && securityMode != SecurityMode.None; @@ -513,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/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java index e219e24a8944..af4e61b3f6bc 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java @@ -89,6 +89,7 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe private final HashMap<View, PendingIntent> mClickActions; private final ActivityStarter mActivityStarter; private final ConfigurationController mConfigurationController; + private final LayoutTransition mLayoutTransition; private Uri mKeyguardSliceUri; @VisibleForTesting TextView mTitle; @@ -126,16 +127,16 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe mActivityStarter = activityStarter; mConfigurationController = configurationController; - LayoutTransition transition = new LayoutTransition(); - transition.setStagger(LayoutTransition.CHANGE_APPEARING, DEFAULT_ANIM_DURATION / 2); - transition.setDuration(LayoutTransition.APPEARING, DEFAULT_ANIM_DURATION); - transition.setDuration(LayoutTransition.DISAPPEARING, DEFAULT_ANIM_DURATION / 2); - transition.disableTransitionType(LayoutTransition.CHANGE_APPEARING); - transition.disableTransitionType(LayoutTransition.CHANGE_DISAPPEARING); - transition.setInterpolator(LayoutTransition.APPEARING, Interpolators.FAST_OUT_SLOW_IN); - transition.setInterpolator(LayoutTransition.DISAPPEARING, Interpolators.ALPHA_OUT); - transition.setAnimateParentHierarchy(false); - setLayoutTransition(transition); + mLayoutTransition = new LayoutTransition(); + mLayoutTransition.setStagger(LayoutTransition.CHANGE_APPEARING, DEFAULT_ANIM_DURATION / 2); + mLayoutTransition.setDuration(LayoutTransition.APPEARING, DEFAULT_ANIM_DURATION); + mLayoutTransition.setDuration(LayoutTransition.DISAPPEARING, DEFAULT_ANIM_DURATION / 2); + mLayoutTransition.disableTransitionType(LayoutTransition.CHANGE_APPEARING); + mLayoutTransition.disableTransitionType(LayoutTransition.CHANGE_DISAPPEARING); + mLayoutTransition.setInterpolator(LayoutTransition.APPEARING, + Interpolators.FAST_OUT_SLOW_IN); + mLayoutTransition.setInterpolator(LayoutTransition.DISAPPEARING, Interpolators.ALPHA_OUT); + mLayoutTransition.setAnimateParentHierarchy(false); } @Override @@ -174,6 +175,12 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe mConfigurationController.removeCallback(this); } + @Override + public void onVisibilityAggregated(boolean isVisible) { + super.onVisibilityAggregated(isVisible); + setLayoutTransition(isVisible ? mLayoutTransition : null); + } + /** * Returns whether the current visible slice has a title/header. */ @@ -419,6 +426,7 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe * their desired positions. */ private final Animation.AnimationListener mKeepAwakeListener; + private LayoutTransition mLayoutTransition; private float mDarkAmount; public Row(Context context) { @@ -440,33 +448,41 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe @Override protected void onFinishInflate() { - LayoutTransition transition = new LayoutTransition(); - transition.setDuration(DEFAULT_ANIM_DURATION); + mLayoutTransition = new LayoutTransition(); + mLayoutTransition.setDuration(DEFAULT_ANIM_DURATION); PropertyValuesHolder left = PropertyValuesHolder.ofInt("left", 0, 1); PropertyValuesHolder right = PropertyValuesHolder.ofInt("right", 0, 1); ObjectAnimator changeAnimator = ObjectAnimator.ofPropertyValuesHolder((Object) null, left, right); - transition.setAnimator(LayoutTransition.CHANGE_APPEARING, changeAnimator); - transition.setAnimator(LayoutTransition.CHANGE_DISAPPEARING, changeAnimator); - transition.setInterpolator(LayoutTransition.CHANGE_APPEARING, + mLayoutTransition.setAnimator(LayoutTransition.CHANGE_APPEARING, changeAnimator); + mLayoutTransition.setAnimator(LayoutTransition.CHANGE_DISAPPEARING, changeAnimator); + mLayoutTransition.setInterpolator(LayoutTransition.CHANGE_APPEARING, Interpolators.ACCELERATE_DECELERATE); - transition.setInterpolator(LayoutTransition.CHANGE_DISAPPEARING, + mLayoutTransition.setInterpolator(LayoutTransition.CHANGE_DISAPPEARING, Interpolators.ACCELERATE_DECELERATE); - transition.setStartDelay(LayoutTransition.CHANGE_APPEARING, DEFAULT_ANIM_DURATION); - transition.setStartDelay(LayoutTransition.CHANGE_DISAPPEARING, DEFAULT_ANIM_DURATION); + mLayoutTransition.setStartDelay(LayoutTransition.CHANGE_APPEARING, + DEFAULT_ANIM_DURATION); + mLayoutTransition.setStartDelay(LayoutTransition.CHANGE_DISAPPEARING, + DEFAULT_ANIM_DURATION); ObjectAnimator appearAnimator = ObjectAnimator.ofFloat(null, "alpha", 0f, 1f); - transition.setAnimator(LayoutTransition.APPEARING, appearAnimator); - transition.setInterpolator(LayoutTransition.APPEARING, Interpolators.ALPHA_IN); + mLayoutTransition.setAnimator(LayoutTransition.APPEARING, appearAnimator); + mLayoutTransition.setInterpolator(LayoutTransition.APPEARING, Interpolators.ALPHA_IN); ObjectAnimator disappearAnimator = ObjectAnimator.ofFloat(null, "alpha", 1f, 0f); - transition.setInterpolator(LayoutTransition.DISAPPEARING, Interpolators.ALPHA_OUT); - transition.setDuration(LayoutTransition.DISAPPEARING, DEFAULT_ANIM_DURATION / 4); - transition.setAnimator(LayoutTransition.DISAPPEARING, disappearAnimator); + mLayoutTransition.setInterpolator(LayoutTransition.DISAPPEARING, + Interpolators.ALPHA_OUT); + mLayoutTransition.setDuration(LayoutTransition.DISAPPEARING, DEFAULT_ANIM_DURATION / 4); + mLayoutTransition.setAnimator(LayoutTransition.DISAPPEARING, disappearAnimator); - transition.setAnimateParentHierarchy(false); - setLayoutTransition(transition); + mLayoutTransition.setAnimateParentHierarchy(false); + } + + @Override + public void onVisibilityAggregated(boolean isVisible) { + super.onVisibilityAggregated(isVisible); + setLayoutTransition(isVisible ? mLayoutTransition : null); } @Override diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 2eb93d3a6075..4e7b15715243 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; } @@ -1782,14 +1783,17 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { && mFpm.getEnrolledFingerprints(userId).size() > 0; } + private boolean isUnlockWithFacePossible(int userId) { + return isFaceAuthEnabledForUser(userId) && !isFaceDisabled(userId); + } + /** - * 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. */ - public boolean isUnlockWithFacePossible(int userId) { + public boolean isFaceAuthEnabledForUser(int userId) { return mFaceManager != null && mFaceManager.isHardwareDetected() - && !isFaceDisabled(userId) - && mFaceManager.hasEnrolledTemplates(userId); + && mFaceManager.hasEnrolledTemplates(userId) + && mFaceSettingEnabledForUser.get(userId); } private void stopListeningForFingerprint() { @@ -2657,7 +2661,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/SystemBars.java b/packages/SystemUI/src/com/android/systemui/SystemBars.java index 4285af08e734..c4c0fd6da124 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemBars.java +++ b/packages/SystemUI/src/com/android/systemui/SystemBars.java @@ -46,6 +46,13 @@ public class SystemBars extends SystemUI { } } + @Override + public void onBootCompleted() { + if (mStatusBar != null) { + mStatusBar.onBootCompleted(); + } + } + private void createStatusBarFromConfig() { if (DEBUG) Log.d(TAG, "createStatusBarFromConfig"); final String clsName = mContext.getString(R.string.config_statusBarComponent); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java index a5857df8ba5a..ce67577ea483 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java @@ -111,6 +111,9 @@ public abstract class BiometricDialogView extends LinearLayout { protected boolean mRequireConfirmation; private int mUserId; // used to determine if we should show work background + private boolean mCompletedAnimatingIn; + private boolean mPendingDismissDialog; + protected abstract int getHintStringResourceId(); protected abstract int getAuthenticatedAccessibilityResourceId(); protected abstract int getIconDescriptionResourceId(); @@ -332,6 +335,7 @@ public abstract class BiometricDialogView extends LinearLayout { mDialog.setAlpha(1.0f); mDialog.setTranslationY(0); mLayout.setAlpha(1.0f); + mCompletedAnimatingIn = true; } else { // Dim the background and slide the dialog up mDialog.setTranslationY(mAnimationTranslationOffset); @@ -352,6 +356,12 @@ public abstract class BiometricDialogView extends LinearLayout { } public void startDismiss() { + if (!mCompletedAnimatingIn) { + Log.w(TAG, "startDismiss(): waiting for onDialogAnimatedIn"); + mPendingDismissDialog = true; + return; + } + mAnimatingAway = true; // This is where final cleanup should occur. @@ -499,6 +509,13 @@ public abstract class BiometricDialogView extends LinearLayout { } public void onDialogAnimatedIn() { + mCompletedAnimatingIn = true; + + if (mPendingDismissDialog) { + Log.d(TAG, "onDialogAnimatedIn(): mPendingDismissDialog=true, dismissing now"); + startDismiss(); + mPendingDismissDialog = false; + } } public void restoreState(Bundle bundle) { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java index 729242e3144f..ae6cb5ce23d3 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java @@ -460,6 +460,7 @@ public class FaceDialogView extends BiometricDialogView { @Override public void onDialogAnimatedIn() { + super.onDialogAnimatedIn(); mDialogAnimatedIn = true; mIconController.startPulsing(); } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java index 8210951e1f96..128cc614fd08 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java @@ -92,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 { 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 cee01a4d3048..41f20ec28902 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java @@ -105,10 +105,12 @@ public class BrightLineFalsingManager implements FalsingManager { } private void sessionStart() { - logDebug("Starting Session"); - mSessionStarted = true; - registerSensors(); - mClassifiers.forEach(FalsingClassifier::onSessionStarted); + if (!mSessionStarted) { + logDebug("Starting Session"); + mSessionStarted = true; + registerSensors(); + mClassifiers.forEach(FalsingClassifier::onSessionStarted); + } } private void sessionEnd() { 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/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/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java index 6918501bfb9f..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; @@ -290,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) { @@ -359,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); } } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java index e877d4446c63..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(); 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/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java index 5136682bb292..48f32cf04fb2 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java @@ -52,6 +52,7 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.systemui.R; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.NotificationMediaManager; +import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.policy.NextAlarmController; @@ -62,7 +63,6 @@ import com.android.systemui.util.wakelock.SettableWakeLock; import com.android.systemui.util.wakelock.WakeLock; import java.util.Date; -import java.util.HashSet; import java.util.Locale; import java.util.TimeZone; import java.util.concurrent.TimeUnit; @@ -128,6 +128,7 @@ public class KeyguardSliceProvider extends SliceProvider implements private CharSequence mMediaTitle; private CharSequence mMediaArtist; protected boolean mDozing; + private int mStatusBarState; private boolean mMediaIsVisible; /** @@ -231,7 +232,11 @@ public class KeyguardSliceProvider extends SliceProvider implements protected boolean needsMediaLocked() { boolean keepWhenAwake = mKeyguardBypassController != null && mKeyguardBypassController.getBypassEnabled() && mDozeParameters.getAlwaysOn(); - return !TextUtils.isEmpty(mMediaTitle) && mMediaIsVisible && (mDozing || keepWhenAwake); + // Show header if music is playing and the status bar is in the shade state. This way, an + // animation isn't necessary when pressing power and transitioning to AOD. + boolean keepWhenShade = mStatusBarState == StatusBarState.SHADE && mMediaIsVisible; + return !TextUtils.isEmpty(mMediaTitle) && mMediaIsVisible && (mDozing || keepWhenAwake + || keepWhenShade); } protected void addMediaLocked(ListBuilder listBuilder) { @@ -458,7 +463,7 @@ public class KeyguardSliceProvider extends SliceProvider implements synchronized (this) { boolean nextVisible = NotificationMediaManager.isPlayingState(state); mHandler.removeCallbacksAndMessages(mMediaToken); - if (mMediaIsVisible && !nextVisible) { + if (mMediaIsVisible && !nextVisible && mStatusBarState != StatusBarState.SHADE) { // We need to delay this event for a few millis when stopping to avoid jank in the // animation. The media app might not send its update when buffering, and the slice // would end up without a header for 0.5 second. @@ -515,5 +520,14 @@ public class KeyguardSliceProvider extends SliceProvider implements @Override public void onStateChanged(int newState) { + final boolean notify; + synchronized (this) { + boolean needsMedia = needsMediaLocked(); + mStatusBarState = newState; + notify = needsMedia != needsMediaLocked(); + } + if (notify) { + notifyChange(); + } } } 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/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java index a9896f51369c..822a6669bd5c 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java @@ -22,6 +22,8 @@ import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; +import android.content.ContentResolver; +import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; @@ -48,6 +50,7 @@ import com.android.systemui.R; import java.io.File; import java.io.IOException; +import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Path; import java.text.SimpleDateFormat; @@ -158,19 +161,34 @@ public class RecordingService extends Service { case ACTION_STOP: stopRecording(); - // Move temp file to user directory - File recordDir = new File( - Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES), - RECORD_DIR); - recordDir.mkdirs(); - String fileName = new SimpleDateFormat("'screen-'yyyyMMdd-HHmmss'.mp4'") .format(new Date()); - Path path = new File(recordDir, fileName).toPath(); + ContentValues values = new ContentValues(); + values.put(MediaStore.Video.Media.DISPLAY_NAME, fileName); + values.put(MediaStore.Video.Media.MIME_TYPE, "video/mp4"); + values.put(MediaStore.Video.Media.DATE_ADDED, System.currentTimeMillis()); + values.put(MediaStore.Video.Media.DATE_TAKEN, System.currentTimeMillis()); + + ContentResolver resolver = getContentResolver(); + Uri collectionUri = MediaStore.Video.Media.getContentUri( + MediaStore.VOLUME_EXTERNAL_PRIMARY); + Uri itemUri = resolver.insert(collectionUri, values); + + File recordDir = new File(getExternalFilesDir(Environment.DIRECTORY_MOVIES), + RECORD_DIR); + recordDir.mkdirs(); + Path path = new File(recordDir, fileName).toPath(); try { + // Move file out of temp directory Files.move(mTempFile.toPath(), path); - Notification notification = createSaveNotification(path); + + // Add to the mediastore + OutputStream os = resolver.openOutputStream(itemUri, "w"); + Files.copy(path, os); + os.close(); + + Notification notification = createSaveNotification(itemUri, path); notificationManager.notify(NOTIFICATION_ID, notification); } catch (IOException e) { e.printStackTrace(); @@ -352,13 +370,12 @@ public class RecordingService extends Service { notificationManager.notify(NOTIFICATION_ID, mRecordingNotificationBuilder.build()); } - private Notification createSaveNotification(Path path) { - Uri saveUri = FileProvider.getUriForFile(this, FILE_PROVIDER, path.toFile()); - Log.d(TAG, "Screen recording saved to " + path.toString()); + private Notification createSaveNotification(Uri uri, Path path) { + Log.d(TAG, "Screen recording saved to " + uri.toString() + ", " + path.toString()); Intent viewIntent = new Intent(Intent.ACTION_VIEW) .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_GRANT_READ_URI_PERMISSION) - .setDataAndType(saveUri, "video/mp4"); + .setDataAndType(uri, "video/mp4"); Notification.Action shareAction = new Notification.Action.Builder( Icon.createWithResource(this, R.drawable.ic_android), @@ -366,7 +383,7 @@ public class RecordingService extends Service { PendingIntent.getService( this, REQUEST_CODE, - getShareIntent(this, path.toString()), + getShareIntent(this, uri.toString()), PendingIntent.FLAG_UPDATE_CURRENT)) .build(); @@ -376,7 +393,7 @@ public class RecordingService extends Service { PendingIntent.getService( this, REQUEST_CODE, - getDeleteIntent(this, path.toString()), + getDeleteIntent(this, uri.toString()), PendingIntent.FLAG_UPDATE_CURRENT)) .build(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java index 0fe5f8a6af5a..4cc5b2144adc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java @@ -15,7 +15,6 @@ package com.android.systemui.statusbar; import android.content.pm.UserInfo; -import android.service.notification.StatusBarNotification; import android.util.SparseArray; import com.android.systemui.statusbar.notification.collection.NotificationEntry; @@ -58,7 +57,7 @@ public interface NotificationLockscreenUserManager { boolean shouldHideNotifications(int userId); boolean shouldHideNotifications(String key); - boolean shouldShowOnKeyguard(StatusBarNotification sbn); + boolean shouldShowOnKeyguard(NotificationEntry entry); boolean isAnyProfilePublicMode(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java index 4ea1ed5b9451..e08a5ae07bd8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java @@ -33,7 +33,6 @@ import android.os.ServiceManager; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; -import android.service.notification.StatusBarNotification; import android.util.Log; import android.util.SparseArray; import android.util.SparseBooleanArray; @@ -302,7 +301,7 @@ public class NotificationLockscreenUserManagerImpl implements Notification.VISIBILITY_SECRET; } - public boolean shouldShowOnKeyguard(StatusBarNotification sbn) { + public boolean shouldShowOnKeyguard(NotificationEntry entry) { if (getEntryManager() == null) { Log.wtf(TAG, "mEntryManager was null!", new Throwable()); return false; @@ -310,10 +309,10 @@ public class NotificationLockscreenUserManagerImpl implements boolean exceedsPriorityThreshold; if (NotificationUtils.useNewInterruptionModel(mContext) && hideSilentNotificationsOnLockscreen()) { - exceedsPriorityThreshold = getEntryManager().getNotificationData().isHighPriority(sbn); + exceedsPriorityThreshold = entry.isTopBucket(); } else { exceedsPriorityThreshold = - !getEntryManager().getNotificationData().isAmbient(sbn.getKey()); + !getEntryManager().getNotificationData().isAmbient(entry.key); } return mShowLockscreenNotifications && exceedsPriorityThreshold; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java index f001561754aa..00a12a9e4409 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java @@ -17,6 +17,10 @@ package com.android.systemui.statusbar; import static com.android.systemui.Dependency.MAIN_HANDLER; import static com.android.systemui.statusbar.StatusBarState.KEYGUARD; +import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_UNLOCK_FADING; +import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK; +import static com.android.systemui.statusbar.phone.BiometricUnlockController + .MODE_WAKE_AND_UNLOCK_PULSING; import static com.android.systemui.statusbar.phone.StatusBar.DEBUG_MEDIA_FAKE_ARTWORK; import static com.android.systemui.statusbar.phone.StatusBar.ENABLE_LOCKSCREEN_WALLPAPER; import static com.android.systemui.statusbar.phone.StatusBar.SHOW_LOCKSCREEN_MEDIA_ARTWORK; @@ -595,9 +599,11 @@ public class NotificationMediaManager implements Dumpable { boolean cannotAnimateDoze = shadeController != null && shadeController.isDozing() && !ScrimState.AOD.getAnimateChange(); - if (mBiometricUnlockController != null && mBiometricUnlockController.getMode() + boolean needsBypassFading = mKeyguardMonitor.isBypassFadingAnimation(); + if (((mBiometricUnlockController != null && mBiometricUnlockController.getMode() == BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING - || hideBecauseOccluded || cannotAnimateDoze) { + || cannotAnimateDoze) && !needsBypassFading) + || hideBecauseOccluded) { // We are unlocking directly - no animation! mBackdrop.setVisibility(View.GONE); @@ -622,9 +628,7 @@ public class NotificationMediaManager implements Dumpable { }); if (mKeyguardMonitor.isKeyguardFadingAway()) { mBackdrop.animate() - // Make it disappear faster, as the focus should be on the activity - // behind. - .setDuration(mKeyguardMonitor.getKeyguardFadingAwayDuration() / 2) + .setDuration(mKeyguardMonitor.getShortenedFadingAwayDuration()) .setStartDelay(mKeyguardMonitor.getKeyguardFadingAwayDelay()) .setInterpolator(Interpolators.LINEAR) .start(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java index 22c91647d7f7..6e75c0375afc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java @@ -397,15 +397,13 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle int userId = entry.notification.getUserId(); boolean suppressedSummary = mGroupManager.isSummaryOfSuppressedGroup( entry.notification) && !entry.isRowRemoved(); - boolean showOnKeyguard = mLockscreenUserManager.shouldShowOnKeyguard(entry - .notification); + boolean showOnKeyguard = mLockscreenUserManager.shouldShowOnKeyguard(entry); if (!showOnKeyguard) { // min priority notifications should show if their summary is showing if (mGroupManager.isChildInGroupWithSummary(entry.notification)) { NotificationEntry summary = mGroupManager.getLogicalGroupSummary( entry.notification); - if (summary != null && mLockscreenUserManager.shouldShowOnKeyguard( - summary.notification)) { + if (summary != null && mLockscreenUserManager.shouldShowOnKeyguard(summary)) { showOnKeyguard = true; } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/BypassHeadsUpNotifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/BypassHeadsUpNotifier.kt index ea474ced7632..314dc04e574f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/BypassHeadsUpNotifier.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/BypassHeadsUpNotifier.kt @@ -21,6 +21,7 @@ import android.media.MediaMetadata import android.provider.Settings import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.statusbar.NotificationLockscreenUserManager import com.android.systemui.statusbar.NotificationMediaManager import com.android.systemui.statusbar.StatusBarState import com.android.systemui.statusbar.notification.collection.NotificationEntry @@ -40,6 +41,7 @@ class BypassHeadsUpNotifier @Inject constructor( private val bypassController: KeyguardBypassController, private val statusBarStateController: StatusBarStateController, private val headsUpManager: HeadsUpManagerPhone, + private val notificationLockscreenUserManager: NotificationLockscreenUserManager, private val mediaManager: NotificationMediaManager, tunerService: TunerService) : StatusBarStateController.StateListener, NotificationMediaManager.MediaListener { @@ -63,7 +65,7 @@ class BypassHeadsUpNotifier @Inject constructor( enabled = Settings.Secure.getIntForUser( context.contentResolver, Settings.Secure.SHOW_MEDIA_WHEN_BYPASSING, - 1 /* default */, + 0 /* default */, KeyguardUpdateMonitor.getCurrentUser()) != 0 }, Settings.Secure.SHOW_MEDIA_WHEN_BYPASSING) } @@ -79,9 +81,6 @@ class BypassHeadsUpNotifier @Inject constructor( if (!NotificationMediaManager.isPlayingState(state)) { newEntry = null } - if (newEntry?.isSensitive == true) { - newEntry = null - } currentMediaEntry = newEntry updateAutoHeadsUp(previous) updateAutoHeadsUp(currentMediaEntry) @@ -89,7 +88,7 @@ class BypassHeadsUpNotifier @Inject constructor( private fun updateAutoHeadsUp(entry: NotificationEntry?) { entry?.let { - val autoHeadsUp = it == currentMediaEntry && canAutoHeadsUp() + val autoHeadsUp = it == currentMediaEntry && canAutoHeadsUp(it) it.isAutoHeadsUp = autoHeadsUp if (autoHeadsUp) { headsUpManager.showNotification(it) @@ -97,11 +96,36 @@ class BypassHeadsUpNotifier @Inject constructor( } } + /** + * @return {@code true} if this entry be autoHeadsUpped right now. + */ + private fun canAutoHeadsUp(entry: NotificationEntry): Boolean { + if (!isAutoHeadsUpAllowed()) { + return false; + } + if (entry.isSensitive) { + // filter sensitive notifications + return false + } + if (!notificationLockscreenUserManager.shouldShowOnKeyguard(entry)) { + // filter notifications invisible on Keyguard + return false + } + if (!entryManager.notificationData.activeNotifications.contains(entry)) { + // filter notifications not the active list currently + return false + } + return true + } + override fun onStatePostChange() { updateAutoHeadsUp(currentMediaEntry) } - private fun canAutoHeadsUp() : Boolean { + /** + * @return {@code true} if autoHeadsUp is possible right now. + */ + private fun isAutoHeadsUpAllowed() : Boolean { if (!enabled) { return false } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt index 6a3816c50330..6af1f5bca07f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt @@ -29,6 +29,7 @@ import com.android.systemui.statusbar.phone.DozeParameters import com.android.systemui.statusbar.phone.HeadsUpManagerPhone import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.phone.NotificationIconAreaController +import com.android.systemui.statusbar.phone.PanelExpansionListener import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener import javax.inject.Inject @@ -40,7 +41,8 @@ class NotificationWakeUpCoordinator @Inject constructor( private val mHeadsUpManagerPhone: HeadsUpManagerPhone, private val statusBarStateController: StatusBarStateController, private val bypassController: KeyguardBypassController) - : OnHeadsUpChangedListener, StatusBarStateController.StateListener { + : OnHeadsUpChangedListener, StatusBarStateController.StateListener, + PanelExpansionListener { private val mNotificationVisibility = object : FloatProperty<NotificationWakeUpCoordinator>("notificationVisibility") { @@ -64,7 +66,6 @@ class NotificationWakeUpCoordinator @Inject constructor( private var mVisibilityAnimator: ObjectAnimator? = null private var mVisibilityAmount = 0.0f private var mLinearVisibilityAmount = 0.0f - private var mWakingUp = false private val mEntrySetToClearWhenFinished = mutableSetOf<NotificationEntry>() private val mDozeParameters: DozeParameters private var pulseExpanding: Boolean = false @@ -73,6 +74,25 @@ class NotificationWakeUpCoordinator @Inject constructor( var fullyAwake: Boolean = false + var wakingUp = false + set(value) { + field = value + willWakeUp = false + if (value) { + if (mNotificationsVisible && !mNotificationsVisibleForExpansion + && !bypassController.bypassEnabled) { + // We're waking up while pulsing, let's make sure the animation looks nice + mStackScroller.wakeUpFromPulse(); + } + if (bypassController.bypassEnabled && !mNotificationsVisible) { + // Let's make sure our huns become visible once we are waking up in case + // they were blocked by the proximity sensor + updateNotificationVisibility(animate = shouldAnimateVisibility(), + increaseSpeed = false) + } + } + } + var willWakeUp = false set(value) { if (!value || mDozeAmount != 0.0f) { @@ -80,7 +100,9 @@ class NotificationWakeUpCoordinator @Inject constructor( } } + private var collapsedEnoughToHide: Boolean = false lateinit var iconAreaController : NotificationIconAreaController + var pulsing: Boolean = false set(value) { field = value @@ -102,7 +124,6 @@ class NotificationWakeUpCoordinator @Inject constructor( } } } - /** * True if we can show pulsing heads up notifications */ @@ -112,8 +133,12 @@ class NotificationWakeUpCoordinator @Inject constructor( var canShow = pulsing if (bypassController.bypassEnabled) { // We also allow pulsing on the lock screen! - canShow = canShow || (mWakingUp || willWakeUp || fullyAwake) + canShow = canShow || (wakingUp || willWakeUp || fullyAwake) && statusBarStateController.state == StatusBarState.KEYGUARD + // We want to hide the notifications when collapsed too much + if (collapsedEnoughToHide) { + canShow = false + } } return canShow } @@ -160,7 +185,7 @@ class NotificationWakeUpCoordinator @Inject constructor( wakeUpListeners.add(listener); } - fun removeFullyHiddenChangedListener(listener: WakeUpListener) { + fun removeListener(listener: WakeUpListener) { wakeUpListeners.remove(listener); } @@ -169,7 +194,7 @@ class NotificationWakeUpCoordinator @Inject constructor( var visible = mNotificationsVisibleForExpansion || mHeadsUpManagerPhone.hasNotifications() visible = visible && canShowPulsingHuns - if (!visible && mNotificationsVisible && (mWakingUp || willWakeUp) && mDozeAmount != 0.0f) { + if (!visible && mNotificationsVisible && (wakingUp || willWakeUp) && mDozeAmount != 0.0f) { // let's not make notifications invisible while waking up, otherwise the animation // is strange return; @@ -229,6 +254,18 @@ class NotificationWakeUpCoordinator @Inject constructor( this.state = newState } + override fun onPanelExpansionChanged(expansion: Float, tracking: Boolean) { + val collapsedEnough = expansion <= 0.9f + if (collapsedEnough != this.collapsedEnoughToHide) { + val couldShowPulsingHuns = canShowPulsingHuns; + this.collapsedEnoughToHide = collapsedEnough + if (couldShowPulsingHuns && !canShowPulsingHuns) { + updateNotificationVisibility(animate = true, increaseSpeed = true) + mHeadsUpManagerPhone.releaseAllImmediately() + } + } + } + private fun updateDozeAmountIfBypass(): Boolean { if (bypassController.bypassEnabled) { var amount = 1.0f; @@ -307,16 +344,6 @@ class NotificationWakeUpCoordinator @Inject constructor( return if (bypassController.bypassEnabled) 0.0f else overflow } - fun setWakingUp(wakingUp: Boolean) { - willWakeUp = false - mWakingUp = wakingUp - if (wakingUp && mNotificationsVisible && !mNotificationsVisibleForExpansion - && !bypassController.bypassEnabled) { - // We're waking up while pulsing, let's make sure the animation looks nice - mStackScroller.wakeUpFromPulse(); - } - } - override fun onHeadsUpStateChanged(entry: NotificationEntry, isHeadsUp: Boolean) { var animate = shouldAnimateVisibility() if (!isHeadsUp) { @@ -325,7 +352,7 @@ class NotificationWakeUpCoordinator @Inject constructor( // if we animate, we see the shelf briefly visible. Instead we fully animate // the notification and its background out animate = false - } else if (!mWakingUp && !willWakeUp){ + } else if (!wakingUp && !willWakeUp){ // TODO: look that this is done properly and not by anyone else entry.setHeadsUpAnimatingAway(true) mEntrySetToClearWhenFinished.add(entry) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java index fca520fe0521..1ce493444e25 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationData.java @@ -25,7 +25,6 @@ import android.service.notification.NotificationListenerService.RankingMap; import android.service.notification.SnoozeCriterion; import android.service.notification.StatusBarNotification; import android.util.ArrayMap; -import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.Dependency; @@ -108,10 +107,19 @@ public class NotificationData { boolean bSystemMax = bImportance >= NotificationManager.IMPORTANCE_HIGH && isSystemNotification(nb); - boolean isHeadsUp = a.getRow().isHeadsUp(); - if (isHeadsUp != b.getRow().isHeadsUp()) { - return isHeadsUp ? -1 : 1; - } else if (isHeadsUp) { + + boolean aHeadsUp = a.getRow().isHeadsUp(); + boolean bHeadsUp = b.getRow().isHeadsUp(); + + // HACK: This should really go elsewhere, but it's currently not straightforward to + // extract the comparison code and we're guaranteed to touch every element, so this is + // the best place to set the buckets for the moment. + a.setIsTopBucket(aHeadsUp || aMedia || aSystemMax || a.isHighPriority()); + b.setIsTopBucket(bHeadsUp || bMedia || bSystemMax || b.isHighPriority()); + + if (aHeadsUp != bHeadsUp) { + return aHeadsUp ? -1 : 1; + } else if (aHeadsUp) { // Provide consistent ranking with headsUpManager return mHeadsUpManager.compare(a, b); } else if (aMedia != bMedia) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java index b19d2ca29c96..d71857082229 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java @@ -173,6 +173,9 @@ public final class NotificationEntry { * the lock screen/status bar and in the top section in the shade. */ private boolean mHighPriority; + + private boolean mIsTopBucket; + private boolean mSensitive = true; private Runnable mOnSensitiveChangedListener; private boolean mAutoHeadsUp; @@ -224,6 +227,18 @@ public final class NotificationEntry { this.mHighPriority = highPriority; } + /** + * @return True if the notif should appear in the "top" or "important" section of notifications + * (as opposed to the "bottom" or "silent" section). This is usually the same as + * {@link #isHighPriority()}, but there are certain exceptions, such as media notifs. + */ + public boolean isTopBucket() { + return mIsTopBucket; + } + public void setIsTopBucket(boolean isTopBucket) { + mIsTopBucket = isTopBucket; + } + public boolean isBubble() { return (notification.getNotification().flags & FLAG_BUBBLE) != 0; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java index d057a1d2f20b..48a82957bf1e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java @@ -349,7 +349,7 @@ public class NotificationContentInflater { } if ((reInflateFlags & FLAG_CONTENT_VIEW_PUBLIC) != 0) { - result.newPublicView = builder.makePublicContentView(); + result.newPublicView = builder.makePublicContentView(isLowPriority); } result.packageContext = packageContext; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java index 212808dae8e3..15cc72c2d7a1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationListContainer.java @@ -184,4 +184,6 @@ public interface NotificationListContainer extends ExpandableView.OnHeightChange default boolean containsView(View v) { return true; } + + default void setWillExpand(boolean willExpand) {}; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java index 170a4d570688..d119fb79e4c6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java @@ -133,7 +133,7 @@ class NotificationSectionsManager implements StackScrollAlgorithm.SectionProvide if (child instanceof ExpandableNotificationRow && child.getVisibility() != View.GONE) { ExpandableNotificationRow row = (ExpandableNotificationRow) child; - if (!row.getEntry().isHighPriority()) { + if (!row.getEntry().isTopBucket()) { firstGentleNotifIndex = i; mFirstGentleNotif = row; break; @@ -248,7 +248,7 @@ class NotificationSectionsManager implements StackScrollAlgorithm.SectionProvide View child = mParent.getChildAt(i); if (child.getVisibility() != View.GONE && child instanceof ExpandableNotificationRow) { ExpandableNotificationRow row = (ExpandableNotificationRow) child; - if (!row.getEntry().isHighPriority()) { + if (!row.getEntry().isTopBucket()) { break; } else { lastChildBeforeGap = row; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index bec95201e4b2..18a51b9abd8c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -499,6 +499,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd private boolean mAnimateBottomOnLayout; private float mLastSentAppear; private float mLastSentExpandedHeight; + private boolean mWillExpand; @Inject public NotificationStackScrollLayout( @@ -2549,13 +2550,21 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd } return; } - int minTopPosition = 0; + int minTopPosition; NotificationSection lastSection = getLastVisibleSection(); boolean onKeyguard = mStatusBarState == StatusBarState.KEYGUARD; if (!onKeyguard) { minTopPosition = (int) (mTopPadding + mStackTranslation); } else if (lastSection == null) { minTopPosition = mTopPadding; + } else { + // The first sections could be empty while there could still be elements in later + // sections. The position of these first few sections is determined by the position of + // the first visible section. + NotificationSection firstVisibleSection = getFirstVisibleSection(); + firstVisibleSection.updateBounds(0 /* minTopPosition*/, 0 /* minBottomPosition */, + false /* shiftPulsingWithFirst */); + minTopPosition = firstVisibleSection.getBounds().top; } boolean shiftPulsingWithFirst = mHeadsUpManager.getAllEntries().count() <= 1 && (mAmbientState.isDozing() @@ -4398,6 +4407,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd mStateAnimator.setShadeExpanded(isExpanded); mSwipeHelper.setIsExpanded(isExpanded); if (changed) { + mWillExpand = false; if (!mIsExpanded) { mGroupManager.collapseAllGroups(); mExpandHelper.cancelImmediately(); @@ -5046,7 +5056,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd if (mAnimationsEnabled && (isHeadsUp || mHeadsUpGoingAwayAnimationsAllowed)) { mHeadsUpChangeAnimations.add(new Pair<>(row, isHeadsUp)); mNeedsAnimation = true; - if (!mIsExpanded && !isHeadsUp) { + if (!mIsExpanded && !mWillExpand && !isHeadsUp) { row.setHeadsUpAnimatingAway(true); } requestChildrenUpdate(); @@ -5067,6 +5077,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd requestChildrenUpdate(); } + @Override + public void setWillExpand(boolean willExpand) { + mWillExpand = willExpand; + } + @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public void setTrackingHeadsUp(ExpandableNotificationRow row) { mTrackingHeadsUp = row != null; @@ -5667,6 +5682,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd // The bottom might change because we're using the final actual height of the view mAnimateBottomOnLayout = true; } + // Let's update the footer once the notifications have been updated (in the next frame) + post(this::updateFooter); } public void setOnPulseHeightChangedListener(Runnable listener) { @@ -5737,7 +5754,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd currentIndex++; boolean beforeSpeedBump; if (mHighPriorityBeforeSpeedBump) { - beforeSpeedBump = row.getEntry().isHighPriority(); + beforeSpeedBump = row.getEntry().isTopBucket(); } else { beforeSpeedBump = !row.getEntry().ambient; } @@ -5795,9 +5812,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd case ROWS_ALL: return true; case ROWS_HIGH_PRIORITY: - return row.getEntry().isHighPriority(); + return row.getEntry().isTopBucket(); case ROWS_GENTLE: - return !row.getEntry().isHighPriority(); + return !row.getEntry().isTopBucket(); default: throw new IllegalArgumentException("Unknown selection: " + selection); } 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 930f57efd2a7..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); @@ -368,6 +369,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback { @Override public void onStartedGoingToSleep(int why) { resetMode(); + mFadedAwayAfterWakeAndUnlock = false; mPendingAuthenticatedUserId = -1; mPendingAuthenticatedBioSourceType = null; } @@ -513,6 +515,9 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback { } public void finishKeyguardFadingAway() { + if (isWakeAndUnlock()) { + mFadedAwayAfterWakeAndUnlock = true; + } resetMode(); } @@ -563,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. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java index 46dd5e62ddda..f53c4e8c818e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java @@ -35,6 +35,7 @@ import com.android.systemui.statusbar.CrossFadeHelper; import com.android.systemui.statusbar.HeadsUpStatusBarView; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.SysuiStatusBarStateController; +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.statusbar.notification.stack.NotificationStackScrollLayout; @@ -48,7 +49,7 @@ import java.util.function.Consumer; * Controls the appearance of heads up notifications in the icon area and the header itself. */ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, - DarkIconDispatcher.DarkReceiver { + DarkIconDispatcher.DarkReceiver, NotificationWakeUpCoordinator.WakeUpListener { public static final int CONTENT_FADE_DURATION = 110; public static final int CONTENT_FADE_DELAY = 100; private final NotificationIconAreaController mNotificationIconAreaController; @@ -67,6 +68,7 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, private final KeyguardBypassController mBypassController; private final StatusBarStateController mStatusBarStateController; private final CommandQueue mCommandQueue; + private final NotificationWakeUpCoordinator mWakeUpCoordinator; @VisibleForTesting float mExpandedHeight; @VisibleForTesting @@ -95,9 +97,10 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, HeadsUpManagerPhone headsUpManager, View statusbarView, SysuiStatusBarStateController statusBarStateController, - KeyguardBypassController keyguardBypassController) { + KeyguardBypassController keyguardBypassController, + NotificationWakeUpCoordinator wakeUpCoordinator) { this(notificationIconAreaController, headsUpManager, statusBarStateController, - keyguardBypassController, + keyguardBypassController, wakeUpCoordinator, statusbarView.findViewById(R.id.heads_up_status_bar_view), statusbarView.findViewById(R.id.notification_stack_scroller), statusbarView.findViewById(R.id.notification_panel), @@ -112,6 +115,7 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, HeadsUpManagerPhone headsUpManager, StatusBarStateController stateController, KeyguardBypassController bypassController, + NotificationWakeUpCoordinator wakeUpCoordinator, HeadsUpStatusBarView headsUpStatusBarView, NotificationStackScrollLayout stackScroller, NotificationPanelView panelView, @@ -153,6 +157,8 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, }); mBypassController = bypassController; mStatusBarStateController = stateController; + mWakeUpCoordinator = wakeUpCoordinator; + wakeUpCoordinator.addListener(this); mCommandQueue = getComponent(headsUpStatusBarView.getContext(), CommandQueue.class); mKeyguardMonitor = Dependency.get(KeyguardMonitor.class); } @@ -161,6 +167,7 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, public void destroy() { mHeadsUpManager.removeListener(this); mHeadsUpStatusBarView.setOnDrawingRectChangedListener(null); + mWakeUpCoordinator.removeListener(this); mPanelView.removeTrackingHeadsUpListener(mSetTrackingHeadsUp); mPanelView.removeVerticalTranslationListener(mUpdatePanelTranslation); mPanelView.setHeadsUpAppearanceController(null); @@ -289,6 +296,11 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, updateParentClipping(true /* shouldClip */); }); } + // Show the status bar icons when the view gets shown / hidden + if (mStatusBarStateController.getState() != StatusBarState.SHADE) { + mCommandQueue.recomputeDisableFlags( + mHeadsUpStatusBarView.getContext().getDisplayId(), false); + } } } @@ -362,10 +374,12 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, * @return if the heads up status bar view should be shown */ public boolean shouldBeVisible() { - boolean canShow = !mIsExpanded; + boolean notificationsShown = !mWakeUpCoordinator.getNotificationsFullyHidden(); + boolean canShow = !mIsExpanded && notificationsShown; if (mBypassController.getBypassEnabled() && (mStatusBarStateController.getState() == StatusBarState.KEYGUARD - || mKeyguardMonitor.isKeyguardGoingAway())) { + || mKeyguardMonitor.isKeyguardGoingAway()) + && notificationsShown) { canShow = true; } return canShow && mHeadsUpManager.hasPinnedHeadsUp(); @@ -377,15 +391,6 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, updateHeader(entry); } - @Override - public void onHeadsUpPinnedModeChanged(boolean inPinnedMode) { - if (mStatusBarStateController.getState() != StatusBarState.SHADE) { - // Show the status bar icons when the pinned mode changes - mCommandQueue.recomputeDisableFlags( - mHeadsUpStatusBarView.getContext().getDisplayId(), false); - } - } - public void setAppearFraction(float expandedHeight, float appearFraction) { boolean changed = expandedHeight != mExpandedHeight; mExpandedHeight = expandedHeight; @@ -451,4 +456,9 @@ public class HeadsUpAppearanceController implements OnHeadsUpChangedListener, mAppearFraction = oldController.mAppearFraction; } } + + @Override + public void onFullyHiddenChanged(boolean isFullyHidden) { + updateTopEntry(); + } } 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/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java index d6f8a606af55..c4d346ccaefb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java @@ -170,7 +170,7 @@ public class KeyguardBouncer { // Split up the work over multiple frames. DejankUtils.removeCallbacks(mResetRunnable); - if (mUnlockMethodCache.isUnlockingWithFacePossible() && !needsFullscreenBouncer() + if (mUnlockMethodCache.isFaceAuthEnabled() && !needsFullscreenBouncer() && !mKeyguardUpdateMonitor.userNeedsStrongAuth()) { mHandler.postDelayed(mShowRunnable, BOUNCER_FACE_DELAY); } else { @@ -207,14 +207,12 @@ public class KeyguardBouncer { * @see #onFullyShown() */ private void onFullyHidden() { - if (!mShowingSoon) { - cancelShowRunnable(); - if (mRoot != null) { - mRoot.setVisibility(View.INVISIBLE); - } - mFalsingManager.onBouncerHidden(); - DejankUtils.postAfterTraversal(mResetRunnable); + cancelShowRunnable(); + if (mRoot != null) { + mRoot.setVisibility(View.INVISIBLE); } + mFalsingManager.onBouncerHidden(); + DejankUtils.postAfterTraversal(mResetRunnable); } private final Runnable mShowRunnable = new Runnable() { @@ -349,7 +347,7 @@ public class KeyguardBouncer { * {@link #show(boolean)} was called but we're not showing yet, or being dragged. */ public boolean inTransit() { - return mShowingSoon || mExpansion != EXPANSION_HIDDEN; + return mShowingSoon || mExpansion != EXPANSION_HIDDEN && mExpansion != EXPANSION_VISIBLE; } /** 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 0aec2b12fa92..70d3bff9b822 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt @@ -47,7 +47,7 @@ class KeyguardBypassController { * If face unlock dismisses the lock screen or keeps user on keyguard for the current user. */ var bypassEnabled: Boolean = false - get() = field && unlockMethodCache.isUnlockingWithFacePossible + get() = field && unlockMethodCache.isFaceAuthEnabled private set var bouncerShowing: Boolean = false 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..c9c80d464b46 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); } @@ -213,7 +217,7 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange mConfigurationController.removeCallback(this); mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback); mKeyguardMonitor.removeCallback(mKeyguardMonitorCallback); - mWakeUpCoordinator.removeFullyHiddenChangedListener(this); + mWakeUpCoordinator.removeListener(this); mUnlockMethodCache.removeListener(this); if (mDockManager != null) { mDockManager.removeListener(mDockEventListener); @@ -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); @@ -317,14 +323,24 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange } updateDarkTint(); + updateIconVisibility(); + updateClickability(); + + return true; + } + + /** + * Update the icon visibility + * @return true if the visibility changed + */ + private boolean updateIconVisibility() { boolean onAodNotPulsingOrDocked = mDozing && (!mPulsing || mDocked); boolean invisible = onAodNotPulsingOrDocked || mWakeAndUnlockRunning || mShowingLaunchAffordance; if (mBypassController.getBypassEnabled() && !mBouncerShowingScrimmed) { - if (mHeadsUpManager.isHeadsUpGoingAway() - || mHeadsUpManager.hasPinnedHeadsUp() - || (mStatusBarStateController.getState() == StatusBarState.KEYGUARD - && !mWakeUpCoordinator.getNotificationsFullyHidden())) { + if ((mHeadsUpManager.isHeadsUpGoingAway() || mHeadsUpManager.hasPinnedHeadsUp()) + && mStatusBarStateController.getState() == StatusBarState.KEYGUARD + && !mWakeUpCoordinator.getNotificationsFullyHidden()) { invisible = true; } } @@ -343,10 +359,9 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange .setDuration(233) .start(); } + return true; } - updateClickability(); - - return true; + return false; } private boolean canBlockUpdates() { @@ -412,7 +427,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 +438,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; @@ -434,7 +449,10 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange @Override public void onFullyHiddenChanged(boolean isFullyHidden) { if (mBypassController.getBypassEnabled()) { - update(); + boolean changed = updateIconVisibility(); + if (changed) { + update(); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java index 22e3edb2bbd8..9296f7e32761 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -22,6 +22,7 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_H import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING; +import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SEARCH_DISABLED; import static com.android.systemui.shared.system.QuickStepContract.isGesturalMode; import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE; @@ -719,6 +720,8 @@ public class NavigationBarView extends FrameLayout implements (mDisabledFlags & View.STATUS_BAR_DISABLE_RECENT) != 0, displayId); mOverviewProxyService.setSystemUiStateFlag(SYSUI_STATE_HOME_DISABLED, (mDisabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0, displayId); + mOverviewProxyService.setSystemUiStateFlag(SYSUI_STATE_SEARCH_DISABLED, + (mDisabledFlags & View.STATUS_BAR_DISABLE_SEARCH) != 0, displayId); if (mPanelView != null) { mOverviewProxyService.setSystemUiStateFlag(SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED, mPanelView.isFullyExpanded() && !mPanelView.isInSettings(), displayId); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java index 64fef55a21d3..1df9411019d2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java @@ -32,8 +32,6 @@ import static com.android.systemui.shared.system.QuickStepContract.ACTION_ENABLE import static com.android.systemui.shared.system.QuickStepContract.ACTION_ENABLE_GESTURE_NAV_RESULT; import static com.android.systemui.shared.system.QuickStepContract.EXTRA_RESULT_INTENT; -import android.app.Notification; -import android.app.NotificationManager; import android.app.PendingIntent; import android.app.admin.DevicePolicyManager; import android.content.BroadcastReceiver; @@ -42,7 +40,6 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.om.IOverlayManager; -import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.ApkAssets; import android.os.PatternMatcher; @@ -52,16 +49,13 @@ import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; import android.provider.Settings.Secure; -import android.text.TextUtils; import android.util.Log; import android.util.SparseBooleanArray; import com.android.systemui.Dumpable; -import com.android.systemui.R; import com.android.systemui.UiOffloadThread; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.statusbar.policy.DeviceProvisionedController; -import com.android.systemui.util.NotificationChannels; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -80,11 +74,6 @@ public class NavigationModeController implements Dumpable { private static final String TAG = NavigationModeController.class.getSimpleName(); private static final boolean DEBUG = false; - private static final int SYSTEM_APP_MASK = - ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; - static final String SHARED_PREFERENCES_NAME = "navigation_mode_controller_preferences"; - static final String PREFS_SWITCHED_FROM_GESTURE_NAV_KEY = "switched_from_gesture_nav"; - public interface ModeChangedListener { void onNavigationModeChanged(int mode); } @@ -100,8 +89,6 @@ public class NavigationModeController implements Dumpable { private int mMode = NAV_BAR_MODE_3BUTTON; private ArrayList<ModeChangedListener> mListeners = new ArrayList<>(); - private String mLastDefaultLauncher; - private BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -112,18 +99,6 @@ public class NavigationModeController implements Dumpable { } updateCurrentInteractionMode(true /* notify */); break; - case ACTION_PREFERRED_ACTIVITY_CHANGED: - if (DEBUG) { - Log.d(TAG, "ACTION_PREFERRED_ACTIVITY_CHANGED"); - } - final String launcher = getDefaultLauncherPackageName(mCurrentUserContext); - // Check if it is a default launcher change - if (!TextUtils.equals(mLastDefaultLauncher, launcher)) { - switchFromGestureNavModeIfNotSupportedByDefaultLauncher(); - showNotificationIfDefaultLauncherSupportsGestureNav(); - mLastDefaultLauncher = launcher; - } - break; } } }; @@ -159,7 +134,6 @@ public class NavigationModeController implements Dumpable { // Update the nav mode for the current user updateCurrentInteractionMode(true /* notify */); - switchFromGestureNavModeIfNotSupportedByDefaultLauncher(); // When switching users, defer enabling the gestural nav overlay until the user // is all set up @@ -190,11 +164,7 @@ public class NavigationModeController implements Dumpable { mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, preferredActivityFilter, null, null); - // We are only interested in launcher changes, so keeping track of the current default. - mLastDefaultLauncher = getDefaultLauncherPackageName(mContext); - updateCurrentInteractionMode(false /* notify */); - switchFromGestureNavModeIfNotSupportedByDefaultLauncher(); // Check if we need to defer enabling gestural nav deferGesturalNavOverlayIfNecessary(); @@ -216,21 +186,13 @@ public class NavigationModeController implements Dumpable { // Already in gesture mode return true; } - final Boolean supported = isGestureNavSupportedByDefaultLauncher(mCurrentUserContext); - if (supported == null || supported) { - Log.d(TAG, "Switching system navigation to full-gesture mode:" - + " defaultLauncher=" - + getDefaultLauncherPackageName(mCurrentUserContext) - + " contextUser=" - + mCurrentUserContext.getUserId()); - - setModeOverlay(NAV_BAR_MODE_GESTURAL_OVERLAY, USER_CURRENT); - return true; - } else { - Log.e(TAG, "Gesture nav is not supported for defaultLauncher=" - + getDefaultLauncherPackageName(mCurrentUserContext)); - return false; - } + + Log.d(TAG, "Switching system navigation to full-gesture mode:" + + " contextUser=" + + mCurrentUserContext.getUserId()); + + setModeOverlay(NAV_BAR_MODE_GESTURAL_OVERLAY, USER_CURRENT); + return true; } private boolean enableGestureNav(Intent intent) { @@ -430,100 +392,6 @@ public class NavigationModeController implements Dumpable { }); } - private void switchFromGestureNavModeIfNotSupportedByDefaultLauncher() { - if (getCurrentInteractionMode(mCurrentUserContext) != NAV_BAR_MODE_GESTURAL) { - return; - } - final Boolean supported = isGestureNavSupportedByDefaultLauncher(mCurrentUserContext); - if (supported == null || supported) { - return; - } - - Log.d(TAG, "Switching system navigation to 3-button mode:" - + " defaultLauncher=" + getDefaultLauncherPackageName(mCurrentUserContext) - + " contextUser=" + mCurrentUserContext.getUserId()); - - setModeOverlay(NAV_BAR_MODE_3BUTTON_OVERLAY, USER_CURRENT); - showNotification(mCurrentUserContext, R.string.notification_content_system_nav_changed); - mCurrentUserContext.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE) - .edit().putBoolean(PREFS_SWITCHED_FROM_GESTURE_NAV_KEY, true).apply(); - } - - private void showNotificationIfDefaultLauncherSupportsGestureNav() { - boolean previouslySwitchedFromGestureNav = mCurrentUserContext - .getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE) - .getBoolean(PREFS_SWITCHED_FROM_GESTURE_NAV_KEY, false); - if (!previouslySwitchedFromGestureNav) { - return; - } - if (getCurrentInteractionMode(mCurrentUserContext) == NAV_BAR_MODE_GESTURAL) { - return; - } - final Boolean supported = isGestureNavSupportedByDefaultLauncher(mCurrentUserContext); - if (supported == null || !supported) { - return; - } - - showNotification(mCurrentUserContext, R.string.notification_content_gesture_nav_available); - mCurrentUserContext.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE) - .edit().putBoolean(PREFS_SWITCHED_FROM_GESTURE_NAV_KEY, false).apply(); - } - - /** - * Returns null if there is no default launcher set for the current user. Returns true if the - * current default launcher supports Gesture Navigation. Returns false otherwise. - */ - private Boolean isGestureNavSupportedByDefaultLauncher(Context context) { - final String defaultLauncherPackageName = getDefaultLauncherPackageName(context); - if (DEBUG) { - Log.d(TAG, "isGestureNavSupportedByDefaultLauncher:" - + " defaultLauncher=" + defaultLauncherPackageName - + " contextUser=" + context.getUserId()); - } - if (defaultLauncherPackageName == null) { - return null; - } - if (isSystemApp(context, defaultLauncherPackageName)) { - return true; - } - return false; - } - - private String getDefaultLauncherPackageName(Context context) { - final ComponentName cn = context.getPackageManager().getHomeActivities(new ArrayList<>()); - if (cn == null) { - return null; - } - return cn.getPackageName(); - } - - /** Returns true if the app for the given package name is a system app for this device */ - private boolean isSystemApp(Context context, String packageName) { - try { - ApplicationInfo ai = context.getPackageManager().getApplicationInfo(packageName, - PackageManager.GET_META_DATA); - return ai != null && ((ai.flags & SYSTEM_APP_MASK) != 0); - } catch (PackageManager.NameNotFoundException e) { - return false; - } - } - - private void showNotification(Context context, int resId) { - final CharSequence message = context.getResources().getString(resId); - if (DEBUG) { - Log.d(TAG, "showNotification: message=" + message); - } - - final Notification.Builder builder = - new Notification.Builder(mContext, NotificationChannels.ALERTS) - .setContentText(message) - .setStyle(new Notification.BigTextStyle()) - .setSmallIcon(R.drawable.ic_info) - .setAutoCancel(true) - .setContentIntent(PendingIntent.getActivity(context, 0, new Intent(), 0)); - context.getSystemService(NotificationManager.class).notify(TAG, 0, builder.build()); - } - @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("NavigationModeController:"); @@ -536,12 +404,6 @@ public class NavigationModeController implements Dumpable { } pw.println(" defaultOverlays=" + defaultOverlays); dumpAssetPaths(mCurrentUserContext); - - pw.println(" defaultLauncher=" + mLastDefaultLauncher); - boolean previouslySwitchedFromGestureNav = mCurrentUserContext - .getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE) - .getBoolean(PREFS_SWITCHED_FROM_GESTURE_NAV_KEY, false); - pw.println(" previouslySwitchedFromGestureNav=" + previouslySwitchedFromGestureNav); } private void dumpAssetPaths(Context context) { 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/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java index 971a7eeaca12..792b83a2827c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -454,6 +454,11 @@ public class NotificationPanelView extends PanelView implements mPulseExpansionHandler.setUp(mNotificationStackScroller, this, mShadeController); mWakeUpCoordinator.addListener(new NotificationWakeUpCoordinator.WakeUpListener() { @Override + public void onFullyHiddenChanged(boolean isFullyHidden) { + updateKeyguardStatusBarForHeadsUp(); + } + + @Override public void onPulseExpansionChanged(boolean expandingChanged) { if (mKeyguardBypassController.getBypassEnabled()) { // Position the notifications while dragging down while pulsing @@ -803,8 +808,7 @@ public class NotificationPanelView extends PanelView implements if (suppressedSummary) { continue; } - if (!mLockscreenUserManager.shouldShowOnKeyguard( - row.getStatusBarNotification())) { + if (!mLockscreenUserManager.shouldShowOnKeyguard(row.getEntry())) { continue; } if (row.isRemoved()) { @@ -1278,6 +1282,7 @@ public class NotificationPanelView extends PanelView implements } mExpectingSynthesizedDown = true; onTrackingStarted(); + updatePanelExpanded(); } /** @@ -1560,9 +1565,15 @@ public class NotificationPanelView extends PanelView implements anim.setStartDelay(mKeyguardMonitor.isKeyguardFadingAway() ? mKeyguardMonitor.getKeyguardFadingAwayDelay() : 0); - anim.setDuration(mKeyguardMonitor.isKeyguardFadingAway() - ? mKeyguardMonitor.getKeyguardFadingAwayDuration() / 2 - : StackStateAnimator.ANIMATION_DURATION_STANDARD); + + long duration; + if (mKeyguardMonitor.isKeyguardFadingAway()) { + duration = mKeyguardMonitor.getShortenedFadingAwayDuration(); + } else { + duration = StackStateAnimator.ANIMATION_DURATION_STANDARD; + } + anim.setDuration(duration); + anim.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN); anim.addListener(new AnimatorListenerAdapter() { @Override @@ -1605,7 +1616,7 @@ public class NotificationPanelView extends PanelView implements mKeyguardBottomArea.animate() .alpha(0f) .setStartDelay(mKeyguardMonitor.getKeyguardFadingAwayDelay()) - .setDuration(mKeyguardMonitor.getKeyguardFadingAwayDuration() / 2) + .setDuration(mKeyguardMonitor.getShortenedFadingAwayDuration()) .setInterpolator(Interpolators.ALPHA_OUT) .withEndAction(mAnimateKeyguardBottomAreaInvisibleEndRunnable) .start(); @@ -1634,7 +1645,7 @@ public class NotificationPanelView extends PanelView implements if (keyguardFadingAway) { mKeyguardStatusView.animate() .setStartDelay(mKeyguardMonitor.getKeyguardFadingAwayDelay()) - .setDuration(mKeyguardMonitor.getKeyguardFadingAwayDuration() / 2) + .setDuration(mKeyguardMonitor.getShortenedFadingAwayDuration()) .start(); } } else if (mBarState == StatusBarState.SHADE_LOCKED @@ -1717,8 +1728,8 @@ public class NotificationPanelView extends PanelView implements mStatusBar.executeRunnableDismissingKeyguard(null, null /* cancelAction */, false /* dismissShade */, true /* afterKeyguardGone */, false /* deferred */); } - if (mExpansionListener != null) { - mExpansionListener.onQsExpansionChanged(mQsMaxExpansionHeight != 0 + for (int i = 0; i < mExpansionListeners.size(); i++) { + mExpansionListeners.get(i).onQsExpansionChanged(mQsMaxExpansionHeight != 0 ? mQsExpansionHeight / mQsMaxExpansionHeight : 0); } if (DEBUG) { @@ -2029,7 +2040,7 @@ public class NotificationPanelView extends PanelView implements } private void updatePanelExpanded() { - boolean isExpanded = !isFullyCollapsed(); + boolean isExpanded = !isFullyCollapsed() || mExpectingSynthesizedDown; if (mPanelExpanded != isExpanded) { mHeadsUpManager.setIsPanelExpanded(isExpanded); mStatusBar.setPanelExpanded(isExpanded); @@ -3374,24 +3385,4 @@ public class NotificationPanelView extends PanelView implements mOnReinflationListener = onReinflationListener; } - /** - * Panel and QS expansion callbacks. - */ - public interface PanelExpansionListener { - /** - * Invoked whenever the notification panel expansion changes, at every animation frame. - * This is the main expansion that happens when the user is swiping up to dismiss the - * lock screen. - * - * @param expansion 0 when collapsed, 1 when expanded. - * @param tracking {@code true} when the user is actively dragging the panel. - */ - void onPanelExpansionChanged(float expansion, boolean tracking); - - /** - * Invoked whenever the QS expansion changes, at every animation frame. - * @param expansion 0 when collapsed, 1 when expanded. - */ - void onQsExpansionChanged(float expansion); - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelExpansionListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelExpansionListener.java new file mode 100644 index 000000000000..655a25d22337 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelExpansionListener.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.statusbar.phone; + +/** + * Panel and QS expansion callbacks. + */ +public interface PanelExpansionListener { + /** + * Invoked whenever the notification panel expansion changes, at every animation frame. + * This is the main expansion that happens when the user is swiping up to dismiss the + * lock screen. + * + * @param expansion 0 when collapsed, 1 when expanded. + * @param tracking {@code true} when the user is actively dragging the panel. + */ + void onPanelExpansionChanged(float expansion, boolean tracking); + + /** + * Invoked whenever the QS expansion changes, at every animation frame. + * @param expansion 0 when collapsed, 1 when expanded. + */ + default void onQsExpansionChanged(float expansion) {}; +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java index 853faabbbc81..727f72b36ed8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java @@ -49,11 +49,11 @@ import com.android.systemui.statusbar.FlingAnimationUtils; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.VibratorHelper; -import com.android.systemui.statusbar.phone.NotificationPanelView.PanelExpansionListener; import com.android.systemui.statusbar.policy.KeyguardMonitor; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.ArrayList; public abstract class PanelView extends FrameLayout { public static final boolean DEBUG = PanelBar.DEBUG; @@ -68,7 +68,7 @@ public abstract class PanelView extends FrameLayout { private boolean mVibrateOnOpening; protected boolean mLaunchingNotification; private int mFixedDuration = NO_FIXED_DURATION; - protected PanelExpansionListener mExpansionListener; + protected ArrayList<PanelExpansionListener> mExpansionListeners = new ArrayList<>(); private final void logf(String fmt, Object... args) { Log.v(TAG, (mViewName != null ? (mViewName + ": ") : "") + String.format(fmt, args)); @@ -1173,13 +1173,13 @@ public abstract class PanelView extends FrameLayout { || mPeekAnimator != null || mInstantExpanding || isPanelVisibleBecauseOfHeadsUp() || mTracking || mHeightAnimator != null); } - if (mExpansionListener != null) { - mExpansionListener.onPanelExpansionChanged(mExpandedFraction, mTracking); + for (int i = 0; i < mExpansionListeners.size(); i++) { + mExpansionListeners.get(i).onPanelExpansionChanged(mExpandedFraction, mTracking); } } - public void setExpansionListener(PanelExpansionListener panelExpansionListener) { - mExpansionListener = panelExpansionListener; + public void addExpansionListener(PanelExpansionListener panelExpansionListener) { + mExpansionListeners.add(panelExpansionListener); } protected abstract boolean isPanelVisibleBecauseOfHeadsUp(); 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 1aec5e4d0b1d..b12bf5c39970 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -491,8 +491,10 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo * away once the display turns on. */ public void prepareForGentleWakeUp() { - if (mState == ScrimState.AOD && mDozeParameters.getAlwaysOn()) { + if (mState == ScrimState.AOD) { mCurrentInFrontAlpha = 1f; + mCurrentInFrontTint = Color.BLACK; + mCurrentBehindTint = Color.BLACK; mAnimateChange = false; updateScrims(); mAnimateChange = true; 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 763e0d70469b..c706062d3bb3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java @@ -129,7 +129,10 @@ public enum ScrimState { public void prepare(ScrimState previousState) { mCurrentInFrontAlpha = 0f; mCurrentBehindTint = Color.BLACK; + mCurrentInFrontTint = Color.BLACK; mBlankScreen = mDisplayRequiresBlanking; + mAnimationDuration = mWakeLockScreenSensorActive + ? ScrimController.ANIMATION_DURATION_LONG : ScrimController.ANIMATION_DURATION; } @Override 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 51dd888fea37..096ca99cf6e7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -813,6 +813,7 @@ public class StatusBar extends SystemUI implements DemoMode, inflateShelf(); mNotificationIconAreaController.setupShelf(mNotificationShelf); mNotificationPanel.setOnReinflationListener(mNotificationIconAreaController::initAodIcons); + mNotificationPanel.addExpansionListener(mWakeUpCoordinator); Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mNotificationIconAreaController); // Allow plugins to reference DarkIconDispatcher and StatusBarStateController @@ -855,7 +856,8 @@ public class StatusBar extends SystemUI implements DemoMode, } mHeadsUpAppearanceController = new HeadsUpAppearanceController( mNotificationIconAreaController, mHeadsUpManager, mStatusBarWindow, - mStatusBarStateController, mKeyguardBypassController); + mStatusBarStateController, mKeyguardBypassController, + mWakeUpCoordinator); mHeadsUpAppearanceController.readFrom(oldController); mStatusBarWindow.setStatusBarView(mStatusBarView); updateAreThereNotifications(); @@ -1229,6 +1231,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, @@ -1800,6 +1803,8 @@ public class StatusBar extends SystemUI implements DemoMode, mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK); } mNotificationPanel.expand(true /* animate */); + ((NotificationListContainer) mStackScroller).setWillExpand(true); + mHeadsUpManager.unpinAll(true /* userUnpinned */); mMetricsLogger.count(NotificationPanelView.COUNTER_PANEL_OPEN, 1); } else if (!mNotificationPanel.isInSettings() && !mNotificationPanel.isExpanding()){ mNotificationPanel.flingSettings(0 /* velocity */, @@ -1937,7 +1942,6 @@ public class StatusBar extends SystemUI implements DemoMode, if (start) { mNotificationPanel.startWaitingForOpenPanelGesture(); - setPanelExpanded(true); } else { mNotificationPanel.stopWaitingForOpenPanelGesture(velocity); } @@ -2386,6 +2390,10 @@ public class StatusBar extends SystemUI implements DemoMode, mLightBarController.dump(fd, pw, args); } + if (mUnlockMethodCache != null) { + mUnlockMethodCache.dump(pw); + } + if (mKeyguardBypassController != null) { mKeyguardBypassController.dump(pw); } @@ -3198,12 +3206,13 @@ public class StatusBar extends SystemUI implements DemoMode, /** * Notifies the status bar the Keyguard is fading away with the specified timings. - * - * @param startTime the start time of the animations in uptime millis + * @param startTime the start time of the animations in uptime millis * @param delay the precalculated animation delay in milliseconds * @param fadeoutDuration the duration of the exit animation, in milliseconds + * @param isBypassFading is this a fading away animation while bypassing */ - public void setKeyguardFadingAway(long startTime, long delay, long fadeoutDuration) { + public void setKeyguardFadingAway(long startTime, long delay, long fadeoutDuration, + boolean isBypassFading) { mCommandQueue.appTransitionStarting(mDisplayId, startTime + fadeoutDuration - LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, true); @@ -3211,7 +3220,7 @@ public class StatusBar extends SystemUI implements DemoMode, mCommandQueue.appTransitionStarting(mDisplayId, startTime - LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION, true); - mKeyguardMonitor.notifyKeyguardFadingAway(delay, fadeoutDuration); + mKeyguardMonitor.notifyKeyguardFadingAway(delay, fadeoutDuration, isBypassFading); } /** @@ -3561,6 +3570,9 @@ public class StatusBar extends SystemUI implements DemoMode, userAllowsPrivateNotificationsInPublic(mLockscreenUserManager.getCurrentUserId()) || !mLockscreenUserManager.shouldShowLockscreenNotifications() || mFalsingManager.shouldEnforceBouncer(); + if (mKeyguardBypassController.getBypassEnabled()) { + fullShadeNeedsBouncer = false; + } if (mLockscreenUserManager.isLockscreenPublicMode(userId) && fullShadeNeedsBouncer) { mStatusBarStateController.setLeaveOpenOnKeyguardHide(true); showBouncerIfKeyguard(); 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 5ce1329d5a89..3508c90bc8a2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -70,7 +70,7 @@ import java.util.ArrayList; */ public class StatusBarKeyguardViewManager implements RemoteInputController.Callback, StatusBarStateController.StateListener, ConfigurationController.ConfigurationListener, - NotificationPanelView.PanelExpansionListener, NavigationModeController.ModeChangedListener { + PanelExpansionListener, NavigationModeController.ModeChangedListener { // When hiding the Keyguard with timing supplied from WindowManager, better be early than late. private static final long HIDE_TIMING_CORRECTION_MS = - 16 * 3; @@ -223,7 +223,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mViewMediatorCallback, mLockPatternUtils, container, dismissCallbackRegistry, mExpansionCallback, falsingManager); mNotificationPanelView = notificationPanelView; - notificationPanelView.setExpansionListener(this); + notificationPanelView.addExpansionListener(this); mBypassController = bypassController; mNotificationContainer = notificationContainer; } @@ -561,18 +561,22 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb executeAfterKeyguardGoneAction(); boolean wakeUnlockPulsing = mBiometricUnlockController.getMode() == MODE_WAKE_AND_UNLOCK_PULSING; - if (wakeUnlockPulsing) { + boolean needsFading = needsBypassFading(); + if (needsFading) { + delay = 0; + fadeoutDuration = KeyguardBypassController.BYPASS_PANEL_FADE_DURATION; + } else if (wakeUnlockPulsing) { delay = 0; fadeoutDuration = 240; } - mStatusBar.setKeyguardFadingAway(startTime, delay, fadeoutDuration); + mStatusBar.setKeyguardFadingAway(startTime, delay, fadeoutDuration, needsFading); mBiometricUnlockController.startKeyguardFadingAway(); hideBouncer(true /* destroyView */); if (wakeUnlockPulsing) { - if (needsBypassFading()) { + if (needsFading) { ViewGroupFadeHelper.fadeOutAllChildrenExcept(mNotificationPanelView, mNotificationContainer, - KeyguardBypassController.BYPASS_PANEL_FADE_DURATION, + fadeoutDuration, () -> { mStatusBar.hideKeyguard(); onKeyguardFadedAway(); @@ -585,10 +589,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb boolean staying = mStatusBarStateController.leaveOpenOnKeyguardHide(); if (!staying) { mStatusBarWindowController.setKeyguardFadingAway(true); - if (needsBypassFading()) { + if (needsFading) { ViewGroupFadeHelper.fadeOutAllChildrenExcept(mNotificationPanelView, mNotificationContainer, - KeyguardBypassController.BYPASS_PANEL_FADE_DURATION, + fadeoutDuration, () -> { mStatusBar.hideKeyguard(); }); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java index f1049f005dea..33b863fd0236 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java @@ -418,7 +418,7 @@ public class StatusBarWindowView extends FrameLayout { if (mNotificationPanel.isFullyExpanded() && mStatusBarStateController.getState() == StatusBarState.KEYGUARD && !mService.isBouncerShowing() - && !mBypassController.getBypassEnabled() + && (!mBypassController.getBypassEnabled() || mNotificationPanel.isQsExpanded()) && !mService.isDozing()) { intercept = mDragDownHelper.onInterceptTouchEvent(ev); } 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..b1d6ca6e5580 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java @@ -28,6 +28,7 @@ import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; +import java.io.PrintWriter; import java.util.ArrayList; /** @@ -51,7 +52,7 @@ public class UnlockMethodCache { private boolean mTrustManaged; private boolean mTrusted; private boolean mDebugUnlocked = false; - private boolean mIsUnlockingWithFacePossible; + private boolean mFaceAuthEnabled; private UnlockMethodCache(Context ctx) { mLockPatternUtils = new LockPatternUtils(ctx); @@ -107,8 +108,11 @@ public class UnlockMethodCache { mListeners.remove(listener); } - public boolean isUnlockingWithFacePossible() { - return mIsUnlockingWithFacePossible; + /** + * If there are faces enrolled and user enabled face auth on keyguard. + */ + public boolean isFaceAuthEnabled() { + return mFaceAuthEnabled; } private void update(boolean updateAlways) { @@ -119,15 +123,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 faceAuthEnabled = mKeyguardUpdateMonitor.isFaceAuthEnabledForUser(user); + boolean changed = secure != mSecure || canSkipBouncer != mCanSkipBouncer + || trustManaged != mTrustManaged + || mFaceAuthEnabled != faceAuthEnabled; if (changed || updateAlways) { mSecure = secure; mCanSkipBouncer = canSkipBouncer; mTrusted = trusted; mTrustManaged = trustManaged; - mIsUnlockingWithFacePossible = hasEnrolledFaces; + mFaceAuthEnabled = faceAuthEnabled; notifyListeners(); } Trace.endSection(); @@ -139,6 +144,16 @@ public class UnlockMethodCache { } } + public void dump(PrintWriter pw) { + pw.println("UnlockMethodCache"); + pw.println(" mSecure: " + mSecure); + pw.println(" mCanSkipBouncer: " + mCanSkipBouncer); + pw.println(" mTrustManaged: " + mTrustManaged); + pw.println(" mTrusted: " + mTrusted); + pw.println(" mDebugUnlocked: " + mDebugUnlocked); + pw.println(" mFaceAuthEnabled: " + mFaceAuthEnabled); + } + private final KeyguardUpdateMonitorCallback mCallback = new KeyguardUpdateMonitorCallback() { @Override public void onUserSwitchComplete(int userId) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java index f61b556e22ab..070136ec94c1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java @@ -29,6 +29,19 @@ public interface KeyguardMonitor extends CallbackController<Callback> { long getKeyguardFadingAwayDelay(); long calculateGoingToFullShadeDelay(); + /** + * @return a shortened fading away duration similar to + * {{@link #getKeyguardFadingAwayDuration()}} which may only span half of the duration, unless + * we're bypassing + */ + default long getShortenedFadingAwayDuration() { + if (isBypassFadingAnimation()) { + return getKeyguardFadingAwayDuration(); + } else { + return getKeyguardFadingAwayDuration() / 2; + } + } + default boolean isDeviceInteractive() { return false; } @@ -39,7 +52,21 @@ public interface KeyguardMonitor extends CallbackController<Callback> { default void notifyKeyguardGoingAway(boolean b) { } - default void notifyKeyguardFadingAway(long delay, long fadeoutDuration) { + /** + * @return {@code true} if the current fading away animation is the fast bypass fading. + */ + default boolean isBypassFadingAnimation() { + return false; + } + + /** + * Notifies that the Keyguard is fading away with the specified timings. + * @param delay the precalculated animation delay in milliseconds + * @param fadeoutDuration the duration of the exit animation, in milliseconds + * @param isBypassFading is this a fading away animation while bypassing + */ + default void notifyKeyguardFadingAway(long delay, long fadeoutDuration, + boolean isBypassFading) { } default void notifyKeyguardDoneFading() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java index 68d00708b0d3..8829be4ee0f8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java @@ -54,6 +54,7 @@ public class KeyguardMonitorImpl extends KeyguardUpdateMonitorCallback private long mKeyguardFadingAwayDuration; private boolean mKeyguardGoingAway; private boolean mLaunchTransitionFadingAway; + private boolean mBypassFadingAnimation; /** */ @@ -140,10 +141,11 @@ public class KeyguardMonitorImpl extends KeyguardUpdateMonitorCallback new ArrayList<>(mCallbacks).forEach(Callback::onKeyguardShowingChanged); } - public void notifyKeyguardFadingAway(long delay, long fadeoutDuration) { + public void notifyKeyguardFadingAway(long delay, long fadeoutDuration, boolean isBypassFading) { setKeyguardFadingAway(true); mKeyguardFadingAwayDelay = delay; mKeyguardFadingAwayDuration = fadeoutDuration; + mBypassFadingAnimation = isBypassFading; } private void setKeyguardFadingAway(boolean keyguardFadingAway) { @@ -172,6 +174,11 @@ public class KeyguardMonitorImpl extends KeyguardUpdateMonitorCallback } @Override + public boolean isBypassFadingAnimation() { + return mBypassFadingAnimation; + } + + @Override public long getKeyguardFadingAwayDelay() { return mKeyguardFadingAwayDelay; } 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/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/keyguard/KeyguardSliceProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java index 893f3d184acb..9576cb2eec45 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java @@ -48,6 +48,7 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.NotificationMediaManager; +import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.policy.ZenModeController; @@ -197,6 +198,7 @@ public class KeyguardSliceProviderTest extends SysuiTestCase { @Test public void onMetadataChanged_updatesSlice() { + mProvider.onStateChanged(StatusBarState.KEYGUARD); mProvider.onDozingChanged(true); reset(mContentResolver); mProvider.onMetadataOrStateChanged(mock(MediaMetadata.class), PlaybackState.STATE_PLAYING); @@ -210,6 +212,7 @@ public class KeyguardSliceProviderTest extends SysuiTestCase { @Test public void onDozingChanged_updatesSliceIfMedia() { + mProvider.onStateChanged(StatusBarState.KEYGUARD); mProvider.onMetadataOrStateChanged(mock(MediaMetadata.class), PlaybackState.STATE_PLAYING); reset(mContentResolver); // Show media when dozing 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/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java index 49a263a8d781..57dd8c94c790 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java @@ -38,7 +38,6 @@ import android.os.Handler; import android.os.Looper; import android.os.UserManager; import android.provider.Settings; -import android.service.notification.StatusBarNotification; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -48,6 +47,7 @@ import com.android.systemui.Dependency; import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationData; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.policy.DeviceProvisionedController; @@ -166,7 +166,7 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 1); when(mNotificationData.isHighPriority(any())).thenReturn(false); - assertTrue(mLockscreenUserManager.shouldShowOnKeyguard(mock(StatusBarNotification.class))); + assertTrue(mLockscreenUserManager.shouldShowOnKeyguard(mock(NotificationEntry.class))); } @Test @@ -179,7 +179,7 @@ public class NotificationLockscreenUserManagerTest extends SysuiTestCase { Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 0); when(mNotificationData.isHighPriority(any())).thenReturn(false); - assertFalse(mLockscreenUserManager.shouldShowOnKeyguard(mock(StatusBarNotification.class))); + assertFalse(mLockscreenUserManager.shouldShowOnKeyguard(mock(NotificationEntry.class))); } private class TestNotificationLockscreenUserManager diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java index 73abda9a5da7..59d0f912d38e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java @@ -263,6 +263,8 @@ public class NotificationSectionsManagerTest extends SysuiTestCase { when(notifRow.getVisibility()).thenReturn(View.VISIBLE); when(notifRow.getEntry().isHighPriority()) .thenReturn(children[i] == ChildType.HIPRI); + when(notifRow.getEntry().isTopBucket()) + .thenReturn(children[i] == ChildType.HIPRI); when(notifRow.getParent()).thenReturn(mNssl); child = notifRow; break; 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 7d9920db36a4..4a41349ca195 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 @@ -82,7 +82,7 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { MockitoAnnotations.initMocks(this); when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(true); when(mUpdateMonitor.isDeviceInteractive()).thenReturn(true); - when(mUnlockMethodCache.isUnlockingWithFacePossible()).thenReturn(true); + when(mUnlockMethodCache.isFaceAuthEnabled()).thenReturn(true); when(mKeyguardBypassController.onBiometricAuthenticated(any())).thenReturn(true); when(mKeyguardBypassController.canPlaySubtleWindowAnimations()).thenReturn(true); mContext.addMockSystemService(PowerManager.class, mPowerManager); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java index b45707ef6c10..a38094da3e1c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java @@ -35,6 +35,7 @@ import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.HeadsUpStatusBarView; import com.android.systemui.statusbar.NotificationTestHelper; +import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; @@ -59,6 +60,7 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase { private View mOperatorNameView; private StatusBarStateController mStatusbarStateController; private KeyguardBypassController mBypassController; + private NotificationWakeUpCoordinator mWakeUpCoordinator; @Before public void setUp() throws Exception { @@ -72,11 +74,13 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase { mOperatorNameView = new View(mContext); mStatusbarStateController = mock(StatusBarStateController.class); mBypassController = mock(KeyguardBypassController.class); + mWakeUpCoordinator = mock(NotificationWakeUpCoordinator.class); mHeadsUpAppearanceController = new HeadsUpAppearanceController( mock(NotificationIconAreaController.class), mHeadsUpManager, mStatusbarStateController, mBypassController, + mWakeUpCoordinator, mHeadsUpStatusBarView, mStackScroller, mPanelView, @@ -153,6 +157,7 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase { mHeadsUpManager, mStatusbarStateController, mBypassController, + mWakeUpCoordinator, mHeadsUpStatusBarView, mStackScroller, mPanelView, 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/statusbar/phone/KeyguardBouncerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java index 4e0ef56ad830..907e695f2513 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java @@ -379,7 +379,7 @@ public class KeyguardBouncerTest extends SysuiTestCase { @Test public void testShow_delaysIfFaceAuthIsRunning() { - when(mUnlockMethodCache.isUnlockingWithFacePossible()).thenReturn(true); + when(mUnlockMethodCache.isFaceAuthEnabled()).thenReturn(true); mBouncer.show(true /* reset */); ArgumentCaptor<Runnable> showRunnable = ArgumentCaptor.forClass(Runnable.class); @@ -394,4 +394,15 @@ public class KeyguardBouncerTest extends SysuiTestCase { public void testRegisterUpdateMonitorCallback() { verify(mKeyguardUpdateMonitor).registerCallback(any()); } + + @Test + public void testInTransit_whenTranslation() { + mBouncer.show(true); + mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN); + assertThat(mBouncer.inTransit()).isFalse(); + mBouncer.setExpansion(0.5f); + assertThat(mBouncer.inTransit()).isTrue(); + mBouncer.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE); + assertThat(mBouncer.inTransit()).isFalse(); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java index d8e90a584058..0dbf30881ffe 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java @@ -225,11 +225,12 @@ public class ScrimControllerTest extends SysuiTestCase { mScrimController.transitionTo(ScrimState.PULSING); mScrimController.finishAnimationsImmediately(); - // Front scrim should be transparent + // Front scrim should be transparent, but tinted // Back scrim should be semi-transparent so the user can see the wallpaper // Pulse callback should have been invoked assertScrimVisibility(VISIBILITY_FULLY_TRANSPARENT, VISIBILITY_FULLY_OPAQUE); assertScrimTint(mScrimBehind, true /* tinted */); + assertScrimTint(mScrimInFront, true /* tinted */); mScrimController.setWakeLockScreenSensorActive(true); mScrimController.finishAnimationsImmediately(); 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/proto/src/wifi.proto b/proto/src/wifi.proto index f9a2ca269335..8ad24894a1b9 100644 --- a/proto/src/wifi.proto +++ b/proto/src/wifi.proto @@ -551,6 +551,30 @@ message WifiLog { // Histogram of the EAP method type of all installed Passpoint profiles for R2 repeated PasspointProfileTypeCount installed_passpoint_profile_type_for_r2 = 148; + + // Histogram of Tx link speed at 2G + repeated Int32Count tx_link_speed_count_2g = 149; + + // Histogram of Tx link speed at 5G low band + repeated Int32Count tx_link_speed_count_5g_low = 150; + + // Histogram of Tx link speed at 5G middle band + repeated Int32Count tx_link_speed_count_5g_mid = 151; + + // Histogram of Tx link speed at 5G high band + repeated Int32Count tx_link_speed_count_5g_high = 152; + + // Histogram of Rx link speed at 2G + repeated Int32Count rx_link_speed_count_2g = 153; + + // Histogram of Rx link speed at 5G low band + repeated Int32Count rx_link_speed_count_5g_low = 154; + + // Histogram of Rx link speed at 5G middle band + repeated Int32Count rx_link_speed_count_5g_mid = 155; + + // Histogram of Rx link speed at 5G high band + repeated Int32Count rx_link_speed_count_5g_high = 156; } // Information that gets logged for every WiFi connection. @@ -827,6 +851,7 @@ message LinkSpeedCount { optional int64 rssi_sum_of_squares_dbm_sq = 4; } + // Number of occurrences of Soft AP session durations message SoftApDurationBucket { // Bucket covers duration : [duration_sec, duration_sec + bucket_size_sec) diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java index 1873de53867e..123dfee03bd9 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; @@ -88,9 +89,10 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ protected final Context mContext; protected final SystemSupport mSystemSupport; - private final WindowManagerInternal mWindowManagerService; + protected 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 { @@ -165,9 +167,10 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ @Nullable MagnificationSpec getCompatibleMagnificationSpecLocked(int windowId); /** - * @return The current injector of motion events, if one exists + * @param displayId The display id. + * @return The current injector of motion events used on the display, if one exists. */ - @Nullable MotionEventInjector getMotionEventInjectorLocked(); + @Nullable MotionEventInjector getMotionEventInjectorForDisplayLocked(int displayId); /** * @return The current dispatcher for fingerprint gestures, if one exists @@ -222,6 +225,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 @@ -712,6 +716,10 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ } @Override + public void dispatchGesture(int sequence, ParceledListSlice gestureSteps, int displayId) { + } + + @Override public boolean performAccessibilityAction(int accessibilityWindowId, long accessibilityNodeId, int action, Bundle arguments, int interactionId, IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) @@ -928,24 +936,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/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java index b6cbbacde118..5111bec4913b 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java @@ -114,7 +114,7 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo private final SparseArray<MagnificationGestureHandler> mMagnificationGestureHandler = new SparseArray<>(0); - private final SparseArray<MotionEventInjector> mMotionEventInjector = new SparseArray<>(0); + private final SparseArray<MotionEventInjector> mMotionEventInjectors = new SparseArray<>(0); private AutoclickController mAutoclickController; @@ -412,12 +412,14 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo MotionEventInjector injector = new MotionEventInjector( mContext.getMainLooper()); addFirstEventHandler(displayId, injector); - // TODO: Need to set MotionEventInjector per display. - mAms.setMotionEventInjector(injector); - mMotionEventInjector.put(displayId, injector); + mMotionEventInjectors.put(displayId, injector); } } + if ((mEnabledFeatures & FLAG_FEATURE_INJECT_MOTION_EVENTS) != 0) { + mAms.setMotionEventInjectors(mMotionEventInjectors); + } + if ((mEnabledFeatures & FLAG_FEATURE_FILTER_KEY_EVENTS) != 0) { mKeyboardInterceptor = new KeyboardInterceptor(mAms, LocalServices.getService(WindowManagerPolicy.class)); @@ -462,15 +464,14 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo } private void disableFeatures() { - for (int i = mMotionEventInjector.size() - 1; i >= 0; i--) { - final MotionEventInjector injector = mMotionEventInjector.valueAt(i); - // TODO: Need to set MotionEventInjector per display. - mAms.setMotionEventInjector(null); + for (int i = mMotionEventInjectors.size() - 1; i >= 0; i--) { + final MotionEventInjector injector = mMotionEventInjectors.valueAt(i); if (injector != null) { injector.onDestroy(); } } - mMotionEventInjector.clear(); + mAms.setMotionEventInjectors(null); + mMotionEventInjectors.clear(); if (mAutoclickController != null) { mAutoclickController.onDestroy(); mAutoclickController = null; diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 47f137c63efa..3db79b2da8a9 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -210,7 +210,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private KeyEventDispatcher mKeyEventDispatcher; - private MotionEventInjector mMotionEventInjector; + private SparseArray<MotionEventInjector> mMotionEventInjectors; private FingerprintGestureDispatcher mFingerprintGestureDispatcher; @@ -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); @@ -859,30 +860,34 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub * Called by AccessibilityInputFilter when it creates or destroys the motionEventInjector. * Not using a getter because the AccessibilityInputFilter isn't thread-safe * - * @param motionEventInjector The new value of the motionEventInjector. May be null. + * @param motionEventInjectors The array of motionEventInjectors. May be null. + * */ - void setMotionEventInjector(MotionEventInjector motionEventInjector) { + void setMotionEventInjectors(SparseArray<MotionEventInjector> motionEventInjectors) { synchronized (mLock) { - mMotionEventInjector = motionEventInjector; + mMotionEventInjectors = motionEventInjectors; // We may be waiting on this object being set mLock.notifyAll(); } } @Override - public MotionEventInjector getMotionEventInjectorLocked() { + public @Nullable MotionEventInjector getMotionEventInjectorForDisplayLocked(int displayId) { final long endMillis = SystemClock.uptimeMillis() + WAIT_MOTION_INJECTOR_TIMEOUT_MILLIS; - while ((mMotionEventInjector == null) && (SystemClock.uptimeMillis() < endMillis)) { + MotionEventInjector motionEventInjector = null; + while ((mMotionEventInjectors == null) && (SystemClock.uptimeMillis() < endMillis)) { try { mLock.wait(endMillis - SystemClock.uptimeMillis()); } catch (InterruptedException ie) { /* ignore */ } } - if (mMotionEventInjector == null) { + if (mMotionEventInjectors == null) { Slog.e(LOG_TAG, "MotionEventInjector installation timed out"); + } else { + motionEventInjector = mMotionEventInjectors.get(displayId); } - return mMotionEventInjector; + return motionEventInjector; } /** @@ -2550,6 +2555,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 +2578,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 +2621,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..961168a46c7d 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); @@ -237,7 +238,6 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect return (userState != null) ? userState.getSoftKeyboardShowMode() : 0; } - @Override public boolean isAccessibilityButtonAvailable() { synchronized (mLock) { @@ -353,12 +353,13 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect } @Override - public void sendGesture(int sequence, ParceledListSlice gestureSteps) { + public void dispatchGesture(int sequence, ParceledListSlice gestureSteps, int displayId) { + final boolean isTouchableDisplay = mWindowManagerService.isTouchableDisplay(displayId); synchronized (mLock) { if (mSecurityPolicy.canPerformGestures(this)) { MotionEventInjector motionEventInjector = - mSystemSupport.getMotionEventInjectorLocked(); - if (motionEventInjector != null) { + mSystemSupport.getMotionEventInjectorForDisplayLocked(displayId); + if (motionEventInjector != null && isTouchableDisplay) { motionEventInjector.injectEvents( gestureSteps.getList(), mServiceInterface, sequence); } else { diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java index 1dea2f2dd954..c12929153781 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java @@ -33,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; @@ -311,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); } } } @@ -322,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(); } @@ -521,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; } 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/autofill/java/com/android/server/autofill/ui/CustomScrollView.java b/services/autofill/java/com/android/server/autofill/ui/CustomScrollView.java index e68263a51415..813fc8d5f561 100644 --- a/services/autofill/java/com/android/server/autofill/ui/CustomScrollView.java +++ b/services/autofill/java/com/android/server/autofill/ui/CustomScrollView.java @@ -64,12 +64,13 @@ public class CustomScrollView extends ScrollView { return; } + mWidth = MeasureSpec.getSize(widthMeasureSpec); calculateDimensions(); setMeasuredDimension(mWidth, mHeight); } private void calculateDimensions() { - if (mWidth != -1) return; + if (mHeight != -1) return; final TypedValue typedValue = new TypedValue(); final Point point = new Point(); @@ -81,7 +82,6 @@ public class CustomScrollView extends ScrollView { final int childHeight = child.getMeasuredHeight(); final int maxHeight = (int) typedValue.getFraction(point.y, point.y); - mWidth = point.x; mHeight = Math.min(childHeight, maxHeight); if (sDebug) { Slog.d(TAG, "calculateDimensions(): maxHeight=" + maxHeight diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java index 73f5cb8326ea..e2cdddb932d7 100644 --- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java +++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java @@ -165,7 +165,13 @@ final class SaveUi { mComponentName = componentName; mCompatMode = compatMode; - context = new ContextThemeWrapper(context, mThemeId); + context = new ContextThemeWrapper(context, mThemeId) { + @Override + public void startActivity(Intent intent) { + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + super.startActivity(intent); + } + }; final LayoutInflater inflater = LayoutInflater.from(context); final View view = inflater.inflate(R.layout.autofill_save, null); 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/Android.bp b/services/core/Android.bp index 9855e4ea339a..9518196c40df 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -13,6 +13,7 @@ java_library_static { }, srcs: [ "java/**/*.java", + ":platformcompat_aidl", ":dumpstate_aidl", ":idmap2_aidl", ":installd_aidl", @@ -80,3 +81,11 @@ prebuilt_etc { name: "gps_debug.conf", src: "java/com/android/server/location/gps_debug.conf", } + +filegroup { + name: "platformcompat_aidl", + srcs: [ + "java/com/android/server/compat/IPlatformCompat.aidl", + ], + path: "java", +}
\ No newline at end of file diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java index 188d65494868..208b63888efb 100644 --- a/services/core/java/com/android/server/BluetoothManagerService.java +++ b/services/core/java/com/android/server/BluetoothManagerService.java @@ -1185,7 +1185,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { if (isBluetoothDisallowed) { return; } - if (mEnableExternal && isBluetoothPersistedStateOnBluetooth()) { + final boolean isSafeMode = mContext.getPackageManager().isSafeMode(); + if (mEnableExternal && isBluetoothPersistedStateOnBluetooth() && !isSafeMode) { if (DBG) { Slog.d(TAG, "Auto-enabling Bluetooth."); } 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/DropBoxManagerService.java b/services/core/java/com/android/server/DropBoxManagerService.java index 9dead161390f..aeb3e7fd94de 100644 --- a/services/core/java/com/android/server/DropBoxManagerService.java +++ b/services/core/java/com/android/server/DropBoxManagerService.java @@ -41,7 +41,7 @@ import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; import android.text.TextUtils; -import android.text.format.Time; +import android.text.format.TimeMigrationUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Slog; @@ -582,11 +582,9 @@ public final class DropBoxManagerService extends SystemService { } int numFound = 0, numArgs = searchArgs.size(); - Time time = new Time(); out.append("\n"); for (EntryFile entry : mAllFiles.contents) { - time.set(entry.timestampMillis); - String date = time.format("%Y-%m-%d %H:%M:%S"); + String date = TimeMigrationUtils.formatMillisWithFixedFormat(entry.timestampMillis); boolean match = true; for (int i = 0; i < numArgs && match; i++) { String arg = searchArgs.get(i); 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/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java index 30a356325ada..b9d7c687704c 100644 --- a/services/core/java/com/android/server/UiModeManagerService.java +++ b/services/core/java/com/android/server/UiModeManagerService.java @@ -48,7 +48,6 @@ import android.os.ShellCallback; import android.os.ShellCommand; import android.os.SystemProperties; import android.os.UserHandle; -import android.os.UserManager; import android.provider.Settings.Secure; import android.service.dreams.Sandman; import android.service.vr.IVrManager; @@ -218,6 +217,15 @@ final class UiModeManagerService extends SystemService { } }; + private final ContentObserver mDarkThemeObserver = new ContentObserver(mHandler) { + @Override + public void onChange(boolean selfChange, Uri uri) { + int mode = Secure.getIntForUser(getContext().getContentResolver(), Secure.UI_NIGHT_MODE, + mNightMode, 0); + SystemProperties.set(SYSTEM_PROPERTY_DEVICE_THEME, Integer.toString(mode)); + } + }; + @Override public void onSwitchUser(int userHandle) { super.onSwitchUser(userHandle); @@ -293,6 +301,9 @@ final class UiModeManagerService extends SystemService { IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_USER_SWITCHED); context.registerReceiver(new UserSwitchedReceiver(), filter, null, mHandler); + + context.getContentResolver().registerContentObserver(Secure.getUriFor(Secure.UI_NIGHT_MODE), + false, mDarkThemeObserver, 0); } // Records whether setup wizard has happened or not and adds an observer for this user if not. @@ -417,11 +428,6 @@ final class UiModeManagerService extends SystemService { if (!mCarModeEnabled) { Secure.putIntForUser(getContext().getContentResolver(), Secure.UI_NIGHT_MODE, mode, user); - - if (UserManager.get(getContext()).isPrimaryUser()) { - SystemProperties.set(SYSTEM_PROPERTY_DEVICE_THEME, - Integer.toString(mode)); - } } mNightMode = mode; diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index ff8c3e954460..268e813c12dd 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -2399,7 +2399,7 @@ public class ActivityManagerService extends IActivityManager.Stub final ActiveUids activeUids = new ActiveUids(this, false /* postChangesToAtm */); mProcessList.init(this, activeUids); mLowMemDetector = null; - mOomAdjuster = new OomAdjuster(this, mProcessList, activeUids); + mOomAdjuster = new OomAdjuster(this, mProcessList, activeUids, handlerThread); mIntentFirewall = hasHandlerThread ? new IntentFirewall(new IntentFirewallInterface(), mHandler) : null; diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index f7512f9d25e7..0dd719994de2 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -79,7 +79,6 @@ import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; import android.text.TextUtils; -import android.text.format.Time; import android.util.ArrayMap; import android.util.DebugUtils; import android.util.DisplayMetrics; @@ -89,6 +88,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; @@ -97,12 +97,16 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.URISyntaxException; +import java.time.Clock; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.List; +import java.util.Locale; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -115,10 +119,14 @@ import javax.microedition.khronos.egl.EGLSurface; final class ActivityManagerShellCommand extends ShellCommand { public static final String NO_CLASS_ERROR_CODE = "Error type 3"; + private static final String SHELL_PACKAGE_NAME = "com.android.shell"; private static final int USER_OPERATION_TIMEOUT_MS = 2 * 60 * 1000; // 2 minutes + private static final DateTimeFormatter LOG_NAME_TIME_FORMATTER = + DateTimeFormatter.ofPattern("yyyyMMdd-HHmmss", Locale.ROOT); + // IPC interface to activity manager -- don't need to do additional security checks. final IActivityManager mInterface; final IActivityTaskManager mTaskInterface; @@ -290,6 +298,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); } @@ -919,9 +929,9 @@ final class ActivityManagerShellCommand extends ShellCommand { String process = getNextArgRequired(); String heapFile = getNextArg(); if (heapFile == null) { - final Time t = new Time(); - t.set(System.currentTimeMillis()); - heapFile = "/data/local/tmp/heapdump-" + t.format("%Y%m%d-%H%M%S") + ".prof"; + LocalDateTime localDateTime = LocalDateTime.now(Clock.systemDefaultZone()); + String logNameTimeString = LOG_NAME_TIME_FORMATTER.format(localDateTime); + heapFile = "/data/local/tmp/heapdump-" + logNameTimeString + ".prof"; } pw.println("File: " + heapFile); pw.flush(); @@ -2874,6 +2884,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 +3235,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/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index a2613d87d923..770cb3da3362 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -162,6 +162,21 @@ public final class OomAdjuster { private final ProcessList mProcessList; OomAdjuster(ActivityManagerService service, ProcessList processList, ActiveUids activeUids) { + this(service, processList, activeUids, createAdjusterThread()); + } + + private static ServiceThread createAdjusterThread() { + // The process group is usually critical to the response time of foreground app, so the + // setter should apply it as soon as possible. + final ServiceThread adjusterThread = + new ServiceThread(TAG, TOP_APP_PRIORITY_BOOST, false /* allowIo */); + adjusterThread.start(); + Process.setThreadGroupAndCpuset(adjusterThread.getThreadId(), THREAD_GROUP_TOP_APP); + return adjusterThread; + } + + OomAdjuster(ActivityManagerService service, ProcessList processList, ActiveUids activeUids, + ServiceThread adjusterThread) { mService = service; mProcessList = processList; mActiveUids = activeUids; @@ -170,12 +185,6 @@ public final class OomAdjuster { mConstants = mService.mConstants; mAppCompact = new AppCompactor(mService); - // The process group is usually critical to the response time of foreground app, so the - // setter should apply it as soon as possible. - final ServiceThread adjusterThread = new ServiceThread(TAG, TOP_APP_PRIORITY_BOOST, - false /* allowIo */); - adjusterThread.start(); - Process.setThreadGroupAndCpuset(adjusterThread.getThreadId(), THREAD_GROUP_TOP_APP); mProcessGroupHandler = new Handler(adjusterThread.getLooper(), msg -> { final int pid = msg.arg1; final int group = msg.arg2; 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/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..f3f9754bd32b 100644 --- a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java +++ b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java @@ -282,10 +282,10 @@ public abstract class BiometricServiceBase extends SystemService public EnrollClientImpl(Context context, DaemonWrapper daemon, long halDeviceId, IBinder token, ServiceListener listener, int userId, int groupId, byte[] cryptoToken, boolean restricted, String owner, - final int[] disabledFeatures) { + final int[] disabledFeatures, int timeoutSec) { super(context, getConstants(), daemon, halDeviceId, token, listener, userId, groupId, cryptoToken, restricted, owner, getBiometricUtils(), - disabledFeatures); + disabledFeatures, timeoutSec); } @Override @@ -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); @@ -908,8 +912,12 @@ public abstract class BiometricServiceBase extends SystemService } protected void setActiveUserInternal(int userId) { - // Do not put on handler, since it should finish before returning to caller. - updateActiveGroup(userId, null /* clientPackage */); + mHandler.post(() -> { + if (DEBUG) { + Slog.d(getTag(), "setActiveUser(" + userId + ")"); + } + updateActiveGroup(userId, null /* clientPackage */); + }); } protected void removeInternal(RemovalClient client) { diff --git a/services/core/java/com/android/server/biometrics/EnrollClient.java b/services/core/java/com/android/server/biometrics/EnrollClient.java index 854528f0654d..7ebb7c059b4c 100644 --- a/services/core/java/com/android/server/biometrics/EnrollClient.java +++ b/services/core/java/com/android/server/biometrics/EnrollClient.java @@ -31,11 +31,11 @@ import java.util.Arrays; * A class to keep track of the enrollment state for a given client. */ public abstract class EnrollClient extends ClientMonitor { - private static final long MS_PER_SEC = 1000; - private static final int ENROLLMENT_TIMEOUT_MS = 60 * 1000; // 1 minute private final byte[] mCryptoToken; private final BiometricUtils mBiometricUtils; private final int[] mDisabledFeatures; + private final int mTimeoutSec; + private long mEnrollmentStartTimeMs; public abstract boolean shouldVibrate(); @@ -44,12 +44,13 @@ public abstract class EnrollClient extends ClientMonitor { BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token, BiometricServiceBase.ServiceListener listener, int userId, int groupId, byte[] cryptoToken, boolean restricted, String owner, BiometricUtils utils, - final int[] disabledFeatures) { + final int[] disabledFeatures, int timeoutSec) { super(context, constants, daemon, halDeviceId, token, listener, userId, groupId, restricted, owner, 0 /* cookie */); mBiometricUtils = utils; mCryptoToken = Arrays.copyOf(cryptoToken, cryptoToken.length); mDisabledFeatures = Arrays.copyOf(disabledFeatures, disabledFeatures.length); + mTimeoutSec = timeoutSec; } @Override @@ -94,14 +95,13 @@ public abstract class EnrollClient extends ClientMonitor { @Override public int start() { mEnrollmentStartTimeMs = System.currentTimeMillis(); - final int timeout = (int) (ENROLLMENT_TIMEOUT_MS / MS_PER_SEC); try { final ArrayList<Integer> disabledFeatures = new ArrayList<>(); for (int i = 0; i < mDisabledFeatures.length; i++) { disabledFeatures.add(mDisabledFeatures[i]); } - final int result = getDaemonWrapper().enroll(mCryptoToken, getGroupId(), timeout, + final int result = getDaemonWrapper().enroll(mCryptoToken, getGroupId(), mTimeoutSec, disabledFeatures); if (result != 0) { Slog.w(getLogTag(), "startEnroll failed, result=" + result); diff --git a/services/core/java/com/android/server/biometrics/LoggableMonitor.java b/services/core/java/com/android/server/biometrics/LoggableMonitor.java index 6c7cbc166241..ecf3864e3362 100644 --- a/services/core/java/com/android/server/biometrics/LoggableMonitor.java +++ b/services/core/java/com/android/server/biometrics/LoggableMonitor.java @@ -93,7 +93,7 @@ public abstract class LoggableMonitor { statsAction(), statsClient(), acquiredInfo, - 0 /* vendorCode */, // Don't log vendorCode for now + vendorCode, Utils.isDebugEnabled(context, targetUserId)); } diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java index a38abdc1bed0..9d51abeed114 100644 --- a/services/core/java/com/android/server/biometrics/face/FaceService.java +++ b/services/core/java/com/android/server/biometrics/face/FaceService.java @@ -329,6 +329,7 @@ public class FaceService extends BiometricServiceBase { * Receives the incoming binder calls from FaceManager. */ private final class FaceServiceWrapper extends IFaceService.Stub { + private static final int ENROLL_TIMEOUT_SEC = 75; /** * The following methods contain common code which is shared in biometrics/common. @@ -368,7 +369,8 @@ public class FaceService extends BiometricServiceBase { final boolean restricted = isRestricted(); final EnrollClientImpl client = new EnrollClientImpl(getContext(), mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver), mCurrentUserId, - 0 /* groupId */, cryptoToken, restricted, opPackageName, disabledFeatures) { + 0 /* groupId */, cryptoToken, restricted, opPackageName, disabledFeatures, + ENROLL_TIMEOUT_SEC) { @Override public int[] getAcquireIgnorelist() { @@ -609,27 +611,32 @@ public class FaceService extends BiometricServiceBase { public void resetLockout(byte[] token) { checkPermission(MANAGE_BIOMETRIC); - if (!FaceService.this.hasEnrolledBiometrics(mCurrentUserId)) { - Slog.w(TAG, "Ignoring lockout reset, no templates enrolled"); - return; - } + mHandler.post(() -> { + if (!FaceService.this.hasEnrolledBiometrics(mCurrentUserId)) { + Slog.w(TAG, "Ignoring lockout reset, no templates enrolled"); + return; + } - Slog.d(TAG, "Resetting lockout for user: " + mCurrentUserId); + Slog.d(TAG, "Resetting lockout for user: " + mCurrentUserId); - try { - mDaemonWrapper.resetLockout(token); - } catch (RemoteException e) { - Slog.e(getTag(), "Unable to reset lockout", e); - } + try { + mDaemonWrapper.resetLockout(token); + } catch (RemoteException e) { + Slog.e(getTag(), "Unable to reset lockout", e); + } + }); } @Override public void setFeature(int userId, int feature, boolean enabled, final byte[] token, IFaceServiceReceiver receiver, final String opPackageName) { checkPermission(MANAGE_BIOMETRIC); - updateActiveGroup(userId, opPackageName); mHandler.post(() -> { + if (DEBUG) { + Slog.d(TAG, "setFeature for user(" + userId + ")"); + } + updateActiveGroup(userId, opPackageName); if (!FaceService.this.hasEnrolledBiometrics(mCurrentUserId)) { Slog.e(TAG, "No enrolled biometrics while setting feature: " + feature); return; @@ -660,9 +667,12 @@ public class FaceService extends BiometricServiceBase { public void getFeature(int userId, int feature, IFaceServiceReceiver receiver, final String opPackageName) { checkPermission(MANAGE_BIOMETRIC); - updateActiveGroup(userId, opPackageName); mHandler.post(() -> { + if (DEBUG) { + Slog.d(TAG, "getFeature for user(" + userId + ")"); + } + updateActiveGroup(userId, opPackageName); // This should ideally return tri-state, but the user isn't shown settings unless // they are enrolled so it's fine for now. if (!FaceService.this.hasEnrolledBiometrics(mCurrentUserId)) { diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java index 28336f459863..320e1022873c 100644 --- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java @@ -176,6 +176,7 @@ public class FingerprintService extends BiometricServiceBase { * Receives the incoming binder calls from FingerprintManager. */ private final class FingerprintServiceWrapper extends IFingerprintService.Stub { + private static final int ENROLL_TIMEOUT_SEC = 60; /** * The following methods contain common code which is shared in biometrics/common. @@ -203,7 +204,8 @@ public class FingerprintService extends BiometricServiceBase { final int groupId = userId; // default group for fingerprint enrollment final EnrollClientImpl client = new EnrollClientImpl(getContext(), mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver), mCurrentUserId, groupId, - cryptoToken, restricted, opPackageName, new int[0] /* disabledFeatures */) { + cryptoToken, restricted, opPackageName, new int[0] /* disabledFeatures */, + ENROLL_TIMEOUT_SEC) { @Override public boolean shouldVibrate() { return true; diff --git a/services/core/java/com/android/server/broadcastradio/hal2/Convert.java b/services/core/java/com/android/server/broadcastradio/hal2/Convert.java index 9730c9a1a380..1a1845ac3d5f 100644 --- a/services/core/java/com/android/server/broadcastradio/hal2/Convert.java +++ b/services/core/java/com/android/server/broadcastradio/hal2/Convert.java @@ -28,7 +28,6 @@ import android.hardware.broadcastradio.V2_0.MetadataKey; import android.hardware.broadcastradio.V2_0.ProgramFilter; import android.hardware.broadcastradio.V2_0.ProgramIdentifier; import android.hardware.broadcastradio.V2_0.ProgramInfo; -import android.hardware.broadcastradio.V2_0.ProgramInfoFlags; import android.hardware.broadcastradio.V2_0.ProgramListChunk; import android.hardware.broadcastradio.V2_0.Properties; import android.hardware.broadcastradio.V2_0.Result; diff --git a/services/core/java/com/android/server/broadcastradio/hal2/ProgramInfoCache.java b/services/core/java/com/android/server/broadcastradio/hal2/ProgramInfoCache.java index b1bd39566ba6..8c9389101141 100644 --- a/services/core/java/com/android/server/broadcastradio/hal2/ProgramInfoCache.java +++ b/services/core/java/com/android/server/broadcastradio/hal2/ProgramInfoCache.java @@ -48,6 +48,10 @@ class ProgramInfoCache { private final Map<ProgramSelector.Identifier, RadioManager.ProgramInfo> mProgramInfoMap = new HashMap<>(); + // Flag indicating whether mProgramInfoMap is considered complete based upon the received + // updates. + private boolean mComplete = true; + // Optional filter used in filterAndUpdateFrom(). Usually this field is null for a HAL-side // cache and non-null for an AIDL-side cache. private final ProgramList.Filter mFilter; @@ -58,9 +62,10 @@ class ProgramInfoCache { // Constructor for testing. @VisibleForTesting - ProgramInfoCache(@Nullable ProgramList.Filter filter, + ProgramInfoCache(@Nullable ProgramList.Filter filter, boolean complete, RadioManager.ProgramInfo... programInfos) { mFilter = filter; + mComplete = complete; for (RadioManager.ProgramInfo programInfo : programInfos) { mProgramInfoMap.put(programInfo.getSelector().getPrimaryId(), programInfo); } @@ -77,15 +82,23 @@ class ProgramInfoCache { @Override public String toString() { - StringBuilder sb = new StringBuilder("ProgramInfoCache("); + StringBuilder sb = new StringBuilder("ProgramInfoCache(mComplete = "); + sb.append(mComplete); + sb.append(", mFilter = "); + sb.append(mFilter); + sb.append(", mProgramInfoMap = ["); mProgramInfoMap.forEach((id, programInfo) -> { sb.append("\n"); sb.append(programInfo.toString()); }); - sb.append(")"); + sb.append("]"); return sb.toString(); } + public boolean isComplete() { + return mComplete; + } + public @Nullable ProgramList.Filter getFilter() { return mFilter; } @@ -102,6 +115,7 @@ class ProgramInfoCache { for (android.hardware.broadcastradio.V2_0.ProgramIdentifier halProgramId : chunk.removed) { mProgramInfoMap.remove(Convert.programIdentifierFromHal(halProgramId)); } + mComplete = chunk.complete; } @NonNull List<ProgramList.Chunk> filterAndUpdateFrom(@NonNull ProgramInfoCache other, @@ -122,26 +136,18 @@ class ProgramInfoCache { purge = true; } - Set<Integer> idTypes = mFilter != null ? mFilter.getIdentifierTypes() : null; - Set<ProgramSelector.Identifier> ids = mFilter != null ? mFilter.getIdentifiers() : null; - boolean includeCategories = mFilter != null ? mFilter.areCategoriesIncluded() : true; - boolean includeModifications = mFilter != null ? !mFilter.areModificationsExcluded() : true; - Set<RadioManager.ProgramInfo> modified = new HashSet<>(); Set<ProgramSelector.Identifier> removed = new HashSet<>(mProgramInfoMap.keySet()); for (Map.Entry<ProgramSelector.Identifier, RadioManager.ProgramInfo> entry : other.mProgramInfoMap.entrySet()) { ProgramSelector.Identifier id = entry.getKey(); - if ((idTypes != null && !idTypes.isEmpty() && !idTypes.contains(id.getType())) - || (ids != null && !ids.isEmpty() && !ids.contains(id)) - || (!includeCategories && id.isCategoryType())) { + if (!passesFilter(id)) { continue; } - removed.remove(id); - RadioManager.ProgramInfo oldInfo = mProgramInfoMap.get(id); + RadioManager.ProgramInfo newInfo = entry.getValue(); - if (oldInfo != null && (!includeModifications || oldInfo.equals(newInfo))) { + if (!shouldIncludeInModified(newInfo)) { continue; } mProgramInfoMap.put(id, newInfo); @@ -150,14 +156,81 @@ class ProgramInfoCache { for (ProgramSelector.Identifier rem : removed) { mProgramInfoMap.remove(rem); } - return buildChunks(purge, modified, maxNumModifiedPerChunk, removed, maxNumRemovedPerChunk); + mComplete = other.mComplete; + return buildChunks(purge, mComplete, modified, maxNumModifiedPerChunk, removed, + maxNumRemovedPerChunk); + } + + @Nullable List<ProgramList.Chunk> filterAndApplyChunk(@NonNull ProgramList.Chunk chunk) { + return filterAndApplyChunkInternal(chunk, MAX_NUM_MODIFIED_PER_CHUNK, + MAX_NUM_REMOVED_PER_CHUNK); + } + + @VisibleForTesting + @Nullable List<ProgramList.Chunk> filterAndApplyChunkInternal(@NonNull ProgramList.Chunk chunk, + int maxNumModifiedPerChunk, int maxNumRemovedPerChunk) { + if (chunk.isPurge()) { + mProgramInfoMap.clear(); + } + + Set<RadioManager.ProgramInfo> modified = new HashSet<>(); + Set<ProgramSelector.Identifier> removed = new HashSet<>(); + for (RadioManager.ProgramInfo info : chunk.getModified()) { + ProgramSelector.Identifier id = info.getSelector().getPrimaryId(); + if (!passesFilter(id) || !shouldIncludeInModified(info)) { + continue; + } + mProgramInfoMap.put(id, info); + modified.add(info); + } + for (ProgramSelector.Identifier id : chunk.getRemoved()) { + if (mProgramInfoMap.containsKey(id)) { + mProgramInfoMap.remove(id); + removed.add(id); + } + } + if (modified.isEmpty() && removed.isEmpty() && mComplete == chunk.isComplete()) { + return null; + } + mComplete = chunk.isComplete(); + return buildChunks(chunk.isPurge(), mComplete, modified, maxNumModifiedPerChunk, removed, + maxNumRemovedPerChunk); + } + + private boolean passesFilter(ProgramSelector.Identifier id) { + if (mFilter == null) { + return true; + } + if (!mFilter.getIdentifierTypes().isEmpty() + && !mFilter.getIdentifierTypes().contains(id.getType())) { + return false; + } + if (!mFilter.getIdentifiers().isEmpty() && !mFilter.getIdentifiers().contains(id)) { + return false; + } + if (!mFilter.areCategoriesIncluded() && id.isCategoryType()) { + return false; + } + return true; + } + + private boolean shouldIncludeInModified(RadioManager.ProgramInfo newInfo) { + RadioManager.ProgramInfo oldInfo = mProgramInfoMap.get( + newInfo.getSelector().getPrimaryId()); + if (oldInfo == null) { + return true; + } + if (mFilter != null && mFilter.areModificationsExcluded()) { + return false; + } + return !oldInfo.equals(newInfo); } private static int roundUpFraction(int numerator, int denominator) { return (numerator / denominator) + (numerator % denominator > 0 ? 1 : 0); } - private @NonNull List<ProgramList.Chunk> buildChunks(boolean purge, + private static @NonNull List<ProgramList.Chunk> buildChunks(boolean purge, boolean complete, @Nullable Collection<RadioManager.ProgramInfo> modified, int maxNumModifiedPerChunk, @Nullable Collection<ProgramSelector.Identifier> removed, int maxNumRemovedPerChunk) { // Communication protocol requires that if purge is set, removed is empty. @@ -205,8 +278,8 @@ class ProgramInfoCache { removedChunk.add(removedIter.next()); } } - chunks.add(new ProgramList.Chunk(purge && i == 0, i == numChunks - 1, modifiedChunk, - removedChunk)); + chunks.add(new ProgramList.Chunk(purge && i == 0, complete && (i == numChunks - 1), + modifiedChunk, removedChunk)); } return chunks; } diff --git a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java index acb0207ff11f..53890a48a674 100644 --- a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java +++ b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java @@ -40,6 +40,7 @@ import android.util.MutableInt; import android.util.Slog; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import java.util.ArrayList; import java.util.HashSet; @@ -64,7 +65,13 @@ class RadioModule { private Boolean mAntennaConnected = null; @GuardedBy("mLock") - private RadioManager.ProgramInfo mProgramInfo = null; + private RadioManager.ProgramInfo mCurrentProgramInfo = null; + + @GuardedBy("mLock") + private final ProgramInfoCache mProgramInfoCache = new ProgramInfoCache(null); + + @GuardedBy("mLock") + private android.hardware.radio.ProgramList.Filter mUnionOfAidlProgramFilters = null; // Callback registered with the HAL to relay callbacks to AIDL clients. private final ITunerCallback mHalTunerCallback = new ITunerCallback.Stub() { @@ -78,17 +85,22 @@ class RadioModule { public void onCurrentProgramInfoChanged(ProgramInfo halProgramInfo) { RadioManager.ProgramInfo programInfo = Convert.programInfoFromHal(halProgramInfo); synchronized (mLock) { - mProgramInfo = programInfo; + mCurrentProgramInfo = programInfo; fanoutAidlCallbackLocked(cb -> cb.onCurrentProgramInfoChanged(programInfo)); } } @Override public void onProgramListUpdated(ProgramListChunk programListChunk) { - // TODO: Cache per-AIDL client filters, send union of filters to HAL, use filters to fan - // back out to clients. - fanoutAidlCallback(cb -> cb.onProgramListUpdated(Convert.programListChunkFromHal( - programListChunk))); + synchronized (mLock) { + android.hardware.radio.ProgramList.Chunk chunk = + Convert.programListChunkFromHal(programListChunk); + mProgramInfoCache.filterAndApplyChunk(chunk); + + for (TunerSession tunerSession : mAidlTunerSessions) { + tunerSession.onMergedProgramListUpdateFromHal(chunk); + } + } } @Override @@ -109,8 +121,9 @@ class RadioModule { @GuardedBy("mLock") private final Set<TunerSession> mAidlTunerSessions = new HashSet<>(); - private RadioModule(@NonNull IBroadcastRadio service, - @NonNull RadioManager.ModuleProperties properties) throws RemoteException { + @VisibleForTesting + RadioModule(@NonNull IBroadcastRadio service, + @NonNull RadioManager.ModuleProperties properties) { mProperties = Objects.requireNonNull(properties); mService = Objects.requireNonNull(service); } @@ -163,8 +176,8 @@ class RadioModule { if (mAntennaConnected != null) { userCb.onAntennaState(mAntennaConnected); } - if (mProgramInfo != null) { - userCb.onCurrentProgramInfoChanged(mProgramInfo); + if (mCurrentProgramInfo != null) { + userCb.onCurrentProgramInfoChanged(mCurrentProgramInfo); } return tunerSession; @@ -186,18 +199,114 @@ class RadioModule { } } + private @Nullable android.hardware.radio.ProgramList.Filter + buildUnionOfTunerSessionFiltersLocked() { + Set<Integer> idTypes = null; + Set<android.hardware.radio.ProgramSelector.Identifier> ids = null; + boolean includeCategories = false; + boolean excludeModifications = true; + + for (TunerSession tunerSession : mAidlTunerSessions) { + android.hardware.radio.ProgramList.Filter filter = + tunerSession.getProgramListFilter(); + if (filter == null) { + continue; + } + + if (idTypes == null) { + idTypes = new HashSet<>(filter.getIdentifierTypes()); + ids = new HashSet<>(filter.getIdentifiers()); + includeCategories = filter.areCategoriesIncluded(); + excludeModifications = filter.areModificationsExcluded(); + continue; + } + if (!idTypes.isEmpty()) { + if (filter.getIdentifierTypes().isEmpty()) { + idTypes.clear(); + } else { + idTypes.addAll(filter.getIdentifierTypes()); + } + } + + if (!ids.isEmpty()) { + if (filter.getIdentifiers().isEmpty()) { + ids.clear(); + } else { + ids.addAll(filter.getIdentifiers()); + } + } + + includeCategories |= filter.areCategoriesIncluded(); + excludeModifications &= filter.areModificationsExcluded(); + } + + return idTypes == null ? null : new android.hardware.radio.ProgramList.Filter(idTypes, ids, + includeCategories, excludeModifications); + } + + void onTunerSessionProgramListFilterChanged(@Nullable TunerSession session) { + synchronized (mLock) { + onTunerSessionProgramListFilterChangedLocked(session); + } + } + + private void onTunerSessionProgramListFilterChangedLocked(@Nullable TunerSession session) { + android.hardware.radio.ProgramList.Filter newFilter = + buildUnionOfTunerSessionFiltersLocked(); + if (newFilter == null) { + // If there are no AIDL clients remaining, we can stop updates from the HAL as well. + if (mUnionOfAidlProgramFilters == null) { + return; + } + mUnionOfAidlProgramFilters = null; + try { + mHalTunerSession.stopProgramListUpdates(); + } catch (RemoteException ex) { + Slog.e(TAG, "mHalTunerSession.stopProgramListUpdates() failed: ", ex); + } + return; + } + + // If the HAL filter doesn't change, we can immediately send an update to the AIDL + // client. + if (newFilter.equals(mUnionOfAidlProgramFilters)) { + if (session != null) { + session.updateProgramInfoFromHalCache(mProgramInfoCache); + } + return; + } + + // Otherwise, update the HAL's filter, and AIDL clients will be updated when + // mHalTunerCallback.onProgramListUpdated() is called. + mUnionOfAidlProgramFilters = newFilter; + try { + int halResult = mHalTunerSession.startProgramListUpdates(Convert.programFilterToHal( + newFilter)); + Convert.throwOnError("startProgramListUpdates", halResult); + } catch (RemoteException ex) { + Slog.e(TAG, "mHalTunerSession.startProgramListUpdates() failed: ", ex); + } + } + void onTunerSessionClosed(TunerSession tunerSession) { synchronized (mLock) { + onTunerSessionsClosedLocked(tunerSession); + } + } + + private void onTunerSessionsClosedLocked(TunerSession... tunerSessions) { + for (TunerSession tunerSession : tunerSessions) { mAidlTunerSessions.remove(tunerSession); - if (mAidlTunerSessions.isEmpty() && mHalTunerSession != null) { - Slog.v(TAG, "closing HAL tuner session"); - try { - mHalTunerSession.close(); - } catch (RemoteException ex) { - Slog.e(TAG, "mHalTunerSession.close() failed: ", ex); - } - mHalTunerSession = null; + } + onTunerSessionProgramListFilterChanged(null); + if (mAidlTunerSessions.isEmpty() && mHalTunerSession != null) { + Slog.v(TAG, "closing HAL tuner session"); + try { + mHalTunerSession.close(); + } catch (RemoteException ex) { + Slog.e(TAG, "mHalTunerSession.close() failed: ", ex); } + mHalTunerSession = null; } } @@ -213,18 +322,25 @@ class RadioModule { } private void fanoutAidlCallbackLocked(AidlCallbackRunnable runnable) { + List<TunerSession> deadSessions = null; for (TunerSession tunerSession : mAidlTunerSessions) { try { runnable.run(tunerSession.mCallback); } catch (DeadObjectException ex) { - // The other side died without calling close(), so just purge it from our - // records. + // The other side died without calling close(), so just purge it from our records. Slog.e(TAG, "Removing dead TunerSession"); - mAidlTunerSessions.remove(tunerSession); + if (deadSessions == null) { + deadSessions = new ArrayList<>(); + } + deadSessions.add(tunerSession); } catch (RemoteException ex) { Slog.e(TAG, "Failed to invoke ITunerCallback: ", ex); } } + if (deadSessions != null) { + onTunerSessionsClosedLocked(deadSessions.toArray( + new TunerSession[deadSessions.size()])); + } } public android.hardware.radio.ICloseHandle addAnnouncementListener(@NonNull int[] enabledTypes, diff --git a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java index 008fea5831ad..764fca9a66b2 100644 --- a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java +++ b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java @@ -46,6 +46,7 @@ class TunerSession extends ITuner.Stub { final android.hardware.radio.ITunerCallback mCallback; private boolean mIsClosed = false; private boolean mIsMuted = false; + private ProgramInfoCache mProgramInfoCache = null; // necessary only for older APIs compatibility private RadioManager.BandConfig mDummyConfig = null; @@ -187,8 +188,51 @@ class TunerSession extends ITuner.Stub { public void startProgramListUpdates(ProgramList.Filter filter) throws RemoteException { synchronized (mLock) { checkNotClosedLocked(); - int halResult = mHwSession.startProgramListUpdates(Convert.programFilterToHal(filter)); - Convert.throwOnError("startProgramListUpdates", halResult); + mProgramInfoCache = new ProgramInfoCache(filter); + } + // Note: RadioModule.onTunerSessionProgramListFilterChanged() must be called without mLock + // held since it can call getProgramListFilter() and onHalProgramInfoUpdated(). + mModule.onTunerSessionProgramListFilterChanged(this); + } + + ProgramList.Filter getProgramListFilter() { + synchronized (mLock) { + return mProgramInfoCache == null ? null : mProgramInfoCache.getFilter(); + } + } + + void onMergedProgramListUpdateFromHal(ProgramList.Chunk mergedChunk) { + List<ProgramList.Chunk> clientUpdateChunks = null; + synchronized (mLock) { + if (mProgramInfoCache == null) { + return; + } + clientUpdateChunks = mProgramInfoCache.filterAndApplyChunk(mergedChunk); + } + dispatchClientUpdateChunks(clientUpdateChunks); + } + + void updateProgramInfoFromHalCache(ProgramInfoCache halCache) { + List<ProgramList.Chunk> clientUpdateChunks = null; + synchronized (mLock) { + if (mProgramInfoCache == null) { + return; + } + clientUpdateChunks = mProgramInfoCache.filterAndUpdateFrom(halCache, true); + } + dispatchClientUpdateChunks(clientUpdateChunks); + } + + private void dispatchClientUpdateChunks(@Nullable List<ProgramList.Chunk> chunks) { + if (chunks == null) { + return; + } + for (ProgramList.Chunk chunk : chunks) { + try { + mCallback.onProgramListUpdated(chunk); + } catch (RemoteException ex) { + Slog.w(TAG, "mCallback.onProgramListUpdated() failed: ", ex); + } } } @@ -196,8 +240,11 @@ class TunerSession extends ITuner.Stub { public void stopProgramListUpdates() throws RemoteException { synchronized (mLock) { checkNotClosedLocked(); - mHwSession.stopProgramListUpdates(); + mProgramInfoCache = null; } + // Note: RadioModule.onTunerSessionProgramListFilterChanged() must be called without mLock + // held since it can call getProgramListFilter() and onHalProgramInfoUpdated(). + mModule.onTunerSessionProgramListFilterChanged(this); } @Override 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..c59b0652af15 100644 --- a/services/core/java/com/android/server/compat/CompatConfig.java +++ b/services/core/java/com/android/server/compat/CompatConfig.java @@ -24,6 +24,7 @@ import android.util.LongSparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import java.io.PrintWriter; /** * This class maintains state relating to platform compatibility changes. * @@ -128,20 +129,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 +156,36 @@ 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; + } + + /** + * Dumps the current list of compatibility config information. + * + * @param pw The {@link PrintWriter} instance to which the information will be dumped. + */ + public void dumpConfig(PrintWriter pw) { + synchronized (mChanges) { + if (mChanges.size() == 0) { + pw.println("No compat overrides."); + return; + } + for (int i = 0; i < mChanges.size(); ++i) { + CompatChange c = mChanges.valueAt(i); + pw.println(c.toString()); + } + } } } diff --git a/services/core/java/com/android/server/compat/IPlatformCompat.aidl b/services/core/java/com/android/server/compat/IPlatformCompat.aidl new file mode 100644 index 000000000000..8ab08f9047cb --- /dev/null +++ b/services/core/java/com/android/server/compat/IPlatformCompat.aidl @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.compat; + +import android.content.pm.ApplicationInfo; + +/** + * System private API for talking with the PlatformCompat service. + * {@hide} + */ +interface IPlatformCompat +{ + + /** + * Reports that a compatibility change is affecting an app process now. + * + * <p>Note: for changes that are gated using {@link #isChangeEnabled(long, ApplicationInfo)}, + * you do not need to call this API directly. The change will be reported for you in the case + * that {@link #isChangeEnabled(long, ApplicationInfo)} returns {@code true}. + * + * @param changeId The ID of the compatibility change taking effect. + * @param appInfo Representing the affected app. + */ + void reportChange(long changeId, in ApplicationInfo appInfo); + + /** + * Query if a given compatibility change is enabled for an app process. This method should + * be called when implementing functionality on behalf of the affected app. + * + * <p>If this method returns {@code true}, the calling code should implement the compatibility + * change, resulting in differing behaviour compared to earlier releases. If this method returns + * {@code false}, the calling code should behave as it did in earlier releases. + * + * <p>When this method returns {@code true}, it will also report the change as + * {@link #reportChange(long, ApplicationInfo)} would, so there is no need to call that method + * directly. + * + * @param changeId The ID of the compatibility change in question. + * @param appInfo Representing the app in question. + * @return {@code true} if the change is enabled for the current app. + */ + boolean isChangeEnabled(long changeId, in ApplicationInfo appInfo); +}
\ No newline at end of file diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java index 456d15e4fba8..3eea194fd73e 100644 --- a/services/core/java/com/android/server/compat/PlatformCompat.java +++ b/services/core/java/com/android/server/compat/PlatformCompat.java @@ -16,52 +16,46 @@ package com.android.server.compat; +import android.content.Context; import android.content.pm.ApplicationInfo; import android.util.Slog; +import com.android.internal.util.DumpUtils; + +import java.io.FileDescriptor; +import java.io.PrintWriter; + /** * System server internal API for gating and reporting compatibility changes. */ -public class PlatformCompat { +public class PlatformCompat extends IPlatformCompat.Stub { private static final String TAG = "Compatibility"; - /** - * Reports that a compatibility change is affecting an app process now. - * - * <p>Note: for changes that are gated using {@link #isChangeEnabled(long, ApplicationInfo)}, - * you do not need to call this API directly. The change will be reported for you in the case - * that {@link #isChangeEnabled(long, ApplicationInfo)} returns {@code true}. - * - * @param changeId The ID of the compatibility change taking effect. - * @param appInfo Representing the affected app. - */ - public static void reportChange(long changeId, ApplicationInfo appInfo) { + private final Context mContext; + + public PlatformCompat(Context context) { + mContext = context; + } + + @Override + public void reportChange(long changeId, ApplicationInfo appInfo) { Slog.d(TAG, "Compat change reported: " + changeId + "; UID " + appInfo.uid); // TODO log via StatsLog } - /** - * Query if a given compatibility change is enabled for an app process. This method should - * be called when implementing functionality on behalf of the affected app. - * - * <p>If this method returns {@code true}, the calling code should implement the compatibility - * change, resulting in differing behaviour compared to earlier releases. If this method returns - * {@code false}, the calling code should behave as it did in earlier releases. - * - * <p>When this method returns {@code true}, it will also report the change as - * {@link #reportChange(long, ApplicationInfo)} would, so there is no need to call that method - * directly. - * - * @param changeId The ID of the compatibility change in question. - * @param appInfo Representing the app in question. - * @return {@code true} if the change is enabled for the current app. - */ - public static boolean isChangeEnabled(long changeId, ApplicationInfo appInfo) { + @Override + public boolean isChangeEnabled(long changeId, ApplicationInfo appInfo) { if (CompatConfig.get().isChangeEnabled(changeId, appInfo)) { reportChange(changeId, appInfo); return true; } return false; } + + @Override + protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, "platform_compat", pw)) return; + CompatConfig.get().dumpConfig(pw); + } } 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/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java index 64a05c94e39e..f6c49ed4d29e 100644 --- a/services/core/java/com/android/server/content/SyncManager.java +++ b/services/core/java/com/android/server/content/SyncManager.java @@ -86,7 +86,7 @@ import android.os.UserHandle; import android.os.UserManager; import android.os.WorkSource; import android.provider.Settings; -import android.text.format.Time; +import android.text.format.TimeMigrationUtils; import android.util.EventLog; import android.util.Log; import android.util.Pair; @@ -1993,9 +1993,7 @@ public class SyncManager { if (time == 0) { return "N/A"; } - Time tobj = new Time(); - tobj.set(time); - return tobj.format("%Y-%m-%d %H:%M:%S"); + return TimeMigrationUtils.formatMillisWithFixedFormat(time); } private final static Comparator<SyncOperation> sOpDumpComparator = (op1, op2) -> { @@ -2561,9 +2559,7 @@ public class SyncManager { accountKey = "Unknown"; } final long elapsedTime = item.elapsedTime; - final Time time = new Time(); final long eventTime = item.eventTime; - time.set(eventTime); final String key = authorityName + "/" + accountKey; final Long lastEventTime = lastTimeMap.get(key); @@ -2628,9 +2624,7 @@ public class SyncManager { authorityName = "Unknown"; accountKey = "Unknown"; } - final Time time = new Time(); final long eventTime = item.eventTime; - time.set(eventTime); pw.printf(" #%-3d: %s %8s ", i + 1, diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java index d73c25336808..6f2b33453891 100644 --- a/services/core/java/com/android/server/job/controllers/JobStatus.java +++ b/services/core/java/com/android/server/job/controllers/JobStatus.java @@ -28,7 +28,7 @@ import android.net.Network; import android.net.Uri; import android.os.RemoteException; import android.os.UserHandle; -import android.text.format.Time; +import android.text.format.TimeMigrationUtils; import android.util.ArraySet; import android.util.Pair; import android.util.Slog; @@ -1660,17 +1660,13 @@ public final class JobStatus { if (numFailures != 0) { pw.print(prefix); pw.print("Num failures: "); pw.println(numFailures); } - final Time t = new Time(); - final String format = "%Y-%m-%d %H:%M:%S"; if (mLastSuccessfulRunTime != 0) { pw.print(prefix); pw.print("Last successful run: "); - t.set(mLastSuccessfulRunTime); - pw.println(t.format(format)); + pw.println(TimeMigrationUtils.formatMillisWithFixedFormat(mLastSuccessfulRunTime)); } if (mLastFailedRunTime != 0) { pw.print(prefix); pw.print("Last failed run: "); - t.set(mLastFailedRunTime); - pw.println(t.format(format)); + pw.println(TimeMigrationUtils.formatMillisWithFixedFormat(mLastFailedRunTime)); } } diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java index 6911b7c8f565..461f19bf8b08 100644 --- a/services/core/java/com/android/server/location/GnssLocationProvider.java +++ b/services/core/java/com/android/server/location/GnssLocationProvider.java @@ -1608,13 +1608,11 @@ public class GnssLocationProvider extends AbstractLocationProvider implements if (DEBUG) Log.d(TAG, "reportGnssServiceDied"); mHandler.post(() -> { setupNativeGnssService(/* reinitializeGnssServiceHandle = */ true); + // resend configuration into the restarted HAL service. + reloadGpsProperties(); if (isGpsEnabled()) { setGpsEnabled(false); - updateEnabled(); - - // resend configuration into the restarted HAL service. - reloadGpsProperties(); } }); } diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java index f0e431ee0354..84ae7c790a74 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java @@ -48,6 +48,8 @@ import java.io.DataOutputStream; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; +import java.nio.channels.FileChannel; +import java.nio.file.StandardOpenOption; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -390,6 +392,17 @@ class LockSettingsStorage { return stored; } + private void fsyncDirectory(File directory) { + try { + try (FileChannel file = FileChannel.open(directory.toPath(), + StandardOpenOption.READ)) { + file.force(true); + } + } catch (IOException e) { + Slog.e(TAG, "Error syncing directory: " + directory, e); + } + } + private void writeFile(String name, byte[] hash) { synchronized (mFileWriteLock) { RandomAccessFile raf = null; @@ -406,6 +419,7 @@ class LockSettingsStorage { raf.write(hash, 0, hash.length); } raf.close(); + fsyncDirectory((new File(name)).getAbsoluteFile().getParentFile()); } catch (IOException e) { Slog.e(TAG, "Error writing to file " + e); } finally { diff --git a/services/core/java/com/android/server/locksettings/TEST_MAPPING b/services/core/java/com/android/server/locksettings/TEST_MAPPING new file mode 100644 index 000000000000..c1cba5f7f22d --- /dev/null +++ b/services/core/java/com/android/server/locksettings/TEST_MAPPING @@ -0,0 +1,15 @@ +{ + "presubmit": [ + { + "name": "CtsDevicePolicyManagerTestCases", + "options": [ + { + "include-annotation": "com.android.cts.devicepolicy.annotations.LockSettingsTest" + }, + { + "exclude-annotation": "android.platform.test.annotations.FlakyTest" + } + ] + } + ] +} diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java index c54bfc01e031..0ad6c2a69556 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/PlatformKeyManager.java @@ -19,6 +19,7 @@ package com.android.server.locksettings.recoverablekeystore; import android.app.KeyguardManager; import android.content.Context; import android.os.RemoteException; +import android.os.UserHandle; import android.security.GateKeeper; import android.security.keystore.AndroidKeyStoreSecretKey; import android.security.keystore.KeyPermanentlyInvalidatedException; @@ -437,25 +438,31 @@ public class PlatformKeyManager { // so it may live in memory for some time. SecretKey secretKey = generateAesKey(); - long secureUserId = getGateKeeperService().getSecureUserId(userId); - // TODO(b/124095438): Propagate this failure instead of silently failing. - if (secureUserId == GateKeeper.INVALID_SECURE_USER_ID) { - Log.e(TAG, "No SID available for user " + userId); - return; - } - - // Store decryption key first since it is more likely to fail. - mKeyStore.setEntry( - decryptAlias, - new KeyStore.SecretKeyEntry(secretKey), + KeyProtection.Builder decryptionKeyProtection = new KeyProtection.Builder(KeyProperties.PURPOSE_DECRYPT) .setUserAuthenticationRequired(true) .setUserAuthenticationValidityDurationSeconds( USER_AUTHENTICATION_VALIDITY_DURATION_SECONDS) .setBlockModes(KeyProperties.BLOCK_MODE_GCM) - .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) + .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE); + if (userId != UserHandle.USER_SYSTEM) { + // Bind decryption key to secondary profile lock screen secret. + long secureUserId = getGateKeeperService().getSecureUserId(userId); + // TODO(b/124095438): Propagate this failure instead of silently failing. + if (secureUserId == GateKeeper.INVALID_SECURE_USER_ID) { + Log.e(TAG, "No SID available for user " + userId); + return; + } + decryptionKeyProtection .setBoundToSpecificSecureUserId(secureUserId) - .build()); + // Ignore caller uid which always belongs to the primary profile. + .setCriticalToDeviceEncryption(true); + } + // Store decryption key first since it is more likely to fail. + mKeyStore.setEntry( + decryptAlias, + new KeyStore.SecretKeyEntry(secretKey), + decryptionKeyProtection.build()); mKeyStore.setEntry( encryptAlias, new KeyStore.SecretKeyEntry(secretKey), diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index 5530b51042e3..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; @@ -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)); @@ -5301,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/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java index 4a6eb276bd02..4828bbfff676 100644 --- a/services/core/java/com/android/server/notification/ManagedServices.java +++ b/services/core/java/com/android/server/notification/ManagedServices.java @@ -196,18 +196,20 @@ abstract public class ManagedServices { public void dump(PrintWriter pw, DumpFilter filter) { pw.println(" Allowed " + getCaption() + "s:"); - final int N = mApproved.size(); - for (int i = 0 ; i < N; i++) { - final int userId = mApproved.keyAt(i); - final ArrayMap<Boolean, ArraySet<String>> approvedByType = mApproved.valueAt(i); - if (approvedByType != null) { - final int M = approvedByType.size(); - for (int j = 0; j < M; j++) { - final boolean isPrimary = approvedByType.keyAt(j); - final ArraySet<String> approved = approvedByType.valueAt(j); - if (approvedByType != null && approvedByType.size() > 0) { - pw.println(" " + String.join(ENABLED_SERVICES_SEPARATOR, approved) - + " (user: " + userId + " isPrimary: " + isPrimary + ")"); + synchronized (mApproved) { + final int N = mApproved.size(); + for (int i = 0; i < N; i++) { + final int userId = mApproved.keyAt(i); + final ArrayMap<Boolean, ArraySet<String>> approvedByType = mApproved.valueAt(i); + if (approvedByType != null) { + final int M = approvedByType.size(); + for (int j = 0; j < M; j++) { + final boolean isPrimary = approvedByType.keyAt(j); + final ArraySet<String> approved = approvedByType.valueAt(j); + if (approvedByType != null && approvedByType.size() > 0) { + pw.println(" " + String.join(ENABLED_SERVICES_SEPARATOR, approved) + + " (user: " + userId + " isPrimary: " + isPrimary + ")"); + } } } } @@ -240,23 +242,25 @@ abstract public class ManagedServices { public void dump(ProtoOutputStream proto, DumpFilter filter) { proto.write(ManagedServicesProto.CAPTION, getCaption()); - final int N = mApproved.size(); - for (int i = 0 ; i < N; i++) { - final int userId = mApproved.keyAt(i); - final ArrayMap<Boolean, ArraySet<String>> approvedByType = mApproved.valueAt(i); - if (approvedByType != null) { - final int M = approvedByType.size(); - for (int j = 0; j < M; j++) { - final boolean isPrimary = approvedByType.keyAt(j); - final ArraySet<String> approved = approvedByType.valueAt(j); - if (approvedByType != null && approvedByType.size() > 0) { - final long sToken = proto.start(ManagedServicesProto.APPROVED); - for (String s : approved) { - proto.write(ServiceProto.NAME, s); + synchronized (mApproved) { + final int N = mApproved.size(); + for (int i = 0; i < N; i++) { + final int userId = mApproved.keyAt(i); + final ArrayMap<Boolean, ArraySet<String>> approvedByType = mApproved.valueAt(i); + if (approvedByType != null) { + final int M = approvedByType.size(); + for (int j = 0; j < M; j++) { + final boolean isPrimary = approvedByType.keyAt(j); + final ArraySet<String> approved = approvedByType.valueAt(j); + if (approvedByType != null && approvedByType.size() > 0) { + final long sToken = proto.start(ManagedServicesProto.APPROVED); + for (String s : approved) { + proto.write(ServiceProto.NAME, s); + } + proto.write(ServiceProto.USER_ID, userId); + proto.write(ServiceProto.IS_PRIMARY, isPrimary); + proto.end(sToken); } - proto.write(ServiceProto.USER_ID, userId); - proto.write(ServiceProto.IS_PRIMARY, isPrimary); - proto.end(sToken); } } } @@ -315,33 +319,36 @@ abstract public class ManagedServices { trimApprovedListsAccordingToInstalledServices(userId); } - final int N = mApproved.size(); - for (int i = 0 ; i < N; i++) { - final int approvedUserId = mApproved.keyAt(i); - if (forBackup && approvedUserId != userId) { - continue; - } - final ArrayMap<Boolean, ArraySet<String>> approvedByType = mApproved.valueAt(i); - if (approvedByType != null) { - final int M = approvedByType.size(); - for (int j = 0; j < M; j++) { - final boolean isPrimary = approvedByType.keyAt(j); - final Set<String> approved = approvedByType.valueAt(j); - if (approved != null) { - String allowedItems = String.join(ENABLED_SERVICES_SEPARATOR, approved); - out.startTag(null, TAG_MANAGED_SERVICES); - out.attribute(null, ATT_APPROVED_LIST, allowedItems); - out.attribute(null, ATT_USER_ID, Integer.toString(approvedUserId)); - out.attribute(null, ATT_IS_PRIMARY, Boolean.toString(isPrimary)); - writeExtraAttributes(out, approvedUserId); - out.endTag(null, TAG_MANAGED_SERVICES); - - if (!forBackup && isPrimary) { - // Also write values to settings, for observers who haven't migrated yet - Settings.Secure.putStringForUser(mContext.getContentResolver(), - getConfig().secureSettingName, allowedItems, approvedUserId); - } + synchronized (mApproved) { + final int N = mApproved.size(); + for (int i = 0; i < N; i++) { + final int approvedUserId = mApproved.keyAt(i); + if (forBackup && approvedUserId != userId) { + continue; + } + final ArrayMap<Boolean, ArraySet<String>> approvedByType = mApproved.valueAt(i); + if (approvedByType != null) { + final int M = approvedByType.size(); + for (int j = 0; j < M; j++) { + final boolean isPrimary = approvedByType.keyAt(j); + final Set<String> approved = approvedByType.valueAt(j); + if (approved != null) { + String allowedItems = String.join(ENABLED_SERVICES_SEPARATOR, approved); + out.startTag(null, TAG_MANAGED_SERVICES); + out.attribute(null, ATT_APPROVED_LIST, allowedItems); + out.attribute(null, ATT_USER_ID, Integer.toString(approvedUserId)); + out.attribute(null, ATT_IS_PRIMARY, Boolean.toString(isPrimary)); + writeExtraAttributes(out, approvedUserId); + out.endTag(null, TAG_MANAGED_SERVICES); + + if (!forBackup && isPrimary) { + // Also write values to settings, for observers who haven't migrated yet + Settings.Secure.putStringForUser(mContext.getContentResolver(), + getConfig().secureSettingName, allowedItems, + approvedUserId); + } + } } } } @@ -440,23 +447,25 @@ abstract public class ManagedServices { if (TextUtils.isEmpty(approved)) { approved = ""; } - ArrayMap<Boolean, ArraySet<String>> approvedByType = mApproved.get(userId); - if (approvedByType == null) { - approvedByType = new ArrayMap<>(); - mApproved.put(userId, approvedByType); - } + synchronized (mApproved) { + ArrayMap<Boolean, ArraySet<String>> approvedByType = mApproved.get(userId); + if (approvedByType == null) { + approvedByType = new ArrayMap<>(); + mApproved.put(userId, approvedByType); + } - ArraySet<String> approvedList = approvedByType.get(isPrimary); - if (approvedList == null) { - approvedList = new ArraySet<>(); - approvedByType.put(isPrimary, approvedList); - } + ArraySet<String> approvedList = approvedByType.get(isPrimary); + if (approvedList == null) { + approvedList = new ArraySet<>(); + approvedByType.put(isPrimary, approvedList); + } - String[] approvedArray = approved.split(ENABLED_SERVICES_SEPARATOR); - for (String pkgOrComponent : approvedArray) { - String approvedItem = getApprovedValue(pkgOrComponent); - if (approvedItem != null) { - approvedList.add(approvedItem); + String[] approvedArray = approved.split(ENABLED_SERVICES_SEPARATOR); + for (String pkgOrComponent : approvedArray) { + String approvedItem = getApprovedValue(pkgOrComponent); + if (approvedItem != null) { + approvedList.add(approvedItem); + } } } } @@ -469,23 +478,25 @@ abstract public class ManagedServices { boolean isPrimary, boolean enabled) { Slog.i(TAG, (enabled ? " Allowing " : "Disallowing ") + mConfig.caption + " " + pkgOrComponent); - ArrayMap<Boolean, ArraySet<String>> allowedByType = mApproved.get(userId); - if (allowedByType == null) { - allowedByType = new ArrayMap<>(); - mApproved.put(userId, allowedByType); - } - ArraySet<String> approved = allowedByType.get(isPrimary); - if (approved == null) { - approved = new ArraySet<>(); - allowedByType.put(isPrimary, approved); - } - String approvedItem = getApprovedValue(pkgOrComponent); - - if (approvedItem != null) { - if (enabled) { - approved.add(approvedItem); - } else { - approved.remove(approvedItem); + synchronized (mApproved) { + ArrayMap<Boolean, ArraySet<String>> allowedByType = mApproved.get(userId); + if (allowedByType == null) { + allowedByType = new ArrayMap<>(); + mApproved.put(userId, allowedByType); + } + ArraySet<String> approved = allowedByType.get(isPrimary); + if (approved == null) { + approved = new ArraySet<>(); + allowedByType.put(isPrimary, approved); + } + String approvedItem = getApprovedValue(pkgOrComponent); + + if (approvedItem != null) { + if (enabled) { + approved.add(approvedItem); + } else { + approved.remove(approvedItem); + } } } @@ -504,22 +515,26 @@ abstract public class ManagedServices { } protected String getApproved(int userId, boolean primary) { - final ArrayMap<Boolean, ArraySet<String>> allowedByType = - mApproved.getOrDefault(userId, new ArrayMap<>()); - ArraySet<String> approved = allowedByType.getOrDefault(primary, new ArraySet<>()); - return String.join(ENABLED_SERVICES_SEPARATOR, approved); + synchronized (mApproved) { + final ArrayMap<Boolean, ArraySet<String>> allowedByType = + mApproved.getOrDefault(userId, new ArrayMap<>()); + ArraySet<String> approved = allowedByType.getOrDefault(primary, new ArraySet<>()); + return String.join(ENABLED_SERVICES_SEPARATOR, approved); + } } protected List<ComponentName> getAllowedComponents(int userId) { final List<ComponentName> allowedComponents = new ArrayList<>(); - final ArrayMap<Boolean, ArraySet<String>> allowedByType = - mApproved.getOrDefault(userId, new ArrayMap<>()); - for (int i = 0; i < allowedByType.size(); i++) { - final ArraySet<String> allowed = allowedByType.valueAt(i); - for (int j = 0; j < allowed.size(); j++) { - ComponentName cn = ComponentName.unflattenFromString(allowed.valueAt(j)); - if (cn != null) { - allowedComponents.add(cn); + synchronized (mApproved) { + final ArrayMap<Boolean, ArraySet<String>> allowedByType = + mApproved.getOrDefault(userId, new ArrayMap<>()); + for (int i = 0; i < allowedByType.size(); i++) { + final ArraySet<String> allowed = allowedByType.valueAt(i); + for (int j = 0; j < allowed.size(); j++) { + ComponentName cn = ComponentName.unflattenFromString(allowed.valueAt(j)); + if (cn != null) { + allowedComponents.add(cn); + } } } } @@ -528,14 +543,16 @@ abstract public class ManagedServices { protected List<String> getAllowedPackages(int userId) { final List<String> allowedPackages = new ArrayList<>(); - final ArrayMap<Boolean, ArraySet<String>> allowedByType = - mApproved.getOrDefault(userId, new ArrayMap<>()); - for (int i = 0; i < allowedByType.size(); i++) { - final ArraySet<String> allowed = allowedByType.valueAt(i); - for (int j = 0; j < allowed.size(); j++) { - String pkgName = getPackageName(allowed.valueAt(j)); - if (!TextUtils.isEmpty(pkgName)) { - allowedPackages.add(pkgName); + synchronized (mApproved) { + final ArrayMap<Boolean, ArraySet<String>> allowedByType = + mApproved.getOrDefault(userId, new ArrayMap<>()); + for (int i = 0; i < allowedByType.size(); i++) { + final ArraySet<String> allowed = allowedByType.valueAt(i); + for (int j = 0; j < allowed.size(); j++) { + String pkgName = getPackageName(allowed.valueAt(j)); + if (!TextUtils.isEmpty(pkgName)) { + allowedPackages.add(pkgName); + } } } } @@ -543,12 +560,14 @@ abstract public class ManagedServices { } protected boolean isPackageOrComponentAllowed(String pkgOrComponent, int userId) { - ArrayMap<Boolean, ArraySet<String>> allowedByType = - mApproved.getOrDefault(userId, new ArrayMap<>()); - for (int i = 0; i < allowedByType.size(); i++) { - ArraySet<String> allowed = allowedByType.valueAt(i); - if (allowed.contains(pkgOrComponent)) { - return true; + synchronized (mApproved) { + ArrayMap<Boolean, ArraySet<String>> allowedByType = + mApproved.getOrDefault(userId, new ArrayMap<>()); + for (int i = 0; i < allowedByType.size(); i++) { + ArraySet<String> allowed = allowedByType.valueAt(i); + if (allowed.contains(pkgOrComponent)) { + return true; + } } } return false; @@ -558,19 +577,21 @@ abstract public class ManagedServices { if (pkg == null) { return false; } - ArrayMap<Boolean, ArraySet<String>> allowedByType = - mApproved.getOrDefault(userId, new ArrayMap<>()); - for (int i = 0; i < allowedByType.size(); i++) { - ArraySet<String> allowed = allowedByType.valueAt(i); - for (String allowedEntry : allowed) { - ComponentName component = ComponentName.unflattenFromString(allowedEntry); - if (component != null) { - if (pkg.equals(component.getPackageName())) { - return true; - } - } else { - if (pkg.equals(allowedEntry)) { - return true; + synchronized (mApproved) { + ArrayMap<Boolean, ArraySet<String>> allowedByType = + mApproved.getOrDefault(userId, new ArrayMap<>()); + for (int i = 0; i < allowedByType.size(); i++) { + ArraySet<String> allowed = allowedByType.valueAt(i); + for (String allowedEntry : allowed) { + ComponentName component = ComponentName.unflattenFromString(allowedEntry); + if (component != null) { + if (pkg.equals(component.getPackageName())) { + return true; + } + } else { + if (pkg.equals(allowedEntry)) { + return true; + } } } } @@ -616,7 +637,9 @@ abstract public class ManagedServices { public void onUserRemoved(int user) { Slog.i(TAG, "Removing approved services for removed user " + user); - mApproved.remove(user); + synchronized (mApproved) { + mApproved.remove(user); + } rebindServices(true, user); } @@ -797,14 +820,16 @@ abstract public class ManagedServices { protected Set<String> getAllowedPackages() { final Set<String> allowedPackages = new ArraySet<>(); - for (int k = 0; k < mApproved.size(); k++) { - ArrayMap<Boolean, ArraySet<String>> allowedByType = mApproved.valueAt(k); - for (int i = 0; i < allowedByType.size(); i++) { - final ArraySet<String> allowed = allowedByType.valueAt(i); - for (int j = 0; j < allowed.size(); j++) { - String pkgName = getPackageName(allowed.valueAt(j)); - if (!TextUtils.isEmpty(pkgName)) { - allowedPackages.add(pkgName); + synchronized (mApproved) { + for (int k = 0; k < mApproved.size(); k++) { + ArrayMap<Boolean, ArraySet<String>> allowedByType = mApproved.valueAt(k); + for (int i = 0; i < allowedByType.size(); i++) { + final ArraySet<String> allowed = allowedByType.valueAt(i); + for (int j = 0; j < allowed.size(); j++) { + String pkgName = getPackageName(allowed.valueAt(j)); + if (!TextUtils.isEmpty(pkgName)) { + allowedPackages.add(pkgName); + } } } } @@ -813,22 +838,24 @@ abstract public class ManagedServices { } private void trimApprovedListsAccordingToInstalledServices(int userId) { - final ArrayMap<Boolean, ArraySet<String>> approvedByType = mApproved.get(userId); - if (approvedByType == null) { - return; - } - for (int i = 0; i < approvedByType.size(); i++) { - final ArraySet<String> approved = approvedByType.valueAt(i); - for (int j = approved.size() - 1; j >= 0; j--) { - final String approvedPackageOrComponent = approved.valueAt(j); - if (!isValidEntry(approvedPackageOrComponent, userId)){ - approved.removeAt(j); - Slog.v(TAG, "Removing " + approvedPackageOrComponent - + " from approved list; no matching services found"); - } else { - if (DEBUG) { - Slog.v(TAG, "Keeping " + approvedPackageOrComponent - + " on approved list; matching services found"); + synchronized (mApproved) { + final ArrayMap<Boolean, ArraySet<String>> approvedByType = mApproved.get(userId); + if (approvedByType == null) { + return; + } + for (int i = 0; i < approvedByType.size(); i++) { + final ArraySet<String> approved = approvedByType.valueAt(i); + for (int j = approved.size() - 1; j >= 0; j--) { + final String approvedPackageOrComponent = approved.valueAt(j); + if (!isValidEntry(approvedPackageOrComponent, userId)) { + approved.removeAt(j); + Slog.v(TAG, "Removing " + approvedPackageOrComponent + + " from approved list; no matching services found"); + } else { + if (DEBUG) { + Slog.v(TAG, "Keeping " + approvedPackageOrComponent + + " on approved list; matching services found"); + } } } } @@ -837,20 +864,23 @@ abstract public class ManagedServices { private boolean removeUninstalledItemsFromApprovedLists(int uninstalledUserId, String pkg) { boolean removed = false; - final ArrayMap<Boolean, ArraySet<String>> approvedByType = mApproved.get(uninstalledUserId); - if (approvedByType != null) { - int M = approvedByType.size(); - for (int j = 0; j < M; j++) { - final ArraySet<String> approved = approvedByType.valueAt(j); - int O = approved.size(); - for (int k = O - 1; k >= 0; k--) { - final String packageOrComponent = approved.valueAt(k); - final String packageName = getPackageName(packageOrComponent); - if (TextUtils.equals(pkg, packageName)) { - approved.removeAt(k); - if (DEBUG) { - Slog.v(TAG, "Removing " + packageOrComponent - + " from approved list; uninstalled"); + synchronized (mApproved) { + final ArrayMap<Boolean, ArraySet<String>> approvedByType = mApproved.get( + uninstalledUserId); + if (approvedByType != null) { + int M = approvedByType.size(); + for (int j = 0; j < M; j++) { + final ArraySet<String> approved = approvedByType.valueAt(j); + int O = approved.size(); + for (int k = O - 1; k >= 0; k--) { + final String packageOrComponent = approved.valueAt(k); + final String packageName = getPackageName(packageOrComponent); + if (TextUtils.equals(pkg, packageName)) { + approved.removeAt(k); + if (DEBUG) { + Slog.v(TAG, "Removing " + packageOrComponent + + " from approved list; uninstalled"); + } } } } @@ -887,17 +917,19 @@ abstract public class ManagedServices { for (int i = 0; i < nUserIds; ++i) { final int userId = userIds.get(i); - final ArrayMap<Boolean, ArraySet<String>> approvedLists = mApproved.get(userId); - if (approvedLists != null) { - final int N = approvedLists.size(); - for (int j = 0; j < N; j++) { - ArraySet<ComponentName> approvedByUser = componentsByUser.get(userId); - if (approvedByUser == null) { - approvedByUser = new ArraySet<>(); - componentsByUser.put(userId, approvedByUser); + synchronized (mApproved) { + final ArrayMap<Boolean, ArraySet<String>> approvedLists = mApproved.get(userId); + if (approvedLists != null) { + final int N = approvedLists.size(); + for (int j = 0; j < N; j++) { + ArraySet<ComponentName> approvedByUser = componentsByUser.get(userId); + if (approvedByUser == null) { + approvedByUser = new ArraySet<>(); + componentsByUser.put(userId, approvedByUser); + } + approvedByUser.addAll( + loadComponentNamesFromValues(approvedLists.valueAt(j), userId)); } - approvedByUser.addAll( - loadComponentNamesFromValues(approvedLists.valueAt(j), userId)); } } } diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java index 6f28675d051b..934511bf88d1 100644 --- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java +++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java @@ -296,22 +296,12 @@ final class OverlayManagerServiceImpl { */ private void updateAndRefreshOverlaysForTarget(@NonNull final String targetPackageName, final int userId, final int flags) { - final List<OverlayInfo> ois = new ArrayList<>(); - - // Framework overlays added first because order matters when resolving a resource - if (!"android".equals(targetPackageName)) { - ois.addAll(mSettings.getOverlaysForTarget("android", userId)); - } - - // Then add the targeted, non-framework overlays which have higher priority - ois.addAll(mSettings.getOverlaysForTarget(targetPackageName, userId)); - - final List<String> enabledBaseCodePaths = new ArrayList<>(ois.size()); + final List<OverlayInfo> targetOverlays = mSettings.getOverlaysForTarget(targetPackageName, + userId); + // Update the state for any overlay that targets this package. boolean modified = false; - final int n = ois.size(); - for (int i = 0; i < n; i++) { - final OverlayInfo oi = ois.get(i); + for (final OverlayInfo oi : targetOverlays) { final PackageInfo overlayPackage = mPackageManager.getPackageInfo(oi.packageName, userId); if (overlayPackage == null) { @@ -324,25 +314,39 @@ final class OverlayManagerServiceImpl { Slog.e(TAG, "failed to update settings", e); modified |= mSettings.remove(oi.packageName, userId); } - - if (oi.isEnabled() && overlayPackage.applicationInfo != null) { - enabledBaseCodePaths.add(overlayPackage.applicationInfo.getBaseCodePath()); - } } } if (!modified) { + // Update the overlay paths of the target within package manager if necessary. + final List<String> enabledOverlayPaths = new ArrayList<>(targetOverlays.size()); + + // Framework overlays are first in the overlay paths of a package within PackageManager. + for (final OverlayInfo oi : mSettings.getOverlaysForTarget("android", userId)) { + if (oi.isEnabled()) { + enabledOverlayPaths.add(oi.baseCodePath); + } + } + + for (final OverlayInfo oi : targetOverlays) { + if (oi.isEnabled()) { + enabledOverlayPaths.add(oi.baseCodePath); + } + } + + // TODO(): Use getEnabledOverlayPaths(userId, targetPackageName) instead of + // resourceDirs if in the future resourceDirs contains APKs other than overlays PackageInfo packageInfo = mPackageManager.getPackageInfo(targetPackageName, userId); ApplicationInfo appInfo = packageInfo == null ? null : packageInfo.applicationInfo; String[] resourceDirs = appInfo == null ? null : appInfo.resourceDirs; // If the lists aren't the same length, the enabled overlays have changed - if (ArrayUtils.size(resourceDirs) != enabledBaseCodePaths.size()) { + if (ArrayUtils.size(resourceDirs) != enabledOverlayPaths.size()) { modified = true; } else if (resourceDirs != null) { // If any element isn't equal, an overlay or the order of overlays has changed for (int index = 0; index < resourceDirs.length; index++) { - if (!resourceDirs[index].equals(enabledBaseCodePaths.get(index))) { + if (!resourceDirs[index].equals(enabledOverlayPaths.get(index))) { modified = true; break; } diff --git a/services/core/java/com/android/server/pm/PackageAbiHelper.java b/services/core/java/com/android/server/pm/PackageAbiHelper.java new file mode 100644 index 000000000000..da4558277b95 --- /dev/null +++ b/services/core/java/com/android/server/pm/PackageAbiHelper.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm; + +import android.annotation.Nullable; +import android.content.pm.PackageParser; + +import com.android.internal.annotations.VisibleForTesting; + +import java.io.File; +import java.util.List; +import java.util.Set; + +@VisibleForTesting +interface PackageAbiHelper { + /** + * Derive and set the location of native libraries for the given package, + * which varies depending on where and how the package was installed. + * + * WARNING: This API enables modifying of the package. + * TODO(b/137881067): Modify so that caller is responsible for setting pkg fields as necessary + */ + void setNativeLibraryPaths(PackageParser.Package pkg, File appLib32InstallDir); + + /** + * Calculate the abis and roots for a bundled app. These can uniquely + * be determined from the contents of the system partition, i.e whether + * it contains 64 or 32 bit shared libraries etc. We do not validate any + * of this information, and instead assume that the system was built + * sensibly. + * + * WARNING: This API enables modifying of the package. + * TODO(b/137881067): Modify so that caller is responsible for setting pkg fields as necessary + */ + void setBundledAppAbisAndRoots(PackageParser.Package pkg, + PackageSetting pkgSetting); + + /** + * Derive the ABI of a non-system package located at {@code scanFile}. This information + * is derived purely on the basis of the contents of {@code scanFile} and + * {@code cpuAbiOverride}. + * + * If {@code extractLibs} is true, native libraries are extracted from the app if required. + * + * WARNING: This API enables modifying of the package. + * TODO(b/137881067): Modify so that caller is responsible for setting pkg fields as necessary + */ + void derivePackageAbi(PackageParser.Package pkg, String cpuAbiOverride, + boolean extractLibs) + throws PackageManagerException; + + /** + * Adjusts ABIs for a set of packages belonging to a shared user so that they all match. + * i.e, so that all packages can be run inside a single process if required. + * + * Optionally, callers can pass in a parsed package via {@code newPackage} in which case + * this function will either try and make the ABI for all packages in + * {@code packagesForUser} match {@code scannedPackage} or will update the ABI of + * {@code scannedPackage} to match the ABI selected for {@code packagesForUser}. This + * variant is used when installing or updating a package that belongs to a shared user. + * + * NOTE: We currently only match for the primary CPU abi string. Matching the secondary + * adds unnecessary complexity. + * + * WARNING: This API enables modifying of the package. + * TODO(b/137881067): Modify so that caller is responsible for setting pkg fields as necessary + */ + @Nullable + List<String> adjustCpuAbisForSharedUser( + Set<PackageSetting> packagesForUser, PackageParser.Package scannedPackage); +} diff --git a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java new file mode 100644 index 000000000000..4ecc888cb33c --- /dev/null +++ b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java @@ -0,0 +1,542 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm; + +import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR; +import static android.content.pm.PackageParser.isApkFile; +import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; + +import static com.android.internal.content.NativeLibraryHelper.LIB64_DIR_NAME; +import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME; +import static com.android.server.pm.InstructionSets.getPreferredInstructionSet; +import static com.android.server.pm.InstructionSets.getPrimaryInstructionSet; + +import android.annotation.Nullable; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageParser; +import android.os.Build; +import android.os.Environment; +import android.os.FileUtils; +import android.os.Trace; +import android.text.TextUtils; +import android.util.Slog; + +import com.android.internal.content.NativeLibraryHelper; +import com.android.internal.util.ArrayUtils; + +import dalvik.system.VMRuntime; + +import libcore.io.IoUtils; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +final class PackageAbiHelperImpl implements PackageAbiHelper { + + @Override + public void setNativeLibraryPaths(PackageParser.Package pkg, File appLib32InstallDir) { + final ApplicationInfo info = pkg.applicationInfo; + final String codePath = pkg.codePath; + final File codeFile = new File(codePath); + final boolean bundledApp = info.isSystemApp() && !info.isUpdatedSystemApp(); + + info.nativeLibraryRootDir = null; + info.nativeLibraryRootRequiresIsa = false; + info.nativeLibraryDir = null; + info.secondaryNativeLibraryDir = null; + + if (isApkFile(codeFile)) { + // Monolithic install + if (bundledApp) { + // If "/system/lib64/apkname" exists, assume that is the per-package + // native library directory to use; otherwise use "/system/lib/apkname". + final String apkRoot = calculateBundledApkRoot(info.sourceDir); + final boolean is64Bit = VMRuntime.is64BitInstructionSet( + getPrimaryInstructionSet(info)); + + // This is a bundled system app so choose the path based on the ABI. + // if it's a 64 bit abi, use lib64 otherwise use lib32. Note that this + // is just the default path. + final String apkName = deriveCodePathName(codePath); + final String libDir = is64Bit ? LIB64_DIR_NAME : LIB_DIR_NAME; + info.nativeLibraryRootDir = Environment.buildPath(new File(apkRoot), libDir, + apkName).getAbsolutePath(); + + if (info.secondaryCpuAbi != null) { + final String secondaryLibDir = is64Bit ? LIB_DIR_NAME : LIB64_DIR_NAME; + info.secondaryNativeLibraryDir = Environment.buildPath(new File(apkRoot), + secondaryLibDir, apkName).getAbsolutePath(); + } + } else { + final String apkName = deriveCodePathName(codePath); + info.nativeLibraryRootDir = new File(appLib32InstallDir, apkName) + .getAbsolutePath(); + } + + info.nativeLibraryRootRequiresIsa = false; + info.nativeLibraryDir = info.nativeLibraryRootDir; + } else { + // Cluster install + info.nativeLibraryRootDir = new File(codeFile, LIB_DIR_NAME).getAbsolutePath(); + info.nativeLibraryRootRequiresIsa = true; + + info.nativeLibraryDir = new File(info.nativeLibraryRootDir, + getPrimaryInstructionSet(info)).getAbsolutePath(); + + if (info.secondaryCpuAbi != null) { + info.secondaryNativeLibraryDir = new File(info.nativeLibraryRootDir, + VMRuntime.getInstructionSet(info.secondaryCpuAbi)).getAbsolutePath(); + } + } + } + + @Override + public void setBundledAppAbisAndRoots(PackageParser.Package pkg, + PackageSetting pkgSetting) { + final String apkName = deriveCodePathName(pkg.applicationInfo.getCodePath()); + + // If "/system/lib64/apkname" exists, assume that is the per-package + // native library directory to use; otherwise use "/system/lib/apkname". + final String apkRoot = calculateBundledApkRoot(pkg.applicationInfo.sourceDir); + setBundledAppAbi(pkg, apkRoot, apkName); + // pkgSetting might be null during rescan following uninstall of updates + // to a bundled app, so accommodate that possibility. The settings in + // that case will be established later from the parsed package. + // + // If the settings aren't null, sync them up with what we've just derived. + // note that apkRoot isn't stored in the package settings. + if (pkgSetting != null) { + pkgSetting.primaryCpuAbiString = pkg.applicationInfo.primaryCpuAbi; + pkgSetting.secondaryCpuAbiString = pkg.applicationInfo.secondaryCpuAbi; + } + } + + /** + * Deduces the ABI of a bundled app and sets the relevant fields on the + * parsed pkg object. + * + * @param apkRoot the root of the installed apk, something like {@code /system} or + * {@code /oem} under which system libraries are installed. + * @param apkName the name of the installed package. + */ + private void setBundledAppAbi(PackageParser.Package pkg, String apkRoot, String apkName) { + final File codeFile = new File(pkg.codePath); + + final boolean has64BitLibs; + final boolean has32BitLibs; + if (isApkFile(codeFile)) { + // Monolithic install + has64BitLibs = + (new File(apkRoot, new File(LIB64_DIR_NAME, apkName).getPath())).exists(); + has32BitLibs = (new File(apkRoot, new File(LIB_DIR_NAME, apkName).getPath())).exists(); + } else { + // Cluster install + final File rootDir = new File(codeFile, LIB_DIR_NAME); + if (!ArrayUtils.isEmpty(Build.SUPPORTED_64_BIT_ABIS) + && !TextUtils.isEmpty(Build.SUPPORTED_64_BIT_ABIS[0])) { + final String isa = VMRuntime.getInstructionSet(Build.SUPPORTED_64_BIT_ABIS[0]); + has64BitLibs = (new File(rootDir, isa)).exists(); + } else { + has64BitLibs = false; + } + if (!ArrayUtils.isEmpty(Build.SUPPORTED_32_BIT_ABIS) + && !TextUtils.isEmpty(Build.SUPPORTED_32_BIT_ABIS[0])) { + final String isa = VMRuntime.getInstructionSet(Build.SUPPORTED_32_BIT_ABIS[0]); + has32BitLibs = (new File(rootDir, isa)).exists(); + } else { + has32BitLibs = false; + } + } + + if (has64BitLibs && !has32BitLibs) { + // The package has 64 bit libs, but not 32 bit libs. Its primary + // ABI should be 64 bit. We can safely assume here that the bundled + // native libraries correspond to the most preferred ABI in the list. + + pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[0]; + pkg.applicationInfo.secondaryCpuAbi = null; + } else if (has32BitLibs && !has64BitLibs) { + // The package has 32 bit libs but not 64 bit libs. Its primary + // ABI should be 32 bit. + + pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0]; + pkg.applicationInfo.secondaryCpuAbi = null; + } else if (has32BitLibs && has64BitLibs) { + // The application has both 64 and 32 bit bundled libraries. We check + // here that the app declares multiArch support, and warn if it doesn't. + // + // We will be lenient here and record both ABIs. The primary will be the + // ABI that's higher on the list, i.e, a device that's configured to prefer + // 64 bit apps will see a 64 bit primary ABI, + + if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_MULTIARCH) == 0) { + Slog.e(PackageManagerService.TAG, + "Package " + pkg + " has multiple bundled libs, but is not multiarch."); + } + + if (VMRuntime.is64BitInstructionSet(getPreferredInstructionSet())) { + pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[0]; + pkg.applicationInfo.secondaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0]; + } else { + pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0]; + pkg.applicationInfo.secondaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[0]; + } + } else { + pkg.applicationInfo.primaryCpuAbi = null; + pkg.applicationInfo.secondaryCpuAbi = null; + } + } + + @Override + public void derivePackageAbi(PackageParser.Package pkg, String cpuAbiOverride, + boolean extractLibs) + throws PackageManagerException { + // Give ourselves some initial paths; we'll come back for another + // pass once we've determined ABI below. + setNativeLibraryPaths(pkg, PackageManagerService.sAppLib32InstallDir); + + // We shouldn't attempt to extract libs from system app when it was not updated. + if (PackageManagerService.isSystemApp(pkg) && !pkg.isUpdatedSystemApp()) { + extractLibs = false; + } + + final String nativeLibraryRootStr = pkg.applicationInfo.nativeLibraryRootDir; + final boolean useIsaSpecificSubdirs = pkg.applicationInfo.nativeLibraryRootRequiresIsa; + + NativeLibraryHelper.Handle handle = null; + try { + handle = NativeLibraryHelper.Handle.create(pkg); + // TODO(multiArch): This can be null for apps that didn't go through the + // usual installation process. We can calculate it again, like we + // do during install time. + // + // TODO(multiArch): Why do we need to rescan ASEC apps again ? It seems totally + // unnecessary. + final File nativeLibraryRoot = new File(nativeLibraryRootStr); + + // Null out the abis so that they can be recalculated. + pkg.applicationInfo.primaryCpuAbi = null; + pkg.applicationInfo.secondaryCpuAbi = null; + if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_MULTIARCH) != 0) { + // Warn if we've set an abiOverride for multi-lib packages.. + // By definition, we need to copy both 32 and 64 bit libraries for + // such packages. + if (pkg.cpuAbiOverride != null + && !NativeLibraryHelper.CLEAR_ABI_OVERRIDE.equals(pkg.cpuAbiOverride)) { + Slog.w(PackageManagerService.TAG, + "Ignoring abiOverride for multi arch application."); + } + + int abi32 = PackageManager.NO_NATIVE_LIBRARIES; + int abi64 = PackageManager.NO_NATIVE_LIBRARIES; + if (Build.SUPPORTED_32_BIT_ABIS.length > 0) { + if (extractLibs) { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries"); + abi32 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle, + nativeLibraryRoot, Build.SUPPORTED_32_BIT_ABIS, + useIsaSpecificSubdirs); + } else { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "findSupportedAbi"); + abi32 = NativeLibraryHelper.findSupportedAbi( + handle, Build.SUPPORTED_32_BIT_ABIS); + } + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + + // Shared library native code should be in the APK zip aligned + if (abi32 >= 0 && pkg.isLibrary() && extractLibs) { + throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, + "Shared library native lib extraction not supported"); + } + + maybeThrowExceptionForMultiArchCopy( + "Error unpackaging 32 bit native libs for multiarch app.", abi32); + + if (Build.SUPPORTED_64_BIT_ABIS.length > 0) { + if (extractLibs) { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries"); + abi64 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle, + nativeLibraryRoot, Build.SUPPORTED_64_BIT_ABIS, + useIsaSpecificSubdirs); + } else { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "findSupportedAbi"); + abi64 = NativeLibraryHelper.findSupportedAbi( + handle, Build.SUPPORTED_64_BIT_ABIS); + } + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + + maybeThrowExceptionForMultiArchCopy( + "Error unpackaging 64 bit native libs for multiarch app.", abi64); + + if (abi64 >= 0) { + // Shared library native libs should be in the APK zip aligned + if (extractLibs && pkg.isLibrary()) { + throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, + "Shared library native lib extraction not supported"); + } + pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[abi64]; + } + + if (abi32 >= 0) { + final String abi = Build.SUPPORTED_32_BIT_ABIS[abi32]; + if (abi64 >= 0) { + if (pkg.use32bitAbi) { + pkg.applicationInfo.secondaryCpuAbi = + pkg.applicationInfo.primaryCpuAbi; + pkg.applicationInfo.primaryCpuAbi = abi; + } else { + pkg.applicationInfo.secondaryCpuAbi = abi; + } + } else { + pkg.applicationInfo.primaryCpuAbi = abi; + } + } + } else { + String[] abiList = (cpuAbiOverride != null) + ? new String[]{cpuAbiOverride} : Build.SUPPORTED_ABIS; + + // Enable gross and lame hacks for apps that are built with old + // SDK tools. We must scan their APKs for renderscript bitcode and + // not launch them if it's present. Don't bother checking on devices + // that don't have 64 bit support. + boolean needsRenderScriptOverride = false; + if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && cpuAbiOverride == null + && NativeLibraryHelper.hasRenderscriptBitcode(handle)) { + abiList = Build.SUPPORTED_32_BIT_ABIS; + needsRenderScriptOverride = true; + } + + final int copyRet; + if (extractLibs) { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries"); + copyRet = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle, + nativeLibraryRoot, abiList, useIsaSpecificSubdirs); + } else { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "findSupportedAbi"); + copyRet = NativeLibraryHelper.findSupportedAbi(handle, abiList); + } + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + + if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) { + throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, + "Error unpackaging native libs for app, errorCode=" + copyRet); + } + + if (copyRet >= 0) { + // Shared libraries that have native libs must be multi-architecture + if (pkg.isLibrary()) { + throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, + "Shared library with native libs must be multiarch"); + } + pkg.applicationInfo.primaryCpuAbi = abiList[copyRet]; + } else if (copyRet == PackageManager.NO_NATIVE_LIBRARIES + && cpuAbiOverride != null) { + pkg.applicationInfo.primaryCpuAbi = cpuAbiOverride; + } else if (needsRenderScriptOverride) { + pkg.applicationInfo.primaryCpuAbi = abiList[0]; + } + } + } catch (IOException ioe) { + Slog.e(PackageManagerService.TAG, "Unable to get canonical file " + ioe.toString()); + } finally { + IoUtils.closeQuietly(handle); + } + + // Now that we've calculated the ABIs and determined if it's an internal app, + // we will go ahead and populate the nativeLibraryPath. + setNativeLibraryPaths(pkg, PackageManagerService.sAppLib32InstallDir); + } + + /** + * Adjusts ABIs for a set of packages belonging to a shared user so that they all match. + * i.e, so that all packages can be run inside a single process if required. + * + * Optionally, callers can pass in a parsed package via {@code newPackage} in which case + * this function will either try and make the ABI for all packages in + * {@code packagesForUser} match {@code scannedPackage} or will update the ABI of + * {@code scannedPackage} to match the ABI selected for {@code packagesForUser}. This + * variant is used when installing or updating a package that belongs to a shared user. + * + * NOTE: We currently only match for the primary CPU abi string. Matching the secondary + * adds unnecessary complexity. + */ + @Override + @Nullable + public List<String> adjustCpuAbisForSharedUser( + Set<PackageSetting> packagesForUser, PackageParser.Package scannedPackage) { + List<String> changedAbiCodePath = null; + String requiredInstructionSet = null; + if (scannedPackage != null && scannedPackage.applicationInfo.primaryCpuAbi != null) { + requiredInstructionSet = VMRuntime.getInstructionSet( + scannedPackage.applicationInfo.primaryCpuAbi); + } + + PackageSetting requirer = null; + for (PackageSetting ps : packagesForUser) { + // If packagesForUser contains scannedPackage, we skip it. This will happen + // when scannedPackage is an update of an existing package. Without this check, + // we will never be able to change the ABI of any package belonging to a shared + // user, even if it's compatible with other packages. + if (scannedPackage == null || !scannedPackage.packageName.equals(ps.name)) { + if (ps.primaryCpuAbiString == null) { + continue; + } + + final String instructionSet = + VMRuntime.getInstructionSet(ps.primaryCpuAbiString); + if (requiredInstructionSet != null + && !instructionSet.equals(requiredInstructionSet)) { + // We have a mismatch between instruction sets (say arm vs arm64) warn about + // this but there's not much we can do. + String errorMessage = "Instruction set mismatch, " + + ((requirer == null) ? "[caller]" : requirer) + + " requires " + requiredInstructionSet + " whereas " + ps + + " requires " + instructionSet; + Slog.w(PackageManagerService.TAG, errorMessage); + } + + if (requiredInstructionSet == null) { + requiredInstructionSet = instructionSet; + requirer = ps; + } + } + } + + if (requiredInstructionSet != null) { + String adjustedAbi; + if (requirer != null) { + // requirer != null implies that either scannedPackage was null or that + // scannedPackage did not require an ABI, in which case we have to adjust + // scannedPackage to match the ABI of the set (which is the same as + // requirer's ABI) + adjustedAbi = requirer.primaryCpuAbiString; + if (scannedPackage != null) { + scannedPackage.applicationInfo.primaryCpuAbi = adjustedAbi; + } + } else { + // requirer == null implies that we're updating all ABIs in the set to + // match scannedPackage. + adjustedAbi = scannedPackage.applicationInfo.primaryCpuAbi; + } + + for (PackageSetting ps : packagesForUser) { + if (scannedPackage == null || !scannedPackage.packageName.equals(ps.name)) { + if (ps.primaryCpuAbiString != null) { + continue; + } + + ps.primaryCpuAbiString = adjustedAbi; + if (ps.pkg != null && ps.pkg.applicationInfo != null + && !TextUtils.equals( + adjustedAbi, ps.pkg.applicationInfo.primaryCpuAbi)) { + ps.pkg.applicationInfo.primaryCpuAbi = adjustedAbi; + if (PackageManagerService.DEBUG_ABI_SELECTION) { + Slog.i(PackageManagerService.TAG, + "Adjusting ABI for " + ps.name + " to " + adjustedAbi + + " (requirer=" + + (requirer != null ? requirer.pkg : "null") + + ", scannedPackage=" + + (scannedPackage != null ? scannedPackage : "null") + + ")"); + } + if (changedAbiCodePath == null) { + changedAbiCodePath = new ArrayList<>(); + } + changedAbiCodePath.add(ps.codePathString); + } + } + } + } + return changedAbiCodePath; + } + + private static String calculateBundledApkRoot(final String codePathString) { + final File codePath = new File(codePathString); + final File codeRoot; + if (FileUtils.contains(Environment.getRootDirectory(), codePath)) { + codeRoot = Environment.getRootDirectory(); + } else if (FileUtils.contains(Environment.getOemDirectory(), codePath)) { + codeRoot = Environment.getOemDirectory(); + } else if (FileUtils.contains(Environment.getVendorDirectory(), codePath)) { + codeRoot = Environment.getVendorDirectory(); + } else if (FileUtils.contains(Environment.getOdmDirectory(), codePath)) { + codeRoot = Environment.getOdmDirectory(); + } else if (FileUtils.contains(Environment.getProductDirectory(), codePath)) { + codeRoot = Environment.getProductDirectory(); + } else if (FileUtils.contains(Environment.getSystemExtDirectory(), codePath)) { + codeRoot = Environment.getSystemExtDirectory(); + } else if (FileUtils.contains(Environment.getOdmDirectory(), codePath)) { + codeRoot = Environment.getOdmDirectory(); + } else { + // Unrecognized code path; take its top real segment as the apk root: + // e.g. /something/app/blah.apk => /something + try { + File f = codePath.getCanonicalFile(); + File parent = f.getParentFile(); // non-null because codePath is a file + File tmp; + while ((tmp = parent.getParentFile()) != null) { + f = parent; + parent = tmp; + } + codeRoot = f; + Slog.w(PackageManagerService.TAG, "Unrecognized code path " + + codePath + " - using " + codeRoot); + } catch (IOException e) { + // Can't canonicalize the code path -- shenanigans? + Slog.w(PackageManagerService.TAG, "Can't canonicalize code path " + codePath); + return Environment.getRootDirectory().getPath(); + } + } + return codeRoot.getPath(); + } + + // Utility method that returns the relative package path with respect + // to the installation directory. Like say for /data/data/com.test-1.apk + // string com.test-1 is returned. + private static String deriveCodePathName(String codePath) { + if (codePath == null) { + return null; + } + final File codeFile = new File(codePath); + final String name = codeFile.getName(); + if (codeFile.isDirectory()) { + return name; + } else if (name.endsWith(".apk") || name.endsWith(".tmp")) { + final int lastDot = name.lastIndexOf('.'); + return name.substring(0, lastDot); + } else { + Slog.w(PackageManagerService.TAG, "Odd, " + codePath + " doesn't look like an APK"); + return null; + } + } + + private static void maybeThrowExceptionForMultiArchCopy(String message, int copyRet) throws + PackageManagerException { + if (copyRet < 0) { + if (copyRet != PackageManager.NO_NATIVE_LIBRARIES + && copyRet != PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS) { + throw new PackageManagerException(copyRet, message); + } + } + } + +} 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 7157fe31cac6..ebbb19349946 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -94,14 +94,12 @@ import static android.permission.PermissionManager.KILL_APP_REASON_GIDS_CHANGED; import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE; import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_PARENT; -import static com.android.internal.content.NativeLibraryHelper.LIB64_DIR_NAME; import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME; import static com.android.server.pm.ComponentResolver.RESOLVE_PRIORITY_SORTER; import static com.android.server.pm.InstructionSets.getAppDexInstructionSets; import static com.android.server.pm.InstructionSets.getDexCodeInstructionSet; import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets; import static com.android.server.pm.InstructionSets.getPreferredInstructionSet; -import static com.android.server.pm.InstructionSets.getPrimaryInstructionSet; import static com.android.server.pm.PackageManagerServiceCompilerMapping.getDefaultCompilerFilter; import static com.android.server.pm.PackageManagerServiceUtils.compareSignatures; import static com.android.server.pm.PackageManagerServiceUtils.compressedFileExists; @@ -277,6 +275,7 @@ import android.view.Display; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.ResolverActivity; import com.android.internal.content.NativeLibraryHelper; import com.android.internal.content.PackageHelper; @@ -311,7 +310,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; @@ -430,7 +428,7 @@ public class PackageManagerService extends IPackageManager.Stub // user, but by default initialize to this. public static final boolean DEBUG_DEXOPT = false; - private static final boolean DEBUG_ABI_SELECTION = false; + static final boolean DEBUG_ABI_SELECTION = false; private static final boolean DEBUG_INSTANT = Build.IS_DEBUGGABLE; private static final boolean DEBUG_APP_DATA = false; @@ -654,7 +652,8 @@ public class PackageManagerService extends IPackageManager.Stub private static final File sAppInstallDir = new File(Environment.getDataDirectory(), "app"); /** Directory where installed application's 32-bit native libraries are copied. */ - private static final File sAppLib32InstallDir = + @VisibleForTesting + static final File sAppLib32InstallDir = new File(Environment.getDataDirectory(), "app-lib"); // ---------------------------------------------------------------- @@ -745,6 +744,30 @@ public class PackageManagerService extends IPackageManager.Stub private final ApexManager mApexManager; + private final Injector mInjector; + + /** + * Unit tests will instantiate and / or extend to mock dependencies / behaviors. + */ + @VisibleForTesting + static class Injector { + private final UserManagerInternal mUserManager; + private final PackageAbiHelper mAbiHelper; + + Injector(UserManagerInternal userManager, PackageAbiHelper abiHelper) { + mUserManager = userManager; + mAbiHelper = abiHelper; + } + + public UserManagerInternal getUserManager() { + return mUserManager; + } + + public PackageAbiHelper getAbiHelper() { + return mAbiHelper; + } + } + class PackageParserCallback implements PackageParser.Callback { @Override public final boolean hasFeature(String feature) { return PackageManagerService.this.hasSystemFeature(feature, 0); @@ -948,8 +971,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 +1013,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 +1978,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); } } } @@ -2019,8 +2031,8 @@ public class PackageManagerService extends IPackageManager.Stub // survive long enough to benefit of background optimizations. for (int userId : firstUserIds) { PackageInfo info = getPackageInfo(packageName, /*flags*/ 0, userId); - // There's a race currently where some install events may interleave with an uninstall. - // This can lead to package info being null (b/36642664). + // There's a race currently where some install events may interleave with an + // uninstall. This can lead to package info being null (b/36642664). if (info != null) { mDexManager.notifyPackageInstalled(info, userId); } @@ -2090,6 +2102,7 @@ public class PackageManagerService extends IPackageManager.Stub * external/removable/unprotected storage. * @return {@link StorageEnum#TYPE_UNKNOWN} if the package is not stored externally or the * corresponding {@link StorageEnum} storage type value if it is. + * corresponding {@link StorageEnum} storage type value if it is. */ private static int getPackageExternalStorageType(VolumeInfo packageVolume, boolean packageIsExternal) { @@ -2348,11 +2361,14 @@ public class PackageManagerService extends IPackageManager.Stub mPackages /*externalLock*/); mPermissionManagerService = (IPermissionManager) ServiceManager.getService("permissionmgr"); - mDefaultPermissionPolicy = mPermissionManager.getDefaultPermissionGrantPolicy(); mSettings = new Settings(Environment.getDataDirectory(), mPermissionManager.getPermissionSettings(), mPackages); } } + + // TODO(b/137961986): We should pass this via constructor, but would first need to create + // a packages lock that could also be passed in. + mInjector = new Injector(getUserManagerInternal(), new PackageAbiHelperImpl()); // CHECKSTYLE:ON IndentationCheck t.traceEnd(); @@ -3061,7 +3077,8 @@ public class PackageManagerService extends IPackageManager.Stub // the rest of the commands above) because there's precious little we // can do about it. A settings error is reported, though. final List<String> changedAbiCodePath = - adjustCpuAbisForSharedUserLPw(setting.packages, null /*scannedPackage*/); + mInjector.getAbiHelper().adjustCpuAbisForSharedUser( + setting.packages, null /*scannedPackage*/); if (changedAbiCodePath != null && changedAbiCodePath.size() > 0) { for (int i = changedAbiCodePath.size() - 1; i >= 0; --i) { final String codePathString = changedAbiCodePath.get(i); @@ -5499,40 +5516,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 +5571,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 +7229,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(); @@ -8877,7 +8821,8 @@ public class PackageManagerService extends IPackageManager.Stub null /* originalPkgSetting */, null, parseFlags, scanFlags, (pkg == mPlatformPackage), user); applyPolicy(pkg, parseFlags, scanFlags, mPlatformPackage); - final ScanResult scanResult = scanPackageOnlyLI(request, mFactoryTest, -1L); + final ScanResult scanResult = + scanPackageOnlyLI(request, mInjector, mFactoryTest, -1L); if (scanResult.existingSettingCopied && scanResult.request.pkgSetting != null) { scanResult.request.pkgSetting.updateFrom(scanResult.pkgSetting); } @@ -10237,7 +10182,8 @@ public class PackageManagerService extends IPackageManager.Stub } /** The result of a package scan. */ - private static class ScanResult { + @VisibleForTesting + static class ScanResult { /** The request that initiated the scan that produced this result. */ public final ScanRequest request; /** Whether or not the package scan was successful */ @@ -10276,7 +10222,8 @@ public class PackageManagerService extends IPackageManager.Stub } /** A package to be scanned */ - private static class ScanRequest { + @VisibleForTesting + static class ScanRequest { /** The parsed package */ @NonNull public final PackageParser.Package pkg; /** The package this package replaces */ @@ -10469,7 +10416,7 @@ public class PackageManagerService extends IPackageManager.Stub pkgSetting == null ? null : pkgSetting.pkg, pkgSetting, disabledPkgSetting, originalPkgSetting, realPkgName, parseFlags, scanFlags, (pkg == mPlatformPackage), user); - return scanPackageOnlyLI(request, mFactoryTest, currentTime); + return scanPackageOnlyLI(request, mInjector, mFactoryTest, currentTime); } } @@ -10695,15 +10642,21 @@ public class PackageManagerService extends IPackageManager.Stub * method potentially modifies a live {@link PackageSetting} object representing * the package being scanned. This will be resolved in the future. * + * @param injector injector for acquiring dependencies * @param request Information about the package to be scanned * @param isUnderFactoryTest Whether or not the device is under factory test * @param currentTime The current time, in millis * @return The results of the scan */ @GuardedBy("mInstallLock") - private static @NonNull ScanResult scanPackageOnlyLI(@NonNull ScanRequest request, + @VisibleForTesting + @NonNull + static ScanResult scanPackageOnlyLI(@NonNull ScanRequest request, + Injector injector, boolean isUnderFactoryTest, long currentTime) - throws PackageManagerException { + throws PackageManagerException { + final PackageAbiHelper packageAbiHelper = injector.getAbiHelper(); + final UserManagerInternal userManager = injector.getUserManager(); final PackageParser.Package pkg = request.pkg; PackageSetting pkgSetting = request.pkgSetting; final PackageSetting disabledPkgSetting = request.disabledPkgSetting; @@ -10809,7 +10762,7 @@ public class PackageManagerService extends IPackageManager.Stub if (!createNewPackage) { final boolean instantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0; final boolean fullApp = (scanFlags & SCAN_AS_FULL_APP) != 0; - setInstantAppForUser(pkgSetting, userId, instantApp, fullApp); + setInstantAppForUser(userManager, pkgSetting, userId, instantApp, fullApp); } // TODO(patb): see if we can do away with disabled check here. if (disabledPkgSetting != null @@ -10855,7 +10808,7 @@ public class PackageManagerService extends IPackageManager.Stub if (needToDeriveAbi) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "derivePackageAbi"); final boolean extractNativeLibs = !pkg.isLibrary(); - derivePackageAbi(pkg, cpuAbiOverride, extractNativeLibs); + packageAbiHelper.derivePackageAbi(pkg, cpuAbiOverride, extractNativeLibs); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); // Some system apps still use directory structure for native libraries @@ -10863,8 +10816,8 @@ public class PackageManagerService extends IPackageManager.Stub // structure. Try to detect abi based on directory structure. if (isSystemApp(pkg) && !pkg.isUpdatedSystemApp() && pkg.applicationInfo.primaryCpuAbi == null) { - setBundledAppAbisAndRoots(pkg, pkgSetting); - setNativeLibraryPaths(pkg, sAppLib32InstallDir); + packageAbiHelper.setBundledAppAbisAndRoots(pkg, pkgSetting); + packageAbiHelper.setNativeLibraryPaths(pkg, sAppLib32InstallDir); } } else { // This is not a first boot or an upgrade, don't bother deriving the @@ -10873,7 +10826,7 @@ public class PackageManagerService extends IPackageManager.Stub pkg.applicationInfo.primaryCpuAbi = primaryCpuAbiFromSettings; pkg.applicationInfo.secondaryCpuAbi = secondaryCpuAbiFromSettings; - setNativeLibraryPaths(pkg, sAppLib32InstallDir); + packageAbiHelper.setNativeLibraryPaths(pkg, sAppLib32InstallDir); if (DEBUG_ABI_SELECTION) { Slog.i(TAG, "Using ABIS and native lib paths from settings : " + @@ -10894,7 +10847,7 @@ public class PackageManagerService extends IPackageManager.Stub // ABIs we've determined above. For non-moves, the path will be updated based on the // ABIs we determined during compilation, but the path will depend on the final // package path (after the rename away from the stage path). - setNativeLibraryPaths(pkg, sAppLib32InstallDir); + packageAbiHelper.setNativeLibraryPaths(pkg, sAppLib32InstallDir); } // This is a special case for the "system" package, where the ABI is @@ -10948,8 +10901,8 @@ public class PackageManagerService extends IPackageManager.Stub // We also do this *before* we perform dexopt on this package, so that // we can avoid redundant dexopts, and also to make sure we've got the // code and package path correct. - changedAbiCodePath = - adjustCpuAbisForSharedUserLPw(pkgSetting.sharedUser.packages, pkg); + changedAbiCodePath = packageAbiHelper.adjustCpuAbisForSharedUser( + pkgSetting.sharedUser.packages, pkg); } if (isUnderFactoryTest && pkg.requestedPermissions.contains( @@ -11799,264 +11752,6 @@ public class PackageManagerService extends IPackageManager.Stub Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } - /** - * Derive the ABI of a non-system package located at {@code scanFile}. This information - * is derived purely on the basis of the contents of {@code scanFile} and - * {@code cpuAbiOverride}. - * - * If {@code extractLibs} is true, native libraries are extracted from the app if required. - */ - private static void derivePackageAbi(PackageParser.Package pkg, String cpuAbiOverride, - boolean extractLibs) - throws PackageManagerException { - // Give ourselves some initial paths; we'll come back for another - // pass once we've determined ABI below. - setNativeLibraryPaths(pkg, sAppLib32InstallDir); - - // We shouldn't attempt to extract libs from system app when it was not updated. - if (isSystemApp(pkg) && !pkg.isUpdatedSystemApp()) { - extractLibs = false; - } - - final String nativeLibraryRootStr = pkg.applicationInfo.nativeLibraryRootDir; - final boolean useIsaSpecificSubdirs = pkg.applicationInfo.nativeLibraryRootRequiresIsa; - - NativeLibraryHelper.Handle handle = null; - try { - handle = NativeLibraryHelper.Handle.create(pkg); - // TODO(multiArch): This can be null for apps that didn't go through the - // usual installation process. We can calculate it again, like we - // do during install time. - // - // TODO(multiArch): Why do we need to rescan ASEC apps again ? It seems totally - // unnecessary. - final File nativeLibraryRoot = new File(nativeLibraryRootStr); - - // Null out the abis so that they can be recalculated. - pkg.applicationInfo.primaryCpuAbi = null; - pkg.applicationInfo.secondaryCpuAbi = null; - if (isMultiArch(pkg.applicationInfo)) { - // Warn if we've set an abiOverride for multi-lib packages.. - // By definition, we need to copy both 32 and 64 bit libraries for - // such packages. - if (pkg.cpuAbiOverride != null - && !NativeLibraryHelper.CLEAR_ABI_OVERRIDE.equals(pkg.cpuAbiOverride)) { - Slog.w(TAG, "Ignoring abiOverride for multi arch application."); - } - - int abi32 = PackageManager.NO_NATIVE_LIBRARIES; - int abi64 = PackageManager.NO_NATIVE_LIBRARIES; - if (Build.SUPPORTED_32_BIT_ABIS.length > 0) { - if (extractLibs) { - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries"); - abi32 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle, - nativeLibraryRoot, Build.SUPPORTED_32_BIT_ABIS, - useIsaSpecificSubdirs); - } else { - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "findSupportedAbi"); - abi32 = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_32_BIT_ABIS); - } - Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); - } - - // Shared library native code should be in the APK zip aligned - if (abi32 >= 0 && pkg.isLibrary() && extractLibs) { - throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, - "Shared library native lib extraction not supported"); - } - - maybeThrowExceptionForMultiArchCopy( - "Error unpackaging 32 bit native libs for multiarch app.", abi32); - - if (Build.SUPPORTED_64_BIT_ABIS.length > 0) { - if (extractLibs) { - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries"); - abi64 = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle, - nativeLibraryRoot, Build.SUPPORTED_64_BIT_ABIS, - useIsaSpecificSubdirs); - } else { - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "findSupportedAbi"); - abi64 = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_64_BIT_ABIS); - } - Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); - } - - maybeThrowExceptionForMultiArchCopy( - "Error unpackaging 64 bit native libs for multiarch app.", abi64); - - if (abi64 >= 0) { - // Shared library native libs should be in the APK zip aligned - if (extractLibs && pkg.isLibrary()) { - throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, - "Shared library native lib extraction not supported"); - } - pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[abi64]; - } - - if (abi32 >= 0) { - final String abi = Build.SUPPORTED_32_BIT_ABIS[abi32]; - if (abi64 >= 0) { - if (pkg.use32bitAbi) { - pkg.applicationInfo.secondaryCpuAbi = pkg.applicationInfo.primaryCpuAbi; - pkg.applicationInfo.primaryCpuAbi = abi; - } else { - pkg.applicationInfo.secondaryCpuAbi = abi; - } - } else { - pkg.applicationInfo.primaryCpuAbi = abi; - } - } - } else { - String[] abiList = (cpuAbiOverride != null) ? - new String[] { cpuAbiOverride } : Build.SUPPORTED_ABIS; - - // Enable gross and lame hacks for apps that are built with old - // SDK tools. We must scan their APKs for renderscript bitcode and - // not launch them if it's present. Don't bother checking on devices - // that don't have 64 bit support. - boolean needsRenderScriptOverride = false; - if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && cpuAbiOverride == null && - NativeLibraryHelper.hasRenderscriptBitcode(handle)) { - abiList = Build.SUPPORTED_32_BIT_ABIS; - needsRenderScriptOverride = true; - } - - final int copyRet; - if (extractLibs) { - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyNativeBinaries"); - copyRet = NativeLibraryHelper.copyNativeBinariesForSupportedAbi(handle, - nativeLibraryRoot, abiList, useIsaSpecificSubdirs); - } else { - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "findSupportedAbi"); - copyRet = NativeLibraryHelper.findSupportedAbi(handle, abiList); - } - Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); - - if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) { - throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, - "Error unpackaging native libs for app, errorCode=" + copyRet); - } - - if (copyRet >= 0) { - // Shared libraries that have native libs must be multi-architecture - if (pkg.isLibrary()) { - throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, - "Shared library with native libs must be multiarch"); - } - pkg.applicationInfo.primaryCpuAbi = abiList[copyRet]; - } else if (copyRet == PackageManager.NO_NATIVE_LIBRARIES && cpuAbiOverride != null) { - pkg.applicationInfo.primaryCpuAbi = cpuAbiOverride; - } else if (needsRenderScriptOverride) { - pkg.applicationInfo.primaryCpuAbi = abiList[0]; - } - } - } catch (IOException ioe) { - Slog.e(TAG, "Unable to get canonical file " + ioe.toString()); - } finally { - IoUtils.closeQuietly(handle); - } - - // Now that we've calculated the ABIs and determined if it's an internal app, - // we will go ahead and populate the nativeLibraryPath. - setNativeLibraryPaths(pkg, sAppLib32InstallDir); - } - - /** - * Adjusts ABIs for a set of packages belonging to a shared user so that they all match. - * i.e, so that all packages can be run inside a single process if required. - * - * Optionally, callers can pass in a parsed package via {@code newPackage} in which case - * this function will either try and make the ABI for all packages in {@code packagesForUser} - * match {@code scannedPackage} or will update the ABI of {@code scannedPackage} to match - * the ABI selected for {@code packagesForUser}. This variant is used when installing or - * updating a package that belongs to a shared user. - * - * NOTE: We currently only match for the primary CPU abi string. Matching the secondary - * adds unnecessary complexity. - */ - private static @Nullable List<String> adjustCpuAbisForSharedUserLPw( - Set<PackageSetting> packagesForUser, PackageParser.Package scannedPackage) { - List<String> changedAbiCodePath = null; - String requiredInstructionSet = null; - if (scannedPackage != null && scannedPackage.applicationInfo.primaryCpuAbi != null) { - requiredInstructionSet = VMRuntime.getInstructionSet( - scannedPackage.applicationInfo.primaryCpuAbi); - } - - PackageSetting requirer = null; - for (PackageSetting ps : packagesForUser) { - // If packagesForUser contains scannedPackage, we skip it. This will happen - // when scannedPackage is an update of an existing package. Without this check, - // we will never be able to change the ABI of any package belonging to a shared - // user, even if it's compatible with other packages. - if (scannedPackage == null || !scannedPackage.packageName.equals(ps.name)) { - if (ps.primaryCpuAbiString == null) { - continue; - } - - final String instructionSet = VMRuntime.getInstructionSet(ps.primaryCpuAbiString); - if (requiredInstructionSet != null && !instructionSet.equals(requiredInstructionSet)) { - // We have a mismatch between instruction sets (say arm vs arm64) warn about - // this but there's not much we can do. - String errorMessage = "Instruction set mismatch, " - + ((requirer == null) ? "[caller]" : requirer) - + " requires " + requiredInstructionSet + " whereas " + ps - + " requires " + instructionSet; - Slog.w(TAG, errorMessage); - } - - if (requiredInstructionSet == null) { - requiredInstructionSet = instructionSet; - requirer = ps; - } - } - } - - if (requiredInstructionSet != null) { - String adjustedAbi; - if (requirer != null) { - // requirer != null implies that either scannedPackage was null or that scannedPackage - // did not require an ABI, in which case we have to adjust scannedPackage to match - // the ABI of the set (which is the same as requirer's ABI) - adjustedAbi = requirer.primaryCpuAbiString; - if (scannedPackage != null) { - scannedPackage.applicationInfo.primaryCpuAbi = adjustedAbi; - } - } else { - // requirer == null implies that we're updating all ABIs in the set to - // match scannedPackage. - adjustedAbi = scannedPackage.applicationInfo.primaryCpuAbi; - } - - for (PackageSetting ps : packagesForUser) { - if (scannedPackage == null || !scannedPackage.packageName.equals(ps.name)) { - if (ps.primaryCpuAbiString != null) { - continue; - } - - ps.primaryCpuAbiString = adjustedAbi; - if (ps.pkg != null && ps.pkg.applicationInfo != null && - !TextUtils.equals(adjustedAbi, ps.pkg.applicationInfo.primaryCpuAbi)) { - ps.pkg.applicationInfo.primaryCpuAbi = adjustedAbi; - if (DEBUG_ABI_SELECTION) { - Slog.i(TAG, "Adjusting ABI for " + ps.name + " to " + adjustedAbi - + " (requirer=" - + (requirer != null ? requirer.pkg : "null") - + ", scannedPackage=" - + (scannedPackage != null ? scannedPackage : "null") - + ")"); - } - if (changedAbiCodePath == null) { - changedAbiCodePath = new ArrayList<>(); - } - changedAbiCodePath.add(ps.codePathString); - } - } - } - } - return changedAbiCodePath; - } - private void setUpCustomResolverActivity(PackageParser.Package pkg) { synchronized (mPackages) { mResolverReplaced = true; @@ -12108,207 +11803,6 @@ public class PackageManagerService extends IPackageManager.Stub | IntentFilter.MATCH_ADJUSTMENT_NORMAL; } - private static String calculateBundledApkRoot(final String codePathString) { - final File codePath = new File(codePathString); - final File codeRoot; - if (FileUtils.contains(Environment.getRootDirectory(), codePath)) { - codeRoot = Environment.getRootDirectory(); - } else if (FileUtils.contains(Environment.getOemDirectory(), codePath)) { - codeRoot = Environment.getOemDirectory(); - } else if (FileUtils.contains(Environment.getVendorDirectory(), codePath)) { - codeRoot = Environment.getVendorDirectory(); - } else if (FileUtils.contains(Environment.getOdmDirectory(), codePath)) { - codeRoot = Environment.getOdmDirectory(); - } else if (FileUtils.contains(Environment.getProductDirectory(), codePath)) { - codeRoot = Environment.getProductDirectory(); - } else if (FileUtils.contains(Environment.getSystemExtDirectory(), codePath)) { - codeRoot = Environment.getSystemExtDirectory(); - } else if (FileUtils.contains(Environment.getOdmDirectory(), codePath)) { - codeRoot = Environment.getOdmDirectory(); - } else { - // Unrecognized code path; take its top real segment as the apk root: - // e.g. /something/app/blah.apk => /something - try { - File f = codePath.getCanonicalFile(); - File parent = f.getParentFile(); // non-null because codePath is a file - File tmp; - while ((tmp = parent.getParentFile()) != null) { - f = parent; - parent = tmp; - } - codeRoot = f; - Slog.w(TAG, "Unrecognized code path " - + codePath + " - using " + codeRoot); - } catch (IOException e) { - // Can't canonicalize the code path -- shenanigans? - Slog.w(TAG, "Can't canonicalize code path " + codePath); - return Environment.getRootDirectory().getPath(); - } - } - return codeRoot.getPath(); - } - - /** - * Derive and set the location of native libraries for the given package, - * which varies depending on where and how the package was installed. - */ - private static void setNativeLibraryPaths(PackageParser.Package pkg, File appLib32InstallDir) { - final ApplicationInfo info = pkg.applicationInfo; - final String codePath = pkg.codePath; - final File codeFile = new File(codePath); - final boolean bundledApp = info.isSystemApp() && !info.isUpdatedSystemApp(); - - info.nativeLibraryRootDir = null; - info.nativeLibraryRootRequiresIsa = false; - info.nativeLibraryDir = null; - info.secondaryNativeLibraryDir = null; - - if (isApkFile(codeFile)) { - // Monolithic install - if (bundledApp) { - // If "/system/lib64/apkname" exists, assume that is the per-package - // native library directory to use; otherwise use "/system/lib/apkname". - final String apkRoot = calculateBundledApkRoot(info.sourceDir); - final boolean is64Bit = VMRuntime.is64BitInstructionSet( - getPrimaryInstructionSet(info)); - - // This is a bundled system app so choose the path based on the ABI. - // if it's a 64 bit abi, use lib64 otherwise use lib32. Note that this - // is just the default path. - final String apkName = deriveCodePathName(codePath); - final String libDir = is64Bit ? LIB64_DIR_NAME : LIB_DIR_NAME; - info.nativeLibraryRootDir = Environment.buildPath(new File(apkRoot), libDir, - apkName).getAbsolutePath(); - - if (info.secondaryCpuAbi != null) { - final String secondaryLibDir = is64Bit ? LIB_DIR_NAME : LIB64_DIR_NAME; - info.secondaryNativeLibraryDir = Environment.buildPath(new File(apkRoot), - secondaryLibDir, apkName).getAbsolutePath(); - } - } else { - final String apkName = deriveCodePathName(codePath); - info.nativeLibraryRootDir = new File(appLib32InstallDir, apkName) - .getAbsolutePath(); - } - - info.nativeLibraryRootRequiresIsa = false; - info.nativeLibraryDir = info.nativeLibraryRootDir; - } else { - // Cluster install - info.nativeLibraryRootDir = new File(codeFile, LIB_DIR_NAME).getAbsolutePath(); - info.nativeLibraryRootRequiresIsa = true; - - info.nativeLibraryDir = new File(info.nativeLibraryRootDir, - getPrimaryInstructionSet(info)).getAbsolutePath(); - - if (info.secondaryCpuAbi != null) { - info.secondaryNativeLibraryDir = new File(info.nativeLibraryRootDir, - VMRuntime.getInstructionSet(info.secondaryCpuAbi)).getAbsolutePath(); - } - } - } - - /** - * Calculate the abis and roots for a bundled app. These can uniquely - * be determined from the contents of the system partition, i.e whether - * it contains 64 or 32 bit shared libraries etc. We do not validate any - * of this information, and instead assume that the system was built - * sensibly. - */ - private static void setBundledAppAbisAndRoots(PackageParser.Package pkg, - PackageSetting pkgSetting) { - final String apkName = deriveCodePathName(pkg.applicationInfo.getCodePath()); - - // If "/system/lib64/apkname" exists, assume that is the per-package - // native library directory to use; otherwise use "/system/lib/apkname". - final String apkRoot = calculateBundledApkRoot(pkg.applicationInfo.sourceDir); - setBundledAppAbi(pkg, apkRoot, apkName); - // pkgSetting might be null during rescan following uninstall of updates - // to a bundled app, so accommodate that possibility. The settings in - // that case will be established later from the parsed package. - // - // If the settings aren't null, sync them up with what we've just derived. - // note that apkRoot isn't stored in the package settings. - if (pkgSetting != null) { - pkgSetting.primaryCpuAbiString = pkg.applicationInfo.primaryCpuAbi; - pkgSetting.secondaryCpuAbiString = pkg.applicationInfo.secondaryCpuAbi; - } - } - - /** - * Deduces the ABI of a bundled app and sets the relevant fields on the - * parsed pkg object. - * - * @param apkRoot the root of the installed apk, something like {@code /system} or {@code /oem} - * under which system libraries are installed. - * @param apkName the name of the installed package. - */ - private static void setBundledAppAbi(PackageParser.Package pkg, String apkRoot, String apkName) { - final File codeFile = new File(pkg.codePath); - - final boolean has64BitLibs; - final boolean has32BitLibs; - if (isApkFile(codeFile)) { - // Monolithic install - has64BitLibs = (new File(apkRoot, new File(LIB64_DIR_NAME, apkName).getPath())).exists(); - has32BitLibs = (new File(apkRoot, new File(LIB_DIR_NAME, apkName).getPath())).exists(); - } else { - // Cluster install - final File rootDir = new File(codeFile, LIB_DIR_NAME); - if (!ArrayUtils.isEmpty(Build.SUPPORTED_64_BIT_ABIS) - && !TextUtils.isEmpty(Build.SUPPORTED_64_BIT_ABIS[0])) { - final String isa = VMRuntime.getInstructionSet(Build.SUPPORTED_64_BIT_ABIS[0]); - has64BitLibs = (new File(rootDir, isa)).exists(); - } else { - has64BitLibs = false; - } - if (!ArrayUtils.isEmpty(Build.SUPPORTED_32_BIT_ABIS) - && !TextUtils.isEmpty(Build.SUPPORTED_32_BIT_ABIS[0])) { - final String isa = VMRuntime.getInstructionSet(Build.SUPPORTED_32_BIT_ABIS[0]); - has32BitLibs = (new File(rootDir, isa)).exists(); - } else { - has32BitLibs = false; - } - } - - if (has64BitLibs && !has32BitLibs) { - // The package has 64 bit libs, but not 32 bit libs. Its primary - // ABI should be 64 bit. We can safely assume here that the bundled - // native libraries correspond to the most preferred ABI in the list. - - pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[0]; - pkg.applicationInfo.secondaryCpuAbi = null; - } else if (has32BitLibs && !has64BitLibs) { - // The package has 32 bit libs but not 64 bit libs. Its primary - // ABI should be 32 bit. - - pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0]; - pkg.applicationInfo.secondaryCpuAbi = null; - } else if (has32BitLibs && has64BitLibs) { - // The application has both 64 and 32 bit bundled libraries. We check - // here that the app declares multiArch support, and warn if it doesn't. - // - // We will be lenient here and record both ABIs. The primary will be the - // ABI that's higher on the list, i.e, a device that's configured to prefer - // 64 bit apps will see a 64 bit primary ABI, - - if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_MULTIARCH) == 0) { - Slog.e(TAG, "Package " + pkg + " has multiple bundled libs, but is not multiarch."); - } - - if (VMRuntime.is64BitInstructionSet(getPreferredInstructionSet())) { - pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[0]; - pkg.applicationInfo.secondaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0]; - } else { - pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0]; - pkg.applicationInfo.secondaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[0]; - } - } else { - pkg.applicationInfo.primaryCpuAbi = null; - pkg.applicationInfo.secondaryCpuAbi = null; - } - } - private void killApplication(String pkgName, int appId, String reason) { killApplication(pkgName, appId, UserHandle.USER_ALL, reason); } @@ -12826,7 +12320,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 +12345,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 @@ -13030,7 +12528,8 @@ public class PackageManagerService extends IPackageManager.Stub // upgrade app from instant to full; we don't allow app downgrade installed = true; } - setInstantAppForUser(pkgSetting, userId, instantApp, fullApp); + setInstantAppForUser( + getUserManagerInternal(), pkgSetting, userId, instantApp, fullApp); } if (installed) { @@ -13078,8 +12577,8 @@ public class PackageManagerService extends IPackageManager.Stub } } - static void setInstantAppForUser(PackageSetting pkgSetting, int userId, - boolean instantApp, boolean fullApp) { + static void setInstantAppForUser(UserManagerInternal userManager, PackageSetting pkgSetting, + int userId, boolean instantApp, boolean fullApp) { // no state specified; do nothing if (!instantApp && !fullApp) { return; @@ -13091,7 +12590,7 @@ public class PackageManagerService extends IPackageManager.Stub pkgSetting.setInstantApp(false /*instantApp*/, userId); } } else { - for (int currentUserId : sUserManager.getUserIds()) { + for (int currentUserId : userManager.getUserIds()) { if (instantApp && !pkgSetting.getInstantApp(currentUserId)) { pkgSetting.setInstantApp(true /*instantApp*/, currentUserId); } else if (fullApp && pkgSetting.getInstantApp(currentUserId)) { @@ -13421,7 +12920,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 +13002,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 +13360,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. * @@ -15365,16 +14782,6 @@ public class PackageManagerService extends IPackageManager.Stub } } - private static void maybeThrowExceptionForMultiArchCopy(String message, int copyRet) throws - PackageManagerException { - if (copyRet < 0) { - if (copyRet != PackageManager.NO_NATIVE_LIBRARIES && - copyRet != PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS) { - throw new PackageManagerException(copyRet, message); - } - } - } - /** * Logic to handle movement of existing installed applications. */ @@ -15501,26 +14908,6 @@ public class PackageManagerService extends IPackageManager.Stub return result; } - // Utility method that returns the relative package path with respect - // to the installation directory. Like say for /data/data/com.test-1.apk - // string com.test-1 is returned. - static String deriveCodePathName(String codePath) { - if (codePath == null) { - return null; - } - final File codeFile = new File(codePath); - final String name = codeFile.getName(); - if (codeFile.isDirectory()) { - return name; - } else if (name.endsWith(".apk") || name.endsWith(".tmp")) { - final int lastDot = name.lastIndexOf('.'); - return name.substring(0, lastDot); - } else { - Slog.w(TAG, "Odd, " + codePath + " doesn't look like an APK"); - return null; - } - } - static class PackageInstalledInfo { String name; int uid; @@ -16082,13 +15469,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: " @@ -16452,7 +15839,8 @@ public class PackageManagerService extends IPackageManager.Stub final PrepareResult prepareResult; try { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "preparePackage"); - prepareResult = preparePackageLI(request.args, request.installResult); + prepareResult = + preparePackageLI(request.args, request.installResult); } catch (PrepareFailure prepareFailure) { request.installResult.setError(prepareFailure.error, prepareFailure.getMessage()); @@ -16527,11 +15915,6 @@ public class PackageManagerService extends IPackageManager.Stub commitPackagesLocked(commitRequest); success = true; } finally { - for (PrepareResult result : prepareResults.values()) { - if (result.freezer != null) { - result.freezer.close(); - } - } Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } } @@ -17117,7 +16500,7 @@ public class PackageManagerService extends IPackageManager.Stub String abiOverride = (TextUtils.isEmpty(pkg.cpuAbiOverride) ? args.abiOverride : pkg.cpuAbiOverride); final boolean extractNativeLibs = !pkg.isLibrary(); - derivePackageAbi(pkg, abiOverride, extractNativeLibs); + mInjector.getAbiHelper().derivePackageAbi(pkg, abiOverride, extractNativeLibs); } catch (PackageManagerException pme) { Slog.e(TAG, "Error deriving application ABI", pme); throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR, @@ -17646,10 +17029,6 @@ public class PackageManagerService extends IPackageManager.Stub } } - private static boolean isMultiArch(ApplicationInfo info) { - return (info.flags & ApplicationInfo.FLAG_MULTIARCH) != 0; - } - private static boolean isExternal(PackageParser.Package pkg) { return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0; } @@ -17658,7 +17037,7 @@ public class PackageManagerService extends IPackageManager.Stub return (ps.pkgFlags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0; } - private static boolean isSystemApp(PackageParser.Package pkg) { + static boolean isSystemApp(PackageParser.Package pkg) { return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; } @@ -19688,10 +19067,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 +19098,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 +19351,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 +19595,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 +19648,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 +19658,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 +20214,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 +20229,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 +20258,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 +22284,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 +22299,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 +22762,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 +22842,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 +22876,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 +23388,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 +23433,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 +23518,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 +23605,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 +23629,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 f8687d37ce20..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; } diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java index 58f262c4c889..029673ffd87b 100644 --- a/services/core/java/com/android/server/pm/PackageSettingBase.java +++ b/services/core/java/com/android/server/pm/PackageSettingBase.java @@ -270,7 +270,8 @@ public abstract class PackageSettingBase extends SettingBase { updateAvailable = orig.updateAvailable; } - private PackageUserState modifyUserState(int userId) { + @VisibleForTesting + PackageUserState modifyUserState(int userId) { PackageUserState state = mUserState.get(userId); if (state == null) { state = new PackageUserState(); @@ -463,6 +464,18 @@ public abstract class PackageSettingBase extends SettingBase { state.harmfulAppWarning = harmfulAppWarning; } + void setUserState(int userId, PackageUserState otherState) { + setUserState(userId, otherState.ceDataInode, otherState.enabled, otherState.installed, + otherState.stopped, otherState.notLaunched, otherState.hidden, + otherState.distractionFlags, otherState.suspended, otherState.suspendingPackage, + otherState.dialogInfo, otherState.suspendedAppExtras, + otherState.suspendedLauncherExtras, otherState.instantApp, + otherState.virtualPreload, otherState.lastDisableAppCaller, + otherState.enabledComponents, otherState.disabledComponents, + otherState.domainVerificationStatus, otherState.appLinkGeneration, + otherState.installReason, otherState.harmfulAppWarning); + } + ArraySet<String> getEnabledComponents(int userId) { return readUserState(userId).enabledComponents; } diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index 2d8a2acd575f..ebba128b5f28 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -79,7 +79,7 @@ import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManagerInternal; import android.text.TextUtils; -import android.text.format.Time; +import android.text.format.TimeMigrationUtils; import android.util.ArraySet; import android.util.AtomicFile; import android.util.KeyValueListParser; @@ -3981,9 +3981,7 @@ public class ShortcutService extends IShortcutService.Stub { } static String formatTime(long time) { - Time tobj = new Time(); - tobj.set(time); - return tobj.format("%Y-%m-%d %H:%M:%S"); + return TimeMigrationUtils.formatMillisWithFixedFormat(time); } private void dumpCurrentTime(PrintWriter pw) { diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING index e375fa44cb3f..f7b60c25ce4a 100644 --- a/services/core/java/com/android/server/pm/TEST_MAPPING +++ b/services/core/java/com/android/server/pm/TEST_MAPPING @@ -10,6 +10,22 @@ "name": "CtsCompilationTestCases" }, { + "name": "FrameworksServicesTests", + "options": [ + { + "include-filter": "com.android.server.pm." + }, + { + "include-annotation": "android.platform.test.annotations.Presubmit" + }, + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + } + ] + } + ], + "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 0185af73882b..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; 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 51fd31788fbb..03be8e119e03 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; @@ -53,6 +54,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.ActivityManager; +import android.app.AppOpsManager; import android.app.ApplicationPackageManager; import android.app.IActivityManager; import android.content.Context; @@ -118,6 +120,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 +255,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 @@ -1434,7 +1448,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { /** * Reverts user permission state changes (permissions and flags). * - * @param ps The package for which to reset. + * @param pkg The package for which to reset. * @param userId The device user for which to do a reset. */ @GuardedBy("mPackages") @@ -1508,6 +1522,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { } }; + final AppOpsManager appOpsManager = mContext.getSystemService(AppOpsManager.class); for (int i = 0; i < permissionCount; i++) { final String permName = pkg.requestedPermissions.get(i); final BasePermission bp; @@ -1575,6 +1590,14 @@ public class PermissionManagerService extends IPermissionManager.Stub { if ((oldFlags & FLAG_PERMISSION_GRANTED_BY_DEFAULT) != 0) { grantRuntimePermissionInternal(permName, packageName, false, Process.SYSTEM_UID, userId, delayingPermCallback); + // Allow app op later as we are holding mPackages + // PermissionPolicyService will handle the app op for foreground/background + // permissions. + String appOp = AppOpsManager.permissionToOp(permName); + if (appOp != null) { + mHandler.post(() -> appOpsManager.setUidMode(appOp, uid, + AppOpsManager.MODE_ALLOWED)); + } // If permission review is enabled the permissions for a legacy apps // are represented as constantly granted runtime ones, so don't revoke. } else if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) == 0) { @@ -1609,6 +1632,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. * @@ -2793,13 +3036,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 +3959,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 +4110,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 +4131,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 +4201,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/slice/PinnedSliceState.java b/services/core/java/com/android/server/slice/PinnedSliceState.java index e139ab86775d..4970862070bb 100644 --- a/services/core/java/com/android/server/slice/PinnedSliceState.java +++ b/services/core/java/com/android/server/slice/PinnedSliceState.java @@ -188,7 +188,7 @@ public class PinnedSliceState { b.putParcelable(SliceProvider.EXTRA_BIND_URI, mUri); try { client.call(SliceProvider.METHOD_PIN, null, b); - } catch (RemoteException e) { + } catch (Exception e) { Log.w(TAG, "Unable to contact " + mUri, e); } } @@ -201,7 +201,7 @@ public class PinnedSliceState { b.putParcelable(SliceProvider.EXTRA_BIND_URI, mUri); try { client.call(SliceProvider.METHOD_UNPIN, null, b); - } catch (RemoteException e) { + } catch (Exception e) { Log.w(TAG, "Unable to contact " + mUri, e); } } 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 b2f115374305..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; @@ -952,7 +953,7 @@ public class UriGrantsManagerService extends IUriGrantsManager.Stub { // 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 (pi.forceUriPermissions) { + if (ENABLE_DYNAMIC_PERMISSIONS && pi.forceUriPermissions) { final int providerUserId = UserHandle.getUserId(pi.applicationInfo.uid); final int clientUserId = UserHandle.getUserId(uid); if (providerUserId == clientUserId) { diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java index 8abfde2c5c36..26ca975bfc8f 100644 --- a/services/core/java/com/android/server/wm/AccessibilityController.java +++ b/services/core/java/com/android/server/wm/AccessibilityController.java @@ -85,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) { @@ -115,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); @@ -147,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(); } } @@ -173,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(); } } @@ -185,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(); } } @@ -206,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(); } } @@ -264,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); @@ -1059,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(); @@ -1100,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; @@ -1305,7 +1371,11 @@ final class AccessibilityController { private void populateVisibleWindowsOnScreenLocked(SparseArray<WindowState> outWindows) { final List<WindowState> tempWindowStatesList = new ArrayList<>(); - final DisplayContent dc = mService.getDefaultDisplayContentLocked(); + final DisplayContent dc = mService.mRoot.getDisplayContent(mDisplayId); + if (dc == null) { + return; + } + dc.forAllWindows(w -> { if (w.isVisibleLw()) { tempWindowStatesList.add(w); @@ -1319,8 +1389,7 @@ final class AccessibilityController { return; } - if (w.isVisibleLw() && parentWindow.getDisplayContent().isDefaultDisplay - && tempWindowStatesList.contains(parentWindow)) { + if (w.isVisibleLw() && tempWindowStatesList.contains(parentWindow)) { tempWindowStatesList.add(tempWindowStatesList.lastIndexOf(parentWindow), w); } }, false /* traverseTopToBottom */); @@ -1341,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 99d290340b77..015464e94ffc 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; @@ -149,8 +163,10 @@ import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; import static org.xmlpull.v1.XmlPullParser.END_TAG; import static org.xmlpull.v1.XmlPullParser.START_TAG; +import android.annotation.IntDef; 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 +210,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 +232,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 +260,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 +342,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 +467,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 +580,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 +642,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; } @@ -957,7 +1013,6 @@ final class ActivityRecord extends ConfigurationContainer { resultWho = _resultWho; requestCode = _reqCode; setState(INITIALIZING, "ActivityRecord ctor"); - frontOfTask = false; launchFailed = false; stopped = false; delayedResume = false; @@ -972,10 +1027,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. @@ -1530,6 +1581,308 @@ final class ActivityRecord extends ConfigurationContainer { 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 */); + } + + /** Activity finish request was not executed. */ + static final int FINISH_RESULT_CANCELLED = 0; + /** Activity finish was requested, activity will be fully removed later. */ + static final int FINISH_RESULT_REQUESTED = 1; + /** Activity finish was requested, activity was removed from history. */ + static final int FINISH_RESULT_REMOVED = 2; + + /** Definition of possible results for activity finish request. */ + @IntDef(prefix = { "FINISH_RESULT_" }, value = { + FINISH_RESULT_CANCELLED, + FINISH_RESULT_REQUESTED, + FINISH_RESULT_REMOVED, + }) + @interface FinishRequest {} + + /** + * See {@link #finishActivityLocked(int, Intent, String, boolean, boolean)} + */ + @FinishRequest int finishActivityLocked(int resultCode, Intent resultData, String reason, + boolean oomAdj) { + return finishActivityLocked(resultCode, resultData, reason, oomAdj, !PAUSE_IMMEDIATELY); + } + + /** + * @return One of {@link FinishRequest} values: + * {@link #FINISH_RESULT_REMOVED} if this activity has been removed from the history list. + * {@link #FINISH_RESULT_REQUESTED} if removal process was started, but it is still in the list + * and will be removed from history later. + * {@link #FINISH_RESULT_CANCELLED} if activity is already finishing or in invalid state and the + * request to finish it was not ignored. + */ + @FinishRequest int finishActivityLocked(int resultCode, Intent resultData, String reason, + boolean oomAdj, boolean pauseImmediately) { + if (DEBUG_RESULTS || DEBUG_STATES) { + Slog.v(TAG_STATES, "Finishing activity r=" + this + ", result=" + resultCode + + ", data=" + resultData + ", reason=" + reason); + } + + if (finishing) { + Slog.w(TAG, "Duplicate finish request for r=" + this); + return FINISH_RESULT_CANCELLED; + } + + if (!isInStackLocked()) { + Slog.w(TAG, "Finish request when not in stack for r=" + this); + return FINISH_RESULT_CANCELLED; + } + + 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 ? FINISH_RESULT_REMOVED : FINISH_RESULT_REQUESTED; + } else { + if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Finish waiting for pause of: " + this); + } + + return FINISH_RESULT_REQUESTED; + } 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; @@ -2185,8 +2538,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) { @@ -2259,19 +2611,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); @@ -2552,7 +2903,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; @@ -2590,7 +2942,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; } @@ -3525,7 +3877,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. @@ -3614,9 +3966,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); } } @@ -3697,7 +4049,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; @@ -3803,6 +4155,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" @@ -3846,7 +4219,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 79098f17899e..17536e42acc8 100644 --- a/services/core/java/com/android/server/wm/ActivityStack.java +++ b/services/core/java/com/android/server/wm/ActivityStack.java @@ -56,9 +56,12 @@ 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.ActivityRecord.FINISH_RESULT_CANCELLED; +import static com.android.server.wm.ActivityRecord.FINISH_RESULT_REMOVED; 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 +81,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 +95,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 +166,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 +186,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; @@ -1157,13 +1155,6 @@ class ActivityStack extends ConfigurationContainer { return null; } - private TaskRecord bottomTask() { - if (mTaskHistory.isEmpty()) { - return null; - } - return mTaskHistory.get(0); - } - TaskRecord taskForIdLocked(int id) { for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { final TaskRecord task = mTaskHistory.get(taskNdx); @@ -1499,8 +1490,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) { @@ -1782,7 +1771,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"); } } @@ -1801,7 +1790,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 @@ -1895,7 +1884,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); @@ -2793,8 +2782,8 @@ class ActivityStack extends ConfigurationContainer { !mLastNoHistoryActivity.finishing) { if (DEBUG_STATES) Slog.d(TAG_STATES, "no-history finish of " + mLastNoHistoryActivity + " on new resume"); - requestFinishActivityLocked(mLastNoHistoryActivity.appToken, Activity.RESULT_CANCELED, - null, "resume-no-history", false); + mLastNoHistoryActivity.finishActivityLocked(Activity.RESULT_CANCELED, + null /* resultData */, "resume-no-history", false /* oomAdj */); mLastNoHistoryActivity = null; } @@ -3029,8 +3018,8 @@ class ActivityStack extends ConfigurationContainer { // If any exception gets thrown, toss away this // activity and try the next one. Slog.w(TAG, "Exception thrown during resume of " + next, e); - requestFinishActivityLocked(next.appToken, Activity.RESULT_CANCELED, null, - "resume-exception", true); + next.finishActivityLocked(Activity.RESULT_CANCELED, null /* resultData */, + "resume-exception", true /* oomAdj */); return true; } } else { @@ -3209,8 +3198,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) { @@ -3322,20 +3309,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; @@ -3343,11 +3327,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 = @@ -3380,12 +3367,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. @@ -3395,7 +3383,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); @@ -3457,8 +3446,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 */) == FINISH_RESULT_REMOVED) { end--; srcPos--; } @@ -3476,28 +3465,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; @@ -3536,8 +3531,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) { @@ -3566,13 +3561,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 */); } } } @@ -3590,11 +3585,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. @@ -3675,7 +3670,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; @@ -3760,10 +3755,9 @@ class ActivityStack extends ConfigurationContainer { if (!r.finishing) { if (!shouldSleepActivities()) { if (DEBUG_STATES) Slog.d(TAG_STATES, "no-history finish of " + r); - if (requestFinishActivityLocked(r.appToken, Activity.RESULT_CANCELED, null, - "stop-no-history", false)) { - // If {@link requestFinishActivityLocked} returns {@code true}, - // {@link adjustFocusedActivityStack} would have been already called. + if (r.finishActivityLocked(Activity.RESULT_CANCELED, null /* resultData */, + "stop-no-history", false /* oomAdj */) != FINISH_RESULT_CANCELLED) { + // {@link adjustFocusedActivityStack} must have been already called. r.resumeKeyDispatchingLocked(); return; } @@ -3812,25 +3806,7 @@ class ActivityStack extends ConfigurationContainer { } } - /** - * @return Returns true if the activity is being finished, false if for - * some reason it is being left as-is. - */ - final boolean requestFinishActivityLocked(IBinder token, int resultCode, - Intent resultData, String reason, boolean oomAdj) { - ActivityRecord r = isInStackLocked(token); - if (DEBUG_RESULTS || DEBUG_STATES) Slog.v(TAG_STATES, - "Finishing activity token=" + token + " r=" - + ", result=" + resultCode + ", data=" + resultData - + ", reason=" + reason); - if (r == null) { - return false; - } - - finishActivityLocked(r, resultCode, resultData, reason, oomAdj); - return true; - } - + /** Finish all activities that were started for result from the specified activity. */ final void finishSubActivityLocked(ActivityRecord self, String resultWho, int requestCode) { for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities; @@ -3839,8 +3815,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 */); } } } @@ -3870,7 +3846,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 @@ -3891,7 +3868,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 */); } } } @@ -3907,8 +3885,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; } } @@ -3937,280 +3915,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) { @@ -4244,7 +3959,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. @@ -4312,8 +4028,8 @@ class ActivityStack extends ConfigurationContainer { } final long origId = Binder.clearCallingIdentity(); for (int i = start; i > finishTo; i--) { - ActivityRecord r = activities.get(i); - requestFinishActivityLocked(r.appToken, resultCode, resultData, "navigate-up", true); + final ActivityRecord r = activities.get(i); + r.finishActivityLocked(resultCode, resultData, "navigate-up", true /* oomAdj */); // Only return the supplied result for the first activity finished resultCode = Activity.RESULT_CANCELED; resultData = null; @@ -4350,8 +4066,8 @@ class ActivityStack extends ConfigurationContainer { } catch (RemoteException e) { foundParentInTask = false; } - requestFinishActivityLocked(parent.appToken, resultCode, - resultData, "navigate-top", true); + parent.finishActivityLocked(resultCode, resultData, "navigate-top", + true /* oomAdj */); } } Binder.restoreCallingIdentity(origId); @@ -4436,7 +4152,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)); @@ -4794,7 +4510,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. @@ -4814,7 +4530,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)); @@ -4837,11 +4553,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) { @@ -5179,7 +4890,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); } } } @@ -5223,8 +4934,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); } } } @@ -5278,7 +4988,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); } } @@ -5315,7 +5025,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"); } } diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java index eb170bdeee2d..1c56a107ab9e 100644 --- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java @@ -813,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()) { @@ -836,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. @@ -851,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)); @@ -889,8 +890,8 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { Slog.e(TAG, "Second failure launching " + r.intent.getComponent().flattenToShortString() + ", giving up", e); proc.appDied(); - stack.requestFinishActivityLocked(r.appToken, Activity.RESULT_CANCELED, null, - "2nd-crash", false); + r.finishActivityLocked(Activity.RESULT_CANCELED, null /* resultData */, + "2nd-crash", false /* oomAdj */); return false; } @@ -1339,8 +1340,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 6fd2ebcd1d4f..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); @@ -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 c37ced55c563..b351faf7e9f1 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -84,6 +84,7 @@ import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.PRE import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.SCREEN_COMPAT_PACKAGES; import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.ScreenCompatPackage.MODE; import static com.android.server.am.ActivityManagerServiceDumpProcessesProto.ScreenCompatPackage.PACKAGE; +import static com.android.server.wm.ActivityRecord.FINISH_RESULT_CANCELLED; import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING; import static com.android.server.wm.ActivityStackSupervisor.DEFER_RESUME; import static com.android.server.wm.ActivityStackSupervisor.ON_TOP; @@ -1553,13 +1554,13 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } synchronized (mGlobalLock) { - ActivityRecord r = ActivityRecord.isInStackLocked(token); + final ActivityRecord r = ActivityRecord.isInStackLocked(token); if (r == null) { return true; } // Keep track of the root activity of the task before we finish it final TaskRecord tr = r.getTaskRecord(); - ActivityRecord rootR = tr.getRootActivity(); + final ActivityRecord rootR = tr.getRootActivity(); if (rootR == null) { Slog.w(TAG, "Finishing task with all activities already finished"); } @@ -1609,7 +1610,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { // because we don't support returning them across task boundaries. Also, to // keep backwards compatibility we remove the task from recents when finishing // task with root activity. - res = mStackSupervisor.removeTaskByIdLocked(tr.taskId, false, + res = mStackSupervisor.removeTaskByIdLocked(tr.taskId, false /* killProcess */, finishWithRootActivity, "finish-activity"); if (!res) { Slog.i(TAG, "Removing task failed to finish activity"); @@ -1617,8 +1618,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { // Explicitly dismissing the activity so reset its relaunch flag. r.mRelaunchReason = RELAUNCH_REASON_NONE; } else { - res = tr.getStack().requestFinishActivityLocked(token, resultCode, - resultData, "app-request", true); + res = r.finishActivityLocked(resultCode, resultData, "app-request", + true /* oomAdj */) != FINISH_RESULT_CANCELLED; if (!res) { Slog.i(TAG, "Failed to finish by app-request"); } @@ -1642,11 +1643,11 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { // Do not allow task to finish if last task in lockTask mode. Launchable priv-apps // can finish. - final TaskRecord task = r.getTaskRecord(); if (getLockTaskController().activityBlockedFromFinish(r)) { return false; } - return task.getStack().finishActivityAffinityLocked(r); + r.finishActivityAffinity(); + return true; } finally { Binder.restoreCallingIdentity(origId); } diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index d15081ca2665..aefc152cb286 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -1015,6 +1015,8 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree void notifyAppStopped() { if (DEBUG_ADD_REMOVE) Slog.v(TAG, "notifyAppStopped: " + this); mAppStopped = true; + // Reset the last saved PiP snap fraction on app stop. + mDisplayContent.mPinnedStackControllerLocked.resetReentrySnapFraction(this); destroySurfaces(); // Remove any starting window that was added for this app if they are still around. removeStartingWindow(); @@ -2485,7 +2487,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree // transformed the task. final RecentsAnimationController controller = mWmService.getRecentsAnimationController(); if (controller != null && controller.isAnimatingTask(getTask()) - && controller.shouldCancelWithDeferredScreenshot()) { + && controller.shouldDeferCancelUntilNextTransition()) { return false; } @@ -3076,11 +3078,6 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree @Override void setHidden(boolean hidden) { super.setHidden(hidden); - - if (hidden) { - // Once the app window is hidden, reset the last saved PiP snap fraction - mDisplayContent.mPinnedStackControllerLocked.resetReentrySnapFraction(this); - } scheduleAnimation(); } 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 61ba69fdf404..15768738c85c 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -879,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) { @@ -912,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) { @@ -927,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( @@ -968,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; } /** @@ -986,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); diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java index a46fa13adf4e..207e8ef728eb 100644 --- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java +++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java @@ -30,6 +30,7 @@ import android.annotation.IntDef; import android.annotation.Nullable; import android.app.WindowConfiguration; import android.os.Environment; +import android.os.FileUtils; import android.provider.Settings; import android.util.AtomicFile; import android.util.Slog; @@ -64,6 +65,11 @@ import java.util.HashMap; class DisplayWindowSettings { private static final String TAG = TAG_WITH_CLASS_NAME ? "DisplayWindowSettings" : TAG_WM; + private static final String SYSTEM_DIRECTORY = "system"; + private static final String DISPLAY_SETTINGS_FILE_NAME = "display_settings.xml"; + private static final String VENDOR_DISPLAY_SETTINGS_PATH = "etc/" + DISPLAY_SETTINGS_FILE_NAME; + private static final String WM_DISPLAY_COMMIT_TAG = "wm-displays"; + private static final int IDENTIFIER_UNIQUE_ID = 0; private static final int IDENTIFIER_PORT = 1; @IntDef(prefix = { "IDENTIFIER_" }, value = { @@ -688,8 +694,26 @@ class DisplayWindowSettings { private final AtomicFile mAtomicFile; AtomicFileStorage() { - final File folder = new File(Environment.getDataDirectory(), "system"); - mAtomicFile = new AtomicFile(new File(folder, "display_settings.xml"), "wm-displays"); + final File folder = new File(Environment.getDataDirectory(), SYSTEM_DIRECTORY); + final File settingsFile = new File(folder, DISPLAY_SETTINGS_FILE_NAME); + // If display_settings.xml doesn't exist, try to copy the vendor's one instead + // in order to provide the vendor specific initialization. + if (!settingsFile.exists()) { + copyVendorSettings(settingsFile); + } + mAtomicFile = new AtomicFile(settingsFile, WM_DISPLAY_COMMIT_TAG); + } + + private static void copyVendorSettings(File target) { + final File vendorFile = new File(Environment.getVendorDirectory(), + VENDOR_DISPLAY_SETTINGS_PATH); + if (vendorFile.canRead()) { + try { + FileUtils.copy(vendorFile, target); + } catch (IOException e) { + Slog.e(TAG, "Failed to copy vendor display_settings.xml"); + } + } } @Override 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 422b6e58e0ed..85ba95f7d6de 100644 --- a/services/core/java/com/android/server/wm/KeyguardController.java +++ b/services/core/java/com/android/server/wm/KeyguardController.java @@ -90,24 +90,11 @@ class KeyguardController { } /** - * @return true if either Keyguard or AOD are showing, not going away, and not being occluded - * on the given display, false otherwise. + * @return true if either 1) AOD is showing, or 2) Keyguard is showing, not going away, and not + * being occluded on the given display, false otherwise. */ boolean isKeyguardOrAodShowing(int displayId) { - return (mKeyguardShowing || mAodShowing) && !mKeyguardGoingAway - && !isDisplayOccluded(displayId); - } - - /** - * @return {@code true} for default display when AOD is showing. Otherwise, same as - * {@link #isKeyguardOrAodShowing(int)} - * TODO(b/125198167): Replace isKeyguardOrAodShowing() by this logic. - */ - boolean isKeyguardUnoccludedOrAodShowing(int displayId) { - if (displayId == DEFAULT_DISPLAY && mAodShowing) { - return true; - } - return isKeyguardOrAodShowing(displayId); + return mAodShowing || isKeyguardShowing(displayId); } /** @@ -115,7 +102,7 @@ class KeyguardController { * display, false otherwise */ boolean isKeyguardShowing(int displayId) { - return mKeyguardShowing && !mKeyguardGoingAway && !isDisplayOccluded(displayId); + return mKeyguardShowing && !mKeyguardGoingAway && !isKeyguardOccluded(displayId); } /** @@ -328,7 +315,7 @@ class KeyguardController { return; } - mWindowManager.onKeyguardOccludedChanged(isDisplayOccluded(DEFAULT_DISPLAY)); + mWindowManager.onKeyguardOccludedChanged(isKeyguardOccluded(DEFAULT_DISPLAY)); if (isKeyguardLocked()) { mWindowManager.deferSurfaceLayout(); try { @@ -373,7 +360,7 @@ class KeyguardController { } } - private boolean isDisplayOccluded(int displayId) { + private boolean isKeyguardOccluded(int displayId) { return getDisplay(displayId).mOccluded; } @@ -391,13 +378,13 @@ class KeyguardController { if (mBeforeUnoccludeTransit != TRANSIT_UNSET && dc.mAppTransition.getAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE // TODO(b/113840485): Handle app transition for individual display. - && isDisplayOccluded(DEFAULT_DISPLAY)) { + && isKeyguardOccluded(DEFAULT_DISPLAY)) { // Reuse old transit in case we are occluding Keyguard again, meaning that we never // actually occclude/unocclude Keyguard, but just run a normal transition. return mBeforeUnoccludeTransit; // TODO(b/113840485): Handle app transition for individual display. - } else if (!isDisplayOccluded(DEFAULT_DISPLAY)) { + } else if (!isKeyguardOccluded(DEFAULT_DISPLAY)) { // Save transit in case we dismiss/occlude Keyguard shortly after. mBeforeUnoccludeTransit = dc.mAppTransition.getAppTransition(); @@ -409,7 +396,7 @@ class KeyguardController { private void dismissDockedStackIfNeeded() { // TODO(b/113840485): Handle docked stack for individual display. - if (mKeyguardShowing && isDisplayOccluded(DEFAULT_DISPLAY)) { + if (mKeyguardShowing && isKeyguardOccluded(DEFAULT_DISPLAY)) { // The lock screen is currently showing, but is occluded by a window that can // show on top of the lock screen. In this can we want to dismiss the docked // stack since it will be complicated/risky to try to put the activity on top @@ -434,9 +421,9 @@ class KeyguardController { private void updateKeyguardSleepToken(int displayId) { final KeyguardDisplayState state = getDisplay(displayId); - if (isKeyguardUnoccludedOrAodShowing(displayId) && state.mSleepToken == null) { + if (isKeyguardOrAodShowing(displayId) && state.mSleepToken == null) { state.acquiredSleepToken(); - } else if (!isKeyguardUnoccludedOrAodShowing(displayId) && state.mSleepToken != null) { + } else if (!isKeyguardOrAodShowing(displayId) && state.mSleepToken != null) { state.releaseSleepToken(); } } diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java index bf627ec5680c..0a3e7a4860d5 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimation.java +++ b/services/core/java/com/android/server/wm/RecentsAnimation.java @@ -355,7 +355,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks, // launch-behind state is restored. That also prevents the next transition // type being disturbed if the visibility is updated after setting the next // transition (the target activity will be one of closing apps). - if (!controller.shouldCancelWithDeferredScreenshot() + if (!controller.shouldDeferCancelWithScreenshot() && !targetStack.isFocusedStackOnDisplay()) { targetStack.ensureActivitiesVisibleLocked(null /* starting */, 0 /* starting */, false /* preserveWindows */); @@ -415,16 +415,18 @@ class RecentsAnimation implements RecentsAnimationCallbacks, final DisplayContent dc = mService.mRootActivityContainer.getDefaultDisplay().mDisplayContent; dc.mBoundsAnimationController.setAnimationType( - controller.shouldCancelWithDeferredScreenshot() ? FADE_IN : BOUNDS); + controller.shouldDeferCancelUntilNextTransition() ? FADE_IN : BOUNDS); - // Cancel running recents animation and screenshot previous task when the next - // transition starts in below cases: - // 1) The next launching task is not in recents animation task. + // We defer canceling the recents animation until the next app transition in the following + // cases: + // 1) The next launching task is not being animated by the recents animation // 2) The next task is home activity. (i.e. pressing home key to back home in recents). if ((!controller.isAnimatingTask(stack.getTaskStack().getTopChild()) || controller.isTargetApp(stack.getTopActivity().mAppWindowToken)) - && controller.shouldCancelWithDeferredScreenshot()) { - controller.cancelOnNextTransitionStart(); + && controller.shouldDeferCancelUntilNextTransition()) { + // Always prepare an app transition since we rely on the transition callbacks to cleanup + mWindowManager.prepareAppTransition(TRANSIT_NONE, false); + controller.setCancelOnNextTransitionStart(); } else { // Just cancel directly to unleash from launcher when the next launching task is the // current top task. diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index 163be1ebd0ed..6ea4d580ea98 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimationController.java +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -97,10 +97,9 @@ public class RecentsAnimationController implements DeathRecipient { private final Runnable mFailsafeRunnable = () -> cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "failSafeRunnable"); - final Object mLock = new Object(); - // The recents component app token that is shown behind the visibile tasks private AppWindowToken mTargetAppToken; + private DisplayContent mDisplayContent; private int mTargetActivityType; private Rect mMinimizedHomeBounds = new Rect(); @@ -124,25 +123,47 @@ public class RecentsAnimationController implements DeathRecipient { private boolean mLinkedToDeathOfRunner; - private boolean mCancelWithDeferredScreenshot; - + // Whether to try to defer canceling from a stack order change until the next transition + private boolean mRequestDeferCancelUntilNextTransition; + // Whether to actually defer canceling until the next transition private boolean mCancelOnNextTransitionStart; + // Whether to take a screenshot when handling a deferred cancel + private boolean mCancelDeferredWithScreenshot; /** * Animates the screenshot of task that used to be controlled by RecentsAnimation. - * @see {@link #cancelOnNextTransitionStart} + * @see {@link #setCancelOnNextTransitionStart} */ SurfaceAnimator mRecentScreenshotAnimator; + /** + * An app transition listener to cancel the recents animation only after the app transition + * starts or is canceled. + */ final AppTransitionListener mAppTransitionListener = new AppTransitionListener() { @Override public int onAppTransitionStartingLocked(int transit, long duration, long statusBarAnimationStartTime, long statusBarAnimationDuration) { - onTransitionStart(); - mService.mRoot.getDisplayContent(mDisplayId).mAppTransition - .unregisterListener(this); + continueDeferredCancel(); return 0; } + + @Override + public void onAppTransitionCancelledLocked(int transit) { + continueDeferredCancel(); + } + + private void continueDeferredCancel() { + mDisplayContent.mAppTransition.unregisterListener(this); + if (mCanceled) { + return; + } + + if (mCancelOnNextTransitionStart) { + mCancelOnNextTransitionStart = false; + cancelAnimationWithScreenshot(mCancelDeferredWithScreenshot); + } + } }; public interface RecentsAnimationCallbacks { @@ -202,8 +223,7 @@ public class RecentsAnimationController implements DeathRecipient { ? REORDER_MOVE_TO_TOP : REORDER_MOVE_TO_ORIGINAL_POSITION, true /* runSynchronously */, sendUserLeaveHint); - final DisplayContent dc = mService.mRoot.getDisplayContent(mDisplayId); - dc.mBoundsAnimationController.setAnimationType(FADE_IN); + mDisplayContent.mBoundsAnimationController.setAnimationType(FADE_IN); } finally { Binder.restoreCallingIdentity(token); } @@ -240,8 +260,7 @@ public class RecentsAnimationController implements DeathRecipient { } mInputConsumerEnabled = enabled; - final InputMonitor inputMonitor = - mService.mRoot.getDisplayContent(mDisplayId).getInputMonitor(); + final InputMonitor inputMonitor = mDisplayContent.getInputMonitor(); inputMonitor.updateInputWindowsLw(true /*force*/); mService.scheduleAnimationLocked(); } @@ -282,15 +301,23 @@ public class RecentsAnimationController implements DeathRecipient { } @Override + @Deprecated public void setCancelWithDeferredScreenshot(boolean screenshot) { - synchronized (mLock) { - setCancelWithDeferredScreenshotLocked(screenshot); + synchronized (mService.mGlobalLock) { + setDeferredCancel(true /* deferred */, screenshot); + } + } + + @Override + public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) { + synchronized (mService.mGlobalLock) { + setDeferredCancel(defer, screenshot); } } @Override public void cleanupScreenshot() { - synchronized (mLock) { + synchronized (mService.mGlobalLock) { if (mRecentScreenshotAnimator != null) { mRecentScreenshotAnimator.cancelAnimation(); mRecentScreenshotAnimator = null; @@ -312,10 +339,7 @@ public class RecentsAnimationController implements DeathRecipient { mCallbacks = callbacks; mDisplayId = displayId; mStatusBar = LocalServices.getService(StatusBarManagerInternal.class); - } - - public void initialize(int targetActivityType, SparseBooleanArray recentTaskIds) { - initialize(mService.mRoot.getDisplayContent(mDisplayId), targetActivityType, recentTaskIds); + mDisplayContent = service.mRoot.getDisplayContent(displayId); } /** @@ -323,15 +347,15 @@ public class RecentsAnimationController implements DeathRecipient { * because it may call cancelAnimation() which needs to properly clean up the controller * in the window manager. */ - @VisibleForTesting - void initialize(DisplayContent dc, int targetActivityType, SparseBooleanArray recentTaskIds) { + public void initialize(int targetActivityType, SparseBooleanArray recentTaskIds) { mTargetActivityType = targetActivityType; - dc.mAppTransition.registerListenerLocked(mAppTransitionListener); + mDisplayContent.mAppTransition.registerListenerLocked(mAppTransitionListener); // Make leashes for each of the visible/target tasks and add it to the recents animation to // be started - final ArrayList<Task> visibleTasks = dc.getVisibleTasks(); - final TaskStack targetStack = dc.getStack(WINDOWING_MODE_UNDEFINED, targetActivityType); + final ArrayList<Task> visibleTasks = mDisplayContent.getVisibleTasks(); + final TaskStack targetStack = mDisplayContent.getStack(WINDOWING_MODE_UNDEFINED, + targetActivityType); if (targetStack != null) { for (int i = targetStack.getChildCount() - 1; i >= 0; i--) { final Task t = targetStack.getChildAt(i); @@ -365,29 +389,31 @@ public class RecentsAnimationController implements DeathRecipient { } // Adjust the wallpaper visibility for the showing target activity - final AppWindowToken recentsComponentAppToken = dc.getStack(WINDOWING_MODE_UNDEFINED, - targetActivityType).getTopChild().getTopFullscreenAppToken(); + final AppWindowToken recentsComponentAppToken = + targetStack.getTopChild().getTopFullscreenAppToken(); if (recentsComponentAppToken != null) { if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "setHomeApp(" + recentsComponentAppToken.getName() + ")"); mTargetAppToken = recentsComponentAppToken; if (recentsComponentAppToken.windowsCanBeWallpaperTarget()) { - dc.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; - dc.setLayoutNeeded(); + mDisplayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; + mDisplayContent.setLayoutNeeded(); } } // Save the minimized home height - final TaskStack dockedStack = dc.getSplitScreenPrimaryStackIgnoringVisibility(); - dc.getDockedDividerController().getHomeStackBoundsInDockedMode( - dc.getConfiguration(), + final TaskStack dockedStack = mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility(); + mDisplayContent.getDockedDividerController().getHomeStackBoundsInDockedMode( + mDisplayContent.getConfiguration(), dockedStack == null ? DOCKED_INVALID : dockedStack.getDockSide(), mMinimizedHomeBounds); mService.mWindowPlacerLocked.performSurfacePlacement(); // Notify that the animation has started - mStatusBar.onRecentsAnimationStateChanged(true /* running */); + if (mStatusBar != null) { + mStatusBar.onRecentsAnimationStateChanged(true /* running */); + } } @VisibleForTesting @@ -441,8 +467,7 @@ public class RecentsAnimationController implements DeathRecipient { // Perform layout if it was scheduled before to make sure that we get correct content // insets for the target app window after a rotation - final DisplayContent displayContent = mService.mRoot.getDisplayContent(mDisplayId); - displayContent.performLayout(false /* initial */, false /* updateInputWindows */); + mDisplayContent.performLayout(false /* initial */, false /* updateInputWindows */); final Rect minimizedHomeBounds = mTargetAppToken != null && mTargetAppToken.inSplitScreenSecondaryWindowingMode() @@ -480,9 +505,8 @@ public class RecentsAnimationController implements DeathRecipient { cancelAnimation(reorderMode, true /* runSynchronously */, false /* screenshot */, reason); } - void cancelAnimationWithScreenShot() { - cancelAnimation(REORDER_KEEP_IN_PLACE, true /* sync */, true /* screenshot */, - "stackOrderChanged"); + void cancelAnimationWithScreenshot(boolean screenshot) { + cancelAnimation(REORDER_KEEP_IN_PLACE, true /* sync */, screenshot, "stackOrderChanged"); } private void cancelAnimation(@ReorderMode int reorderMode, boolean runSynchronously, @@ -496,21 +520,29 @@ public class RecentsAnimationController implements DeathRecipient { } mService.mH.removeCallbacks(mFailsafeRunnable); mCanceled = true; - try { - if (screenshot) { - // Screen shot previous task when next task starts transition. - final Task task = mPendingAnimations.get(0).mTask; - screenshotRecentTask(task, reorderMode, runSynchronously); + + if (screenshot) { + // Screen shot previous task when next task starts transition and notify the runner. + // We will actually finish the animation once the runner calls cleanUpScreenshot(). + final Task task = mPendingAnimations.get(0).mTask; + screenshotRecentTask(task, reorderMode, runSynchronously); + try { mRunner.onAnimationCanceled(true /* deferredWithScreenshot */); - return; + } catch (RemoteException e) { + Slog.e(TAG, "Failed to cancel recents animation", e); + } + } else { + // Otherwise, notify the runner and clean up the animation immediately + // Note: In the fallback case, this can trigger multiple onAnimationCancel() calls + // to the runner if we this actually triggers cancel twice on the caller + try { + mRunner.onAnimationCanceled(false /* deferredWithScreenshot */); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to cancel recents animation", e); } - mRunner.onAnimationCanceled(false /* deferredWithScreenshot */); - } catch (RemoteException e) { - Slog.e(TAG, "Failed to cancel recents animation", e); + mCallbacks.onAnimationFinished(reorderMode, runSynchronously, + false /* sendUserLeaveHint */); } - // Clean up and return to the previous app - mCallbacks.onAnimationFinished(reorderMode, runSynchronously, - false /* sendUserLeaveHint */); } } @@ -523,27 +555,36 @@ public class RecentsAnimationController implements DeathRecipient { * screenshot, so that Launcher can still control the leash lifecycle & make the next app * transition animate smoothly without flickering. */ - void cancelOnNextTransitionStart() { + void setCancelOnNextTransitionStart() { mCancelOnNextTransitionStart = true; } - void setCancelWithDeferredScreenshotLocked(boolean screenshot) { - mCancelWithDeferredScreenshot = screenshot; + /** + * Requests that we attempt to defer the cancel until the next app transition if we are + * canceling from a stack order change. If {@param screenshot} is specified, then the system + * will replace the contents of the leash with a screenshot, which must be cleaned up when the + * runner calls cleanUpScreenshot(). + */ + void setDeferredCancel(boolean defer, boolean screenshot) { + mRequestDeferCancelUntilNextTransition = defer; + mCancelDeferredWithScreenshot = screenshot; } - boolean shouldCancelWithDeferredScreenshot() { - return mCancelWithDeferredScreenshot; + /** + * @return Whether we should defer the cancel from a stack order change until the next app + * transition. + */ + boolean shouldDeferCancelUntilNextTransition() { + return mRequestDeferCancelUntilNextTransition; } - void onTransitionStart() { - if (mCanceled) { - return; - } - - if (mCancelOnNextTransitionStart) { - mCancelOnNextTransitionStart = false; - cancelAnimationWithScreenShot(); - } + /** + * @return Whether we should both defer the cancel from a stack order change until the next + * app transition, and also that the deferred cancel should replace the contents of the leash + * with a screenshot. + */ + boolean shouldDeferCancelWithScreenshot() { + return mRequestDeferCancelUntilNextTransition && mCancelDeferredWithScreenshot; } void screenshotRecentTask(Task task, @ReorderMode int reorderMode, boolean runSynchronously) { @@ -576,6 +617,7 @@ public class RecentsAnimationController implements DeathRecipient { // Clear any pending failsafe runnables mService.mH.removeCallbacks(mFailsafeRunnable); + mDisplayContent.mAppTransition.unregisterListener(mAppTransitionListener); // Clear references to the runner unlinkToDeathOfRunner(); @@ -589,21 +631,22 @@ public class RecentsAnimationController implements DeathRecipient { } // Update the input windows after the animation is complete - final InputMonitor inputMonitor = - mService.mRoot.getDisplayContent(mDisplayId).getInputMonitor(); + final InputMonitor inputMonitor = mDisplayContent.getInputMonitor(); inputMonitor.updateInputWindowsLw(true /*force*/); // We have deferred all notifications to the target app as a part of the recents animation, // so if we are actually transitioning there, notify again here if (mTargetAppToken != null) { if (reorderMode == REORDER_MOVE_TO_TOP || reorderMode == REORDER_KEEP_IN_PLACE) { - mService.mRoot.getDisplayContent(mDisplayId) - .mAppTransition.notifyAppTransitionFinishedLocked(mTargetAppToken.token); + mDisplayContent.mAppTransition.notifyAppTransitionFinishedLocked( + mTargetAppToken.token); } } // Notify that the animation has ended - mStatusBar.onRecentsAnimationStateChanged(false /* running */); + if (mStatusBar != null) { + mStatusBar.onRecentsAnimationStateChanged(false /* running */); + } } void scheduleFailsafe() { @@ -630,8 +673,7 @@ public class RecentsAnimationController implements DeathRecipient { synchronized (mService.getWindowManagerLock()) { // Clear associated input consumers on runner death - final InputMonitor inputMonitor = - mService.mRoot.getDisplayContent(mDisplayId).getInputMonitor(); + final InputMonitor inputMonitor = mDisplayContent.getInputMonitor(); inputMonitor.destroyInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION); } } @@ -827,5 +869,11 @@ public class RecentsAnimationController implements DeathRecipient { pw.print(innerPrefix); pw.println("mSplitScreenMinimized=" + mSplitScreenMinimized); pw.print(innerPrefix); pw.println("mTargetAppToken=" + mTargetAppToken); pw.print(innerPrefix); pw.println("isTargetOverWallpaper=" + isTargetOverWallpaper()); + pw.print(innerPrefix); pw.println("mRequestDeferCancelUntilNextTransition=" + + mRequestDeferCancelUntilNextTransition); + pw.print(innerPrefix); pw.println("mCancelOnNextTransitionStart=" + + mCancelOnNextTransitionStart); + pw.print(innerPrefix); pw.println("mCancelDeferredWithScreenshot=" + + mCancelDeferredWithScreenshot); } } 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/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java index dc62877bca8d..b94a7dc781e8 100644 --- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java +++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java @@ -937,6 +937,7 @@ class ScreenRotationAnimation { } } + t.setEarlyWakeup(); setSnapshotTransform(t, mSnapshotFinalMatrix, mExitTransformation.getAlpha()); } diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java index 7c7fdae45a47..882f41152886 100644 --- a/services/core/java/com/android/server/wm/TaskRecord.java +++ b/services/core/java/com/android/server/wm/TaskRecord.java @@ -66,6 +66,7 @@ import static com.android.server.am.TaskRecordProto.ORIG_ACTIVITY; import static com.android.server.am.TaskRecordProto.REAL_ACTIVITY; import static com.android.server.am.TaskRecordProto.RESIZE_MODE; import static com.android.server.am.TaskRecordProto.STACK_ID; +import static com.android.server.wm.ActivityRecord.FINISH_RESULT_REMOVED; import static com.android.server.wm.ActivityRecord.STARTING_WINDOW_SHOWN; import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_MOVING; import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_MOVING_TO_TOP; @@ -1113,16 +1114,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 +1236,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 +1250,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 +1419,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) == FINISH_RESULT_REMOVED) { --activityNdx; --numActivities; } @@ -1498,8 +1474,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 */) == FINISH_RESULT_REMOVED) { --activityNdx; --numActivities; } @@ -1512,10 +1488,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 +1632,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 +1711,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 +1731,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/TaskScreenshotAnimatable.java b/services/core/java/com/android/server/wm/TaskScreenshotAnimatable.java index 1a571683de34..143543e8c74b 100644 --- a/services/core/java/com/android/server/wm/TaskScreenshotAnimatable.java +++ b/services/core/java/com/android/server/wm/TaskScreenshotAnimatable.java @@ -28,7 +28,7 @@ import android.view.SurfaceSession; * Class used by {@link RecentsAnimationController} to create a surface control with taking * screenshot of task when canceling recents animation. * - * @see {@link RecentsAnimationController#cancelOnNextTransitionStart} + * @see {@link RecentsAnimationController#setCancelOnNextTransitionStart} */ class TaskScreenshotAnimatable implements SurfaceAnimator.Animatable { private static final String TAG = "TaskScreenshotAnim"; 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/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index eb45e73d47a4..79367a050d46 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -321,7 +321,9 @@ public class TaskStack extends WindowContainer<Task> implements */ private void setAnimationFinalBounds(Rect sourceHintBounds, Rect destBounds, boolean toFullscreen) { - mBoundsAnimatingRequested = true; + if (mAnimationType == BoundsAnimationController.BOUNDS) { + mBoundsAnimatingRequested = true; + } mBoundsAnimatingToFullscreen = toFullscreen; if (destBounds != null) { mBoundsAnimationTarget.set(destBounds); @@ -1586,8 +1588,10 @@ public class TaskStack extends WindowContainer<Task> implements return false; } - mBoundsAnimatingRequested = false; - mBoundsAnimating = true; + if (animationType == BoundsAnimationController.BOUNDS) { + mBoundsAnimatingRequested = false; + mBoundsAnimating = true; + } mAnimationType = animationType; // If we are changing UI mode, as in the PiP to fullscreen diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java index 6910ce91cce2..05cfbd4e39d9 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 @@ -507,4 +511,9 @@ public abstract class WindowManagerInternal { */ public abstract void removeNonHighRefreshRatePackage(@NonNull String packageName); + /** + * Checks if this display is touchable. + */ + public abstract boolean isTouchableDisplay(int displayId); + } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index f52bde9a035c..fbdc54a2435b 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); } } @@ -7491,6 +7455,17 @@ public class WindowManagerService extends IWindowManager.Stub .removeNonHighRefreshRatePackage(packageName)); } } + + @Override + public boolean isTouchableDisplay(int displayId) { + synchronized (mGlobalLock) { + final DisplayContent displayContent = mRoot.getDisplayContent(displayId); + final Configuration configuration = + displayContent != null ? displayContent.getConfiguration() : null; + return configuration != null + && configuration.touchscreen == Configuration.TOUCHSCREEN_FINGER; + } + } } void registerAppFreezeListener(AppFreezeListener listener) { 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 a3ee1088bc37..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() @@ -3196,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(); @@ -3848,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); @@ -4296,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..41840a4c531c 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -729,12 +729,7 @@ class WindowStateAnimator { if (!mService.mLimitedAlphaCompositing || (!PixelFormat.formatHasAlpha(mWin.mAttrs.format) || (mWin.isIdentityMatrix(mDsDx, mDtDx, mDtDy, mDsDy)))) { - //Slog.i(TAG_WM, "Applying alpha transform"); - if (screenAnimation) { - mShownAlpha *= screenRotationAnimation.getEnterTransformation().getAlpha(); - } - } else { - //Slog.i(TAG_WM, "Not applying alpha transform"); + mShownAlpha *= screenRotationAnimation.getEnterTransformation().getAlpha(); } if ((DEBUG_ANIM || WindowManagerService.localLOGV) @@ -1299,7 +1294,7 @@ class WindowStateAnimator { mSurfaceController.mSurfaceControl); } - mPostDrawTransaction.apply(); + SurfaceControl.mergeToGlobalTransaction(mPostDrawTransaction); return true; } @@ -1508,6 +1503,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/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index 1c8c46c74002..466ca9315f6f 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -41,7 +41,6 @@ #include <utils/Looper.h> #include <utils/threads.h> #include <utils/Trace.h> -#include <utils/SortedVector.h> #include <binder/IServiceManager.h> @@ -307,7 +306,7 @@ private: wp<PointerController> pointerController; // Input devices to be disabled - SortedVector<int32_t> disabledInputDevices; + std::set<int32_t> disabledInputDevices; // Associated Pointer controller display. int32_t pointerDisplayId; @@ -898,13 +897,13 @@ void NativeInputManager::setInputDeviceEnabled(uint32_t deviceId, bool enabled) { // acquire lock AutoMutex _l(mLock); - ssize_t index = mLocked.disabledInputDevices.indexOf(deviceId); - bool currentlyEnabled = index < 0; + auto it = mLocked.disabledInputDevices.find(deviceId); + bool currentlyEnabled = it == mLocked.disabledInputDevices.end(); if (!enabled && currentlyEnabled) { - mLocked.disabledInputDevices.add(deviceId); + mLocked.disabledInputDevices.insert(deviceId); } if (enabled && !currentlyEnabled) { - mLocked.disabledInputDevices.remove(deviceId); + mLocked.disabledInputDevices.erase(deviceId); } } // release lock diff --git a/services/core/xsd/Android.bp b/services/core/xsd/Android.bp index 98e4343e6e57..8b2cbbde2db8 100644 --- a/services/core/xsd/Android.bp +++ b/services/core/xsd/Android.bp @@ -4,3 +4,11 @@ xsd_config { api_dir: "schema", package_name: "com.android.server.pm.permission.configfile", } + + +xsd_config { + name: "platform-compat-config", + srcs: ["platform-compat-config.xsd"], + api_dir: "platform-compat-schema", + package_name: "com.android.server.compat.config", +} diff --git a/services/core/xsd/platform-compat-config.xsd b/services/core/xsd/platform-compat-config.xsd new file mode 100644 index 000000000000..ee39e507aff1 --- /dev/null +++ b/services/core/xsd/platform-compat-config.xsd @@ -0,0 +1,51 @@ +<?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. + --> + +<!-- This defines the format of the XML file generated by + ~ com.android.compat.annotation.ChangeIdProcessor annotation processor (from + ~ tools/platform-compat), and is parsed in com/android/server/compat/CompatConfig.java. +--> +<xs:schema version="2.0" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema"> + + <xs:complexType name="change"> + <xs:simpleContent> + <xs:extension base="xs:string"> + <xs:attribute type="xs:long" name="id" use="required"/> + <xs:attribute type="xs:string" name="name" use="required"/> + <xs:attribute type="xs:boolean" name="disabled"/> + <xs:attribute type="xs:int" name="enableAfterTargetSdk"/> + </xs:extension> + </xs:simpleContent> + </xs:complexType> + + <xs:element name="config"> + <xs:complexType> + <xs:sequence> + <xs:element name="compat-change" type="change" maxOccurs="unbounded" + minOccurs="0"/> + </xs:sequence> + </xs:complexType> + <xs:unique name="UniqueId"> + <xs:selector xpath="compat-change" /> + <xs:field xpath="@id" /> + </xs:unique> + </xs:element> +</xs:schema> + + + + diff --git a/services/core/xsd/platform-compat-schema/current.txt b/services/core/xsd/platform-compat-schema/current.txt new file mode 100644 index 000000000000..84567851da2c --- /dev/null +++ b/services/core/xsd/platform-compat-schema/current.txt @@ -0,0 +1,31 @@ +// Signature format: 2.0 +package com.android.server.compat.config { + + public class Change { + ctor public Change(); + method public boolean getDisabled(); + method public int getEnableAfterTargetSdk(); + method public long getId(); + method public String getName(); + method public String getValue(); + method public void setDisabled(boolean); + method public void setEnableAfterTargetSdk(int); + method public void setId(long); + method public void setName(String); + method public void setValue(String); + } + + public class Config { + ctor public Config(); + method public java.util.List<com.android.server.compat.config.Change> getCompatChange(); + } + + public class XmlParser { + ctor public XmlParser(); + method public static com.android.server.compat.config.Config read(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException; + method public static String readText(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; + method public static void skip(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException; + } + +} + diff --git a/services/core/xsd/platform-compat-schema/last_current.txt b/services/core/xsd/platform-compat-schema/last_current.txt new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/services/core/xsd/platform-compat-schema/last_current.txt diff --git a/services/core/xsd/platform-compat-schema/last_removed.txt b/services/core/xsd/platform-compat-schema/last_removed.txt new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/services/core/xsd/platform-compat-schema/last_removed.txt diff --git a/services/core/xsd/platform-compat-schema/removed.txt b/services/core/xsd/platform-compat-schema/removed.txt new file mode 100644 index 000000000000..d802177e249b --- /dev/null +++ b/services/core/xsd/platform-compat-schema/removed.txt @@ -0,0 +1 @@ +// Signature format: 2.0 diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index a04875f1a283..9fe44dc7b2f0 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -87,6 +87,7 @@ import com.android.server.biometrics.iris.IrisService; import com.android.server.broadcastradio.BroadcastRadioService; import com.android.server.camera.CameraServiceProxy; import com.android.server.clipboard.ClipboardService; +import com.android.server.compat.PlatformCompat; import com.android.server.connectivity.IpConnectivityMetrics; import com.android.server.contentcapture.ContentCaptureManagerInternal; import com.android.server.coverage.CoverageService; @@ -1098,6 +1099,11 @@ public final class SystemServer { t.traceBegin("SignedConfigService"); SignedConfigService.registerUpdateReceiver(mSystemContext); t.traceEnd(); + + t.traceBegin("PlatformCompat"); + ServiceManager.addService("platform_compat", new PlatformCompat(context)); + t.traceEnd(); + } catch (RuntimeException e) { Slog.e("System", "******************************************"); Slog.e("System", "************ Failure starting core service", e); diff --git a/services/robotests/Android.bp b/services/robotests/Android.bp new file mode 100644 index 000000000000..3ce514a56b60 --- /dev/null +++ b/services/robotests/Android.bp @@ -0,0 +1,53 @@ +// Copyright (C) 2016 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//################################################################## +// FrameworksServicesLib app just for Robolectric test target # +//################################################################## + +android_app { + name: "FrameworksServicesLib", + platform_apis: true, + + privileged: true, + + static_libs: [ + "services.core", + "services.net", + ], +} + +//################################################################## +// FrameworksServicesLib Robolectric test target. # +//################################################################## +android_robolectric_test { + name: "FrameworksServicesRoboTests", + + srcs: ["src/**/*.java"], + + java_resource_dirs: ["config"], + + // Include the testing libraries + libs: [ + "platform-test-annotations", + "testng", + ], + + instrumentation_for: "FrameworksServicesLib", +} + +filegroup { + name: "FrameworksServicesRoboShadows", + srcs: ["src/com/android/server/testing/shadows/**/*.java"], +} diff --git a/services/robotests/Android.mk b/services/robotests/Android.mk deleted file mode 100644 index 0cf0d3402dea..000000000000 --- a/services/robotests/Android.mk +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (C) 2016 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -################################################################### -# FrameworksServicesLib app just for Robolectric test target # -################################################################### -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_PACKAGE_NAME := FrameworksServicesLib -LOCAL_PRIVATE_PLATFORM_APIS := true -LOCAL_MODULE_TAGS := optional - -LOCAL_PRIVILEGED_MODULE := true - -LOCAL_STATIC_JAVA_LIBRARIES := \ - services.core \ - services.net - -include $(BUILD_PACKAGE) - -################################################################### -# FrameworksServicesLib Robolectric test target. # -################################################################### -include $(CLEAR_VARS) - -LOCAL_MODULE := FrameworksServicesRoboTests - -LOCAL_SRC_FILES := $(call all-java-files-under, src) - -LOCAL_RESOURCE_DIR := \ - $(LOCAL_PATH)/res - -LOCAL_JAVA_RESOURCE_DIRS := config - -# Include the testing libraries -LOCAL_JAVA_LIBRARIES := \ - platform-test-annotations \ - robolectric_android-all-stub \ - Robolectric_all-target \ - mockito-robolectric-prebuilt \ - truth-prebuilt \ - testng - -LOCAL_INSTRUMENTATION_FOR := FrameworksServicesLib - -LOCAL_MODULE_TAGS := optional - -include $(BUILD_STATIC_JAVA_LIBRARY) - -################################################################### -# FrameworksServicesLib runner target to run the previous target. # -################################################################### -include $(CLEAR_VARS) - -LOCAL_MODULE := RunFrameworksServicesRoboTests - -LOCAL_JAVA_LIBRARIES := \ - FrameworksServicesRoboTests \ - platform-test-annotations \ - robolectric_android-all-stub \ - Robolectric_all-target \ - mockito-robolectric-prebuilt \ - truth-prebuilt \ - testng - -LOCAL_TEST_PACKAGE := FrameworksServicesLib - -LOCAL_ROBOTEST_FILES := $(call find-files-in-subdirs,$(LOCAL_PATH)/src,*Test.java,.) - -include external/robolectric-shadows/run_robotests.mk - -################################################################### -# include subdir Android.mk files -################################################################### -include $(CLEAR_VARS) -include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/services/robotests/AndroidManifest.xml b/services/robotests/AndroidManifest.xml new file mode 100644 index 000000000000..828c8fade6fc --- /dev/null +++ b/services/robotests/AndroidManifest.xml @@ -0,0 +1,24 @@ +<?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. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + coreApp="true" + package="com.android.server.robotests"> + + <application/> + +</manifest> diff --git a/services/robotests/backup/Android.bp b/services/robotests/backup/Android.bp new file mode 100644 index 000000000000..9d384e90d253 --- /dev/null +++ b/services/robotests/backup/Android.bp @@ -0,0 +1,53 @@ +// 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. + +//################################################################## +// BackupFrameworksServicesLib app just for Robolectric test target # +//################################################################## +android_app { + name: "BackupFrameworksServicesLib", + platform_apis: true, + + privileged: true, + + static_libs: [ + "bmgr", + "bu", + "services.backup", + "services.core", + "services.net", + ], +} + +//################################################################## +// BackupFrameworksServicesLib Robolectric test target. # +//################################################################## +android_robolectric_test { + name: "BackupFrameworksServicesRoboTests", + srcs: [ + "src/**/*.java", + ":FrameworksServicesRoboShadows", + ], + + java_resource_dirs: ["config"], + + // Include the testing libraries + libs: [ + "platform-test-annotations", + "testng", + ], + + instrumentation_for: "BackupFrameworksServicesLib", + +} diff --git a/services/robotests/backup/Android.mk b/services/robotests/backup/Android.mk deleted file mode 100644 index bd4ebbd393fa..000000000000 --- a/services/robotests/backup/Android.mk +++ /dev/null @@ -1,84 +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. - -LOCAL_PATH := $(call my-dir) - -################################################################### -# BackupFrameworksServicesLib app just for Robolectric test target # -################################################################### -include $(CLEAR_VARS) - -LOCAL_PACKAGE_NAME := BackupFrameworksServicesLib -LOCAL_PRIVATE_PLATFORM_APIS := true -LOCAL_MODULE_TAGS := optional - -LOCAL_PRIVILEGED_MODULE := true - -LOCAL_STATIC_JAVA_LIBRARIES := \ - bmgr \ - bu \ - services.backup \ - services.core \ - services.net - -include $(BUILD_PACKAGE) - -################################################################### -# BackupFrameworksServicesLib Robolectric test target. # -################################################################### -include $(CLEAR_VARS) - -LOCAL_MODULE := BackupFrameworksServicesRoboTests - -LOCAL_SRC_FILES := $(call all-java-files-under, src) \ - $(call all-java-files-under, ../src/com/android/server/testing/shadows) - -LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res - -LOCAL_JAVA_RESOURCE_DIRS := config - -# Include the testing libraries -LOCAL_JAVA_LIBRARIES := \ - platform-test-annotations \ - robolectric_android-all-stub \ - Robolectric_all-target \ - mockito-robolectric-prebuilt \ - truth-prebuilt \ - testng - -LOCAL_INSTRUMENTATION_FOR := BackupFrameworksServicesLib - -LOCAL_MODULE_TAGS := optional - -include $(BUILD_STATIC_JAVA_LIBRARY) - -################################################################### -# BackupFrameworksServicesLib runner target to run the previous target. # -################################################################### -include $(CLEAR_VARS) - -LOCAL_MODULE := RunBackupFrameworksServicesRoboTests - -LOCAL_JAVA_LIBRARIES := \ - BackupFrameworksServicesRoboTests \ - platform-test-annotations \ - robolectric_android-all-stub \ - Robolectric_all-target \ - mockito-robolectric-prebuilt \ - truth-prebuilt \ - testng - -LOCAL_TEST_PACKAGE := BackupFrameworksServicesLib - -include external/robolectric-shadows/run_robotests.mk diff --git a/services/robotests/backup/AndroidManifest.xml b/services/robotests/backup/AndroidManifest.xml new file mode 100644 index 000000000000..09323782a228 --- /dev/null +++ b/services/robotests/backup/AndroidManifest.xml @@ -0,0 +1,24 @@ +<?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. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + coreApp="true" + package="com.android.server.backup.robotests"> + + <application/> + +</manifest> 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/AccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java index ba2959f7f965..2f9f9bbecbdf 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java @@ -23,21 +23,25 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.accessibilityservice.AccessibilityServiceInfo; +import android.accessibilityservice.GestureDescription; import android.accessibilityservice.IAccessibilityServiceClient; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; +import android.content.pm.ParceledListSlice; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.os.IBinder; import android.os.RemoteException; import android.os.UserHandle; +import android.view.Display; import com.android.server.wm.WindowManagerInternal; @@ -50,6 +54,7 @@ import org.mockito.MockitoAnnotations; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; +import java.util.List; /** @@ -73,6 +78,9 @@ public class AccessibilityServiceConnectionTest { @Mock GlobalActionPerformer mMockGlobalActionPerformer; @Mock KeyEventDispatcher mMockKeyEventDispatcher; @Mock MagnificationController mMockMagnificationController; + @Mock IBinder mMockIBinder; + @Mock IAccessibilityServiceClient mMockServiceClient; + @Mock MotionEventInjector mMockMotionEventInjector; MessageCapturingHandler mHandler = new MessageCapturingHandler(null); @@ -82,15 +90,22 @@ public class AccessibilityServiceConnectionTest { when(mMockSystemSupport.getKeyEventDispatcher()).thenReturn(mMockKeyEventDispatcher); when(mMockSystemSupport.getMagnificationController()) .thenReturn(mMockMagnificationController); + when(mMockSystemSupport.getMotionEventInjectorForDisplayLocked( + Display.DEFAULT_DISPLAY)).thenReturn(mMockMotionEventInjector); when(mMockServiceInfo.getResolveInfo()).thenReturn(mMockResolveInfo); mMockResolveInfo.serviceInfo = mock(ServiceInfo.class); mMockResolveInfo.serviceInfo.applicationInfo = mock(ApplicationInfo.class); + when(mMockIBinder.queryLocalInterface(any())).thenReturn(mMockServiceClient); + when(mMockWindowManagerInternal.isTouchableDisplay(Display.DEFAULT_DISPLAY)).thenReturn( + true); + mConnection = new AccessibilityServiceConnection(mMockUserState, mMockContext, COMPONENT_NAME, mMockServiceInfo, SERVICE_ID, mHandler, new Object(), mMockSecurityPolicy, mMockSystemSupport, mMockWindowManagerInternal, mMockGlobalActionPerformer, mMockA11yWindowManager); + when(mMockSecurityPolicy.canPerformGestures(mConnection)).thenReturn(true); } @After @@ -115,25 +130,23 @@ public class AccessibilityServiceConnectionTest { @Test public void bindConnectUnbind_linksAndUnlinksToServiceDeath() throws RemoteException { - IBinder mockBinder = mock(IBinder.class); setServiceBinding(COMPONENT_NAME); mConnection.bindLocked(); - mConnection.onServiceConnected(COMPONENT_NAME, mockBinder); - verify(mockBinder).linkToDeath(eq(mConnection), anyInt()); + mConnection.onServiceConnected(COMPONENT_NAME, mMockIBinder); + verify(mMockIBinder).linkToDeath(eq(mConnection), anyInt()); mConnection.unbindLocked(); - verify(mockBinder).unlinkToDeath(eq(mConnection), anyInt()); + verify(mMockIBinder).unlinkToDeath(eq(mConnection), anyInt()); } @Test public void connectedServiceCrashedAndRestarted_crashReportedInServiceInfo() { - IBinder mockBinder = mock(IBinder.class); setServiceBinding(COMPONENT_NAME); mConnection.bindLocked(); - mConnection.onServiceConnected(COMPONENT_NAME, mockBinder); + mConnection.onServiceConnected(COMPONENT_NAME, mMockIBinder); assertFalse(mConnection.getServiceInfo().crashed); mConnection.binderDied(); assertTrue(mConnection.getServiceInfo().crashed); - mConnection.onServiceConnected(COMPONENT_NAME, mockBinder); + mConnection.onServiceConnected(COMPONENT_NAME, mMockIBinder); mHandler.sendAllMessages(); assertFalse(mConnection.getServiceInfo().crashed); } @@ -145,10 +158,9 @@ public class AccessibilityServiceConnectionTest { @Test public void binderDied_keysGetFlushed() { - IBinder mockBinder = mock(IBinder.class); setServiceBinding(COMPONENT_NAME); mConnection.bindLocked(); - mConnection.onServiceConnected(COMPONENT_NAME, mockBinder); + mConnection.onServiceConnected(COMPONENT_NAME, mMockIBinder); mConnection.binderDied(); assertTrue(mConnection.getServiceInfo().crashed); verify(mMockKeyEventDispatcher).flush(mConnection); @@ -157,17 +169,63 @@ public class AccessibilityServiceConnectionTest { @Test public void connectedService_notInEnabledServiceList_doNotInitClient() throws RemoteException { - IBinder mockBinder = mock(IBinder.class); - IAccessibilityServiceClient mockClient = mock(IAccessibilityServiceClient.class); - when(mockBinder.queryLocalInterface(any())).thenReturn(mockClient); when(mMockUserState.getEnabledServicesLocked()) .thenReturn(Collections.emptySet()); setServiceBinding(COMPONENT_NAME); mConnection.bindLocked(); - mConnection.onServiceConnected(COMPONENT_NAME, mockBinder); + mConnection.onServiceConnected(COMPONENT_NAME, mMockIBinder); mHandler.sendAllMessages(); verify(mMockSystemSupport, times(2)).onClientChangeLocked(false); - verify(mockClient, times(0)).init(any(), anyInt(), any()); + verify(mMockServiceClient, times(0)).init(any(), anyInt(), any()); + } + + @Test + public void sendGesture_touchableDisplay_injectEvents() + throws RemoteException { + setServiceBinding(COMPONENT_NAME); + mConnection.bindLocked(); + mConnection.onServiceConnected(COMPONENT_NAME, mMockIBinder); + + ParceledListSlice parceledListSlice = mock(ParceledListSlice.class); + List<GestureDescription.GestureStep> gestureSteps = mock(List.class); + when(parceledListSlice.getList()).thenReturn(gestureSteps); + mConnection.dispatchGesture(0, parceledListSlice, Display.DEFAULT_DISPLAY); + + verify(mMockMotionEventInjector).injectEvents(gestureSteps, mMockServiceClient, 0); + } + + @Test + public void sendGesture_untouchableDisplay_performGestureResultFailed() + throws RemoteException { + when(mMockWindowManagerInternal.isTouchableDisplay(Display.DEFAULT_DISPLAY)).thenReturn( + false); + setServiceBinding(COMPONENT_NAME); + mConnection.bindLocked(); + mConnection.onServiceConnected(COMPONENT_NAME, mMockIBinder); + + ParceledListSlice parceledListSlice = mock(ParceledListSlice.class); + List<GestureDescription.GestureStep> gestureSteps = mock(List.class); + when(parceledListSlice.getList()).thenReturn(gestureSteps); + mConnection.dispatchGesture(0, parceledListSlice, Display.DEFAULT_DISPLAY); + + verify(mMockMotionEventInjector, never()).injectEvents(gestureSteps, mMockServiceClient, 0); + verify(mMockServiceClient).onPerformGestureResult(0, false); } + + @Test + public void sendGesture_invalidDisplay_performGestureResultFailed() + throws RemoteException { + setServiceBinding(COMPONENT_NAME); + mConnection.bindLocked(); + mConnection.onServiceConnected(COMPONENT_NAME, mMockIBinder); + + ParceledListSlice parceledListSlice = mock(ParceledListSlice.class); + List<GestureDescription.GestureStep> gestureSteps = mock(List.class); + when(parceledListSlice.getList()).thenReturn(gestureSteps); + mConnection.dispatchGesture(0, parceledListSlice, Display.INVALID_DISPLAY); + + verify(mMockServiceClient).onPerformGestureResult(0, false); + } + } 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 e12532931e7c..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 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/pm/PackageBuilder.java b/services/tests/servicestests/src/com/android/server/pm/PackageBuilder.java new file mode 100644 index 000000000000..470d4fa92833 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/pm/PackageBuilder.java @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm; + +import android.content.pm.PackageParser; + +import com.android.internal.util.ArrayUtils; + +class PackageBuilder { + final PackageParser.Package mPkg; + + PackageBuilder(String packageName) { + mPkg = new PackageParser.Package(packageName); + } + + PackageBuilder setApplicationInfoCodePath(String codePath) { + mPkg.applicationInfo.setCodePath(codePath); + return this; + } + + PackageBuilder setApplicationInfoResourcePath(String resourcePath) { + mPkg.applicationInfo.setResourcePath(resourcePath); + return this; + } + + PackageBuilder setCodePath(String codePath) { + mPkg.codePath = codePath; + return this; + } + + PackageBuilder setBaseCodePath(String baseCodePath) { + mPkg.baseCodePath = baseCodePath; + return this; + } + + PackageBuilder addUsesStaticLibrary(String name, long version) { + mPkg.usesStaticLibraries = ArrayUtils.add(mPkg.usesStaticLibraries, name); + mPkg.usesStaticLibrariesVersions = + ArrayUtils.appendLong(mPkg.usesStaticLibrariesVersions, version); + return this; + } + + PackageBuilder setApplicationInfoNativeLibraryRootDir(String dir) { + mPkg.applicationInfo.nativeLibraryRootDir = dir; + return this; + } + + PackageBuilder setStaticSharedLib(String staticSharedLibName, long staticSharedLibVersion) { + mPkg.staticSharedLibVersion = staticSharedLibVersion; + mPkg.staticSharedLibName = staticSharedLibName; + return this; + } + + PackageBuilder setManifestPackageName(String manifestPackageName) { + mPkg.manifestPackageName = manifestPackageName; + return this; + } + + PackageBuilder setVersionCodeMajor(int versionCodeMajor) { + mPkg.mVersionCodeMajor = versionCodeMajor; + return this; + } + + PackageBuilder setVersionCode(int versionCode) { + mPkg.mVersionCode = versionCode; + return this; + } + + PackageBuilder addSplitCodePath(String splitCodePath) { + mPkg.splitCodePaths = + ArrayUtils.appendElement(String.class, mPkg.splitCodePaths, splitCodePath); + return this; + } + + PackageBuilder setApplicationInfoVolumeUuid(String volumeUuid) { + mPkg.applicationInfo.volumeUuid = volumeUuid; + return this; + } + + PackageBuilder addLibraryName(String libraryName) { + mPkg.libraryNames = ArrayUtils.add(mPkg.libraryNames, libraryName); + return this; + } + + PackageBuilder setRealPackageName(String realPackageName) { + mPkg.mRealPackage = realPackageName; + return this; + } + + PackageBuilder setCpuAbiOVerride(String cpuAbiOverride) { + mPkg.cpuAbiOverride = cpuAbiOverride; + return this; + } + + PackageBuilder addPermissionRequest(String permissionName) { + mPkg.requestedPermissions.add(permissionName); + return this; + } + + PackageParser.Package build() { + return mPkg; + } + + public PackageBuilder addApplicationInfoFlag(int flag) { + mPkg.applicationInfo.flags |= flag; + return this; + } +} diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java index 13a8eb1d7fad..e33d8ca66ed0 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java @@ -22,6 +22,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; +import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.ConfigurationInfo; @@ -557,6 +558,9 @@ public class PackageParserTest { pkg.mRequiredForAllUsers = true; pkg.visibleToInstantApps = true; pkg.use32bitAbi = true; + pkg.mForceQueryable = true; + pkg.mQueriesPackages = new ArrayList<>(Arrays.asList("foo27")); + pkg.mQueriesIntents = new ArrayList<>(Arrays.asList(new Intent("foo28"))); } private static void assertAllFieldsExist(PackageParser.Package pkg) throws Exception { diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java new file mode 100644 index 000000000000..b42cfd8be4a6 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm; + +import android.content.pm.PackageUserState; +import android.util.SparseArray; + +import java.io.File; +import java.util.List; + +class PackageSettingBuilder { + private String mName; + private String mRealName; + private String mCodePath; + private String mResourcePath; + private String mLegacyNativeLibraryPathString; + private String mPrimaryCpuAbiString; + private String mSecondaryCpuAbiString; + private String mCpuAbiOverrideString; + private long mPVersionCode; + private int mPkgFlags; + private int mPrivateFlags; + private String mParentPackageName; + private List<String> mChildPackageNames; + private int mSharedUserId; + private String[] mUsesStaticLibraries; + private long[] mUsesStaticLibrariesVersions; + private String mVolumeUuid; + private SparseArray<PackageUserState> mUserStates = new SparseArray<>(); + + public PackageSettingBuilder setName(String name) { + this.mName = name; + return this; + } + + public PackageSettingBuilder setRealName(String realName) { + this.mRealName = realName; + return this; + } + + public PackageSettingBuilder setCodePath(String codePath) { + this.mCodePath = codePath; + return this; + } + + public PackageSettingBuilder setResourcePath(String resourcePath) { + this.mResourcePath = resourcePath; + return this; + } + + public PackageSettingBuilder setLegacyNativeLibraryPathString( + String legacyNativeLibraryPathString) { + this.mLegacyNativeLibraryPathString = legacyNativeLibraryPathString; + return this; + } + + public PackageSettingBuilder setPrimaryCpuAbiString(String primaryCpuAbiString) { + this.mPrimaryCpuAbiString = primaryCpuAbiString; + return this; + } + + public PackageSettingBuilder setSecondaryCpuAbiString(String secondaryCpuAbiString) { + this.mSecondaryCpuAbiString = secondaryCpuAbiString; + return this; + } + + public PackageSettingBuilder setCpuAbiOverrideString(String cpuAbiOverrideString) { + this.mCpuAbiOverrideString = cpuAbiOverrideString; + return this; + } + + public PackageSettingBuilder setPVersionCode(long pVersionCode) { + this.mPVersionCode = pVersionCode; + return this; + } + + public PackageSettingBuilder setPkgFlags(int pkgFlags) { + this.mPkgFlags = pkgFlags; + return this; + } + + public PackageSettingBuilder setPrivateFlags(int privateFlags) { + this.mPrivateFlags = privateFlags; + return this; + } + + public PackageSettingBuilder setParentPackageName(String parentPackageName) { + this.mParentPackageName = parentPackageName; + return this; + } + + public PackageSettingBuilder setChildPackageNames(List<String> childPackageNames) { + this.mChildPackageNames = childPackageNames; + return this; + } + + public PackageSettingBuilder setSharedUserId(int sharedUserId) { + this.mSharedUserId = sharedUserId; + return this; + } + + public PackageSettingBuilder setUsesStaticLibraries(String[] usesStaticLibraries) { + this.mUsesStaticLibraries = usesStaticLibraries; + return this; + } + + public PackageSettingBuilder setUsesStaticLibrariesVersions( + long[] usesStaticLibrariesVersions) { + this.mUsesStaticLibrariesVersions = usesStaticLibrariesVersions; + return this; + } + + public PackageSettingBuilder setVolumeUuid(String volumeUuid) { + this.mVolumeUuid = volumeUuid; + return this; + } + + public PackageSettingBuilder setInstantAppUserState(int userId, boolean isInstant) { + if (mUserStates.indexOfKey(userId) < 0) { + mUserStates.put(userId, new PackageUserState()); + } + mUserStates.get(userId).instantApp = isInstant; + return this; + } + + public PackageSetting build() { + final PackageSetting packageSetting = new PackageSetting(mName, mRealName, + new File(mCodePath), new File(mResourcePath), + mLegacyNativeLibraryPathString, mPrimaryCpuAbiString, mSecondaryCpuAbiString, + mCpuAbiOverrideString, mPVersionCode, mPkgFlags, mPrivateFlags, mParentPackageName, + mChildPackageNames, mSharedUserId, mUsesStaticLibraries, + mUsesStaticLibrariesVersions); + packageSetting.volumeUuid = this.mVolumeUuid; + for (int i = 0; i < mUserStates.size(); i++) { + packageSetting.setUserState(mUserStates.keyAt(i), mUserStates.valueAt(i)); + } + return packageSetting; + + } +} diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanRequestBuilder.java b/services/tests/servicestests/src/com/android/server/pm/ScanRequestBuilder.java new file mode 100644 index 000000000000..34a3f860496a --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/pm/ScanRequestBuilder.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm; + +import android.content.pm.PackageParser; +import android.os.UserHandle; + +class ScanRequestBuilder { + private final PackageParser.Package mPkg; + private PackageParser.Package mOldPkg; + private SharedUserSetting mSharedUserSetting; + private PackageSetting mPkgSetting; + private PackageSetting mDisabledPkgSetting; + private PackageSetting mOriginalPkgSetting; + private String mRealPkgName; + private int mParseFlags; + private int mScanFlags; + private UserHandle mUser; + private boolean mIsPlatformPackage; + + ScanRequestBuilder(PackageParser.Package pkg) { + this.mPkg = pkg; + } + + public ScanRequestBuilder setOldPkg(PackageParser.Package oldPkg) { + this.mOldPkg = oldPkg; + return this; + } + + public ScanRequestBuilder setSharedUserSetting(SharedUserSetting sharedUserSetting) { + this.mSharedUserSetting = sharedUserSetting; + return this; + } + + public ScanRequestBuilder setPkgSetting(PackageSetting pkgSetting) { + this.mPkgSetting = pkgSetting; + return this; + } + + public ScanRequestBuilder setDisabledPkgSetting(PackageSetting disabledPkgSetting) { + this.mDisabledPkgSetting = disabledPkgSetting; + return this; + } + + public ScanRequestBuilder setOriginalPkgSetting(PackageSetting originalPkgSetting) { + this.mOriginalPkgSetting = originalPkgSetting; + return this; + } + + public ScanRequestBuilder setRealPkgName(String realPkgName) { + this.mRealPkgName = realPkgName; + return this; + } + + public ScanRequestBuilder setParseFlags(int parseFlags) { + this.mParseFlags = parseFlags; + return this; + } + + public ScanRequestBuilder addParseFlag(int parseFlag) { + this.mParseFlags |= parseFlag; + return this; + } + + public ScanRequestBuilder setScanFlags(int scanFlags) { + this.mScanFlags = scanFlags; + return this; + } + + public ScanRequestBuilder addScanFlag(int scanFlag) { + this.mScanFlags |= scanFlag; + return this; + } + + public ScanRequestBuilder setUser(UserHandle user) { + this.mUser = user; + return this; + } + + public ScanRequestBuilder setIsPlatformPackage(boolean isPlatformPackage) { + this.mIsPlatformPackage = isPlatformPackage; + return this; + } + + PackageManagerService.ScanRequest build() { + return new PackageManagerService.ScanRequest( + mPkg, mSharedUserSetting, mOldPkg, mPkgSetting, mDisabledPkgSetting, + mOriginalPkgSetting, mRealPkgName, mParseFlags, mScanFlags, mIsPlatformPackage, + mUser); + } +} diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java new file mode 100644 index 000000000000..cc70ef89c75f --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java @@ -0,0 +1,520 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm; + +import static android.content.pm.SharedLibraryInfo.TYPE_DYNAMIC; +import static android.content.pm.SharedLibraryInfo.TYPE_STATIC; +import static android.content.pm.SharedLibraryInfo.VERSION_UNDEFINED; + +import static com.android.server.pm.PackageManagerService.SCAN_AS_FULL_APP; +import static com.android.server.pm.PackageManagerService.SCAN_AS_INSTANT_APP; +import static com.android.server.pm.PackageManagerService.SCAN_FIRST_BOOT_OR_UPGRADE; +import static com.android.server.pm.PackageManagerService.SCAN_NEW_INSTALL; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.hasItems; +import static org.hamcrest.Matchers.nullValue; +import static org.hamcrest.collection.IsArrayContainingInOrder.arrayContaining; +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertNotSame; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.Manifest; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageParser; +import android.content.pm.SharedLibraryInfo; +import android.os.Environment; +import android.os.UserHandle; +import android.os.UserManagerInternal; +import android.platform.test.annotations.Presubmit; + +import org.hamcrest.BaseMatcher; +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import java.io.File; + +@RunWith(MockitoJUnitRunner.class) +@Presubmit +public class ScanTests { + + private static final String DUMMY_PACKAGE_NAME = "some.app.to.test"; + + @Mock + PackageAbiHelper mMockPackageAbiHelper; + @Mock + UserManagerInternal mMockUserManager; + + @Before + public void setupDefaultUser() { + when(mMockUserManager.getUserIds()).thenReturn(new int[]{0}); + } + + @Test + public void newInstallSimpleAllNominal() throws Exception { + final PackageManagerService.ScanRequest scanRequest = + createBasicScanRequestBuilder(createBasicPackage(DUMMY_PACKAGE_NAME).build()) + .addScanFlag(PackageManagerService.SCAN_NEW_INSTALL) + .addScanFlag(PackageManagerService.SCAN_AS_FULL_APP) + .build(); + + + final PackageManagerService.ScanResult scanResult = executeScan(scanRequest); + + assertBasicPackageScanResult(scanResult, DUMMY_PACKAGE_NAME, false /*isInstant*/); + assertThat(scanResult.existingSettingCopied, is(false)); + verify(mMockPackageAbiHelper, never()).derivePackageAbi(any(PackageParser.Package.class), + anyString() /*abioverride*/, anyBoolean() /*extractNativeLibs*/); + verify(mMockPackageAbiHelper).setNativeLibraryPaths( + scanResult.pkgSetting.pkg, PackageManagerService.sAppLib32InstallDir); + } + + @Test + public void newInstallForAllUsers() throws Exception { + final int[] userIds = {0, 10, 11}; + when(mMockUserManager.getUserIds()).thenReturn(userIds); + + final PackageManagerService.ScanRequest scanRequest = + createBasicScanRequestBuilder(createBasicPackage(DUMMY_PACKAGE_NAME).build()) + .setRealPkgName(null) + .addScanFlag(PackageManagerService.SCAN_NEW_INSTALL) + .addScanFlag(PackageManagerService.SCAN_AS_FULL_APP) + .build(); + final PackageManagerService.ScanResult scanResult = executeScan(scanRequest); + + for (int uid : userIds) { + assertThat(scanResult.pkgSetting.readUserState(uid).installed, is(true)); + } + } + + @Test + public void installRealPackageName() throws Exception { + final PackageManagerService.ScanRequest scanRequest = + createBasicScanRequestBuilder(createBasicPackage(DUMMY_PACKAGE_NAME).build()) + .setRealPkgName("com.package.real") + .build(); + + final PackageManagerService.ScanResult scanResult = executeScan(scanRequest); + + assertThat(scanResult.pkgSetting.realName, is("com.package.real")); + + final PackageManagerService.ScanRequest scanRequestNoRealPkg = + createBasicScanRequestBuilder( + createBasicPackage(DUMMY_PACKAGE_NAME) + .setRealPackageName("com.package.real").build()) + .build(); + + final PackageManagerService.ScanResult scanResultNoReal = executeScan(scanRequestNoRealPkg); + assertThat(scanResultNoReal.pkgSetting.realName, nullValue()); + } + + @Test + public void updateSimpleNominal() throws Exception { + when(mMockUserManager.getUserIds()).thenReturn(new int[]{0}); + + final PackageSetting pkgSetting = createBasicPackageSettingBuilder(DUMMY_PACKAGE_NAME) + .setPrimaryCpuAbiString("primaryCpuAbi") + .setSecondaryCpuAbiString("secondaryCpuAbi") + .build(); + final PackageManagerService.ScanRequest scanRequest = + createBasicScanRequestBuilder(createBasicPackage(DUMMY_PACKAGE_NAME).build()) + .addScanFlag(PackageManagerService.SCAN_AS_FULL_APP) + .setPkgSetting(pkgSetting) + .build(); + + + final PackageManagerService.ScanResult scanResult = executeScan(scanRequest); + + assertThat(scanResult.existingSettingCopied, is(true)); + + // ensure we don't overwrite the existing pkgSetting, in case something post-scan fails + assertNotSame(pkgSetting, scanResult.pkgSetting); + + assertBasicPackageScanResult(scanResult, DUMMY_PACKAGE_NAME, false /*isInstant*/); + + assertThat(scanResult.pkgSetting.primaryCpuAbiString, is("primaryCpuAbi")); + assertThat(scanResult.pkgSetting.secondaryCpuAbiString, is("secondaryCpuAbi")); + assertThat(scanResult.pkgSetting.cpuAbiOverrideString, nullValue()); + + verify(mMockPackageAbiHelper, never()).derivePackageAbi(any(PackageParser.Package.class), + anyString() /*abioverride*/, anyBoolean() /*extractNativeLibs*/); + verify(mMockPackageAbiHelper).setNativeLibraryPaths( + scanResult.pkgSetting.pkg, PackageManagerService.sAppLib32InstallDir); + } + + @Test + public void updateInstantSimpleNominal() throws Exception { + when(mMockUserManager.getUserIds()).thenReturn(new int[]{0}); + + final PackageSetting existingPkgSetting = + createBasicPackageSettingBuilder(DUMMY_PACKAGE_NAME) + .setInstantAppUserState(0, true) + .build(); + + final PackageManagerService.ScanRequest scanRequest = + createBasicScanRequestBuilder(createBasicPackage(DUMMY_PACKAGE_NAME).build()) + .setPkgSetting(existingPkgSetting) + .build(); + + + final PackageManagerService.ScanResult scanResult = executeScan(scanRequest); + + assertBasicPackageScanResult(scanResult, DUMMY_PACKAGE_NAME, true /*isInstant*/); + } + + @Test + public void installStaticSharedLibrary() throws Exception { + final PackageParser.Package pkg = createBasicPackage("static.lib.pkg.123") + .setStaticSharedLib("static.lib", 123L) + .setManifestPackageName("static.lib.pkg") + .setVersionCodeMajor(1) + .setVersionCode(234) + .setBaseCodePath("/some/path.apk") + .addSplitCodePath("/some/other/path.apk") + .build(); + + final PackageManagerService.ScanRequest scanRequest = new ScanRequestBuilder( + pkg).setUser(UserHandle.of(0)).build(); + + + final PackageManagerService.ScanResult scanResult = executeScan(scanRequest); + + assertThat(scanResult.staticSharedLibraryInfo.getPackageName(), is("static.lib.pkg.123")); + assertThat(scanResult.staticSharedLibraryInfo.getName(), is("static.lib")); + assertThat(scanResult.staticSharedLibraryInfo.getLongVersion(), is(123L)); + assertThat(scanResult.staticSharedLibraryInfo.getType(), is(TYPE_STATIC)); + assertThat(scanResult.staticSharedLibraryInfo.getDeclaringPackage().getPackageName(), + is("static.lib.pkg")); + assertThat(scanResult.staticSharedLibraryInfo.getDeclaringPackage().getLongVersionCode(), + is(pkg.getLongVersionCode())); + assertThat(scanResult.staticSharedLibraryInfo.getAllCodePaths(), + hasItems("/some/path.apk", "/some/other/path.apk")); + assertThat(scanResult.staticSharedLibraryInfo.getDependencies(), nullValue()); + assertThat(scanResult.staticSharedLibraryInfo.getDependentPackages(), empty()); + } + + @Test + public void installDynamicLibraries() throws Exception { + final PackageParser.Package pkg = createBasicPackage("dynamic.lib.pkg") + .setManifestPackageName("dynamic.lib.pkg") + .addLibraryName("liba") + .addLibraryName("libb") + .setVersionCodeMajor(1) + .setVersionCode(234) + .setBaseCodePath("/some/path.apk") + .addSplitCodePath("/some/other/path.apk") + .build(); + + final PackageManagerService.ScanRequest scanRequest = + new ScanRequestBuilder(pkg).setUser(UserHandle.of(0)).build(); + + + final PackageManagerService.ScanResult scanResult = executeScan(scanRequest); + + final SharedLibraryInfo dynamicLib0 = scanResult.dynamicSharedLibraryInfos.get(0); + assertThat(dynamicLib0.getPackageName(), is("dynamic.lib.pkg")); + assertThat(dynamicLib0.getName(), is("liba")); + assertThat(dynamicLib0.getLongVersion(), is((long) VERSION_UNDEFINED)); + assertThat(dynamicLib0.getType(), is(TYPE_DYNAMIC)); + assertThat(dynamicLib0.getDeclaringPackage().getPackageName(), is("dynamic.lib.pkg")); + assertThat(dynamicLib0.getDeclaringPackage().getLongVersionCode(), + is(pkg.getLongVersionCode())); + assertThat(dynamicLib0.getAllCodePaths(), + hasItems("/some/path.apk", "/some/other/path.apk")); + assertThat(dynamicLib0.getDependencies(), nullValue()); + assertThat(dynamicLib0.getDependentPackages(), empty()); + + final SharedLibraryInfo dynamicLib1 = scanResult.dynamicSharedLibraryInfos.get(1); + assertThat(dynamicLib1.getPackageName(), is("dynamic.lib.pkg")); + assertThat(dynamicLib1.getName(), is("libb")); + assertThat(dynamicLib1.getLongVersion(), is((long) VERSION_UNDEFINED)); + assertThat(dynamicLib1.getType(), is(TYPE_DYNAMIC)); + assertThat(dynamicLib1.getDeclaringPackage().getPackageName(), is("dynamic.lib.pkg")); + assertThat(dynamicLib1.getDeclaringPackage().getLongVersionCode(), + is(pkg.getLongVersionCode())); + assertThat(dynamicLib1.getAllCodePaths(), + hasItems("/some/path.apk", "/some/other/path.apk")); + assertThat(dynamicLib1.getDependencies(), nullValue()); + assertThat(dynamicLib1.getDependentPackages(), empty()); + } + + @Test + public void volumeUuidChangesOnUpdate() throws Exception { + final PackageSetting pkgSetting = + createBasicPackageSettingBuilder(DUMMY_PACKAGE_NAME) + .setVolumeUuid("someUuid") + .build(); + + final PackageParser.Package basicPackage = createBasicPackage(DUMMY_PACKAGE_NAME) + .setApplicationInfoVolumeUuid("someNewUuid") + .build(); + + + final PackageManagerService.ScanResult scanResult = executeScan( + new ScanRequestBuilder(basicPackage).setPkgSetting(pkgSetting).build()); + + assertThat(scanResult.pkgSetting.volumeUuid, is("someNewUuid")); + } + + @Test + public void scanFirstBoot_derivesAbis() throws Exception { + final PackageSetting pkgSetting = + createBasicPackageSettingBuilder(DUMMY_PACKAGE_NAME).build(); + + final PackageParser.Package basicPackage = + createBasicPackage(DUMMY_PACKAGE_NAME) + .setCpuAbiOVerride("testOverride") + .build(); + + + executeScan(new ScanRequestBuilder(basicPackage) + .setPkgSetting(pkgSetting) + .addScanFlag(SCAN_FIRST_BOOT_OR_UPGRADE) + .build()); + + verify(mMockPackageAbiHelper).derivePackageAbi(basicPackage, "testOverride", true); + } + + @Test + public void scanWithOriginalPkgSetting_packageNameChanges() throws Exception { + final PackageSetting originalPkgSetting = + createBasicPackageSettingBuilder("original.package").build(); + + final PackageParser.Package basicPackage = + createBasicPackage(DUMMY_PACKAGE_NAME) + .build(); + + + final PackageManagerService.ScanResult result = + executeScan(new ScanRequestBuilder(basicPackage) + .setOriginalPkgSetting(originalPkgSetting) + .build()); + + assertThat(result.request.pkg.packageName, is("original.package")); + } + + @Test + public void updateInstant_changeToFull() throws Exception { + when(mMockUserManager.getUserIds()).thenReturn(new int[]{0}); + + final PackageSetting existingPkgSetting = + createBasicPackageSettingBuilder(DUMMY_PACKAGE_NAME) + .setInstantAppUserState(0, true) + .build(); + + final PackageManagerService.ScanRequest scanRequest = + createBasicScanRequestBuilder(createBasicPackage(DUMMY_PACKAGE_NAME).build()) + .setPkgSetting(existingPkgSetting) + .addScanFlag(SCAN_AS_FULL_APP) + .build(); + + + final PackageManagerService.ScanResult scanResult = executeScan(scanRequest); + + assertBasicPackageScanResult(scanResult, DUMMY_PACKAGE_NAME, false /*isInstant*/); + } + + @Test + public void updateFull_changeToInstant() throws Exception { + when(mMockUserManager.getUserIds()).thenReturn(new int[]{0}); + + final PackageSetting existingPkgSetting = + createBasicPackageSettingBuilder(DUMMY_PACKAGE_NAME) + .setInstantAppUserState(0, false) + .build(); + + final PackageManagerService.ScanRequest scanRequest = + createBasicScanRequestBuilder(createBasicPackage(DUMMY_PACKAGE_NAME).build()) + .setPkgSetting(existingPkgSetting) + .addScanFlag(SCAN_AS_INSTANT_APP) + .build(); + + + final PackageManagerService.ScanResult scanResult = executeScan(scanRequest); + + assertBasicPackageScanResult(scanResult, DUMMY_PACKAGE_NAME, true /*isInstant*/); + } + + @Test + public void updateSystemApp_applicationInfoFlagSet() throws Exception { + final PackageSetting existingPkgSetting = + createBasicPackageSettingBuilder(DUMMY_PACKAGE_NAME) + .setPkgFlags(ApplicationInfo.FLAG_SYSTEM) + .build(); + + final PackageManagerService.ScanRequest scanRequest = + createBasicScanRequestBuilder(createBasicPackage(DUMMY_PACKAGE_NAME).build()) + .setPkgSetting(existingPkgSetting) + .setDisabledPkgSetting(existingPkgSetting) + .addScanFlag(SCAN_NEW_INSTALL) + .build(); + + final PackageManagerService.ScanResult scanResult = executeScan(scanRequest); + + assertThat(scanResult.request.pkg.applicationInfo.flags, + hasFlag(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)); + } + + @Test + public void factoryTestFlagSet() throws Exception { + final PackageParser.Package basicPackage = createBasicPackage(DUMMY_PACKAGE_NAME) + .addPermissionRequest(Manifest.permission.FACTORY_TEST) + .build(); + + final PackageManagerService.ScanResult scanResult = PackageManagerService.scanPackageOnlyLI( + createBasicScanRequestBuilder(basicPackage).build(), + new PackageManagerService.Injector(mMockUserManager, mMockPackageAbiHelper), + true /*isUnderFactoryTest*/, + System.currentTimeMillis()); + + assertThat(scanResult.request.pkg.applicationInfo.flags, + hasFlag(ApplicationInfo.FLAG_FACTORY_TEST)); + } + + @Test + public void scanSystemApp_isOrphanedTrue() throws Exception { + final PackageParser.Package pkg = createBasicPackage(DUMMY_PACKAGE_NAME) + .addApplicationInfoFlag(ApplicationInfo.FLAG_SYSTEM) + .build(); + + final PackageManagerService.ScanRequest scanRequest = + createBasicScanRequestBuilder(pkg) + .build(); + + final PackageManagerService.ScanResult scanResult = executeScan(scanRequest); + + assertThat(scanResult.pkgSetting.isOrphaned, is(true)); + } + + private static Matcher<Integer> hasFlag(final int flag) { + return new BaseMatcher<Integer>() { + @Override public void describeTo(Description description) { + description.appendText("flags "); + } + + @Override public boolean matches(Object item) { + return ((int) item & flag) != 0; + } + + @Override + public void describeMismatch(Object item, Description mismatchDescription) { + mismatchDescription + .appendValue(item) + .appendText(" does not contain flag ") + .appendValue(flag); + } + }; + } + + private PackageManagerService.ScanResult executeScan( + PackageManagerService.ScanRequest scanRequest) throws PackageManagerException { + return PackageManagerService.scanPackageOnlyLI( + scanRequest, + new PackageManagerService.Injector(mMockUserManager, mMockPackageAbiHelper), + false /*isUnderFactoryTest*/, + System.currentTimeMillis()); + } + + private static String createResourcePath(String packageName) { + return "/data/app/" + packageName + "-randompath/base.apk"; + } + + private static String createCodePath(String packageName) { + return "/data/app/" + packageName + "-randompath"; + } + + private static PackageSettingBuilder createBasicPackageSettingBuilder(String packageName) { + return new PackageSettingBuilder() + .setName(packageName) + .setCodePath(createCodePath(packageName)) + .setResourcePath(createResourcePath(packageName)); + } + + private static ScanRequestBuilder createBasicScanRequestBuilder(PackageParser.Package pkg) { + return new ScanRequestBuilder(pkg) + .setUser(UserHandle.of(0)); + } + + + private static PackageBuilder createBasicPackage(String packageName) { + return new PackageBuilder(packageName) + .setCodePath("/data/tmp/randompath") + .setApplicationInfoCodePath(createCodePath(packageName)) + .setApplicationInfoResourcePath(createResourcePath(packageName)) + .setApplicationInfoVolumeUuid("volumeUuid") + .setBaseCodePath("/data/tmp/randompath/base.apk") + .addUsesStaticLibrary("some.static.library", 234L) + .addUsesStaticLibrary("some.other.static.library", 456L) + .setApplicationInfoNativeLibraryRootDir("/data/tmp/randompath/base.apk:/lib") + .setVersionCodeMajor(1) + .setVersionCode(2345); + } + + private static void assertBasicPackageScanResult( + PackageManagerService.ScanResult scanResult, String packageName, boolean isInstant) { + assertThat(scanResult.success, is(true)); + + final PackageSetting pkgSetting = scanResult.pkgSetting; + assertBasicPackageSetting(scanResult, packageName, isInstant, pkgSetting); + + final ApplicationInfo applicationInfo = pkgSetting.pkg.applicationInfo; + verifyBasicApplicationInfo(scanResult, applicationInfo); + + } + + private static void assertBasicPackageSetting(PackageManagerService.ScanResult scanResult, + String packageName, boolean isInstant, PackageSetting pkgSetting) { + assertThat(pkgSetting.pkg.packageName, is(packageName)); + assertThat(pkgSetting.getInstantApp(0), is(isInstant)); + assertThat(pkgSetting.usesStaticLibraries, + arrayContaining("some.static.library", "some.other.static.library")); + assertThat(pkgSetting.usesStaticLibrariesVersions, is(new long[]{234L, 456L})); + assertThat(pkgSetting.pkg, is(scanResult.request.pkg)); + assertThat(pkgSetting.pkg.mExtras, is(pkgSetting)); + assertThat(pkgSetting.legacyNativeLibraryPathString, + is("/data/tmp/randompath/base.apk:/lib")); + assertThat(pkgSetting.codePath, is(new File(createCodePath(packageName)))); + assertThat(pkgSetting.resourcePath, is(new File(createResourcePath(packageName)))); + assertThat(pkgSetting.versionCode, is(PackageInfo.composeLongVersionCode(1, 2345))); + } + + private static void verifyBasicApplicationInfo(PackageManagerService.ScanResult scanResult, + ApplicationInfo applicationInfo) { + assertThat(applicationInfo.processName, is(scanResult.request.pkg.packageName)); + + final int uid = applicationInfo.uid; + assertThat(UserHandle.getUserId(uid), is(UserHandle.USER_SYSTEM)); + + final String calculatedCredentialId = Environment.getDataUserCePackageDirectory( + applicationInfo.volumeUuid, UserHandle.USER_SYSTEM, + scanResult.request.pkg.packageName).getAbsolutePath(); + assertThat(applicationInfo.credentialProtectedDataDir, is(calculatedCredentialId)); + assertThat(applicationInfo.dataDir, is(applicationInfo.credentialProtectedDataDir)); + } +} diff --git a/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java b/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java index f5002ace6690..3b336eb7aec9 100644 --- a/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java +++ b/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java @@ -7,10 +7,12 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -159,4 +161,25 @@ public class PinnedSliceStateTest extends UiServiceTestCase { verify(mSliceService).removePinnedSlice(eq(TEST_URI)); assertFalse(mPinnedSliceManager.hasPinOrListener()); } + + @Test + public void testPinFailed() throws Exception { + // Throw exception when trying to pin + doAnswer(invocation -> { + throw new Exception("Pin failed"); + }).when(mIContentProvider).call( + anyString(), anyString(), anyString(), eq(null), any()); + + TestableLooper.get(this).processAllMessages(); + + // When pinned for the first time, a pinned message should be sent. + mPinnedSliceManager.pin("pkg", FIRST_SPECS, mToken); + TestableLooper.get(this).processAllMessages(); + + verify(mIContentProvider).call(anyString(), anyString(), eq(SliceProvider.METHOD_PIN), + eq(null), argThat(b -> { + assertEquals(TEST_URI, b.getParcelable(SliceProvider.EXTRA_BIND_URI)); + return true; + })); + } } 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 deca57e98f7c..7b252cbfc0b0 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -31,11 +31,15 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; +import static com.android.server.wm.ActivityRecord.FINISH_RESULT_CANCELLED; +import static com.android.server.wm.ActivityRecord.FINISH_RESULT_REMOVED; +import static com.android.server.wm.ActivityRecord.FINISH_RESULT_REQUESTED; import static com.android.server.wm.ActivityStack.ActivityState.INITIALIZING; 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; @@ -62,6 +66,8 @@ 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; @@ -200,7 +206,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(); @@ -626,6 +632,125 @@ public class ActivityRecordTests extends ActivityTestsBase { 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()); + } + + /** + * Verify that activity finish request is not performed if activity is finishing or is in + * incorrect state. + */ + @Test + public void testFinishActivityLocked_cancelled() { + // Mark activity as finishing + mActivity.finishing = true; + assertEquals("Duplicate finish request must be ignored", FINISH_RESULT_CANCELLED, + mActivity.finishActivityLocked(0 /* resultCode */, null /* resultData */, "test", + false /* oomAdj */)); + assertTrue(mActivity.finishing); + assertTrue(mActivity.isInStackLocked()); + + // Remove activity from task + mActivity.finishing = false; + mActivity.setTask(null); + assertEquals("Activity outside of task/stack cannot be finished", FINISH_RESULT_CANCELLED, + mActivity.finishActivityLocked(0 /* resultCode */, null /* resultData */, "test", + false /* oomAdj */)); + assertFalse(mActivity.finishing); + } + + /** + * Verify that activity finish request is requested, but not executed immediately if activity is + * not ready yet. + */ + @Test + public void testFinishActivityLocked_requested() { + mActivity.finishing = false; + assertEquals("Currently resumed activity be paused removal", FINISH_RESULT_REQUESTED, + mActivity.finishActivityLocked(0 /* resultCode */, null /* resultData */, "test", + false /* oomAdj */)); + assertTrue(mActivity.finishing); + assertTrue(mActivity.isInStackLocked()); + + // First request to finish activity must schedule a "destroy" request to the client. + // Activity must be removed from history after the client reports back or after timeout. + mActivity.finishing = false; + mActivity.setState(STOPPED, "test"); + assertEquals("Activity outside of task/stack cannot be finished", FINISH_RESULT_REQUESTED, + mActivity.finishActivityLocked(0 /* resultCode */, null /* resultData */, "test", + false /* oomAdj */)); + assertTrue(mActivity.finishing); + assertTrue(mActivity.isInStackLocked()); + } + + /** + * Verify that activity finish request removes activity immediately if it's ready. + */ + @Test + public void testFinishActivityLocked_removed() { + // Prepare the activity record to be ready for immediate removal. It should be invisible and + // have no process. Otherwise, request to finish it will send a message to client first. + mActivity.setState(STOPPED, "test"); + mActivity.visible = false; + mActivity.nowVisible = false; + // Set process to 'null' to allow immediate removal, but don't call mActivity.setProcess() - + // this will cause NPE when updating task's process. + mActivity.app = null; + assertEquals("Activity outside of task/stack cannot be finished", FINISH_RESULT_REMOVED, + mActivity.finishActivityLocked(0 /* resultCode */, null /* resultData */, "test", + false /* oomAdj */)); + assertTrue(mActivity.finishing); + assertFalse(mActivity.isInStackLocked()); + } + /** 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/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/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java index 26cd63c62cc1..cd292b2494ff 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java @@ -25,6 +25,7 @@ import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE; import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeast; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMoreInteractions; @@ -35,10 +36,12 @@ import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_O 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.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import android.os.Binder; @@ -79,6 +82,7 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { // Hold the lock to protect the stubbing from being accessed by other threads. spyOn(mWm.mRoot); doNothing().when(mWm.mRoot).performSurfacePlacement(anyBoolean()); + doReturn(mDisplayContent).when(mWm.mRoot).getDisplayContent(anyInt()); } when(mMockRunner.asBinder()).thenReturn(new Binder()); mController = new RecentsAnimationController(mWm, mMockRunner, mAnimationCallbacks, @@ -135,7 +139,7 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { hiddenAppWindow.setHidden(true); mDisplayContent.getConfiguration().windowConfiguration.setRotation( mDisplayContent.getRotation()); - mController.initialize(mDisplayContent, ACTIVITY_TYPE_HOME, new SparseBooleanArray()); + mController.initialize(ACTIVITY_TYPE_HOME, new SparseBooleanArray()); // Ensure that we are animating the target activity as well assertTrue(mController.isAnimatingTask(homeAppWindow.getTask())); @@ -144,7 +148,7 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { } @Test - public void testCancelAnimationWithScreenShot() throws Exception { + public void testDeferCancelAnimation() throws Exception { mWm.setRecentsAnimationController(mController); final AppWindowToken appWindow = createAppWindowToken(mDisplayContent, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); @@ -156,8 +160,31 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { mController.addAnimation(appWindow.getTask(), false /* isRecentTaskInvisible */); assertTrue(mController.isAnimatingTask(appWindow.getTask())); - mController.setCancelWithDeferredScreenshotLocked(true); - mController.cancelAnimationWithScreenShot(); + mController.setDeferredCancel(true /* deferred */, false /* screenshot */); + mController.cancelAnimationWithScreenshot(false /* screenshot */); + verify(mMockRunner).onAnimationCanceled(false /* deferredWithScreenshot */); + assertNull(mController.mRecentScreenshotAnimator); + + // Simulate the app transition finishing + mController.mAppTransitionListener.onAppTransitionStartingLocked(0, 0, 0, 0); + verify(mAnimationCallbacks).onAnimationFinished(REORDER_KEEP_IN_PLACE, true, false); + } + + @Test + public void testDeferCancelAnimationWithScreenShot() throws Exception { + mWm.setRecentsAnimationController(mController); + final AppWindowToken appWindow = createAppWindowToken(mDisplayContent, + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); + final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION, appWindow, "win1"); + appWindow.addWindow(win1); + assertEquals(appWindow.getTask().getTopVisibleAppToken(), appWindow); + assertEquals(appWindow.findMainWindow(), win1); + + mController.addAnimation(appWindow.getTask(), false /* isRecentTaskInvisible */); + assertTrue(mController.isAnimatingTask(appWindow.getTask())); + + mController.setDeferredCancel(true /* deferred */, true /* screenshot */); + mController.cancelAnimationWithScreenshot(true /* screenshot */); verify(mMockRunner).onAnimationCanceled(true /* deferredWithScreenshot */); assertNotNull(mController.mRecentScreenshotAnimator); assertTrue(mController.mRecentScreenshotAnimator.isAnimating()); @@ -185,7 +212,7 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { // Assume appWindow transition should animate when no // IRecentsAnimationController#setCancelWithDeferredScreenshot called. - assertFalse(mController.shouldCancelWithDeferredScreenshot()); + assertFalse(mController.shouldDeferCancelWithScreenshot()); assertTrue(appWindow.shouldAnimate(TRANSIT_ACTIVITY_CLOSE)); } diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java index 9630b7d46e3c..0e119e3cc375 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java @@ -274,12 +274,13 @@ public class RecentsAnimationTest extends ActivityTestsBase { // Assume recents animation already started, set a state that cancel recents animation // with screenshot. - doReturn(true).when(mRecentsAnimationController).shouldCancelWithDeferredScreenshot(); + doReturn(true).when(mRecentsAnimationController).shouldDeferCancelUntilNextTransition(); + doReturn(true).when(mRecentsAnimationController).shouldDeferCancelWithScreenshot(); // Start another fullscreen activity. fullscreenStack2.moveToFront("Activity start"); - // Ensure that the recents animation was canceled by cancelOnNextTransitionStart(). - verify(mRecentsAnimationController, times(1)).cancelOnNextTransitionStart(); + // Ensure that the recents animation was canceled by setCancelOnNextTransitionStart(). + verify(mRecentsAnimationController, times(1)).setCancelOnNextTransitionStart(); } @Test @@ -315,7 +316,7 @@ public class RecentsAnimationTest extends ActivityTestsBase { // Ensure that the recents animation was NOT canceled verify(mService.mWindowManager, times(0)).cancelRecentsAnimationSynchronously( eq(REORDER_KEEP_IN_PLACE), any()); - verify(mRecentsAnimationController, times(0)).cancelOnNextTransitionStart(); + verify(mRecentsAnimationController, times(0)).setCancelOnNextTransitionStart(); } @Test 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/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/soundtrigger/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java index 94352b21db87..99337565e128 100644 --- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java +++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java @@ -104,8 +104,8 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { // This is an indirect indication of the microphone being open in some other application. private boolean mServiceDisabled = false; - // Whether we have ANY recognition (keyphrase or generic) running. - private boolean mRecognitionRunning = false; + // Whether ANY recognition (keyphrase or generic) has been requested. + private boolean mRecognitionRequested = false; private PowerSaveModeListener mPowerSaveModeListener; @@ -252,11 +252,6 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { } } - // Initialize power save, call active state monitoring logic. - if (!mRecognitionRunning) { - initializeTelephonyAndPowerStateListeners(); - } - // If the existing SoundModel is different (for the same UUID for Generic and same // keyphrase ID for voice), ensure that it is unloaded and stopped before proceeding. // This works for both keyphrase and generic models. This logic also ensures that a @@ -326,8 +321,16 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { modelData.setRecognitionConfig(recognitionConfig); modelData.setSoundModel(soundModel); - return startRecognitionLocked(modelData, + int status = startRecognitionLocked(modelData, false /* Don't notify for synchronous calls */); + + // Initialize power save, call active state monitoring logic. + if (status == STATUS_OK && !mRecognitionRequested) { + initializeTelephonyAndPowerStateListeners(); + mRecognitionRequested = true; + } + + return status; } } @@ -450,7 +453,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { modelData.clearCallback(); modelData.setRecognitionConfig(null); - if (!computeRecognitionRunningLocked()) { + if (!computeRecognitionRequestedLocked()) { internalClearGlobalStateLocked(); } @@ -1196,20 +1199,20 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { } // Computes whether we have any recognition running at all (voice or generic). Sets - // the mRecognitionRunning variable with the result. - private boolean computeRecognitionRunningLocked() { + // the mRecognitionRequested variable with the result. + private boolean computeRecognitionRequestedLocked() { if (mModuleProperties == null || mModule == null) { - mRecognitionRunning = false; - return mRecognitionRunning; + mRecognitionRequested = false; + return mRecognitionRequested; } for (ModelData modelData : mModelDataMap.values()) { - if (modelData.isModelStarted()) { - mRecognitionRunning = true; - return mRecognitionRunning; + if (modelData.isRequested()) { + mRecognitionRequested = true; + return mRecognitionRequested; } } - mRecognitionRunning = false; - return mRecognitionRunning; + mRecognitionRequested = false; + return mRecognitionRequested; } // This class encapsulates the callbacks, state, handles and any other information that 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/apps/test/Android.bp b/startop/apps/test/Android.bp index 7a1678ac9a5a..a4906d7b4cd3 100644 --- a/startop/apps/test/Android.bp +++ b/startop/apps/test/Android.bp @@ -23,4 +23,5 @@ android_app { "src/FrameLayoutInflationActivity.java", "src/TextViewInflationActivity.java", ], + platform_apis: true, } 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..a335b30da8a5 100755 --- a/startop/scripts/iorap/compiler.py +++ b/startop/scripts/iorap/compiler.py @@ -23,22 +23,27 @@ # $> pip3 install --user protobuf sqlalchemy sqlite3 # -import collections import optparse import os import re import sys +import tempfile +from pathlib import Path +from typing import Iterable, Optional, List -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 +sys.path.append(parent_dir_name) +from trace_analyzer.lib.trace2db import Trace2Db, MmFilemapAddToPageCache, \ + RawFtraceEntry +import lib.cmd_utils as cmd_utils _PAGE_SIZE = 4096 # adb shell getconf PAGESIZE ## size of a memory page in bytes. +ANDROID_BUILD_TOP = Path(parent_dir_name).parents[3] +TRACECONV_BIN = ANDROID_BUILD_TOP.joinpath( + 'external/perfetto/tools/traceconv') class PageRun: """ @@ -165,9 +170,79 @@ 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 transform_perfetto_trace_to_systrace(path_to_perfetto_trace: str, + path_to_tmp_systrace: str) -> None: + """ Transforms the systrace file from perfetto trace. """ + cmd_utils.run_command_nofail([str(TRACECONV_BIN), + 'systrace', + path_to_perfetto_trace, + path_to_tmp_systrace]) + + +def run(sql_db_path:str, + trace_file:str, + trace_duration:Optional[int], + output_file:str, + inode_table:str, + filter:List[str]) -> int: + trace2db = Trace2Db(sql_db_path) + # Speed optimization: Skip any entries that aren't mm_filemap_add_to_pagecache. + trace2db.set_raw_ftrace_entry_filter(\ + lambda entry: entry['function'] == 'mm_filemap_add_to_page_cache') + # TODO: parse multiple trace files here. + parse_count = trace2db.parse_file_into_db(trace_file) + + mm_filemap_add_to_page_cache_rows = query_add_to_page_cache(trace2db, + 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) + print("DONE. Converted %d entries" %(len(page_runs))) + + # TODO: flags to select optimizations. + optimized_page_runs = optimize_page_runs(page_runs) + print("DONE. Optimized down to %d entries" %(len(optimized_page_runs))) + + print("Build protobuf...") + trace_file = build_protobuf(optimized_page_runs, inode_table, filter) + + print("Write protobuf to file...") + output_file = open(output_file, 'wb') + output_file.write(trace_file.SerializeToString()) + output_file.close() + + print("DONE") + + # TODO: Silent running mode [no output except on error] for build runs. + + return 0 def main(argv): parser = optparse.OptionParser(usage="Usage: %prog [options]", description="Compile systrace file into TraceFile.pb") @@ -175,6 +250,9 @@ def main(argv): help='Read cached inode data from a file saved earlier with pagecache.py -d') parser.add_option('-t', dest='trace_file', metavar='FILE', help='Path to systrace file (trace.html) that will be parsed') + parser.add_option('--perfetto-trace', dest='perfetto_trace_file', + metavar='FILE', + help='Path to perfetto trace that will be parsed') parser.add_option('--db', dest='sql_db', metavar='FILE', help='Path to intermediate sqlite3 database [default: in-memory].') @@ -188,57 +266,51 @@ 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. if not options.inode_data_file: parser.error("-i is required") - if not options.trace_file: - parser.error("-t is required") + if not options.trace_file and not options.perfetto_trace_file: + parser.error("one of -t or --perfetto-trace is required") + if options.trace_file and options.perfetto_trace_file: + parser.error("please enter either -t or --perfetto-trace, not both") if not options.output_file: parser.error("-o is required") if options.launch_lock: print("INFO: Launch lock flag (-l) enabled; filtering all events not inside launch_lock.") - inode_table = Inode2Filename.new_from_filename(options.inode_data_file) - trace_file = open(options.trace_file) - sql_db_path = ":memory:" if options.sql_db: sql_db_path = options.sql_db - trace2db = Trace2Db(sql_db_path) - # Speed optimization: Skip any entries that aren't mm_filemap_add_to_pagecache. - trace2db.set_raw_ftrace_entry_filter(\ - lambda entry: entry['function'] == 'mm_filemap_add_to_page_cache') - # 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) - 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) - print("DONE. Converted %d entries" %(len(page_runs))) - - # TODO: flags to select optimizations. - optimized_page_runs = optimize_page_runs(page_runs) - print("DONE. Optimized down to %d entries" %(len(optimized_page_runs))) - - print("Build protobuf...") - trace_file = build_protobuf(optimized_page_runs, inode_table, options.filter) - - print("Write protobuf to file...") - output_file = open(options.output_file, 'wb') - output_file.write(trace_file.SerializeToString()) - output_file.close() - - print("DONE") - - # TODO: Silent running mode [no output except on error] for build runs. - - return 0 - -sys.exit(main(sys.argv)) + # if the input is systrace + if options.trace_file: + return run(sql_db_path, + options.trace_file, + options.trace_duration, + options.output_file, + inode_table, + options.filter) + + # if the input is perfetto trace + # TODO python 3.7 switch to using nullcontext + with tempfile.NamedTemporaryFile() as trace_file: + transform_perfetto_trace_to_systrace(options.perfetto_trace_file, + trace_file.name) + return run(sql_db_path, + options.trace_file, + options.trace_duration, + options.output_file, + inode_table, + options.filter) + +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..b5d28b03a8d5 --- /dev/null +++ b/startop/scripts/iorap/compiler_test.py @@ -0,0 +1,78 @@ +#!/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] +PERFETTO_TRACE = os.path.join(DIR, + 'test_fixtures/compiler/common_perfetto_trace.pb') + +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() + +### Unit tests - testing compiler code directly +def test_transform_perfetto_trace_to_systrace(tmpdir): + expected = os.path.join(DIR, + 'test_fixtures/compiler/test_result_systrace') + output = tmpdir.mkdir('compiler').join('tmp_systrace') + + compiler.transform_perfetto_trace_to_systrace(PERFETTO_TRACE, str(output)) + + with open(output, 'rb') as f1, open(expected, 'rb') as f2: + assert f1.read() == f2.read() + +### Functional tests - calls 'compiler.py --args...' +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_perfetto_trace.pb b/startop/scripts/iorap/test_fixtures/compiler/common_perfetto_trace.pb Binary files differnew file mode 100644 index 000000000000..a47ad3d5d9ec --- /dev/null +++ b/startop/scripts/iorap/test_fixtures/compiler/common_perfetto_trace.pb 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_systrace b/startop/scripts/iorap/test_fixtures/compiler/test_result_systrace new file mode 100644 index 000000000000..f731e73ba9b2 --- /dev/null +++ b/startop/scripts/iorap/test_fixtures/compiler/test_result_systrace @@ -0,0 +1,748 @@ +TRACE: +# tracer: nop +# +# entries-in-buffer/entries-written: 30624/30624 #P:4 +# +# _-----=> irqs-off +# / _----=> need-resched +# | / _---=> hardirq/softirq +# || / _--=> preempt-depth +# ||| / delay +# TASK-PID TGID CPU# |||| TIMESTAMP FUNCTION +# | | | | |||| | | + <unknown>-27388 (-----) [004] .... 1920260.530929: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1461937 ofs=9535488 + <unknown>-27388 (-----) [005] .... 1920260.532161: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1344589 ofs=9474048 + <unknown>-27388 (-----) [005] .... 1920260.532183: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1153671 ofs=9478144 + <unknown>-27388 (-----) [005] .... 1920260.532184: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1219563 ofs=9482240 + <unknown>-27388 (-----) [005] .... 1920260.532185: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1083162 ofs=9486336 + <unknown>-27388 (-----) [005] .... 1920260.532185: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1147318 ofs=9490432 + <unknown>-27388 (-----) [005] .... 1920260.532186: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1333594 ofs=9494528 + <unknown>-27388 (-----) [005] .... 1920260.532186: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1375715 ofs=9498624 + <unknown>-27388 (-----) [005] .... 1920260.532186: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1184831 ofs=9502720 + <unknown>-27388 (-----) [005] .... 1920260.532187: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1241653 ofs=9506816 + <unknown>-27388 (-----) [005] .... 1920260.532187: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1134975 ofs=9510912 + <unknown>-27388 (-----) [005] .... 1920260.532190: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1145772 ofs=9515008 + <unknown>-27388 (-----) [005] .... 1920260.532190: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1090457 ofs=9519104 + <unknown>-27388 (-----) [005] .... 1920260.532190: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1137942 ofs=9523200 + <unknown>-27388 (-----) [005] .... 1920260.532191: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1130123 ofs=9527296 + <unknown>-27388 (-----) [005] .... 1920260.532191: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1208783 ofs=9531392 + <unknown>-27388 (-----) [005] .... 1920260.532192: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1294989 ofs=9539584 + <unknown>-27388 (-----) [005] .... 1920260.532206: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1163979 ofs=9543680 + <unknown>-27388 (-----) [005] .... 1920260.532206: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1350628 ofs=9547776 + <unknown>-27388 (-----) [005] .... 1920260.532206: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1386717 ofs=9551872 + <unknown>-27388 (-----) [005] .... 1920260.532207: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1316148 ofs=9555968 + <unknown>-27388 (-----) [005] .... 1920260.532208: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1316419 ofs=9560064 + <unknown>-27388 (-----) [005] .... 1920260.532208: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1149076 ofs=9564160 + <unknown>-27388 (-----) [005] .... 1920260.532209: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1372772 ofs=9568256 + <unknown>-27388 (-----) [005] .... 1920260.532209: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1116389 ofs=9572352 + <unknown>-27388 (-----) [005] .... 1920260.532211: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1325458 ofs=9576448 + <unknown>-27388 (-----) [005] .... 1920260.532211: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1195423 ofs=9580544 + <unknown>-27388 (-----) [005] .... 1920260.532211: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1250964 ofs=9584640 + <unknown>-27388 (-----) [005] .... 1920260.532212: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1196027 ofs=9588736 + <unknown>-27388 (-----) [005] .... 1920260.532212: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1354059 ofs=9592832 + <unknown>-27388 (-----) [005] .... 1920260.532213: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1264649 ofs=9596928 + <unknown>-27388 (-----) [005] .... 1920260.532213: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1245285 ofs=9601024 + <unknown>-27388 (-----) [005] .... 1920260.535119: mm_filemap_add_to_page_cache: dev 0:64768 ino 1416 page=0000000000000000 pfn=1411552 ofs=44244992 + <unknown>-27388 (-----) [005] .... 1920260.535129: mm_filemap_add_to_page_cache: dev 0:3 ino 0 page=0000000000000000 pfn=1483081 ofs=433524736 + <unknown>-27388 (-----) [004] .... 1920260.536144: mm_filemap_add_to_page_cache: dev 0:3 ino 0 page=0000000000000000 pfn=1276173 ofs=438185984 + <unknown>-27388 (-----) [004] .... 1920260.536462: mm_filemap_add_to_page_cache: dev 0:64768 ino 1416 page=0000000000000000 pfn=1174575 ofs=44249088 + <unknown>-27388 (-----) [004] .... 1920260.536464: mm_filemap_add_to_page_cache: dev 0:64768 ino 1416 page=0000000000000000 pfn=1126294 ofs=44253184 + <unknown>-27388 (-----) [004] .... 1920260.536464: mm_filemap_add_to_page_cache: dev 0:64768 ino 1416 page=0000000000000000 pfn=1248232 ofs=44257280 + <unknown>-27388 (-----) [004] .... 1920260.537065: mm_filemap_add_to_page_cache: dev 0:64768 ino 1416 page=0000000000000000 pfn=1332993 ofs=44240896 + <unknown>-27388 (-----) [006] .... 1920260.537646: mm_filemap_add_to_page_cache: dev 0:64768 ino 1416 page=0000000000000000 pfn=1153343 ofs=44400640 + <unknown>-27388 (-----) [005] .... 1920260.538777: mm_filemap_add_to_page_cache: dev 0:64768 ino 1416 page=0000000000000000 pfn=1358397 ofs=44474368 + <unknown>-12683 (-----) [006] .... 1920260.560094: mm_filemap_add_to_page_cache: dev 0:64771 ino 286 page=0000000000000000 pfn=1426577 ofs=0 + <unknown>-12683 (-----) [006] .... 1920260.560105: mm_filemap_add_to_page_cache: dev 0:64771 ino 1 page=0000000000000000 pfn=1117587 ofs=1171456 + <unknown>-12683 (-----) [006] .... 1920260.561199: mm_filemap_add_to_page_cache: dev 0:64771 ino 286 page=0000000000000000 pfn=1099987 ofs=4096 + <unknown>-12683 (-----) [006] .... 1920260.561411: mm_filemap_add_to_page_cache: dev 0:64771 ino 286 page=0000000000000000 pfn=1099910 ofs=16384 + <unknown>-12683 (-----) [006] .... 1920260.561598: mm_filemap_add_to_page_cache: dev 0:64771 ino 286 page=0000000000000000 pfn=1099905 ofs=20480 + <unknown>-12683 (-----) [006] .... 1920260.561758: mm_filemap_add_to_page_cache: dev 0:64771 ino 286 page=0000000000000000 pfn=1099883 ofs=32768 + <unknown>-12683 (-----) [006] .... 1920260.562088: mm_filemap_add_to_page_cache: dev 0:64771 ino 286 page=0000000000000000 pfn=1099809 ofs=36864 + <unknown>-12683 (-----) [006] .... 1920260.562325: mm_filemap_add_to_page_cache: dev 0:64771 ino 286 page=0000000000000000 pfn=1099803 ofs=98304 + <unknown>-12683 (-----) [006] .... 1920260.562516: mm_filemap_add_to_page_cache: dev 0:64771 ino 286 page=0000000000000000 pfn=1099795 ofs=102400 + <unknown>-12683 (-----) [006] .... 1920260.563094: mm_filemap_add_to_page_cache: dev 0:64768 ino 1523 page=0000000000000000 pfn=1107649 ofs=12288 + <unknown>-12683 (-----) [006] .... 1920260.563105: mm_filemap_add_to_page_cache: dev 0:64768 ino 1523 page=0000000000000000 pfn=1269029 ofs=16384 + <unknown>-12683 (-----) [006] .... 1920260.563785: mm_filemap_add_to_page_cache: dev 0:64768 ino 1242 page=0000000000000000 pfn=1451096 ofs=8192 + <unknown>-12683 (-----) [006] .... 1920260.563790: mm_filemap_add_to_page_cache: dev 0:64768 ino 1242 page=0000000000000000 pfn=1301480 ofs=12288 + <unknown>-12683 (-----) [006] .... 1920260.563790: mm_filemap_add_to_page_cache: dev 0:64768 ino 1242 page=0000000000000000 pfn=1314353 ofs=16384 + <unknown>-12683 (-----) [006] .... 1920260.563791: mm_filemap_add_to_page_cache: dev 0:64768 ino 1242 page=0000000000000000 pfn=1216744 ofs=24576 + <unknown>-12683 (-----) [006] .... 1920260.564309: mm_filemap_add_to_page_cache: dev 0:64771 ino 286 page=0000000000000000 pfn=1099787 ofs=49152 + <unknown>-12683 (-----) [006] .... 1920260.564514: mm_filemap_add_to_page_cache: dev 0:64771 ino 286 page=0000000000000000 pfn=1099778 ofs=53248 + <unknown>-12683 (-----) [005] .... 1920260.564756: mm_filemap_add_to_page_cache: dev 0:64771 ino 286 page=0000000000000000 pfn=1148849 ofs=114688 + <unknown>-12683 (-----) [005] .... 1920260.564973: mm_filemap_add_to_page_cache: dev 0:64771 ino 286 page=0000000000000000 pfn=1164731 ofs=118784 + <unknown>-12683 (-----) [005] .... 1920260.565000: mm_filemap_add_to_page_cache: dev 0:2053 ino 26 page=0000000000000000 pfn=1170255 ofs=0 + <unknown>-12683 (-----) [005] .... 1920260.565003: mm_filemap_add_to_page_cache: dev 0:2053 ino 26 page=0000000000000000 pfn=1181043 ofs=4096 + <unknown>-12683 (-----) [005] .... 1920260.565004: mm_filemap_add_to_page_cache: dev 0:2053 ino 26 page=0000000000000000 pfn=1296004 ofs=8192 + <unknown>-12683 (-----) [005] .... 1920260.565004: mm_filemap_add_to_page_cache: dev 0:2053 ino 26 page=0000000000000000 pfn=1102004 ofs=12288 + <unknown>-12683 (-----) [005] .... 1920260.565626: mm_filemap_add_to_page_cache: dev 0:3 ino 0 page=0000000000000000 pfn=1351232 ofs=470597632 + <unknown>-12683 (-----) [005] .... 1920260.565982: mm_filemap_add_to_page_cache: dev 0:64771 ino 1 page=0000000000000000 pfn=1391336 ofs=40210432 + <unknown>-12683 (-----) [005] .... 1920260.565985: mm_filemap_add_to_page_cache: dev 0:64771 ino 2 page=0000000000000000 pfn=1267536 ofs=12668928 + <unknown>-27388 (-----) [007] .... 1920260.566082: mm_filemap_add_to_page_cache: dev 0:64768 ino 1416 page=0000000000000000 pfn=1256752 ofs=43921408 + <unknown>-12683 (-----) [005] .... 1920260.566516: mm_filemap_add_to_page_cache: dev 0:64771 ino 1 page=0000000000000000 pfn=1110966 ofs=176226304 + <unknown>-12683 (-----) [005] .... 1920260.566519: mm_filemap_add_to_page_cache: dev 0:64771 ino 2 page=0000000000000000 pfn=1060586 ofs=12967936 + <unknown>-12683 (-----) [004] .... 1920260.567773: mm_filemap_add_to_page_cache: dev 0:64771 ino 1 page=0000000000000000 pfn=1117234 ofs=421888 + <unknown>-12683 (-----) [005] .... 1920260.568604: mm_filemap_add_to_page_cache: dev 0:64771 ino 1 page=0000000000000000 pfn=1210571 ofs=430080 + <unknown>-12683 (-----) [005] .... 1920260.568887: mm_filemap_add_to_page_cache: dev 0:64771 ino 105 page=0000000000000000 pfn=1055640 ofs=0 + <unknown>-12683 (-----) [005] .... 1920260.568908: mm_filemap_add_to_page_cache: dev 0:64771 ino 73 page=0000000000000000 pfn=1142694 ofs=0 + <unknown>-12683 (-----) [005] .... 1920260.568910: mm_filemap_add_to_page_cache: dev 0:64771 ino 1 page=0000000000000000 pfn=1060788 ofs=299008 + <unknown>-12683 (-----) [005] .... 1920260.569418: mm_filemap_add_to_page_cache: dev 0:64771 ino 73 page=0000000000000000 pfn=1085046 ofs=4096 + <unknown>-12683 (-----) [005] .... 1920260.569640: mm_filemap_add_to_page_cache: dev 0:64771 ino 73 page=0000000000000000 pfn=1057135 ofs=8192 + <unknown>-12683 (-----) [005] .... 1920260.569833: mm_filemap_add_to_page_cache: dev 0:64771 ino 1 page=0000000000000000 pfn=1058976 ofs=19406848 + <unknown>-12683 (-----) [005] .... 1920260.569835: mm_filemap_add_to_page_cache: dev 0:64771 ino 2 page=0000000000000000 pfn=1477947 ofs=10526720 + <unknown>-12683 (-----) [005] .... 1920260.572285: mm_filemap_add_to_page_cache: dev 0:64768 ino 1565 page=0000000000000000 pfn=1237492 ofs=299008 + <unknown>-12683 (-----) [005] .... 1920260.572297: mm_filemap_add_to_page_cache: dev 0:64768 ino 1565 page=0000000000000000 pfn=1264914 ofs=339968 + <unknown>-12683 (-----) [005] .... 1920260.572314: mm_filemap_add_to_page_cache: dev 0:64768 ino 1565 page=0000000000000000 pfn=1434748 ofs=348160 + <unknown>-12683 (-----) [005] .... 1920260.572316: mm_filemap_add_to_page_cache: dev 0:64768 ino 1565 page=0000000000000000 pfn=1372959 ofs=352256 + <unknown>-12683 (-----) [005] .... 1920260.572317: mm_filemap_add_to_page_cache: dev 0:64768 ino 1565 page=0000000000000000 pfn=1258955 ofs=356352 + <unknown>-12683 (-----) [005] .... 1920260.572317: mm_filemap_add_to_page_cache: dev 0:64768 ino 1565 page=0000000000000000 pfn=1113420 ofs=360448 + <unknown>-12683 (-----) [005] .... 1920260.572318: mm_filemap_add_to_page_cache: dev 0:64768 ino 1565 page=0000000000000000 pfn=1137083 ofs=364544 + <unknown>-12683 (-----) [004] .... 1920260.575490: mm_filemap_add_to_page_cache: dev 0:64771 ino 286 page=0000000000000000 pfn=1379679 ofs=65536 + <unknown>-12683 (-----) [006] .... 1920260.576194: mm_filemap_add_to_page_cache: dev 0:64771 ino 286 page=0000000000000000 pfn=1323898 ofs=69632 + <unknown>-12683 (-----) [006] .... 1920260.576248: mm_filemap_add_to_page_cache: dev 0:64771 ino 1 page=0000000000000000 pfn=1323895 ofs=262623232 + <unknown>-12683 (-----) [006] .... 1920260.576251: mm_filemap_add_to_page_cache: dev 0:64771 ino 2 page=0000000000000000 pfn=1323861 ofs=13156352 + <unknown>-12683 (-----) [005] .... 1920260.576810: mm_filemap_add_to_page_cache: dev 0:64771 ino 1 page=0000000000000000 pfn=1477585 ofs=262590464 + <unknown>-12683 (-----) [004] .... 1920260.577197: mm_filemap_add_to_page_cache: dev 0:64771 ino 1 page=0000000000000000 pfn=1267617 ofs=25206784 + <unknown>-12683 (-----) [004] .... 1920260.577200: mm_filemap_add_to_page_cache: dev 0:64771 ino 2 page=0000000000000000 pfn=1267618 ofs=12636160 + <unknown>-12683 (-----) [005] .... 1920260.577725: mm_filemap_add_to_page_cache: dev 0:64771 ino 1 page=0000000000000000 pfn=1056225 ofs=228618240 + <unknown>-12683 (-----) [005] .... 1920260.577727: mm_filemap_add_to_page_cache: dev 0:64771 ino 2 page=0000000000000000 pfn=1164942 ofs=13082624 + <unknown>-12683 (-----) [007] .... 1920260.578411: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1372616 ofs=0 + <unknown>-12683 (-----) [007] .... 1920260.578422: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1307468 ofs=4096 + <unknown>-12683 (-----) [007] .... 1920260.578428: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1120117 ofs=8192 + <unknown>-12683 (-----) [007] .... 1920260.578428: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1217989 ofs=12288 + <unknown>-12683 (-----) [007] .... 1920260.578650: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1475011 ofs=5419008 + <unknown>-12683 (-----) [007] .... 1920260.578653: mm_filemap_add_to_page_cache: dev 0:64771 ino 1 page=0000000000000000 pfn=1066084 ofs=236453888 + <unknown>-12683 (-----) [007] .... 1920260.578654: mm_filemap_add_to_page_cache: dev 0:64771 ino 2 page=0000000000000000 pfn=1100271 ofs=13099008 + <unknown>-12683 (-----) [004] .... 1920260.579004: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1485156 ofs=5423104 + <unknown>-12683 (-----) [004] .... 1920260.579005: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1124212 ofs=5427200 + <unknown>-12683 (-----) [004] .... 1920260.579006: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1195377 ofs=5431296 + <unknown>-12683 (-----) [004] .... 1920260.579006: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1265888 ofs=5435392 + <unknown>-12683 (-----) [004] .... 1920260.579007: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1170194 ofs=5439488 + <unknown>-12683 (-----) [004] .... 1920260.579007: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1403742 ofs=5443584 + <unknown>-12683 (-----) [004] .... 1920260.579008: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1123826 ofs=5447680 + <unknown>-12683 (-----) [004] .... 1920260.579008: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1255034 ofs=5451776 + <unknown>-12683 (-----) [004] .... 1920260.579011: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1190447 ofs=5455872 + <unknown>-12683 (-----) [004] .... 1920260.579011: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1286864 ofs=5459968 + <unknown>-12683 (-----) [004] .... 1920260.579012: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1428535 ofs=5464064 + <unknown>-12683 (-----) [004] .... 1920260.579012: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1184092 ofs=5468160 + <unknown>-12683 (-----) [004] .... 1920260.579013: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1411906 ofs=5472256 + <unknown>-12683 (-----) [004] .... 1920260.579013: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1342349 ofs=5476352 + <unknown>-12683 (-----) [004] .... 1920260.579013: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1188185 ofs=5480448 + <unknown>-12683 (-----) [004] .... 1920260.579014: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1158702 ofs=5484544 + <unknown>-12683 (-----) [005] .... 1920260.579430: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1299421 ofs=5230592 + <unknown>-12683 (-----) [005] .... 1920260.579435: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1317097 ofs=5234688 + <unknown>-12683 (-----) [005] .... 1920260.579435: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1441714 ofs=5238784 + <unknown>-12683 (-----) [005] .... 1920260.579438: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1081974 ofs=5242880 + <unknown>-12683 (-----) [005] .... 1920260.579439: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1128684 ofs=5246976 + <unknown>-12683 (-----) [005] .... 1920260.579439: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1447381 ofs=5251072 + <unknown>-12683 (-----) [005] .... 1920260.579440: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1466410 ofs=5255168 + <unknown>-12683 (-----) [005] .... 1920260.579440: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1259909 ofs=5259264 + <unknown>-12683 (-----) [005] .... 1920260.579441: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1125784 ofs=5263360 + <unknown>-12683 (-----) [005] .... 1920260.579441: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1270592 ofs=5267456 + <unknown>-12683 (-----) [005] .... 1920260.579442: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1246070 ofs=5271552 + <unknown>-12683 (-----) [005] .... 1920260.579442: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1472544 ofs=5275648 + <unknown>-12683 (-----) [005] .... 1920260.579442: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1113357 ofs=5279744 + <unknown>-12683 (-----) [005] .... 1920260.579443: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1202021 ofs=5283840 + <unknown>-12683 (-----) [005] .... 1920260.579443: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1078639 ofs=5287936 + <unknown>-12683 (-----) [005] .... 1920260.579449: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1176171 ofs=5292032 + <unknown>-12683 (-----) [005] .... 1920260.579450: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1089516 ofs=5296128 + <unknown>-12683 (-----) [005] .... 1920260.579451: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1400065 ofs=5300224 + <unknown>-12683 (-----) [005] .... 1920260.579452: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1300489 ofs=5304320 + <unknown>-12683 (-----) [005] .... 1920260.579452: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1452081 ofs=5308416 + <unknown>-12683 (-----) [005] .... 1920260.579452: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1161862 ofs=5312512 + <unknown>-12683 (-----) [005] .... 1920260.579453: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1161871 ofs=5316608 + <unknown>-12683 (-----) [005] .... 1920260.579453: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1263798 ofs=5320704 + <unknown>-12683 (-----) [005] .... 1920260.579454: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1126887 ofs=5324800 + <unknown>-12683 (-----) [005] .... 1920260.579454: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1375498 ofs=5328896 + <unknown>-12683 (-----) [005] .... 1920260.579455: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1328067 ofs=5332992 + <unknown>-12683 (-----) [005] .... 1920260.579455: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1420691 ofs=5337088 + <unknown>-12683 (-----) [005] .... 1920260.579456: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1298707 ofs=5341184 + <unknown>-12683 (-----) [005] .... 1920260.579456: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1078670 ofs=5345280 + <unknown>-12683 (-----) [005] .... 1920260.579457: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1430498 ofs=5349376 + <unknown>-12683 (-----) [005] .... 1920260.579458: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1338720 ofs=5353472 + <unknown>-12683 (-----) [005] .... 1920260.579476: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1452611 ofs=5357568 + <unknown>-12683 (-----) [006] .... 1920260.580451: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1241967 ofs=0 + <unknown>-12683 (-----) [006] .... 1920260.580454: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1116541 ofs=4096 + <unknown>-12683 (-----) [006] .... 1920260.580461: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1145049 ofs=8192 + <unknown>-12683 (-----) [006] .... 1920260.580462: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1277255 ofs=12288 + <unknown>-12683 (-----) [006] .... 1920260.580462: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1098037 ofs=16384 + <unknown>-12683 (-----) [006] .... 1920260.580463: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1135986 ofs=20480 + <unknown>-12683 (-----) [006] .... 1920260.580464: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1154455 ofs=24576 + <unknown>-12683 (-----) [006] .... 1920260.580464: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1221822 ofs=28672 + <unknown>-12683 (-----) [006] .... 1920260.580465: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1078684 ofs=32768 + <unknown>-12683 (-----) [006] .... 1920260.580465: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1158876 ofs=36864 + <unknown>-12683 (-----) [006] .... 1920260.580465: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1289644 ofs=40960 + <unknown>-12683 (-----) [006] .... 1920260.580466: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1289386 ofs=45056 + <unknown>-12683 (-----) [006] .... 1920260.580466: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1131002 ofs=49152 + <unknown>-12683 (-----) [006] .... 1920260.580467: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1464335 ofs=53248 + <unknown>-12683 (-----) [006] .... 1920260.580468: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1135789 ofs=57344 + <unknown>-12683 (-----) [006] .... 1920260.580469: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1240897 ofs=61440 + <unknown>-12683 (-----) [006] .... 1920260.580469: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1241770 ofs=65536 + <unknown>-12683 (-----) [006] .... 1920260.580470: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1421959 ofs=69632 + <unknown>-12683 (-----) [006] .... 1920260.580470: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1230007 ofs=73728 + <unknown>-12683 (-----) [006] .... 1920260.580471: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1109271 ofs=77824 + <unknown>-12683 (-----) [006] .... 1920260.580471: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1159974 ofs=81920 + <unknown>-12683 (-----) [006] .... 1920260.580471: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1154528 ofs=86016 + <unknown>-12683 (-----) [006] .... 1920260.580472: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1315790 ofs=90112 + <unknown>-12683 (-----) [006] .... 1920260.580473: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1185583 ofs=94208 + <unknown>-12683 (-----) [006] .... 1920260.580473: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1253153 ofs=98304 + <unknown>-12683 (-----) [006] .... 1920260.580473: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1103982 ofs=102400 + <unknown>-12683 (-----) [006] .... 1920260.580474: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1284589 ofs=106496 + <unknown>-12683 (-----) [006] .... 1920260.580474: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1169601 ofs=110592 + <unknown>-12683 (-----) [006] .... 1920260.580476: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1206248 ofs=114688 + <unknown>-12683 (-----) [006] .... 1920260.580476: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1261161 ofs=118784 + <unknown>-12683 (-----) [006] .... 1920260.580477: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1305841 ofs=122880 + <unknown>-12683 (-----) [006] .... 1920260.580477: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1468293 ofs=126976 + <unknown>-12683 (-----) [004] .... 1920260.580646: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1318816 ofs=16384 + <unknown>-12683 (-----) [004] .... 1920260.580649: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1472922 ofs=20480 + <unknown>-12683 (-----) [004] .... 1920260.580650: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1473229 ofs=24576 + <unknown>-12683 (-----) [004] .... 1920260.580650: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1524262 ofs=28672 + <unknown>-12683 (-----) [004] .... 1920260.580656: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1205714 ofs=32768 + <unknown>-12683 (-----) [004] .... 1920260.580657: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1310560 ofs=36864 + <unknown>-12683 (-----) [004] .... 1920260.580658: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1295070 ofs=40960 + <unknown>-12683 (-----) [004] .... 1920260.580659: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1404093 ofs=45056 + <unknown>-12683 (-----) [004] .... 1920260.580659: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1435814 ofs=49152 + <unknown>-12683 (-----) [004] .... 1920260.580660: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1435442 ofs=53248 + <unknown>-12683 (-----) [004] .... 1920260.580660: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1096077 ofs=57344 + <unknown>-12683 (-----) [004] .... 1920260.580661: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1483793 ofs=61440 + <unknown>-12683 (-----) [004] .... 1920260.580661: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1231298 ofs=65536 + <unknown>-12683 (-----) [004] .... 1920260.580661: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1215648 ofs=69632 + <unknown>-12683 (-----) [004] .... 1920260.580662: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1327326 ofs=73728 + <unknown>-12683 (-----) [004] .... 1920260.580662: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1108894 ofs=77824 + <unknown>-12683 (-----) [004] .... 1920260.580663: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1327545 ofs=81920 + <unknown>-12683 (-----) [004] .... 1920260.580663: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1328804 ofs=86016 + <unknown>-12683 (-----) [004] .... 1920260.580664: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1300171 ofs=90112 + <unknown>-12683 (-----) [004] .... 1920260.580664: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1353250 ofs=94208 + <unknown>-12683 (-----) [004] .... 1920260.580668: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1333681 ofs=98304 + <unknown>-12683 (-----) [004] .... 1920260.580668: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1144969 ofs=102400 + <unknown>-12683 (-----) [004] .... 1920260.580669: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1450962 ofs=106496 + <unknown>-12683 (-----) [004] .... 1920260.580669: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1255701 ofs=110592 + <unknown>-12683 (-----) [004] .... 1920260.580670: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1294782 ofs=114688 + <unknown>-12683 (-----) [004] .... 1920260.580670: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1226912 ofs=118784 + <unknown>-12683 (-----) [004] .... 1920260.580671: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1294579 ofs=122880 + <unknown>-12683 (-----) [004] .... 1920260.580671: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1246960 ofs=126976 + <unknown>-12683 (-----) [004] .... 1920260.580671: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1199086 ofs=131072 + <unknown>-12683 (-----) [004] .... 1920260.580672: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1449590 ofs=135168 + <unknown>-12683 (-----) [004] .... 1920260.580672: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1276363 ofs=139264 + <unknown>-12683 (-----) [004] .... 1920260.580675: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1389998 ofs=143360 + <unknown>-12683 (-----) [004] .... 1920260.580739: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1423031 ofs=1249280 + <unknown>-12683 (-----) [004] .... 1920260.580741: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1171032 ofs=1253376 + <unknown>-12683 (-----) [004] .... 1920260.580742: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1320946 ofs=1257472 + <unknown>-12683 (-----) [004] .... 1920260.580743: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1314696 ofs=1261568 + <unknown>-12683 (-----) [004] .... 1920260.580743: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1414864 ofs=1265664 + <unknown>-12683 (-----) [004] .... 1920260.580744: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1334933 ofs=1269760 + <unknown>-12683 (-----) [004] .... 1920260.580744: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1242845 ofs=1273856 + <unknown>-12683 (-----) [004] .... 1920260.580747: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1289488 ofs=1277952 + <unknown>-12683 (-----) [004] .... 1920260.580748: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1335445 ofs=1282048 + <unknown>-12683 (-----) [004] .... 1920260.580748: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1289663 ofs=1286144 + <unknown>-12683 (-----) [004] .... 1920260.580749: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1080462 ofs=1290240 + <unknown>-12683 (-----) [004] .... 1920260.580749: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1286303 ofs=1294336 + <unknown>-12683 (-----) [004] .... 1920260.580750: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1353531 ofs=1298432 + <unknown>-12683 (-----) [004] .... 1920260.580750: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1280701 ofs=1302528 + <unknown>-12683 (-----) [004] .... 1920260.580751: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1107730 ofs=1306624 + <unknown>-12683 (-----) [004] .... 1920260.580752: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1242729 ofs=1310720 + <unknown>-12683 (-----) [004] .... 1920260.580753: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1078336 ofs=1314816 + <unknown>-12683 (-----) [004] .... 1920260.580753: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1372425 ofs=1318912 + <unknown>-12683 (-----) [004] .... 1920260.580754: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1248813 ofs=1323008 + <unknown>-12683 (-----) [004] .... 1920260.580754: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1201155 ofs=1327104 + <unknown>-12683 (-----) [004] .... 1920260.580755: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1250103 ofs=1331200 + <unknown>-12683 (-----) [004] .... 1920260.580755: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1359710 ofs=1335296 + <unknown>-12683 (-----) [004] .... 1920260.580756: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1272462 ofs=1339392 + <unknown>-12683 (-----) [004] .... 1920260.580758: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1097035 ofs=1343488 + <unknown>-12683 (-----) [004] .... 1920260.580759: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1233124 ofs=1347584 + <unknown>-12683 (-----) [004] .... 1920260.580759: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1455812 ofs=1351680 + <unknown>-12683 (-----) [004] .... 1920260.580759: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1355689 ofs=1355776 + <unknown>-12683 (-----) [004] .... 1920260.580760: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1263593 ofs=1359872 + <unknown>-12683 (-----) [004] .... 1920260.580760: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1230789 ofs=1363968 + <unknown>-12683 (-----) [004] .... 1920260.580761: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1143766 ofs=1368064 + <unknown>-12683 (-----) [004] .... 1920260.580762: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1269666 ofs=1372160 + <unknown>-12683 (-----) [004] .... 1920260.580762: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1353022 ofs=1376256 + <unknown>-12683 (-----) [004] .... 1920260.581613: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1355509 ofs=258048 + <unknown>-12683 (-----) [004] .... 1920260.581615: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1178902 ofs=262144 + <unknown>-12683 (-----) [004] .... 1920260.581616: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1193649 ofs=266240 + <unknown>-12683 (-----) [004] .... 1920260.581618: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1225497 ofs=270336 + <unknown>-12683 (-----) [004] .... 1920260.581618: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1228259 ofs=274432 + <unknown>-12683 (-----) [004] .... 1920260.581635: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1309674 ofs=278528 + <unknown>-12683 (-----) [004] .... 1920260.581635: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1239390 ofs=282624 + <unknown>-12683 (-----) [004] .... 1920260.581636: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1468083 ofs=286720 + <unknown>-12683 (-----) [004] .... 1920260.581636: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1292751 ofs=290816 + <unknown>-12683 (-----) [004] .... 1920260.581637: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1318066 ofs=294912 + <unknown>-12683 (-----) [004] .... 1920260.581637: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1489314 ofs=299008 + <unknown>-12683 (-----) [004] .... 1920260.581637: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1169867 ofs=303104 + <unknown>-12683 (-----) [004] .... 1920260.581639: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1314256 ofs=307200 + <unknown>-12683 (-----) [004] .... 1920260.581639: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1310230 ofs=311296 + <unknown>-12683 (-----) [004] .... 1920260.581640: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1356180 ofs=315392 + <unknown>-12683 (-----) [004] .... 1920260.581640: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1419179 ofs=319488 + <unknown>-12683 (-----) [004] .... 1920260.581641: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1307265 ofs=323584 + <unknown>-12683 (-----) [004] .... 1920260.581641: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1218590 ofs=327680 + <unknown>-12683 (-----) [004] .... 1920260.581642: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1447586 ofs=331776 + <unknown>-12683 (-----) [004] .... 1920260.581642: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1209382 ofs=335872 + <unknown>-12683 (-----) [004] .... 1920260.581642: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1072148 ofs=339968 + <unknown>-12683 (-----) [004] .... 1920260.581645: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1227195 ofs=344064 + <unknown>-12683 (-----) [004] .... 1920260.581646: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1246369 ofs=348160 + <unknown>-12683 (-----) [004] .... 1920260.581646: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1193845 ofs=352256 + <unknown>-12683 (-----) [004] .... 1920260.581647: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1137553 ofs=356352 + <unknown>-12683 (-----) [004] .... 1920260.581647: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1475215 ofs=360448 + <unknown>-12683 (-----) [004] .... 1920260.581648: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1258935 ofs=364544 + <unknown>-12683 (-----) [004] .... 1920260.581649: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1448788 ofs=368640 + <unknown>-12683 (-----) [004] .... 1920260.581649: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1447611 ofs=372736 + <unknown>-12683 (-----) [004] .... 1920260.581650: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1290842 ofs=376832 + <unknown>-12683 (-----) [004] .... 1920260.581650: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1447826 ofs=380928 + <unknown>-12683 (-----) [004] .... 1920260.581650: mm_filemap_add_to_page_cache: dev 0:64771 ino 55815 page=0000000000000000 pfn=1181016 ofs=385024 + <unknown>-12683 (-----) [005] .... 1920260.582230: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1216810 ofs=1662976 + <unknown>-12683 (-----) [005] .... 1920260.582234: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1175966 ofs=1667072 + <unknown>-12683 (-----) [005] .... 1920260.582235: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1449798 ofs=1671168 + <unknown>-12683 (-----) [005] .... 1920260.582236: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1273480 ofs=1675264 + <unknown>-12683 (-----) [005] .... 1920260.582236: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1152779 ofs=1679360 + <unknown>-12683 (-----) [005] .... 1920260.582237: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1272810 ofs=1683456 + <unknown>-12683 (-----) [005] .... 1920260.582237: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1248634 ofs=1687552 + <unknown>-12683 (-----) [005] .... 1920260.582237: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1203376 ofs=1691648 + <unknown>-12683 (-----) [005] .... 1920260.582238: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1138880 ofs=1695744 + <unknown>-12683 (-----) [005] .... 1920260.582238: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1344591 ofs=1699840 + <unknown>-12683 (-----) [005] .... 1920260.582239: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1416060 ofs=1703936 + <unknown>-12683 (-----) [005] .... 1920260.582246: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1128676 ofs=1708032 + <unknown>-12683 (-----) [005] .... 1920260.582247: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1301921 ofs=1712128 + <unknown>-12683 (-----) [005] .... 1920260.582248: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1384569 ofs=1716224 + <unknown>-12683 (-----) [005] .... 1920260.582248: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1249106 ofs=1720320 + <unknown>-12683 (-----) [005] .... 1920260.582249: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1206596 ofs=1724416 + <unknown>-12683 (-----) [005] .... 1920260.582249: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1429831 ofs=1728512 + <unknown>-12683 (-----) [005] .... 1920260.582252: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1107796 ofs=1732608 + <unknown>-12683 (-----) [005] .... 1920260.582255: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1098336 ofs=1736704 + <unknown>-12683 (-----) [005] .... 1920260.582255: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1230286 ofs=1740800 + <unknown>-12683 (-----) [005] .... 1920260.582256: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1100370 ofs=1744896 + <unknown>-12683 (-----) [005] .... 1920260.582256: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1241930 ofs=1748992 + <unknown>-12683 (-----) [005] .... 1920260.582257: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1366807 ofs=1753088 + <unknown>-12683 (-----) [005] .... 1920260.582257: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1136252 ofs=1757184 + <unknown>-12683 (-----) [005] .... 1920260.582258: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1274291 ofs=1761280 + <unknown>-12683 (-----) [005] .... 1920260.582258: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1254775 ofs=1765376 + <unknown>-12683 (-----) [005] .... 1920260.582259: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1194679 ofs=1769472 + <unknown>-12683 (-----) [005] .... 1920260.582262: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1177090 ofs=1773568 + <unknown>-12683 (-----) [005] .... 1920260.582263: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1343925 ofs=1777664 + <unknown>-12683 (-----) [005] .... 1920260.582263: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1159217 ofs=1781760 + <unknown>-12683 (-----) [005] .... 1920260.582263: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1435471 ofs=1785856 + <unknown>-12683 (-----) [005] .... 1920260.582264: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1435529 ofs=1789952 + <unknown>-12683 (-----) [004] .... 1920260.582524: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1181910 ofs=0 + <unknown>-12683 (-----) [004] .... 1920260.582528: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1212021 ofs=4096 + <unknown>-12683 (-----) [004] .... 1920260.582529: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1162778 ofs=8192 + <unknown>-12683 (-----) [004] .... 1920260.582529: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1107700 ofs=12288 + <unknown>-12683 (-----) [004] .... 1920260.583553: mm_filemap_add_to_page_cache: dev 0:64771 ino 57137 page=0000000000000000 pfn=1093394 ofs=3399680 + <unknown>-12683 (-----) [004] .... 1920260.583984: mm_filemap_add_to_page_cache: dev 0:64771 ino 1 page=0000000000000000 pfn=1121431 ofs=242503680 + <unknown>-12683 (-----) [004] .... 1920260.583986: mm_filemap_add_to_page_cache: dev 0:64771 ino 2 page=0000000000000000 pfn=1168551 ofs=13115392 + <unknown>-12683 (-----) [004] .... 1920260.584304: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1347409 ofs=0 + <unknown>-12683 (-----) [004] .... 1920260.584307: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1428681 ofs=4096 + <unknown>-12683 (-----) [004] .... 1920260.584307: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1259106 ofs=8192 + <unknown>-12683 (-----) [004] .... 1920260.584308: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1343229 ofs=12288 + <unknown>-12694 (-----) [005] .... 1920260.584622: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1098733 ofs=1531904 + <unknown>-12696 (-----) [006] .... 1920260.584626: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1331319 ofs=1536000 + <unknown>-12694 (-----) [005] .... 1920260.584626: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1278537 ofs=1540096 + <unknown>-12696 (-----) [006] .... 1920260.584631: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1492534 ofs=1544192 + <unknown>-12694 (-----) [005] .... 1920260.584636: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1460878 ofs=1548288 + <unknown>-12694 (-----) [005] .... 1920260.584640: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1092973 ofs=1552384 + <unknown>-12694 (-----) [005] .... 1920260.584641: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1103200 ofs=1556480 + <unknown>-12694 (-----) [005] .... 1920260.584642: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1257426 ofs=1560576 + <unknown>-12694 (-----) [005] .... 1920260.584642: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1219424 ofs=1564672 + <unknown>-12683 (-----) [004] .... 1920260.584660: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1279352 ofs=1568768 + <unknown>-12696 (-----) [006] .... 1920260.584662: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1260572 ofs=1572864 + <unknown>-12683 (-----) [004] .... 1920260.584663: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1225809 ofs=1576960 + <unknown>-12696 (-----) [006] .... 1920260.584665: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1350766 ofs=1585152 + <unknown>-12697 (-----) [007] .... 1920260.584666: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1107173 ofs=1581056 + <unknown>-12683 (-----) [004] .... 1920260.584668: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1305885 ofs=1589248 + <unknown>-12694 (-----) [005] .... 1920260.584669: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1293385 ofs=1593344 + <unknown>-12696 (-----) [006] .... 1920260.584670: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1173841 ofs=1597440 + <unknown>-12697 (-----) [007] .... 1920260.584670: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1080021 ofs=1601536 + <unknown>-12683 (-----) [004] .... 1920260.584673: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1147419 ofs=1605632 + <unknown>-12696 (-----) [006] .... 1920260.584673: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1252762 ofs=1609728 + <unknown>-12694 (-----) [005] .... 1920260.584674: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1323916 ofs=1613824 + <unknown>-12683 (-----) [004] .... 1920260.584675: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1155631 ofs=1617920 + <unknown>-12696 (-----) [006] .... 1920260.584676: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1449815 ofs=1622016 + <unknown>-12694 (-----) [005] .... 1920260.584678: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1227069 ofs=1626112 + <unknown>-12696 (-----) [006] .... 1920260.584680: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1317692 ofs=1630208 + <unknown>-12694 (-----) [005] .... 1920260.584681: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1492244 ofs=1634304 + <unknown>-12683 (-----) [004] .... 1920260.584682: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1241876 ofs=1638400 + <unknown>-12697 (-----) [007] .... 1920260.585446: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1402958 ofs=167936 + <unknown>-12697 (-----) [007] .... 1920260.585449: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1133263 ofs=172032 + <unknown>-12697 (-----) [007] .... 1920260.585450: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1295502 ofs=176128 + <unknown>-12697 (-----) [007] .... 1920260.585450: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1249495 ofs=180224 + <unknown>-12697 (-----) [007] .... 1920260.585451: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1237999 ofs=184320 + <unknown>-12697 (-----) [007] .... 1920260.585451: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1280965 ofs=188416 + <unknown>-12697 (-----) [007] .... 1920260.585454: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1208361 ofs=192512 + <unknown>-12697 (-----) [007] .... 1920260.585454: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1308840 ofs=196608 + <unknown>-12695 (-----) [004] .... 1920260.585455: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1138875 ofs=569344 + <unknown>-12695 (-----) [004] .... 1920260.585458: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1314886 ofs=573440 + <unknown>-12697 (-----) [007] .... 1920260.585458: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1242734 ofs=200704 + <unknown>-12695 (-----) [004] .... 1920260.585458: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1447386 ofs=577536 + <unknown>-12697 (-----) [007] .... 1920260.585459: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1241302 ofs=204800 + <unknown>-12695 (-----) [004] .... 1920260.585459: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1328663 ofs=581632 + <unknown>-12697 (-----) [007] .... 1920260.585459: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1476101 ofs=208896 + <unknown>-12695 (-----) [004] .... 1920260.585460: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1209461 ofs=585728 + <unknown>-12697 (-----) [007] .... 1920260.585460: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1080147 ofs=212992 + <unknown>-12697 (-----) [007] .... 1920260.585461: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1128509 ofs=217088 + <unknown>-12697 (-----) [007] .... 1920260.585461: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1371915 ofs=221184 + <unknown>-12697 (-----) [007] .... 1920260.585461: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1264015 ofs=225280 + <unknown>-12697 (-----) [007] .... 1920260.585462: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1211695 ofs=229376 + <unknown>-12697 (-----) [007] .... 1920260.585462: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1150386 ofs=233472 + <unknown>-12697 (-----) [007] .... 1920260.585463: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1135747 ofs=237568 + <unknown>-12697 (-----) [007] .... 1920260.585463: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1128230 ofs=241664 + <unknown>-12697 (-----) [007] .... 1920260.585464: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1155451 ofs=245760 + <unknown>-12697 (-----) [007] .... 1920260.585465: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1246841 ofs=249856 + <unknown>-12697 (-----) [007] .... 1920260.585465: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1462971 ofs=253952 + <unknown>-12697 (-----) [007] .... 1920260.585466: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1131333 ofs=258048 + <unknown>-12697 (-----) [007] .... 1920260.585466: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1289407 ofs=262144 + <unknown>-12695 (-----) [004] .... 1920260.585467: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1134730 ofs=589824 + <unknown>-12697 (-----) [007] .... 1920260.585467: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1289873 ofs=266240 + <unknown>-12697 (-----) [007] .... 1920260.585468: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1448734 ofs=270336 + <unknown>-12695 (-----) [004] .... 1920260.585468: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1129776 ofs=593920 + <unknown>-12697 (-----) [007] .... 1920260.585468: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1524090 ofs=274432 + <unknown>-12695 (-----) [004] .... 1920260.585468: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1399725 ofs=598016 + <unknown>-12697 (-----) [007] .... 1920260.585469: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1524081 ofs=278528 + <unknown>-12695 (-----) [004] .... 1920260.585469: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1276535 ofs=602112 + <unknown>-12697 (-----) [007] .... 1920260.585469: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1524060 ofs=282624 + <unknown>-12695 (-----) [004] .... 1920260.585470: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1449847 ofs=606208 + <unknown>-12697 (-----) [007] .... 1920260.585470: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1158944 ofs=286720 + <unknown>-12695 (-----) [004] .... 1920260.585470: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1384536 ofs=610304 + <unknown>-12697 (-----) [007] .... 1920260.585470: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1116785 ofs=290816 + <unknown>-12695 (-----) [004] .... 1920260.585471: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1308118 ofs=614400 + <unknown>-12697 (-----) [007] .... 1920260.585471: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1448669 ofs=294912 + <unknown>-12695 (-----) [004] .... 1920260.585471: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1227050 ofs=618496 + <unknown>-12695 (-----) [004] .... 1920260.585473: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1289324 ofs=622592 + <unknown>-12695 (-----) [004] .... 1920260.585473: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1187869 ofs=626688 + <unknown>-12695 (-----) [004] .... 1920260.585474: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1400523 ofs=630784 + <unknown>-12695 (-----) [004] .... 1920260.585474: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1344176 ofs=634880 + <unknown>-12695 (-----) [004] .... 1920260.585475: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1092871 ofs=638976 + <unknown>-12695 (-----) [004] .... 1920260.585475: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1092021 ofs=643072 + <unknown>-12695 (-----) [004] .... 1920260.585476: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1198169 ofs=647168 + <unknown>-12695 (-----) [004] .... 1920260.585476: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1371540 ofs=651264 + <unknown>-12683 (-----) [005] .... 1920260.585476: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1195003 ofs=348160 + <unknown>-12695 (-----) [004] .... 1920260.585477: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1228787 ofs=655360 + <unknown>-12695 (-----) [004] .... 1920260.585477: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1236123 ofs=659456 + <unknown>-12695 (-----) [004] .... 1920260.585477: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1137213 ofs=663552 + <unknown>-12695 (-----) [004] .... 1920260.585478: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1294618 ofs=667648 + <unknown>-12695 (-----) [004] .... 1920260.585478: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1241048 ofs=671744 + <unknown>-12695 (-----) [004] .... 1920260.585479: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1228779 ofs=675840 + <unknown>-12683 (-----) [005] .... 1920260.585479: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1199292 ofs=352256 + <unknown>-12683 (-----) [005] .... 1920260.585480: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1200861 ofs=356352 + <unknown>-12695 (-----) [004] .... 1920260.585480: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1309572 ofs=679936 + <unknown>-12683 (-----) [005] .... 1920260.585480: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1215770 ofs=360448 + <unknown>-12695 (-----) [004] .... 1920260.585481: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1409002 ofs=684032 + <unknown>-12683 (-----) [005] .... 1920260.585481: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1151883 ofs=364544 + <unknown>-12695 (-----) [004] .... 1920260.585481: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1103729 ofs=688128 + <unknown>-12683 (-----) [005] .... 1920260.585482: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1468126 ofs=368640 + <unknown>-12695 (-----) [004] .... 1920260.585482: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1162720 ofs=692224 + <unknown>-12683 (-----) [005] .... 1920260.585482: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1251672 ofs=372736 + <unknown>-12695 (-----) [004] .... 1920260.585482: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1199221 ofs=696320 + <unknown>-12683 (-----) [005] .... 1920260.585483: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1283325 ofs=376832 + <unknown>-12683 (-----) [005] .... 1920260.585483: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1190489 ofs=380928 + <unknown>-12683 (-----) [005] .... 1920260.585484: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1489117 ofs=385024 + <unknown>-12683 (-----) [005] .... 1920260.585484: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1273899 ofs=389120 + <unknown>-12683 (-----) [005] .... 1920260.585485: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1274459 ofs=393216 + <unknown>-12683 (-----) [005] .... 1920260.585486: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1316649 ofs=397312 + <unknown>-12683 (-----) [005] .... 1920260.585491: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1375678 ofs=401408 + <unknown>-12683 (-----) [005] .... 1920260.585491: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1483317 ofs=405504 + <unknown>-12683 (-----) [005] .... 1920260.585492: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1240286 ofs=409600 + <unknown>-12683 (-----) [005] .... 1920260.585492: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1131345 ofs=413696 + <unknown>-12683 (-----) [005] .... 1920260.585493: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1200483 ofs=417792 + <unknown>-12683 (-----) [005] .... 1920260.585493: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1384693 ofs=421888 + <unknown>-12683 (-----) [005] .... 1920260.585493: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1161385 ofs=425984 + <unknown>-12683 (-----) [005] .... 1920260.585494: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1452025 ofs=430080 + <unknown>-12683 (-----) [005] .... 1920260.585495: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1253654 ofs=434176 + <unknown>-12683 (-----) [005] .... 1920260.585495: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1116697 ofs=438272 + <unknown>-12683 (-----) [005] .... 1920260.585495: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1432645 ofs=442368 + <unknown>-12694 (-----) [006] .... 1920260.585495: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1337397 ofs=16384 + <unknown>-12683 (-----) [005] .... 1920260.585496: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1304229 ofs=446464 + <unknown>-12683 (-----) [005] .... 1920260.585496: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1419147 ofs=450560 + <unknown>-12683 (-----) [005] .... 1920260.585498: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1349246 ofs=454656 + <unknown>-12683 (-----) [005] .... 1920260.585499: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1128519 ofs=458752 + <unknown>-12683 (-----) [005] .... 1920260.585499: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1125168 ofs=462848 + <unknown>-12694 (-----) [006] .... 1920260.585509: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1081031 ofs=20480 + <unknown>-12694 (-----) [006] .... 1920260.585509: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1293022 ofs=24576 + <unknown>-12694 (-----) [006] .... 1920260.585510: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1113007 ofs=28672 + <unknown>-12694 (-----) [006] .... 1920260.585510: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1339312 ofs=32768 + <unknown>-12694 (-----) [006] .... 1920260.585511: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1412311 ofs=36864 + <unknown>-12694 (-----) [006] .... 1920260.585511: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1260960 ofs=40960 + <unknown>-12694 (-----) [006] .... 1920260.585512: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1189529 ofs=45056 + <unknown>-12694 (-----) [006] .... 1920260.585512: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1412184 ofs=49152 + <unknown>-12694 (-----) [006] .... 1920260.585513: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1481227 ofs=53248 + <unknown>-12694 (-----) [006] .... 1920260.585513: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1455940 ofs=57344 + <unknown>-12694 (-----) [006] .... 1920260.585514: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1299132 ofs=61440 + <unknown>-12694 (-----) [006] .... 1920260.585514: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1337375 ofs=65536 + <unknown>-12694 (-----) [006] .... 1920260.585529: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1328742 ofs=69632 + <unknown>-12694 (-----) [006] .... 1920260.585529: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1315646 ofs=73728 + <unknown>-12694 (-----) [006] .... 1920260.585531: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1225475 ofs=77824 + <unknown>-12694 (-----) [006] .... 1920260.585531: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1146097 ofs=81920 + <unknown>-12694 (-----) [006] .... 1920260.585532: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1318775 ofs=86016 + <unknown>-12694 (-----) [006] .... 1920260.585532: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1448391 ofs=90112 + <unknown>-12694 (-----) [006] .... 1920260.585532: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1441412 ofs=94208 + <unknown>-12694 (-----) [006] .... 1920260.585533: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1138111 ofs=98304 + <unknown>-12694 (-----) [006] .... 1920260.585533: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1143223 ofs=102400 + <unknown>-12683 (-----) [005] .... 1920260.585534: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1079876 ofs=466944 + <unknown>-12694 (-----) [006] .... 1920260.585534: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1447637 ofs=106496 + <unknown>-12694 (-----) [006] .... 1920260.585534: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1220585 ofs=110592 + <unknown>-12694 (-----) [006] .... 1920260.585535: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1449051 ofs=114688 + <unknown>-12694 (-----) [006] .... 1920260.585535: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1313180 ofs=118784 + <unknown>-12694 (-----) [006] .... 1920260.585535: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1313166 ofs=122880 + <unknown>-12694 (-----) [006] .... 1920260.585536: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1313154 ofs=126976 + <unknown>-12683 (-----) [005] .... 1920260.585536: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1218394 ofs=471040 + <unknown>-12694 (-----) [006] .... 1920260.585536: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1144047 ofs=131072 + <unknown>-12683 (-----) [005] .... 1920260.585537: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1218579 ofs=475136 + <unknown>-12694 (-----) [006] .... 1920260.585543: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1241332 ofs=135168 + <unknown>-12694 (-----) [006] .... 1920260.585543: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1097199 ofs=139264 + <unknown>-12694 (-----) [006] .... 1920260.585545: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1214197 ofs=143360 + <unknown>-12694 (-----) [006] .... 1920260.585645: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1197633 ofs=147456 + <unknown>-12694 (-----) [006] .... 1920260.585647: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1311536 ofs=151552 + <unknown>-12694 (-----) [006] .... 1920260.585647: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1322952 ofs=155648 + <unknown>-12694 (-----) [006] .... 1920260.585647: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1346974 ofs=159744 + <unknown>-12694 (-----) [006] .... 1920260.585648: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1257232 ofs=163840 + <unknown>-12695 (-----) [004] .... 1920260.586355: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1204484 ofs=700416 + <unknown>-12695 (-----) [004] .... 1920260.586357: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1326426 ofs=704512 + <unknown>-12695 (-----) [004] .... 1920260.586358: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1151808 ofs=708608 + <unknown>-12695 (-----) [004] .... 1920260.586358: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1209422 ofs=712704 + <unknown>-12695 (-----) [004] .... 1920260.586359: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1408387 ofs=716800 + <unknown>-12695 (-----) [004] .... 1920260.586359: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1197336 ofs=720896 + <unknown>-12695 (-----) [004] .... 1920260.586363: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1205652 ofs=724992 + <unknown>-12695 (-----) [004] .... 1920260.586363: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1133421 ofs=729088 + <unknown>-12695 (-----) [004] .... 1920260.586364: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1092173 ofs=733184 + <unknown>-12695 (-----) [004] .... 1920260.586365: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1124430 ofs=737280 + <unknown>-12695 (-----) [004] .... 1920260.586365: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1143926 ofs=741376 + <unknown>-12695 (-----) [004] .... 1920260.586366: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1090109 ofs=745472 + <unknown>-12695 (-----) [004] .... 1920260.586366: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1102012 ofs=749568 + <unknown>-12695 (-----) [004] .... 1920260.586367: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1154930 ofs=753664 + <unknown>-12695 (-----) [004] .... 1920260.586368: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1132993 ofs=757760 + <unknown>-12695 (-----) [004] .... 1920260.586369: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1430780 ofs=761856 + <unknown>-12695 (-----) [004] .... 1920260.586369: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1197452 ofs=765952 + <unknown>-12695 (-----) [004] .... 1920260.586369: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1075111 ofs=770048 + <unknown>-12695 (-----) [004] .... 1920260.586370: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1275616 ofs=774144 + <unknown>-12695 (-----) [004] .... 1920260.586370: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1444981 ofs=778240 + <unknown>-12695 (-----) [004] .... 1920260.586371: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1452592 ofs=782336 + <unknown>-12695 (-----) [004] .... 1920260.586374: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1102857 ofs=786432 + <unknown>-12695 (-----) [004] .... 1920260.586376: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1406969 ofs=790528 + <unknown>-12695 (-----) [004] .... 1920260.586378: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1522553 ofs=794624 + <unknown>-12695 (-----) [004] .... 1920260.586378: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1260771 ofs=798720 + <unknown>-12695 (-----) [004] .... 1920260.586379: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1474649 ofs=802816 + <unknown>-12695 (-----) [004] .... 1920260.586379: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1268708 ofs=806912 + <unknown>-12695 (-----) [004] .... 1920260.586379: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1346144 ofs=811008 + <unknown>-12695 (-----) [004] .... 1920260.586380: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1081167 ofs=815104 + <unknown>-12695 (-----) [004] .... 1920260.586380: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1137677 ofs=819200 + <unknown>-12695 (-----) [004] .... 1920260.586381: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1161175 ofs=823296 + <unknown>-12695 (-----) [004] .... 1920260.586381: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1461331 ofs=827392 + <unknown>-12695 (-----) [004] .... 1920260.586492: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1347219 ofs=831488 + <unknown>-12695 (-----) [004] .... 1920260.586494: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1290004 ofs=835584 + <unknown>-12695 (-----) [004] .... 1920260.586494: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1299174 ofs=839680 + <unknown>-12695 (-----) [004] .... 1920260.586496: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1317595 ofs=843776 + <unknown>-12695 (-----) [004] .... 1920260.586496: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1484924 ofs=847872 + <unknown>-12695 (-----) [004] .... 1920260.586497: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1169920 ofs=851968 + <unknown>-12695 (-----) [004] .... 1920260.586501: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1359189 ofs=856064 + <unknown>-12695 (-----) [004] .... 1920260.586501: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1307842 ofs=860160 + <unknown>-12695 (-----) [004] .... 1920260.586502: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1237858 ofs=864256 + <unknown>-12695 (-----) [004] .... 1920260.586502: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1189461 ofs=868352 + <unknown>-12695 (-----) [004] .... 1920260.586503: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1223232 ofs=872448 + <unknown>-12695 (-----) [004] .... 1920260.586503: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1104076 ofs=876544 + <unknown>-12695 (-----) [004] .... 1920260.586504: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1079223 ofs=880640 + <unknown>-12695 (-----) [004] .... 1920260.586504: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1092537 ofs=884736 + <unknown>-12695 (-----) [004] .... 1920260.586505: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1353960 ofs=888832 + <unknown>-12695 (-----) [004] .... 1920260.586505: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1346330 ofs=892928 + <unknown>-12695 (-----) [004] .... 1920260.586506: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1345764 ofs=897024 + <unknown>-12695 (-----) [004] .... 1920260.586507: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1363913 ofs=901120 + <unknown>-12695 (-----) [004] .... 1920260.586508: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1319570 ofs=905216 + <unknown>-12695 (-----) [004] .... 1920260.586508: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1367024 ofs=909312 + <unknown>-12695 (-----) [004] .... 1920260.586508: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1333808 ofs=913408 + <unknown>-12695 (-----) [004] .... 1920260.586509: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1158627 ofs=917504 + <unknown>-12695 (-----) [004] .... 1920260.586509: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1300368 ofs=921600 + <unknown>-12695 (-----) [004] .... 1920260.586510: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1245363 ofs=925696 + <unknown>-12695 (-----) [004] .... 1920260.586510: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1345609 ofs=929792 + <unknown>-12695 (-----) [004] .... 1920260.586510: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1393826 ofs=933888 + <unknown>-12695 (-----) [004] .... 1920260.586511: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1200552 ofs=937984 + <unknown>-12695 (-----) [004] .... 1920260.586511: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1170885 ofs=942080 + <unknown>-12695 (-----) [004] .... 1920260.586512: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1536209 ofs=946176 + <unknown>-12695 (-----) [004] .... 1920260.586512: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1189630 ofs=950272 + <unknown>-12695 (-----) [004] .... 1920260.586513: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1121010 ofs=954368 + <unknown>-12695 (-----) [004] .... 1920260.586514: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1324474 ofs=958464 + <unknown>-12697 (-----) [007] .... 1920260.586578: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1129628 ofs=299008 + <unknown>-12697 (-----) [007] .... 1920260.586579: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1307120 ofs=303104 + <unknown>-12697 (-----) [007] .... 1920260.586580: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1347284 ofs=307200 + <unknown>-12697 (-----) [007] .... 1920260.586580: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1312996 ofs=311296 + <unknown>-12697 (-----) [007] .... 1920260.586581: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1170623 ofs=315392 + <unknown>-12697 (-----) [007] .... 1920260.586581: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1359281 ofs=319488 + <unknown>-12697 (-----) [007] .... 1920260.586582: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1180021 ofs=323584 + <unknown>-12697 (-----) [007] .... 1920260.586582: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1195728 ofs=327680 + <unknown>-12697 (-----) [007] .... 1920260.586582: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1163642 ofs=331776 + <unknown>-12697 (-----) [007] .... 1920260.586587: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1152538 ofs=335872 + <unknown>-12697 (-----) [007] .... 1920260.586589: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1345922 ofs=339968 + <unknown>-12697 (-----) [007] .... 1920260.586589: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1343604 ofs=344064 + <unknown>-12697 (-----) [007] .... 1920260.586721: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1399371 ofs=479232 + <unknown>-12697 (-----) [007] .... 1920260.586723: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1106549 ofs=483328 + <unknown>-12697 (-----) [007] .... 1920260.586724: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1331546 ofs=487424 + <unknown>-12697 (-----) [007] .... 1920260.586724: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1299299 ofs=491520 + <unknown>-12697 (-----) [007] .... 1920260.586725: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1288883 ofs=495616 + <unknown>-12697 (-----) [007] .... 1920260.586725: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1399049 ofs=499712 + <unknown>-12697 (-----) [007] .... 1920260.586726: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1146931 ofs=503808 + <unknown>-12697 (-----) [007] .... 1920260.586726: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1296592 ofs=507904 + <unknown>-12697 (-----) [007] .... 1920260.586727: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1468397 ofs=512000 + <unknown>-12697 (-----) [007] .... 1920260.586727: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1215698 ofs=516096 + <unknown>-12697 (-----) [007] .... 1920260.586727: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1177341 ofs=520192 + <unknown>-12697 (-----) [007] .... 1920260.586731: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1189162 ofs=524288 + <unknown>-12697 (-----) [007] .... 1920260.586732: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1435997 ofs=528384 + <unknown>-12697 (-----) [007] .... 1920260.586732: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1209896 ofs=532480 + <unknown>-12697 (-----) [007] .... 1920260.586733: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1255888 ofs=536576 + <unknown>-12697 (-----) [007] .... 1920260.586734: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1234200 ofs=540672 + <unknown>-12697 (-----) [007] .... 1920260.586734: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1422854 ofs=544768 + <unknown>-12697 (-----) [007] .... 1920260.586735: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1435794 ofs=548864 + <unknown>-12697 (-----) [007] .... 1920260.586735: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1236279 ofs=552960 + <unknown>-12697 (-----) [007] .... 1920260.586736: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1485732 ofs=557056 + <unknown>-12683 (-----) [005] .... 1920260.586743: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1417198 ofs=561152 + <unknown>-12683 (-----) [005] .... 1920260.586746: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1469450 ofs=565248 + <unknown>-12696 (-----) [004] .... 1920260.587465: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1489023 ofs=1040384 + <unknown>-12696 (-----) [004] .... 1920260.587469: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1449498 ofs=1044480 + <unknown>-12696 (-----) [004] .... 1920260.587469: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1447737 ofs=1048576 + <unknown>-12696 (-----) [004] .... 1920260.587470: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1124530 ofs=1052672 + <unknown>-12696 (-----) [004] .... 1920260.587476: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1246743 ofs=1056768 + <unknown>-12696 (-----) [004] .... 1920260.587476: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1441927 ofs=1060864 + <unknown>-12696 (-----) [004] .... 1920260.587477: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1280581 ofs=1064960 + <unknown>-12696 (-----) [004] .... 1920260.587477: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1289438 ofs=1069056 + <unknown>-12696 (-----) [004] .... 1920260.587477: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1464236 ofs=1073152 + <unknown>-12696 (-----) [004] .... 1920260.587478: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1125808 ofs=1077248 + <unknown>-12696 (-----) [004] .... 1920260.587478: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1329385 ofs=1081344 + <unknown>-12696 (-----) [004] .... 1920260.587480: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1314093 ofs=1085440 + <unknown>-12696 (-----) [004] .... 1920260.587480: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1201837 ofs=1089536 + <unknown>-12696 (-----) [004] .... 1920260.587481: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1327734 ofs=1093632 + <unknown>-12696 (-----) [004] .... 1920260.587481: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1406568 ofs=1097728 + <unknown>-12696 (-----) [004] .... 1920260.587481: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1331873 ofs=1101824 + <unknown>-12696 (-----) [004] .... 1920260.587482: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1331898 ofs=1105920 + <unknown>-12696 (-----) [004] .... 1920260.587482: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1331917 ofs=1110016 + <unknown>-12696 (-----) [004] .... 1920260.587483: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1332091 ofs=1114112 + <unknown>-12696 (-----) [004] .... 1920260.587483: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1108186 ofs=1118208 + <unknown>-12696 (-----) [004] .... 1920260.587486: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1182631 ofs=1122304 + <unknown>-12696 (-----) [004] .... 1920260.587486: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1085941 ofs=1126400 + <unknown>-12696 (-----) [004] .... 1920260.587487: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1433982 ofs=1130496 + <unknown>-12696 (-----) [004] .... 1920260.587487: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1387028 ofs=1134592 + <unknown>-12696 (-----) [004] .... 1920260.587488: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1353117 ofs=1138688 + <unknown>-12696 (-----) [004] .... 1920260.587489: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1352364 ofs=1142784 + <unknown>-12696 (-----) [004] .... 1920260.587489: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1144513 ofs=1146880 + <unknown>-12696 (-----) [004] .... 1920260.587490: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1403984 ofs=1150976 + <unknown>-12696 (-----) [004] .... 1920260.587490: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1278970 ofs=1155072 + <unknown>-12696 (-----) [004] .... 1920260.587491: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1326743 ofs=1159168 + <unknown>-12696 (-----) [004] .... 1920260.587491: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1221809 ofs=1163264 + <unknown>-12696 (-----) [004] .... 1920260.587492: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1268668 ofs=1167360 + <unknown>-12695 (-----) [005] .... 1920260.587502: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1074544 ofs=962560 + <unknown>-12695 (-----) [005] .... 1920260.587506: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1074294 ofs=966656 + <unknown>-12695 (-----) [005] .... 1920260.587506: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1075097 ofs=970752 + <unknown>-12695 (-----) [005] .... 1920260.587507: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1162407 ofs=974848 + <unknown>-12695 (-----) [005] .... 1920260.587507: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1141370 ofs=978944 + <unknown>-12695 (-----) [005] .... 1920260.587508: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1306487 ofs=983040 + <unknown>-12695 (-----) [005] .... 1920260.587508: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1306434 ofs=987136 + <unknown>-12695 (-----) [005] .... 1920260.587514: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1306347 ofs=991232 + <unknown>-12695 (-----) [005] .... 1920260.587514: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1306247 ofs=995328 + <unknown>-12695 (-----) [005] .... 1920260.587515: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1306195 ofs=999424 + <unknown>-12695 (-----) [005] .... 1920260.587516: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1306039 ofs=1003520 + <unknown>-12695 (-----) [005] .... 1920260.587516: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1305983 ofs=1007616 + <unknown>-12694 (-----) [006] .... 1920260.587701: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1216391 ofs=1171456 + <unknown>-12694 (-----) [006] .... 1920260.587705: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1262462 ofs=1175552 + <unknown>-12694 (-----) [006] .... 1920260.587706: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1358114 ofs=1179648 + <unknown>-12694 (-----) [006] .... 1920260.587706: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1357898 ofs=1183744 + <unknown>-12694 (-----) [006] .... 1920260.587707: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1237003 ofs=1187840 + <unknown>-12694 (-----) [006] .... 1920260.587707: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1126319 ofs=1191936 + <unknown>-12694 (-----) [006] .... 1920260.587708: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1415489 ofs=1196032 + <unknown>-12694 (-----) [006] .... 1920260.587708: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1279558 ofs=1200128 + <unknown>-12694 (-----) [006] .... 1920260.587708: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1434022 ofs=1204224 + <unknown>-12694 (-----) [006] .... 1920260.587709: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1220130 ofs=1208320 + <unknown>-12694 (-----) [006] .... 1920260.587710: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1163037 ofs=1212416 + <unknown>-12694 (-----) [006] .... 1920260.587711: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1404501 ofs=1216512 + <unknown>-12694 (-----) [006] .... 1920260.587711: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1406287 ofs=1220608 + <unknown>-12697 (-----) [007] .... 1920260.588132: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1355143 ofs=1376256 + <unknown>-12697 (-----) [007] .... 1920260.588136: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1213923 ofs=1380352 + <unknown>-12697 (-----) [007] .... 1920260.588136: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1243190 ofs=1384448 + <unknown>-12697 (-----) [007] .... 1920260.588143: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1300698 ofs=1388544 + <unknown>-12697 (-----) [007] .... 1920260.588144: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1482568 ofs=1392640 + <unknown>-12697 (-----) [007] .... 1920260.588144: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1461789 ofs=1396736 + <unknown>-12697 (-----) [007] .... 1920260.588145: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1242314 ofs=1400832 + <unknown>-12697 (-----) [007] .... 1920260.588145: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1471996 ofs=1404928 + <unknown>-12697 (-----) [007] .... 1920260.588146: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1242742 ofs=1409024 + <unknown>-12697 (-----) [007] .... 1920260.588146: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1242579 ofs=1413120 + <unknown>-12697 (-----) [007] .... 1920260.588148: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1242553 ofs=1417216 + <unknown>-12697 (-----) [007] .... 1920260.588148: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1457332 ofs=1421312 + <unknown>-12697 (-----) [007] .... 1920260.588149: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1315431 ofs=1425408 + <unknown>-12697 (-----) [007] .... 1920260.588149: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1080653 ofs=1429504 + <unknown>-12697 (-----) [007] .... 1920260.588149: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1324174 ofs=1433600 + <unknown>-12697 (-----) [007] .... 1920260.588150: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1324142 ofs=1437696 + <unknown>-12697 (-----) [007] .... 1920260.588150: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1157760 ofs=1441792 + <unknown>-12697 (-----) [007] .... 1920260.588151: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1075059 ofs=1445888 + <unknown>-12683 (-----) [006] .... 1920260.589785: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1279192 ofs=1486848 + <unknown>-12683 (-----) [006] .... 1920260.589790: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1278527 ofs=1490944 + <unknown>-12683 (-----) [006] .... 1920260.589791: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1091778 ofs=1495040 + <unknown>-12683 (-----) [006] .... 1920260.589791: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1339447 ofs=1499136 + <unknown>-12683 (-----) [006] .... 1920260.589792: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1254007 ofs=1503232 + <unknown>-12683 (-----) [006] .... 1920260.589793: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1115173 ofs=1507328 + <unknown>-12683 (-----) [006] .... 1920260.589793: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1393985 ofs=1511424 + <unknown>-12683 (-----) [006] .... 1920260.589794: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1369123 ofs=1515520 + <unknown>-12683 (-----) [006] .... 1920260.589794: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1314257 ofs=1519616 + <unknown>-12683 (-----) [006] .... 1920260.589802: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1404487 ofs=1523712 + <unknown>-12683 (-----) [006] .... 1920260.589803: mm_filemap_add_to_page_cache: dev 0:64771 ino 59205 page=0000000000000000 pfn=1354554 ofs=1527808 + <unknown>-12683 (-----) [006] .... 1920260.594312: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1141445 ofs=9801728 + <unknown>-12683 (-----) [006] .... 1920260.594322: mm_filemap_add_to_page_cache: dev 0:64771 ino 1 page=0000000000000000 pfn=1323774 ofs=231460864 + <unknown>-12683 (-----) [006] .... 1920260.594326: mm_filemap_add_to_page_cache: dev 0:64771 ino 2 page=0000000000000000 pfn=1323772 ofs=10993664 + <unknown>-12683 (-----) [006] .... 1920260.595212: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1481305 ofs=9805824 + <unknown>-12683 (-----) [006] .... 1920260.595214: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1481306 ofs=9809920 + <unknown>-12683 (-----) [006] .... 1920260.595214: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1481316 ofs=9814016 + <unknown>-12683 (-----) [006] .... 1920260.595215: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1481340 ofs=9818112 + <unknown>-12683 (-----) [006] .... 1920260.595216: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1394587 ofs=9822208 + <unknown>-12683 (-----) [006] .... 1920260.595216: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1103455 ofs=9826304 + <unknown>-12683 (-----) [006] .... 1920260.595217: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1103271 ofs=9830400 + <unknown>-12683 (-----) [006] .... 1920260.595218: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1103168 ofs=9834496 + <unknown>-12683 (-----) [006] .... 1920260.595218: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1103145 ofs=9838592 + <unknown>-12683 (-----) [006] .... 1920260.595219: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1103115 ofs=9842688 + <unknown>-12683 (-----) [006] .... 1920260.595222: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1103057 ofs=9846784 + <unknown>-12683 (-----) [006] .... 1920260.595222: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1331958 ofs=9850880 + <unknown>-12683 (-----) [006] .... 1920260.595227: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1356305 ofs=9854976 + <unknown>-12683 (-----) [006] .... 1920260.595228: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1103708 ofs=9859072 + <unknown>-12683 (-----) [006] .... 1920260.595228: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1099286 ofs=9863168 + <unknown>-12683 (-----) [006] .... 1920260.595229: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1435190 ofs=9867264 + <unknown>-12683 (-----) [006] .... 1920260.595229: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1395504 ofs=9871360 + <unknown>-12683 (-----) [006] .... 1920260.595230: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1352916 ofs=9875456 + <unknown>-12683 (-----) [006] .... 1920260.595231: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1255529 ofs=9879552 + <unknown>-12683 (-----) [006] .... 1920260.595231: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1336145 ofs=9883648 + <unknown>-12683 (-----) [006] .... 1920260.595232: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1334143 ofs=9887744 + <unknown>-12683 (-----) [006] .... 1920260.595232: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1328548 ofs=9891840 + <unknown>-12683 (-----) [006] .... 1920260.595232: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1222215 ofs=9895936 + <unknown>-12683 (-----) [006] .... 1920260.595233: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1461056 ofs=9900032 + <unknown>-12683 (-----) [006] .... 1920260.595234: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1228276 ofs=9904128 + <unknown>-12683 (-----) [006] .... 1920260.595235: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1151188 ofs=9908224 + <unknown>-12683 (-----) [006] .... 1920260.595236: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1443605 ofs=9912320 + <unknown>-12683 (-----) [006] .... 1920260.595236: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1146821 ofs=9916416 + <unknown>-12683 (-----) [006] .... 1920260.595237: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1103669 ofs=9920512 + <unknown>-12683 (-----) [006] .... 1920260.595238: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1103744 ofs=9924608 + <unknown>-12683 (-----) [006] .... 1920260.595238: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1103868 ofs=9928704 + <unknown>-12683 (-----) [006] .... 1920260.595789: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1465942 ofs=15855616 + <unknown>-12683 (-----) [006] .... 1920260.595792: mm_filemap_add_to_page_cache: dev 0:64771 ino 1 page=0000000000000000 pfn=1323712 ofs=261189632 + <unknown>-12683 (-----) [006] .... 1920260.595998: mm_filemap_add_to_page_cache: dev 0:64771 ino 1 page=0000000000000000 pfn=1323701 ofs=262094848 + <unknown>-12683 (-----) [006] .... 1920260.596191: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1222287 ofs=15859712 + <unknown>-12683 (-----) [006] .... 1920260.596192: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1213146 ofs=15863808 + <unknown>-12683 (-----) [006] .... 1920260.596192: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1310396 ofs=15867904 + <unknown>-12683 (-----) [006] .... 1920260.596193: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1310177 ofs=15872000 + <unknown>-12683 (-----) [006] .... 1920260.596194: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1187914 ofs=15876096 + <unknown>-12683 (-----) [006] .... 1920260.596195: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1322409 ofs=15880192 + <unknown>-12683 (-----) [006] .... 1920260.596195: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1282484 ofs=15884288 + <unknown>-12683 (-----) [006] .... 1920260.596200: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1097245 ofs=15888384 + <unknown>-12683 (-----) [006] .... 1920260.596200: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1416816 ofs=15892480 + <unknown>-12683 (-----) [006] .... 1920260.596201: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1257125 ofs=15896576 + <unknown>-12683 (-----) [006] .... 1920260.596201: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1403527 ofs=15900672 + <unknown>-12683 (-----) [006] .... 1920260.596202: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1218006 ofs=15904768 + <unknown>-12683 (-----) [006] .... 1920260.596202: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1153893 ofs=15908864 + <unknown>-12683 (-----) [006] .... 1920260.596202: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1328023 ofs=15912960 + <unknown>-12683 (-----) [006] .... 1920260.596203: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1465412 ofs=15917056 + <unknown>-12683 (-----) [006] .... 1920260.596203: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1092448 ofs=15921152 + <unknown>-12683 (-----) [006] .... 1920260.596204: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1239220 ofs=15925248 + <unknown>-12683 (-----) [006] .... 1920260.596204: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1276491 ofs=15929344 + <unknown>-12683 (-----) [006] .... 1920260.596205: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1262240 ofs=15933440 + <unknown>-12683 (-----) [006] .... 1920260.596206: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1323793 ofs=15937536 + <unknown>-12683 (-----) [006] .... 1920260.596206: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1074937 ofs=15941632 + <unknown>-12683 (-----) [006] .... 1920260.596207: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1311157 ofs=15945728 + <unknown>-12683 (-----) [006] .... 1920260.596207: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1308442 ofs=15949824 + <unknown>-12683 (-----) [006] .... 1920260.596210: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1467709 ofs=15953920 + <unknown>-12683 (-----) [006] .... 1920260.596211: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1394299 ofs=15958016 + <unknown>-12683 (-----) [004] .... 1920260.612586: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1316156 ofs=344064 + <unknown>-12683 (-----) [004] .... 1920260.612591: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1406323 ofs=348160 + <unknown>-12683 (-----) [004] .... 1920260.612601: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1216972 ofs=352256 + <unknown>-12683 (-----) [004] .... 1920260.612605: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1271924 ofs=356352 + <unknown>-12683 (-----) [004] .... 1920260.612605: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1369225 ofs=360448 + <unknown>-12683 (-----) [004] .... 1920260.612608: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1318474 ofs=364544 + <unknown>-12683 (-----) [004] .... 1920260.612609: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1227283 ofs=368640 + <unknown>-12683 (-----) [004] .... 1920260.612613: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1364376 ofs=372736 + <unknown>-12683 (-----) [004] .... 1920260.612613: mm_filemap_add_to_page_cache: dev 0:64771 ino 6154 page=0000000000000000 pfn=1073400 ofs=376832 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/lib/cmd_utils.py b/startop/scripts/lib/cmd_utils.py index bc5ca3140d3d..6071f145fe1d 100644 --- a/startop/scripts/lib/cmd_utils.py +++ b/startop/scripts/lib/cmd_utils.py @@ -26,6 +26,18 @@ import lib.print_utils as print_utils TIMEOUT = 50 SIMULATE = False +def run_command_nofail(cmd: List[str], **kwargs) -> None: + """Runs cmd list with default timeout. + + Throws exception if the execution fails. + """ + my_kwargs = {"timeout": TIMEOUT, "shell": False, "simulate": False} + my_kwargs.update(kwargs) + passed, out = execute_arbitrary_command(cmd, **my_kwargs) + if not passed: + raise RuntimeError( + "Failed to execute %s (kwargs=%s), output=%s" % (cmd, kwargs, out)) + def run_adb_shell_command(cmd: str) -> Tuple[bool, str]: """Runs command using adb shell. 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/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 91bea5ff479b..4016c5c6c6f3 100755 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -3029,6 +3029,23 @@ public class CarrierConfigManager { "is_opportunistic_subscription_bool"; /** + * Configs used by the IMS stack. + */ + public static final class Ims { + /** Prefix of all Ims.KEY_* constants. */ + public static final String KEY_PREFIX = "ims."; + + //TODO: Add configs related to IMS. + + private Ims() {} + + private static PersistableBundle getDefaults() { + PersistableBundle defaults = new PersistableBundle(); + return defaults; + } + } + + /** * A list of 4 GSM RSSI thresholds above which a signal level is considered POOR, * MODERATE, GOOD, or EXCELLENT, to be used in SignalStrength reporting. * @@ -3463,6 +3480,7 @@ public class CarrierConfigManager { -89, /* SIGNAL_STRENGTH_GREAT */ }); sDefaults.putBoolean(KEY_SUPPORT_WPS_OVER_IMS_BOOL, true); + sDefaults.putAll(Ims.getDefaults()); } /** @@ -3659,4 +3677,75 @@ public class CarrierConfigManager { return ICarrierConfigLoader.Stub .asInterface(ServiceManager.getService(Context.CARRIER_CONFIG_SERVICE)); } + + /** + * Gets the configuration values for a component using its prefix. + * + * <p>Requires Permission: + * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} + * + * @param prefix prefix of the component. + * @param subId the subscription ID, normally obtained from {@link SubscriptionManager}. + * + * @see #getConfigForSubId + */ + @Nullable + public PersistableBundle getConfigByComponentForSubId(String prefix, int subId) { + PersistableBundle configs = getConfigForSubId(subId); + + if (configs == null) { + return null; + } + + PersistableBundle ret = new PersistableBundle(); + for (String configKey : configs.keySet()) { + if (configKey.startsWith(prefix)) { + addConfig(configKey, configs.get(configKey), ret); + } + } + + return ret; + } + + private void addConfig(String key, Object value, PersistableBundle configs) { + if (value instanceof String) { + configs.putString(key, (String) value); + } + + if (value instanceof String[]) { + configs.putStringArray(key, (String[]) value); + } + + if (value instanceof Integer) { + configs.putInt(key, (Integer) value); + } + + if (value instanceof Long) { + configs.putLong(key, (Long) value); + } + + if (value instanceof Double) { + configs.putDouble(key, (Double) value); + } + + if (value instanceof Boolean) { + configs.putBoolean(key, (Boolean) value); + } + + if (value instanceof int[]) { + configs.putIntArray(key, (int[]) value); + } + + if (value instanceof double[]) { + configs.putDoubleArray(key, (double[]) value); + } + + if (value instanceof boolean[]) { + configs.putBooleanArray(key, (boolean[]) value); + } + + if (value instanceof long[]) { + configs.putLongArray(key, (long[]) value); + } + } } diff --git a/telephony/java/android/telephony/ims/ImsException.java b/telephony/java/android/telephony/ims/ImsException.java index 8c686f704967..8e1324b3be0b 100644 --- a/telephony/java/android/telephony/ims/ImsException.java +++ b/telephony/java/android/telephony/ims/ImsException.java @@ -20,6 +20,7 @@ import android.annotation.IntDef; import android.annotation.Nullable; import android.annotation.SystemApi; import android.content.pm.PackageManager; +import android.telephony.SubscriptionManager; import android.text.TextUtils; import java.lang.annotation.Retention; @@ -55,12 +56,23 @@ public final class ImsException extends Exception { */ public static final int CODE_ERROR_UNSUPPORTED_OPERATION = 2; + /** + * The subscription ID associated with this operation is invalid or not active. + * <p> + * This is a configuration error and there should be no retry. The subscription used for this + * operation is either invalid or has become inactive. The active subscriptions can be queried + * with {@link SubscriptionManager#getActiveSubscriptionInfoList()}. + * @hide + */ + public static final int CODE_ERROR_INVALID_SUBSCRIPTION = 3; + /**@hide*/ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = "CODE_ERROR_", value = { CODE_ERROR_UNSPECIFIED, CODE_ERROR_SERVICE_UNAVAILABLE, - CODE_ERROR_UNSUPPORTED_OPERATION + CODE_ERROR_UNSUPPORTED_OPERATION, + CODE_ERROR_INVALID_SUBSCRIPTION }) public @interface ImsErrorCode {} diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java index be5872387d7b..a1a7fcc5dd51 100644 --- a/telephony/java/android/telephony/ims/ImsMmTelManager.java +++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java @@ -31,6 +31,7 @@ import android.net.Uri; import android.os.Binder; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.ServiceSpecificException; import android.telephony.AccessNetworkConstants; import android.telephony.SubscriptionManager; import android.telephony.ims.aidl.IImsCapabilityCallback; @@ -375,6 +376,13 @@ public class ImsMmTelManager { c.setExecutor(executor); try { getITelephony().registerImsRegistrationCallback(mSubId, c.getBinder()); + } catch (ServiceSpecificException e) { + if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { + // Rethrow as runtime error to keep API compatible. + throw new IllegalArgumentException(e.getMessage()); + } else { + throw new RuntimeException(e.getMessage()); + } } catch (RemoteException | IllegalStateException e) { throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); } @@ -390,8 +398,6 @@ public class ImsMmTelManager { * @param c The {@link RegistrationCallback} to be removed. * @see SubscriptionManager.OnSubscriptionsChangedListener * @see #registerImsRegistrationCallback(Executor, RegistrationCallback) - * @throws IllegalArgumentException if the subscription ID associated with this callback is - * invalid. */ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterImsRegistrationCallback(@NonNull RegistrationCallback c) { @@ -445,6 +451,13 @@ public class ImsMmTelManager { c.setExecutor(executor); try { getITelephony().registerMmTelCapabilityCallback(mSubId, c.getBinder()); + } catch (ServiceSpecificException e) { + if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { + // Rethrow as runtime error to keep API compatible. + throw new IllegalArgumentException(e.getMessage()); + } else { + throw new RuntimeException(e.getMessage()); + } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } catch (IllegalStateException e) { @@ -460,8 +473,6 @@ public class ImsMmTelManager { * inactive subscription, it will result in a no-op. * @param c The MmTel {@link CapabilityCallback} to be removed. * @see #registerMmTelCapabilityCallback(Executor, CapabilityCallback) - * @throws IllegalArgumentException if the subscription ID associated with this callback is - * invalid. */ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterMmTelCapabilityCallback(@NonNull CapabilityCallback c) { @@ -482,12 +493,9 @@ public class ImsMmTelManager { * be enabled as long as the carrier has provisioned these services for the specified * subscription. Other IMS services (SMS/UT) are not affected by this user setting and depend on * carrier requirements. - * - * Modifying this value may also trigger an IMS registration or deregistration, depending on - * whether or not the new value is enabled or disabled. - * + * <p> * Note: If the carrier configuration for advanced calling is not editable or hidden, this - * method will do nothing and will instead always use the default value. + * method will always return the default value. * * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL * @see android.telephony.CarrierConfigManager#KEY_EDITABLE_ENHANCED_4G_LTE_BOOL @@ -495,12 +503,21 @@ public class ImsMmTelManager { * @see android.telephony.CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_AVAILABLE_BOOL * @see #setAdvancedCallingSettingEnabled(boolean) + * @throws IllegalArgumentException if the subscription associated with this operation is not + * active (SIM is not inserted, ESIM inactive) or invalid. * @return true if the user's setting for advanced calling is enabled, false otherwise. */ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAdvancedCallingSettingEnabled() { try { return getITelephony().isAdvancedCallingSettingEnabled(mSubId); + } catch (ServiceSpecificException e) { + if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { + // Rethrow as runtime error to keep API compatible. + throw new IllegalArgumentException(e.getMessage()); + } else { + throw new RuntimeException(e.getMessage()); + } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } @@ -526,12 +543,20 @@ public class ImsMmTelManager { * @see android.telephony.CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_AVAILABLE_BOOL * @see #isAdvancedCallingSettingEnabled() + * @throws IllegalArgumentException if the subscription associated with this operation is not + * active (SIM is not inserted, ESIM inactive) or invalid. */ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void setAdvancedCallingSettingEnabled(boolean isEnabled) { try { getITelephony().setAdvancedCallingSettingEnabled(mSubId, isEnabled); - return; + } catch (ServiceSpecificException e) { + if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { + // Rethrow as runtime error to keep API compatible. + throw new IllegalArgumentException(e.getMessage()); + } else { + throw new RuntimeException(e.getMessage()); + } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } @@ -597,6 +622,9 @@ public class ImsMmTelManager { /** * The user's setting for whether or not they have enabled the "Video Calling" setting. + * + * @throws IllegalArgumentException if the subscription associated with this operation is not + * active (SIM is not inserted, ESIM inactive) or invalid. * @return true if the user’s “Video Calling†setting is currently enabled. * @see #setVtSettingEnabled(boolean) */ @@ -604,6 +632,13 @@ public class ImsMmTelManager { public boolean isVtSettingEnabled() { try { return getITelephony().isVtSettingEnabled(mSubId); + } catch (ServiceSpecificException e) { + if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { + // Rethrow as runtime error to keep API compatible. + throw new IllegalArgumentException(e.getMessage()); + } else { + throw new RuntimeException(e.getMessage()); + } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } @@ -611,13 +646,22 @@ public class ImsMmTelManager { /** * Change the user's setting for Video Telephony and enable the Video Telephony capability. + * + * @throws IllegalArgumentException if the subscription associated with this operation is not + * active (SIM is not inserted, ESIM inactive) or invalid. * @see #isVtSettingEnabled() */ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void setVtSettingEnabled(boolean isEnabled) { try { getITelephony().setVtSettingEnabled(mSubId, isEnabled); - return; + } catch (ServiceSpecificException e) { + if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { + // Rethrow as runtime error to keep API compatible. + throw new IllegalArgumentException(e.getMessage()); + } else { + throw new RuntimeException(e.getMessage()); + } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } @@ -625,12 +669,22 @@ public class ImsMmTelManager { /** * @return true if the user's setting for Voice over WiFi is enabled and false if it is not. + * + * @throws IllegalArgumentException if the subscription associated with this operation is not + * active (SIM is not inserted, ESIM inactive) or invalid. * @see #setVoWiFiSettingEnabled(boolean) */ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isVoWiFiSettingEnabled() { try { return getITelephony().isVoWiFiSettingEnabled(mSubId); + } catch (ServiceSpecificException e) { + if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { + // Rethrow as runtime error to keep API compatible. + throw new IllegalArgumentException(e.getMessage()); + } else { + throw new RuntimeException(e.getMessage()); + } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } @@ -638,6 +692,9 @@ public class ImsMmTelManager { /** * Sets the user's setting for whether or not Voice over WiFi is enabled. + * + * @throws IllegalArgumentException if the subscription associated with this operation is not + * active (SIM is not inserted, ESIM inactive) or invalid. * @param isEnabled true if the user's setting for Voice over WiFi is enabled, false otherwise= * @see #isVoWiFiSettingEnabled() */ @@ -645,13 +702,23 @@ public class ImsMmTelManager { public void setVoWiFiSettingEnabled(boolean isEnabled) { try { getITelephony().setVoWiFiSettingEnabled(mSubId, isEnabled); - return; + } catch (ServiceSpecificException e) { + if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { + // Rethrow as runtime error to keep API compatible. + throw new IllegalArgumentException(e.getMessage()); + } else { + throw new RuntimeException(e.getMessage()); + } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } /** + * Returns the user's voice over WiFi roaming setting associated with the current subscription. + * + * @throws IllegalArgumentException if the subscription associated with this operation is not + * active (SIM is not inserted, ESIM inactive) or invalid. * @return true if the user's setting for Voice over WiFi while roaming is enabled, false * if disabled. * @see #setVoWiFiRoamingSettingEnabled(boolean) @@ -660,6 +727,13 @@ public class ImsMmTelManager { public boolean isVoWiFiRoamingSettingEnabled() { try { return getITelephony().isVoWiFiRoamingSettingEnabled(mSubId); + } catch (ServiceSpecificException e) { + if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { + // Rethrow as runtime error to keep API compatible. + throw new IllegalArgumentException(e.getMessage()); + } else { + throw new RuntimeException(e.getMessage()); + } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } @@ -667,15 +741,24 @@ public class ImsMmTelManager { /** * Change the user's setting for Voice over WiFi while roaming. + * * @param isEnabled true if the user's setting for Voice over WiFi while roaming is enabled, * false otherwise. + * @throws IllegalArgumentException if the subscription associated with this operation is not + * active (SIM is not inserted, ESIM inactive) or invalid. * @see #isVoWiFiRoamingSettingEnabled() */ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiRoamingSettingEnabled(boolean isEnabled) { try { getITelephony().setVoWiFiRoamingSettingEnabled(mSubId, isEnabled); - return; + } catch (ServiceSpecificException e) { + if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { + // Rethrow as runtime error to keep API compatible. + throw new IllegalArgumentException(e.getMessage()); + } else { + throw new RuntimeException(e.getMessage()); + } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } @@ -691,19 +774,31 @@ public class ImsMmTelManager { * - {@link #WIFI_MODE_WIFI_ONLY} * - {@link #WIFI_MODE_CELLULAR_PREFERRED} * - {@link #WIFI_MODE_WIFI_PREFERRED} + * @throws IllegalArgumentException if the subscription associated with this operation is not + * active (SIM is not inserted, ESIM inactive) or invalid. * @see #setVoWiFiSettingEnabled(boolean) */ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiNonPersistent(boolean isCapable, int mode) { try { getITelephony().setVoWiFiNonPersistent(mSubId, isCapable, mode); - return; + } catch (ServiceSpecificException e) { + if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { + // Rethrow as runtime error to keep API compatible. + throw new IllegalArgumentException(e.getMessage()); + } else { + throw new RuntimeException(e.getMessage()); + } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } /** + * Returns the user's voice over WiFi Roaming mode setting associated with the device. + * + * @throws IllegalArgumentException if the subscription associated with this operation is not + * active (SIM is not inserted, ESIM inactive) or invalid. * @return The Voice over WiFi Mode preference set by the user, which can be one of the * following: * - {@link #WIFI_MODE_WIFI_ONLY} @@ -715,6 +810,13 @@ public class ImsMmTelManager { public @WiFiCallingMode int getVoWiFiModeSetting() { try { return getITelephony().getVoWiFiModeSetting(mSubId); + } catch (ServiceSpecificException e) { + if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { + // Rethrow as runtime error to keep API compatible. + throw new IllegalArgumentException(e.getMessage()); + } else { + throw new RuntimeException(e.getMessage()); + } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } @@ -727,13 +829,21 @@ public class ImsMmTelManager { * - {@link #WIFI_MODE_WIFI_ONLY} * - {@link #WIFI_MODE_CELLULAR_PREFERRED} * - {@link #WIFI_MODE_WIFI_PREFERRED} + * @throws IllegalArgumentException if the subscription associated with this operation is not + * active (SIM is not inserted, ESIM inactive) or invalid. * @see #getVoWiFiModeSetting() */ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiModeSetting(@WiFiCallingMode int mode) { try { getITelephony().setVoWiFiModeSetting(mSubId, mode); - return; + } catch (ServiceSpecificException e) { + if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { + // Rethrow as runtime error to keep API compatible. + throw new IllegalArgumentException(e.getMessage()); + } else { + throw new RuntimeException(e.getMessage()); + } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } @@ -748,12 +858,21 @@ public class ImsMmTelManager { * - {@link #WIFI_MODE_WIFI_ONLY} * - {@link #WIFI_MODE_CELLULAR_PREFERRED} * - {@link #WIFI_MODE_WIFI_PREFERRED} + * @throws IllegalArgumentException if the subscription associated with this operation is not + * active (SIM is not inserted, ESIM inactive) or invalid. * @see #setVoWiFiRoamingSettingEnabled(boolean) */ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public @WiFiCallingMode int getVoWiFiRoamingModeSetting() { try { return getITelephony().getVoWiFiRoamingModeSetting(mSubId); + } catch (ServiceSpecificException e) { + if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { + // Rethrow as runtime error to keep API compatible. + throw new IllegalArgumentException(e.getMessage()); + } else { + throw new RuntimeException(e.getMessage()); + } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } @@ -768,13 +887,21 @@ public class ImsMmTelManager { * - {@link #WIFI_MODE_WIFI_ONLY} * - {@link #WIFI_MODE_CELLULAR_PREFERRED} * - {@link #WIFI_MODE_WIFI_PREFERRED} + * @throws IllegalArgumentException if the subscription associated with this operation is not + * active (SIM is not inserted, ESIM inactive) or invalid. * @see #getVoWiFiRoamingModeSetting() */ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiRoamingModeSetting(@WiFiCallingMode int mode) { try { getITelephony().setVoWiFiRoamingModeSetting(mSubId, mode); - return; + } catch (ServiceSpecificException e) { + if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { + // Rethrow as runtime error to keep API compatible. + throw new IllegalArgumentException(e.getMessage()); + } else { + throw new RuntimeException(e.getMessage()); + } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } @@ -787,13 +914,21 @@ public class ImsMmTelManager { * {@link android.provider.Settings.Secure#RTT_CALLING_MODE}, which is the global user setting * for RTT. That value is enabled/disabled separately by the user through the Accessibility * settings. + * @throws IllegalArgumentException if the subscription associated with this operation is not + * active (SIM is not inserted, ESIM inactive) or invalid. * @param isEnabled if true RTT should be enabled during calls made on this subscription. */ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void setRttCapabilitySetting(boolean isEnabled) { try { getITelephony().setRttCapabilitySetting(mSubId, isEnabled); - return; + } catch (ServiceSpecificException e) { + if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { + // Rethrow as runtime error to keep API compatible. + throw new IllegalArgumentException(e.getMessage()); + } else { + throw new RuntimeException(e.getMessage()); + } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } @@ -801,6 +936,9 @@ public class ImsMmTelManager { /** * @return true if TTY over VoLTE is supported + * + * @throws IllegalArgumentException if the subscription associated with this operation is not + * active (SIM is not inserted, ESIM inactive) or invalid. * @see android.telecom.TelecomManager#getCurrentTtyMode * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL */ @@ -808,6 +946,13 @@ public class ImsMmTelManager { boolean isTtyOverVolteEnabled() { try { return getITelephony().isTtyOverVolteEnabled(mSubId); + } catch (ServiceSpecificException e) { + if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) { + // Rethrow as runtime error to keep API compatible. + throw new IllegalArgumentException(e.getMessage()); + } else { + throw new RuntimeException(e.getMessage()); + } } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } diff --git a/telephony/java/android/telephony/ims/aidl/IImsConfig.aidl b/telephony/java/android/telephony/ims/aidl/IImsConfig.aidl index 4433c1c03c1f..53e459697958 100644 --- a/telephony/java/android/telephony/ims/aidl/IImsConfig.aidl +++ b/telephony/java/android/telephony/ims/aidl/IImsConfig.aidl @@ -17,6 +17,8 @@ package android.telephony.ims.aidl; +import android.os.PersistableBundle; + import android.telephony.ims.aidl.IImsConfigCallback; import com.android.ims.ImsConfigListener; @@ -37,4 +39,5 @@ interface IImsConfig { int setConfigInt(int item, int value); // Return result code defined in ImsConfig#OperationStatusConstants int setConfigString(int item, String value); + void updateImsCarrierConfigs(in PersistableBundle bundle); } diff --git a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java index 321bfff40652..91b0a43844c9 100644 --- a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java @@ -19,6 +19,7 @@ package android.telephony.ims.stub; import android.annotation.IntDef; import android.annotation.SystemApi; import android.content.Context; +import android.os.PersistableBundle; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.telephony.ims.aidl.IImsConfig; @@ -182,6 +183,11 @@ public class ImsConfigImplBase { return retVal; } + @Override + public void updateImsCarrierConfigs(PersistableBundle bundle) throws RemoteException { + getImsConfigImpl().updateImsCarrierConfigs(bundle); + } + private ImsConfigImplBase getImsConfigImpl() throws RemoteException { ImsConfigImplBase ref = mImsConfigImplBaseWeakReference.get(); if (ref == null) { @@ -387,4 +393,11 @@ public class ImsConfigImplBase { // Base Implementation - To be overridden. return null; } + + /** + * @hide + */ + public void updateImsCarrierConfigs(PersistableBundle bundle) { + // Base Implementation - Should be overridden + } } 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/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/tools/preload2/src/com/android/preload/classdataretrieval/ClassDataRetriever.java b/tests/Codegen/src/com/android/codegentest/SampleDataClass.aidl index f04360fc1942..f14d47cde13f 100644 --- a/tools/preload2/src/com/android/preload/classdataretrieval/ClassDataRetriever.java +++ b/tests/Codegen/src/com/android/codegentest/SampleDataClass.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 The Android Open Source Project + * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,17 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package com.android.codegentest; -package com.android.preload.classdataretrieval; - -import com.android.ddmlib.Client; - -import java.util.Map; - -/** - * Retrieve a class-to-classloader map for loaded classes from the client. - */ -public interface ClassDataRetriever { - - public Map<String, String> getClassData(Client client); -} +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/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp index 11150dd8e882..b725920ba014 100644 --- a/tools/aapt2/link/ManifestFixer.cpp +++ b/tools/aapt2/link/ManifestFixer.cpp @@ -386,6 +386,9 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor, manifest_action["package-verifier"]; manifest_action["meta-data"] = meta_data_action; manifest_action["uses-split"].Action(RequiredNameIsJavaPackage); + manifest_action["queries"]["package"].Action(RequiredNameIsJavaPackage); + manifest_action["queries"]["intent"] = intent_filter_action; + // TODO: more complicated component name tag manifest_action["key-sets"]["key-set"]["public-key"]; manifest_action["key-sets"]["upgrade-key-set"]; 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/preload2/Android.bp b/tools/preload2/Android.bp deleted file mode 100644 index 5809421da3e8..000000000000 --- a/tools/preload2/Android.bp +++ /dev/null @@ -1,50 +0,0 @@ -// 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. - -java_library_host { - name: "preload2", - - srcs: ["src/**/*.java"], - - // To connect to devices (and take hprof dumps). - static_libs: [ - "ddmlib-prebuilt", - "tools-common-prebuilt", - - // To process hprof dumps. - "perflib-prebuilt", - - "trove-prebuilt", - "guavalib", - - // For JDWP access we use the framework in the JDWP tests from Apache Harmony, for - // convenience (and to not depend on internal JDK APIs). - "apache-harmony-jdwp-tests", - "junit", - ], - - // Copy to build artifacts - dist: { - targets: [ - "dist_files", - ], - }, -} - -// Copy the preload-tool shell script to the host's bin directory. -sh_binary_host { - name: "preload-tool", - src: "preload-tool", - required: ["preload2"], -} diff --git a/tools/preload2/preload-tool b/tools/preload2/preload-tool deleted file mode 100644 index 322b62fda071..000000000000 --- a/tools/preload2/preload-tool +++ /dev/null @@ -1,37 +0,0 @@ -# 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. - -# This script is used on the host only. It uses a common subset -# shell dialect that should work well. It is partially derived -# from art/tools/art. - -function follow_links() { - if [ z"$BASH_SOURCE" != z ]; then - file="$BASH_SOURCE" - else - file="$0" - fi - while [ -h "$file" ]; do - # On Mac OS, readlink -f doesn't work. - file="$(readlink "$file")" - done - echo "$file" -} - - -PROG_NAME="$(follow_links)" -PROG_DIR="$(cd "${PROG_NAME%/*}" ; pwd -P)" -ANDROID_ROOT=$PROG_DIR/.. - -java -cp $ANDROID_ROOT/framework/preload2.jar com.android.preload.Main $@ diff --git a/tools/preload2/src/com/android/preload/ClientUtils.java b/tools/preload2/src/com/android/preload/ClientUtils.java deleted file mode 100644 index 71ef025d6587..000000000000 --- a/tools/preload2/src/com/android/preload/ClientUtils.java +++ /dev/null @@ -1,224 +0,0 @@ -/* - * 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. - */ - -package com.android.preload; - -import com.android.ddmlib.AndroidDebugBridge; -import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener; -import com.android.ddmlib.Client; -import com.android.ddmlib.IDevice; - -/** - * Helper class for common communication with a Client (the ddms name for a running application). - * - * Instances take a default timeout parameter that's applied to all functions without explicit - * timeout. Timeouts are in milliseconds. - */ -public class ClientUtils { - - private int defaultTimeout; - - public ClientUtils() { - this(10000); - } - - public ClientUtils(int defaultTimeout) { - this.defaultTimeout = defaultTimeout; - } - - /** - * Shortcut for findClient with default timeout. - */ - public Client findClient(IDevice device, String processName, int processPid) { - return findClient(device, processName, processPid, defaultTimeout); - } - - /** - * Find the client with the given process name or process id. The name takes precedence over - * the process id (if valid). Stop looking after the given timeout. - * - * @param device The device to communicate with. - * @param processName The name of the process. May be null. - * @param processPid The pid of the process. Values less than or equal to zero are ignored. - * @param timeout The amount of milliseconds to wait, at most. - * @return The client, if found. Otherwise null. - */ - public Client findClient(IDevice device, String processName, int processPid, int timeout) { - WaitForClient wfc = new WaitForClient(device, processName, processPid, timeout); - return wfc.get(); - } - - /** - * Shortcut for findAllClients with default timeout. - */ - public Client[] findAllClients(IDevice device) { - return findAllClients(device, defaultTimeout); - } - - /** - * Retrieve all clients known to the given device. Wait at most the given timeout. - * - * @param device The device to investigate. - * @param timeout The amount of milliseconds to wait, at most. - * @return An array of clients running on the given device. May be null depending on the - * device implementation. - */ - public Client[] findAllClients(IDevice device, int timeout) { - if (device.hasClients()) { - return device.getClients(); - } - WaitForClients wfc = new WaitForClients(device, timeout); - return wfc.get(); - } - - private static class WaitForClient implements IClientChangeListener { - - private IDevice device; - private String processName; - private int processPid; - private long timeout; - private Client result; - - public WaitForClient(IDevice device, String processName, int processPid, long timeout) { - this.device = device; - this.processName = processName; - this.processPid = processPid; - this.timeout = timeout; - this.result = null; - } - - public Client get() { - synchronized (this) { - AndroidDebugBridge.addClientChangeListener(this); - - // Maybe it's already there. - if (result == null) { - result = searchForClient(device); - } - - if (result == null) { - try { - wait(timeout); - } catch (InterruptedException e) { - // Note: doesn't guard for spurious wakeup. - } - } - } - - AndroidDebugBridge.removeClientChangeListener(this); - return result; - } - - private Client searchForClient(IDevice device) { - if (processName != null) { - Client tmp = device.getClient(processName); - if (tmp != null) { - return tmp; - } - } - if (processPid > 0) { - String name = device.getClientName(processPid); - if (name != null && !name.isEmpty()) { - Client tmp = device.getClient(name); - if (tmp != null) { - return tmp; - } - } - } - if (processPid > 0) { - // Try manual search. - for (Client cl : device.getClients()) { - if (cl.getClientData().getPid() == processPid - && cl.getClientData().getClientDescription() != null) { - return cl; - } - } - } - return null; - } - - private boolean isTargetClient(Client c) { - if (processPid > 0 && c.getClientData().getPid() == processPid) { - return true; - } - if (processName != null - && processName.equals(c.getClientData().getClientDescription())) { - return true; - } - return false; - } - - @Override - public void clientChanged(Client arg0, int arg1) { - synchronized (this) { - if ((arg1 & Client.CHANGE_INFO) != 0 && (arg0.getDevice() == device)) { - if (isTargetClient(arg0)) { - result = arg0; - notifyAll(); - } - } - } - } - } - - private static class WaitForClients implements IClientChangeListener { - - private IDevice device; - private long timeout; - - public WaitForClients(IDevice device, long timeout) { - this.device = device; - this.timeout = timeout; - } - - public Client[] get() { - synchronized (this) { - AndroidDebugBridge.addClientChangeListener(this); - - if (device.hasClients()) { - return device.getClients(); - } - - try { - wait(timeout); // Note: doesn't guard for spurious wakeup. - } catch (InterruptedException exc) { - } - - // We will be woken up when the first client data arrives. Sleep a little longer - // to give (hopefully all of) the rest of the clients a chance to become available. - // Note: a loop with timeout is brittle as well and complicated, just accept this - // for now. - try { - Thread.sleep(500); - } catch (InterruptedException exc) { - } - } - - AndroidDebugBridge.removeClientChangeListener(this); - - return device.getClients(); - } - - @Override - public void clientChanged(Client arg0, int arg1) { - synchronized (this) { - if ((arg1 & Client.CHANGE_INFO) != 0 && (arg0.getDevice() == device)) { - notifyAll(); - } - } - } - } -} diff --git a/tools/preload2/src/com/android/preload/DeviceUtils.java b/tools/preload2/src/com/android/preload/DeviceUtils.java deleted file mode 100644 index 18cab7bee12d..000000000000 --- a/tools/preload2/src/com/android/preload/DeviceUtils.java +++ /dev/null @@ -1,420 +0,0 @@ -/* - * 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. - */ - -package com.android.preload; - -import com.android.ddmlib.AdbCommandRejectedException; -import com.android.ddmlib.AndroidDebugBridge; -import com.android.ddmlib.AndroidDebugBridge.IDeviceChangeListener; -import com.android.preload.classdataretrieval.hprof.Hprof; -import com.android.ddmlib.DdmPreferences; -import com.android.ddmlib.IDevice; -import com.android.ddmlib.IShellOutputReceiver; -import com.android.ddmlib.SyncException; -import com.android.ddmlib.TimeoutException; - -import java.io.File; -import java.io.IOException; -import java.util.Date; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; - -/** - * Helper class for some device routines. - */ -public class DeviceUtils { - - // Locations - private static final String PRELOADED_CLASSES_FILE = "/etc/preloaded-classes"; - // Shell commands - private static final String CREATE_EMPTY_PRELOADED_CMD = "touch " + PRELOADED_CLASSES_FILE; - private static final String DELETE_CACHE_CMD = "rm /data/dalvik-cache/*/*boot.art"; - private static final String DELETE_PRELOADED_CMD = "rm " + PRELOADED_CLASSES_FILE; - private static final String READ_PRELOADED_CMD = "cat " + PRELOADED_CLASSES_FILE; - private static final String START_SHELL_CMD = "start"; - private static final String STOP_SHELL_CMD = "stop"; - private static final String REMOUNT_SYSTEM_CMD = "mount -o rw,remount /system"; - private static final String UNSET_BOOTCOMPLETE_CMD = "setprop dev.bootcomplete \"0\""; - - public static void init(int debugPort) { - DdmPreferences.setSelectedDebugPort(debugPort); - - Hprof.init(); - - AndroidDebugBridge.init(true); - - AndroidDebugBridge.createBridge(); - } - - /** - * Run a command in the shell on the device. - */ - public static void doShell(IDevice device, String cmdline, long timeout, TimeUnit unit) { - doShell(device, cmdline, new NullShellOutputReceiver(), timeout, unit); - } - - /** - * Run a command in the shell on the device. Collects and returns the console output. - */ - public static String doShellReturnString(IDevice device, String cmdline, long timeout, - TimeUnit unit) { - CollectStringShellOutputReceiver rec = new CollectStringShellOutputReceiver(); - doShell(device, cmdline, rec, timeout, unit); - return rec.toString(); - } - - /** - * Run a command in the shell on the device, directing all output to the given receiver. - */ - public static void doShell(IDevice device, String cmdline, IShellOutputReceiver receiver, - long timeout, TimeUnit unit) { - try { - device.executeShellCommand(cmdline, receiver, timeout, unit); - } catch (Exception e) { - e.printStackTrace(); - } - } - - /** - * Run am start on the device. - */ - public static void doAMStart(IDevice device, String name, String activity) { - doShell(device, "am start -n " + name + " /." + activity, 30, TimeUnit.SECONDS); - } - - /** - * Find the device with the given serial. Give up after the given timeout (in milliseconds). - */ - public static IDevice findDevice(String serial, int timeout) { - WaitForDevice wfd = new WaitForDevice(serial, timeout); - return wfd.get(); - } - - /** - * Get all devices ddms knows about. Wait at most for the given timeout. - */ - public static IDevice[] findDevices(int timeout) { - WaitForDevice wfd = new WaitForDevice(null, timeout); - wfd.get(); - return AndroidDebugBridge.getBridge().getDevices(); - } - - /** - * Return the build type of the given device. This is the value of the "ro.build.type" - * system property. - */ - public static String getBuildType(IDevice device) { - try { - Future<String> buildType = device.getSystemProperty("ro.build.type"); - return buildType.get(500, TimeUnit.MILLISECONDS); - } catch (Exception e) { - } - return null; - } - - /** - * Check whether the given device has a pre-optimized boot image. More precisely, checks - * whether /system/framework/ * /boot.art exists. - */ - public static boolean hasPrebuiltBootImage(IDevice device) { - String ret = - doShellReturnString(device, "ls /system/framework/*/boot.art", 500, TimeUnit.MILLISECONDS); - - return !ret.contains("No such file or directory"); - } - - /** - * Write over the preloaded-classes file with an empty or existing file and regenerate the boot - * image as necessary. - * - * @param device - * @param pcFile - * @param bootTimeout - * @throws AdbCommandRejectedException - * @throws IOException - * @throws TimeoutException - * @throws SyncException - * @return true if successfully overwritten, false otherwise - */ - public static boolean overwritePreloaded(IDevice device, File pcFile, long bootTimeout) - throws AdbCommandRejectedException, IOException, TimeoutException, SyncException { - boolean writeEmpty = (pcFile == null); - if (writeEmpty) { - // Check if the preloaded-classes file is already empty. - String oldContent = - doShellReturnString(device, READ_PRELOADED_CMD, 1, TimeUnit.SECONDS); - if (oldContent.trim().equals("")) { - System.out.println("Preloaded-classes already empty."); - return true; - } - } - - // Stop the system server etc. - doShell(device, STOP_SHELL_CMD, 1, TimeUnit.SECONDS); - // Remount the read-only system partition - doShell(device, REMOUNT_SYSTEM_CMD, 1, TimeUnit.SECONDS); - // Delete the preloaded-classes file - doShell(device, DELETE_PRELOADED_CMD, 1, TimeUnit.SECONDS); - // Delete the dalvik cache files - doShell(device, DELETE_CACHE_CMD, 1, TimeUnit.SECONDS); - if (writeEmpty) { - // Write an empty preloaded-classes file - doShell(device, CREATE_EMPTY_PRELOADED_CMD, 500, TimeUnit.MILLISECONDS); - } else { - // Push the new preloaded-classes file - device.pushFile(pcFile.getAbsolutePath(), PRELOADED_CLASSES_FILE); - } - // Manually reset the boot complete flag - doShell(device, UNSET_BOOTCOMPLETE_CMD, 1, TimeUnit.SECONDS); - // Restart system server on the device - doShell(device, START_SHELL_CMD, 1, TimeUnit.SECONDS); - // Wait for the boot complete flag and return the outcome. - return waitForBootComplete(device, bootTimeout); - } - - private static boolean waitForBootComplete(IDevice device, long timeout) { - // Do a loop checking each second whether bootcomplete. Wait for at most the given - // threshold. - Date startDate = new Date(); - for (;;) { - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - // Ignore spurious wakeup. - } - // Check whether bootcomplete. - String ret = - doShellReturnString(device, "getprop dev.bootcomplete", 500, TimeUnit.MILLISECONDS); - if (ret.trim().equals("1")) { - break; - } - System.out.println("Still not booted: " + ret); - - // Check whether we timed out. This is a simplistic check that doesn't take into account - // things like switches in time. - Date endDate = new Date(); - long seconds = - TimeUnit.SECONDS.convert(endDate.getTime() - startDate.getTime(), TimeUnit.MILLISECONDS); - if (seconds > timeout) { - return false; - } - } - - return true; - } - - /** - * Enable method-tracing on device. The system should be restarted after this. - */ - public static void enableTracing(IDevice device) { - // Disable selinux. - doShell(device, "setenforce 0", 100, TimeUnit.MILLISECONDS); - - // Make the profile directory world-writable. - doShell(device, "chmod 777 /data/dalvik-cache/profiles", 100, TimeUnit.MILLISECONDS); - - // Enable streaming method tracing with a small 1K buffer. - doShell(device, "setprop dalvik.vm.method-trace true", 100, TimeUnit.MILLISECONDS); - doShell(device, "setprop dalvik.vm.method-trace-file " - + "/data/dalvik-cache/profiles/zygote.trace.bin", 100, TimeUnit.MILLISECONDS); - doShell(device, "setprop dalvik.vm.method-trace-file-siz 1024", 100, TimeUnit.MILLISECONDS); - doShell(device, "setprop dalvik.vm.method-trace-stream true", 100, TimeUnit.MILLISECONDS); - } - - private static class NullShellOutputReceiver implements IShellOutputReceiver { - @Override - public boolean isCancelled() { - return false; - } - - @Override - public void flush() {} - - @Override - public void addOutput(byte[] arg0, int arg1, int arg2) {} - } - - private static class CollectStringShellOutputReceiver implements IShellOutputReceiver { - - private StringBuilder builder = new StringBuilder(); - - @Override - public String toString() { - String ret = builder.toString(); - // Strip trailing newlines. They are especially ugly because adb uses DOS line endings. - while (ret.endsWith("\r") || ret.endsWith("\n")) { - ret = ret.substring(0, ret.length() - 1); - } - return ret; - } - - @Override - public void addOutput(byte[] arg0, int arg1, int arg2) { - builder.append(new String(arg0, arg1, arg2)); - } - - @Override - public void flush() {} - - @Override - public boolean isCancelled() { - return false; - } - } - - private static class WaitForDevice { - - private String serial; - private long timeout; - private IDevice device; - - public WaitForDevice(String serial, long timeout) { - this.serial = serial; - this.timeout = timeout; - device = null; - } - - public IDevice get() { - if (device == null) { - WaitForDeviceListener wfdl = new WaitForDeviceListener(serial); - synchronized (wfdl) { - AndroidDebugBridge.addDeviceChangeListener(wfdl); - - // Check whether we already know about this device. - IDevice[] devices = AndroidDebugBridge.getBridge().getDevices(); - if (serial != null) { - for (IDevice d : devices) { - if (serial.equals(d.getSerialNumber())) { - // Only accept if there are clients already. Else wait for the callback informing - // us that we now have clients. - if (d.hasClients()) { - device = d; - } - - break; - } - } - } else { - if (devices.length > 0) { - device = devices[0]; - } - } - - if (device == null) { - try { - wfdl.wait(timeout); - } catch (InterruptedException e) { - // Ignore spurious wakeups. - } - device = wfdl.getDevice(); - } - - AndroidDebugBridge.removeDeviceChangeListener(wfdl); - } - } - - if (device != null) { - // Wait for clients. - WaitForClientsListener wfcl = new WaitForClientsListener(device); - synchronized (wfcl) { - AndroidDebugBridge.addDeviceChangeListener(wfcl); - - if (!device.hasClients()) { - try { - wfcl.wait(timeout); - } catch (InterruptedException e) { - // Ignore spurious wakeups. - } - } - - AndroidDebugBridge.removeDeviceChangeListener(wfcl); - } - } - - return device; - } - - private static class WaitForDeviceListener implements IDeviceChangeListener { - - private String serial; - private IDevice device; - - public WaitForDeviceListener(String serial) { - this.serial = serial; - } - - public IDevice getDevice() { - return device; - } - - @Override - public void deviceChanged(IDevice arg0, int arg1) { - // We may get a device changed instead of connected. Handle like a connection. - deviceConnected(arg0); - } - - @Override - public void deviceConnected(IDevice arg0) { - if (device != null) { - // Ignore updates. - return; - } - - if (serial == null || serial.equals(arg0.getSerialNumber())) { - device = arg0; - synchronized (this) { - notifyAll(); - } - } - } - - @Override - public void deviceDisconnected(IDevice arg0) { - // Ignore disconnects. - } - - } - - private static class WaitForClientsListener implements IDeviceChangeListener { - - private IDevice myDevice; - - public WaitForClientsListener(IDevice myDevice) { - this.myDevice = myDevice; - } - - @Override - public void deviceChanged(IDevice arg0, int arg1) { - if (arg0 == myDevice && (arg1 & IDevice.CHANGE_CLIENT_LIST) != 0) { - // Got a client list, done here. - synchronized (this) { - notifyAll(); - } - } - } - - @Override - public void deviceConnected(IDevice arg0) { - } - - @Override - public void deviceDisconnected(IDevice arg0) { - } - - } - } - -} diff --git a/tools/preload2/src/com/android/preload/DumpData.java b/tools/preload2/src/com/android/preload/DumpData.java deleted file mode 100644 index d99722416a1d..000000000000 --- a/tools/preload2/src/com/android/preload/DumpData.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * 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. - */ - -package com.android.preload; - -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -/** - * Holds the collected data for a process. - */ -public class DumpData { - /** - * Name of the package (=application). - */ - String packageName; - - /** - * A map of class name to a string for the classloader. This may be a toString equivalent, - * or just a unique ID. - */ - Map<String, String> dumpData; - - /** - * The Date when this data was captured. Mostly for display purposes. - */ - Date date; - - /** - * A cached value for the number of boot classpath classes (classloader value in dumpData is - * null). - */ - int bcpClasses; - - public DumpData(String packageName, Map<String, String> dumpData, Date date) { - this.packageName = packageName; - this.dumpData = dumpData; - this.date = date; - - countBootClassPath(); - } - - public String getPackageName() { - return packageName; - } - - public Date getDate() { - return date; - } - - public Map<String, String> getDumpData() { - return dumpData; - } - - public void countBootClassPath() { - bcpClasses = 0; - for (Map.Entry<String, String> e : dumpData.entrySet()) { - if (e.getValue() == null) { - bcpClasses++; - } - } - } - - // Return an inverted mapping. - public Map<String, Set<String>> invertData() { - Map<String, Set<String>> ret = new HashMap<>(); - for (Map.Entry<String, String> e : dumpData.entrySet()) { - if (!ret.containsKey(e.getValue())) { - ret.put(e.getValue(), new HashSet<String>()); - } - ret.get(e.getValue()).add(e.getKey()); - } - return ret; - } -}
\ No newline at end of file diff --git a/tools/preload2/src/com/android/preload/DumpDataIO.java b/tools/preload2/src/com/android/preload/DumpDataIO.java deleted file mode 100644 index 28625c5531e4..000000000000 --- a/tools/preload2/src/com/android/preload/DumpDataIO.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * 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. - */ - -package com.android.preload; - -import org.xml.sax.Attributes; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; -import org.xml.sax.XMLReader; -import org.xml.sax.helpers.DefaultHandler; - -import java.io.File; -import java.io.FileReader; -import java.text.DateFormat; -import java.util.Collection; -import java.util.Date; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.Map; - -import javax.xml.parsers.SAXParser; -import javax.xml.parsers.SAXParserFactory; - -/** - * Helper class for serialization and deserialization of a collection of DumpData objects to XML. - */ -public class DumpDataIO { - - /** - * Serialize the given collection to an XML document. Returns the produced string. - */ - public static String serialize(Collection<DumpData> data) { - // We'll do this by hand, constructing a DOM or similar is too complicated for our simple - // use case. - - StringBuilder sb = new StringBuilder(); - sb.append("<preloaded-classes-data>\n"); - - for (DumpData d : data) { - serialize(d, sb); - } - - sb.append("</preloaded-classes-data>\n"); - return sb.toString(); - } - - private static void serialize(DumpData d, StringBuilder sb) { - sb.append("<data package=\"" + d.packageName + "\" date=\"" + - DateFormat.getDateTimeInstance().format(d.date) +"\">\n"); - - for (Map.Entry<String, String> e : d.dumpData.entrySet()) { - sb.append("<class name=\"" + e.getKey() + "\" classloader=\"" + e.getValue() + "\"/>\n"); - } - - sb.append("</data>\n"); - } - - /** - * Load a collection of DumpData objects from the given file. - */ - public static Collection<DumpData> deserialize(File f) throws Exception { - // Use SAX parsing. Our format is very simple. Don't do any schema validation or such. - - SAXParserFactory spf = SAXParserFactory.newInstance(); - spf.setNamespaceAware(false); - SAXParser saxParser = spf.newSAXParser(); - - XMLReader xmlReader = saxParser.getXMLReader(); - DumpDataContentHandler ddch = new DumpDataContentHandler(); - xmlReader.setContentHandler(ddch); - xmlReader.parse(new InputSource(new FileReader(f))); - - return ddch.data; - } - - private static class DumpDataContentHandler extends DefaultHandler { - Collection<DumpData> data = new LinkedList<DumpData>(); - DumpData openData = null; - - @Override - public void startElement(String uri, String localName, String qName, Attributes attributes) - throws SAXException { - if (qName.equals("data")) { - if (openData != null) { - throw new IllegalStateException(); - } - String pkg = attributes.getValue("package"); - String dateString = attributes.getValue("date"); - - if (pkg == null || dateString == null) { - throw new IllegalArgumentException(); - } - - try { - Date date = DateFormat.getDateTimeInstance().parse(dateString); - openData = new DumpData(pkg, new HashMap<String, String>(), date); - } catch (Exception e) { - throw new RuntimeException(e); - } - } else if (qName.equals("class")) { - if (openData == null) { - throw new IllegalStateException(); - } - String className = attributes.getValue("name"); - String classLoader = attributes.getValue("classloader"); - - if (className == null || classLoader == null) { - throw new IllegalArgumentException(); - } - - openData.dumpData.put(className, classLoader.equals("null") ? null : classLoader); - } - } - - @Override - public void endElement(String uri, String localName, String qName) throws SAXException { - if (qName.equals("data")) { - if (openData == null) { - throw new IllegalStateException(); - } - openData.countBootClassPath(); - - data.add(openData); - openData = null; - } - } - } -} diff --git a/tools/preload2/src/com/android/preload/DumpTableModel.java b/tools/preload2/src/com/android/preload/DumpTableModel.java deleted file mode 100644 index d97cbf0df5e5..000000000000 --- a/tools/preload2/src/com/android/preload/DumpTableModel.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * 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. - */ - -package com.android.preload; - -import java.util.ArrayList; -import java.util.List; - -import javax.swing.table.AbstractTableModel; - -/** - * A table model for collected DumpData. This is both the internal storage as well as the model - * for display. - */ -public class DumpTableModel extends AbstractTableModel { - - private List<DumpData> data = new ArrayList<DumpData>(); - - public void addData(DumpData d) { - data.add(d); - fireTableRowsInserted(data.size() - 1, data.size() - 1); - } - - public void clear() { - int size = data.size(); - if (size > 0) { - data.clear(); - fireTableRowsDeleted(0, size - 1); - } - } - - public List<DumpData> getData() { - return data; - } - - @Override - public int getRowCount() { - return data.size(); - } - - @Override - public int getColumnCount() { - return 4; - } - - @Override - public String getColumnName(int column) { - switch (column) { - case 0: - return "Package"; - case 1: - return "Date"; - case 2: - return "# All Classes"; - case 3: - return "# Boot Classpath Classes"; - - default: - throw new IndexOutOfBoundsException(String.valueOf(column)); - } - } - - @Override - public Object getValueAt(int rowIndex, int columnIndex) { - DumpData d = data.get(rowIndex); - switch (columnIndex) { - case 0: - return d.packageName; - case 1: - return d.date; - case 2: - return d.dumpData.size(); - case 3: - return d.bcpClasses; - - default: - throw new IndexOutOfBoundsException(String.valueOf(columnIndex)); - } - } -}
\ No newline at end of file diff --git a/tools/preload2/src/com/android/preload/Main.java b/tools/preload2/src/com/android/preload/Main.java deleted file mode 100644 index 2265e9557c4b..000000000000 --- a/tools/preload2/src/com/android/preload/Main.java +++ /dev/null @@ -1,341 +0,0 @@ -/* - * 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. - */ - -package com.android.preload; - -import com.android.ddmlib.Client; -import com.android.ddmlib.IDevice; -import com.android.preload.actions.ClearTableAction; -import com.android.preload.actions.ComputeThresholdAction; -import com.android.preload.actions.ComputeThresholdXAction; -import com.android.preload.actions.DeviceSpecific; -import com.android.preload.actions.ExportAction; -import com.android.preload.actions.ImportAction; -import com.android.preload.actions.ReloadListAction; -import com.android.preload.actions.RunMonkeyAction; -import com.android.preload.actions.ScanAllPackagesAction; -import com.android.preload.actions.ScanPackageAction; -import com.android.preload.actions.ShowDataAction; -import com.android.preload.actions.WritePreloadedClassesAction; -import com.android.preload.classdataretrieval.ClassDataRetriever; -import com.android.preload.classdataretrieval.hprof.Hprof; -import com.android.preload.classdataretrieval.jdwp.JDWPClassDataRetriever; -import com.android.preload.ui.IUI; -import com.android.preload.ui.SequenceUI; -import com.android.preload.ui.SwingUI; - -import java.io.File; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.NoSuchElementException; - -import javax.swing.Action; -import javax.swing.DefaultListModel; - -public class Main { - - /** - * Enable tracing mode. This is a work-in-progress to derive compiled-methods data, so it is - * off for now. - */ - public final static boolean ENABLE_TRACING = false; - - /** - * Ten-second timeout. - */ - public final static int DEFAULT_TIMEOUT_MILLIS = 10 * 1000; - - /** - * Hprof timeout. Two minutes. - */ - public final static int HPROF_TIMEOUT_MILLIS = 120 * 1000; - - private IDevice device; - private static ClientUtils clientUtils; - - private DumpTableModel dataTableModel; - private DefaultListModel<Client> clientListModel; - - private IUI ui; - - // Actions that need to be updated once a device is selected. - private Collection<DeviceSpecific> deviceSpecificActions; - - // Current main instance. - private static Main top; - private static boolean useJdwpClassDataRetriever = false; - - public final static String CLASS_PRELOAD_BLACKLIST = "android.app.AlarmManager$" + "|" - + "android.app.SearchManager$" + "|" + "android.os.FileObserver$" + "|" - + "com.android.server.PackageManagerService\\$AppDirObserver$" + "|" + - - - // Threads - "android.os.AsyncTask$" + "|" + "android.pim.ContactsAsyncHelper$" + "|" - + "android.webkit.WebViewClassic\\$1$" + "|" + "java.lang.ProcessManager$" + "|" - + "(.*\\$NoPreloadHolder$)"; - - public final static String SCAN_ALL_CMD = "scan-all"; - public final static String SCAN_PACKAGE_CMD = "scan"; - public final static String COMPUTE_FILE_CMD = "comp"; - public final static String EXPORT_CMD = "export"; - public final static String IMPORT_CMD = "import"; - public final static String WRITE_CMD = "write"; - - /** - * @param args - */ - public static void main(String[] args) { - Main m; - if (args.length > 0 && args[0].equals("--seq")) { - m = createSequencedMain(args); - } else { - m = new Main(new SwingUI()); - } - - top = m; - m.startUp(); - } - - public Main(IUI ui) { - this.ui = ui; - - clientListModel = new DefaultListModel<Client>(); - dataTableModel = new DumpTableModel(); - - clientUtils = new ClientUtils(DEFAULT_TIMEOUT_MILLIS); // Client utils with 10s timeout. - - List<Action> actions = new ArrayList<Action>(); - actions.add(new ReloadListAction(clientUtils, null, clientListModel)); - actions.add(new ClearTableAction(dataTableModel)); - actions.add(new RunMonkeyAction(null, dataTableModel)); - actions.add(new ScanPackageAction(clientUtils, null, dataTableModel)); - actions.add(new ScanAllPackagesAction(clientUtils, null, dataTableModel)); - actions.add(new ComputeThresholdAction("Compute preloaded-classes", dataTableModel, 2, - CLASS_PRELOAD_BLACKLIST)); - actions.add(new ComputeThresholdAction("Compute compiled-classes", dataTableModel, 1, - null)); - actions.add(new ComputeThresholdXAction("Compute(X)", dataTableModel, - CLASS_PRELOAD_BLACKLIST)); - actions.add(new WritePreloadedClassesAction(clientUtils, null, dataTableModel)); - actions.add(new ShowDataAction(dataTableModel)); - actions.add(new ImportAction(dataTableModel)); - actions.add(new ExportAction(dataTableModel)); - - deviceSpecificActions = new ArrayList<DeviceSpecific>(); - for (Action a : actions) { - if (a instanceof DeviceSpecific) { - deviceSpecificActions.add((DeviceSpecific)a); - } - } - - ui.prepare(clientListModel, dataTableModel, actions); - } - - /** - * @param args - * @return - */ - private static Main createSequencedMain(String[] args) { - SequenceUI ui = new SequenceUI(); - Main main = new Main(ui); - - Iterator<String> it = Arrays.asList(args).iterator(); - it.next(); // --seq - // Setup - ui.choice("#" + it.next()); // Device. - ui.confirmNo(); // Prepare: no. - // Actions - try { - while (it.hasNext()) { - String op = it.next(); - // Operation: Scan a single package - if (SCAN_PACKAGE_CMD.equals(op)) { - System.out.println("Scanning package."); - ui.action(ScanPackageAction.class); - ui.client(it.next()); - // Operation: Scan all packages - } else if (SCAN_ALL_CMD.equals(op)) { - System.out.println("Scanning all packages."); - ui.action(ScanAllPackagesAction.class); - // Operation: Export the output to a file - } else if (EXPORT_CMD.equals(op)) { - System.out.println("Exporting data."); - ui.action(ExportAction.class); - ui.output(new File(it.next())); - // Operation: Import the input from a file or directory - } else if (IMPORT_CMD.equals(op)) { - System.out.println("Importing data."); - File file = new File(it.next()); - if (!file.exists()) { - throw new RuntimeException( - String.format("File does not exist, %s.", file.getAbsolutePath())); - } else if (file.isFile()) { - ui.action(ImportAction.class); - ui.input(file); - } else if (file.isDirectory()) { - for (File content : file.listFiles()) { - ui.action(ImportAction.class); - ui.input(content); - } - } - // Operation: Compute preloaded classes with specific threshold - } else if (COMPUTE_FILE_CMD.equals(op)) { - System.out.println("Compute preloaded classes."); - ui.action(ComputeThresholdXAction.class); - ui.input(it.next()); - ui.confirmYes(); - ui.output(new File(it.next())); - // Operation: Write preloaded classes from a specific file - } else if (WRITE_CMD.equals(op)) { - System.out.println("Writing preloaded classes."); - ui.action(WritePreloadedClassesAction.class); - ui.input(new File(it.next())); - } - } - } catch (NoSuchElementException e) { - System.out.println("Failed to parse action sequence correctly."); - throw e; - } - - return main; - } - - public static IUI getUI() { - return top.ui; - } - - public static ClassDataRetriever getClassDataRetriever() { - if (useJdwpClassDataRetriever) { - return new JDWPClassDataRetriever(); - } else { - return new Hprof(HPROF_TIMEOUT_MILLIS); - } - } - - public IDevice getDevice() { - return device; - } - - public void setDevice(IDevice device) { - this.device = device; - for (DeviceSpecific ds : deviceSpecificActions) { - ds.setDevice(device); - } - } - - public DefaultListModel<Client> getClientListModel() { - return clientListModel; - } - - static class DeviceWrapper { - IDevice device; - - public DeviceWrapper(IDevice d) { - device = d; - } - - @Override - public String toString() { - return device.getName() + " (#" + device.getSerialNumber() + ")"; - } - } - - private void startUp() { - getUI().showWaitDialog(); - initDevice(); - - // Load clients. - new ReloadListAction(clientUtils, getDevice(), clientListModel).run(); - - getUI().hideWaitDialog(); - getUI().ready(); - } - - private void initDevice() { - DeviceUtils.init(DEFAULT_TIMEOUT_MILLIS); - - IDevice devices[] = DeviceUtils.findDevices(DEFAULT_TIMEOUT_MILLIS); - if (devices == null || devices.length == 0) { - throw new RuntimeException("Could not find any devices..."); - } - - getUI().hideWaitDialog(); - - DeviceWrapper deviceWrappers[] = new DeviceWrapper[devices.length]; - for (int i = 0; i < devices.length; i++) { - deviceWrappers[i] = new DeviceWrapper(devices[i]); - } - - DeviceWrapper ret = Main.getUI().showChoiceDialog("Choose a device", "Choose device", - deviceWrappers); - if (ret != null) { - setDevice(ret.device); - } else { - System.exit(0); - } - - boolean prepare = Main.getUI().showConfirmDialog("Prepare device?", - "Do you want to prepare the device? This is highly recommended."); - if (prepare) { - String buildType = DeviceUtils.getBuildType(device); - if (buildType == null || (!buildType.equals("userdebug") && !buildType.equals("eng"))) { - Main.getUI().showMessageDialog("Need a userdebug or eng build! (Found " + buildType - + ")"); - return; - } - if (DeviceUtils.hasPrebuiltBootImage(device)) { - Main.getUI().showMessageDialog("Cannot prepare a device with pre-optimized boot " - + "image!"); - return; - } - - if (ENABLE_TRACING) { - DeviceUtils.enableTracing(device); - } - - Main.getUI().showMessageDialog("The device will reboot. This will potentially take a " - + "long time. Please be patient."); - boolean success = false; - try { - success = DeviceUtils.overwritePreloaded(device, null, 15 * 60); - } catch (Exception e) { - System.err.println(e); - } finally { - if (!success) { - Main.getUI().showMessageDialog( - "Removing preloaded-classes failed unexpectedly!"); - } - } - } - } - - public static Map<String, String> findAndGetClassData(IDevice device, String packageName) - throws Exception { - Client client = clientUtils.findClient(device, packageName, -1); - if (client == null) { - throw new RuntimeException("Could not find client..."); - } - System.out.println("Found client: " + client); - - return getClassDataRetriever().getClassData(client); - } - -} diff --git a/tools/preload2/src/com/android/preload/actions/AbstractThreadedAction.java b/tools/preload2/src/com/android/preload/actions/AbstractThreadedAction.java deleted file mode 100644 index 5787d8507230..000000000000 --- a/tools/preload2/src/com/android/preload/actions/AbstractThreadedAction.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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. - */ - -package com.android.preload.actions; - -import com.android.preload.Main; -import java.awt.event.ActionEvent; - -import javax.swing.AbstractAction; - -public abstract class AbstractThreadedAction extends AbstractAction implements Runnable { - - protected AbstractThreadedAction(String title) { - super(title); - } - - @Override - public void actionPerformed(ActionEvent e) { - if (Main.getUI().isSingleThreaded()) { - run(); - } else { - new Thread(this).start(); - } - } - -} diff --git a/tools/preload2/src/com/android/preload/actions/AbstractThreadedDeviceSpecificAction.java b/tools/preload2/src/com/android/preload/actions/AbstractThreadedDeviceSpecificAction.java deleted file mode 100644 index 7906417b7a8d..000000000000 --- a/tools/preload2/src/com/android/preload/actions/AbstractThreadedDeviceSpecificAction.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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. - */ - -package com.android.preload.actions; - -import com.android.ddmlib.IDevice; - -import java.awt.event.ActionEvent; - -public abstract class AbstractThreadedDeviceSpecificAction extends AbstractThreadedAction - implements DeviceSpecific { - - protected IDevice device; - - protected AbstractThreadedDeviceSpecificAction(String title, IDevice device) { - super(title); - this.device = device; - } - - @Override - public void setDevice(IDevice device) { - this.device = device; - } - - @Override - public void actionPerformed(ActionEvent e) { - if (device == null) { - return; - } - super.actionPerformed(e); - } -} diff --git a/tools/preload2/src/com/android/preload/actions/ClearTableAction.java b/tools/preload2/src/com/android/preload/actions/ClearTableAction.java deleted file mode 100644 index c0e4795b6d90..000000000000 --- a/tools/preload2/src/com/android/preload/actions/ClearTableAction.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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. - */ - -package com.android.preload.actions; - -import com.android.preload.DumpTableModel; - -import java.awt.event.ActionEvent; - -import javax.swing.AbstractAction; - -public class ClearTableAction extends AbstractAction { - private final DumpTableModel dataTableModel; - - public ClearTableAction(DumpTableModel dataTableModel) { - super("Clear"); - this.dataTableModel = dataTableModel; - } - - @Override - public void actionPerformed(ActionEvent e) { - dataTableModel.clear(); - } -}
\ No newline at end of file diff --git a/tools/preload2/src/com/android/preload/actions/ComputeThresholdAction.java b/tools/preload2/src/com/android/preload/actions/ComputeThresholdAction.java deleted file mode 100644 index 3a7f7f74d755..000000000000 --- a/tools/preload2/src/com/android/preload/actions/ComputeThresholdAction.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * 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. - */ - -package com.android.preload.actions; - -import com.android.preload.DumpData; -import com.android.preload.DumpTableModel; -import com.android.preload.Main; - -import java.awt.event.ActionEvent; -import java.io.File; -import java.io.PrintWriter; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeSet; -import java.util.regex.Pattern; - -import javax.swing.AbstractAction; - -/** - * Compute an intersection of classes from the given data. A class is in the intersection if it - * appears in at least the number of threshold given packages. An optional blacklist can be - * used to filter classes from the intersection. - */ -public class ComputeThresholdAction extends AbstractThreadedAction { - protected int threshold; - private Pattern blacklist; - private DumpTableModel dataTableModel; - - /** - * Create an action with the given parameters. The blacklist is a regular expression - * that filters classes. - */ - public ComputeThresholdAction(String name, DumpTableModel dataTableModel, int threshold, - String blacklist) { - super(name); - this.dataTableModel = dataTableModel; - this.threshold = threshold; - if (blacklist != null) { - this.blacklist = Pattern.compile(blacklist); - } - } - - @Override - public void actionPerformed(ActionEvent e) { - List<DumpData> data = dataTableModel.getData(); - if (data.size() == 0) { - Main.getUI().showMessageDialog("No data available, please scan packages or run " - + "monkeys."); - return; - } - if (data.size() == 1) { - Main.getUI().showMessageDialog("Cannot compute list from only one data set, please " - + "scan packages or run monkeys."); - return; - } - - super.actionPerformed(e); - } - - @Override - public void run() { - Main.getUI().showWaitDialog(); - - Map<String, Set<String>> uses = new HashMap<String, Set<String>>(); - for (DumpData d : dataTableModel.getData()) { - Main.getUI().updateWaitDialog("Merging " + d.getPackageName()); - updateClassUse(d.getPackageName(), uses, getBootClassPathClasses(d.getDumpData())); - } - - Main.getUI().updateWaitDialog("Computing thresholded set"); - Set<String> result = fromThreshold(uses, blacklist, threshold); - Main.getUI().hideWaitDialog(); - - boolean ret = Main.getUI().showConfirmDialog("Computed a set with " + result.size() - + " classes, would you like to save to disk?", "Save?"); - if (ret) { - File f = Main.getUI().showSaveDialog(); - if (f != null) { - saveSet(result, f); - } - } - } - - private Set<String> fromThreshold(Map<String, Set<String>> classUses, Pattern blacklist, - int threshold) { - TreeSet<String> ret = new TreeSet<>(); // TreeSet so it's nicely ordered by name. - - for (Map.Entry<String, Set<String>> e : classUses.entrySet()) { - if (e.getValue().size() >= threshold) { - if (blacklist == null || !blacklist.matcher(e.getKey()).matches()) { - ret.add(e.getKey()); - } - } - } - - return ret; - } - - private static void updateClassUse(String pkg, Map<String, Set<String>> classUses, - Set<String> classes) { - for (String className : classes) { - Set<String> old = classUses.get(className); - if (old == null) { - classUses.put(className, new HashSet<String>()); - } - classUses.get(className).add(pkg); - } - } - - private static Set<String> getBootClassPathClasses(Map<String, String> source) { - Set<String> ret = new HashSet<>(); - for (Map.Entry<String, String> e : source.entrySet()) { - if (e.getValue() == null) { - ret.add(e.getKey()); - } - } - return ret; - } - - private static void saveSet(Set<String> result, File f) { - try { - PrintWriter out = new PrintWriter(f); - for (String s : result) { - out.println(s); - } - out.close(); - } catch (Exception e) { - e.printStackTrace(); - } - } -}
\ No newline at end of file diff --git a/tools/preload2/src/com/android/preload/actions/ComputeThresholdXAction.java b/tools/preload2/src/com/android/preload/actions/ComputeThresholdXAction.java deleted file mode 100644 index 3ec0a4c18db1..000000000000 --- a/tools/preload2/src/com/android/preload/actions/ComputeThresholdXAction.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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. - */ - -package com.android.preload.actions; - -import com.android.preload.DumpTableModel; -import com.android.preload.Main; - -public class ComputeThresholdXAction extends ComputeThresholdAction { - - public ComputeThresholdXAction(String name, DumpTableModel dataTableModel, - String blacklist) { - super(name, dataTableModel, 1, blacklist); - } - - @Override - public void run() { - String value = Main.getUI().showInputDialog("Threshold?"); - - if (value != null) { - try { - threshold = Integer.parseInt(value); - super.run(); - } catch (Exception exc) { - } - } - } -}
\ No newline at end of file diff --git a/tools/preload2/src/com/android/preload/actions/DeviceSpecific.java b/tools/preload2/src/com/android/preload/actions/DeviceSpecific.java deleted file mode 100644 index 35a8f26a99fe..000000000000 --- a/tools/preload2/src/com/android/preload/actions/DeviceSpecific.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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. - */ - -package com.android.preload.actions; - -import com.android.ddmlib.IDevice; - -/** - * Marks an action as being device-specific. The user must set the device through the specified - * method if the device selection changes. - * - * Implementors must tolerate a null device (for example, with a no-op). This includes calling - * any methods before setDevice has been called. - */ -public interface DeviceSpecific { - - /** - * Set the device that should be used. Note that there is no restriction on calling other - * methods of the implementor before a setDevice call. Neither is device guaranteed to be - * non-null. - * - * @param device The device to use going forward. - */ - public void setDevice(IDevice device); -} diff --git a/tools/preload2/src/com/android/preload/actions/ExportAction.java b/tools/preload2/src/com/android/preload/actions/ExportAction.java deleted file mode 100644 index 848a56826788..000000000000 --- a/tools/preload2/src/com/android/preload/actions/ExportAction.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * 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. - */ - -package com.android.preload.actions; - -import com.android.preload.DumpDataIO; -import com.android.preload.DumpTableModel; -import com.android.preload.Main; -import java.awt.event.ActionEvent; -import java.io.File; -import java.io.PrintWriter; - -public class ExportAction extends AbstractThreadedAction { - private File lastSaveFile; - private DumpTableModel dataTableModel; - - public ExportAction(DumpTableModel dataTableModel) { - super("Export data"); - this.dataTableModel = dataTableModel; - } - - @Override - public void actionPerformed(ActionEvent e) { - lastSaveFile = Main.getUI().showSaveDialog(); - if (lastSaveFile != null) { - super.actionPerformed(e); - } - } - - @Override - public void run() { - Main.getUI().showWaitDialog(); - - String serialized = DumpDataIO.serialize(dataTableModel.getData()); - - if (serialized != null) { - try { - PrintWriter out = new PrintWriter(lastSaveFile); - out.println(serialized); - out.close(); - - Main.getUI().hideWaitDialog(); - } catch (Exception e) { - Main.getUI().hideWaitDialog(); - Main.getUI().showMessageDialog("Failed writing: " + e.getMessage()); - } - } - } -}
\ No newline at end of file diff --git a/tools/preload2/src/com/android/preload/actions/ImportAction.java b/tools/preload2/src/com/android/preload/actions/ImportAction.java deleted file mode 100644 index bfeeb836fd45..000000000000 --- a/tools/preload2/src/com/android/preload/actions/ImportAction.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * 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. - */ - -package com.android.preload.actions; - -import com.android.preload.DumpData; -import com.android.preload.DumpDataIO; -import com.android.preload.DumpTableModel; -import com.android.preload.Main; - -import java.awt.event.ActionEvent; -import java.io.File; -import java.util.Collection; - -import javax.swing.AbstractAction; - -public class ImportAction extends AbstractThreadedAction { - private File[] lastOpenFiles; - private DumpTableModel dataTableModel; - - public ImportAction(DumpTableModel dataTableModel) { - super("Import data"); - this.dataTableModel = dataTableModel; - } - - @Override - public void actionPerformed(ActionEvent e) { - lastOpenFiles = Main.getUI().showOpenDialog(true); - if (lastOpenFiles != null) { - super.actionPerformed(e); - } - } - - @Override - public void run() { - Main.getUI().showWaitDialog(); - - try { - for (File f : lastOpenFiles) { - try { - Collection<DumpData> data = DumpDataIO.deserialize(f); - - for (DumpData d : data) { - dataTableModel.addData(d); - } - } catch (Exception e) { - Main.getUI().showMessageDialog("Failed reading: " + e.getMessage()); - } - } - } finally { - Main.getUI().hideWaitDialog(); - } - - } -}
\ No newline at end of file diff --git a/tools/preload2/src/com/android/preload/actions/ReloadListAction.java b/tools/preload2/src/com/android/preload/actions/ReloadListAction.java deleted file mode 100644 index 29f055700dfa..000000000000 --- a/tools/preload2/src/com/android/preload/actions/ReloadListAction.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * 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. - */ - -package com.android.preload.actions; - -import com.android.ddmlib.Client; -import com.android.ddmlib.IDevice; -import com.android.preload.ClientUtils; - -import java.util.Arrays; -import java.util.Comparator; - -import javax.swing.DefaultListModel; - -public class ReloadListAction extends AbstractThreadedDeviceSpecificAction { - - private ClientUtils clientUtils; - private final DefaultListModel<Client> clientListModel; - - public ReloadListAction(ClientUtils utils, IDevice device, - DefaultListModel<Client> clientListModel) { - super("Reload", device); - this.clientUtils = utils; - this.clientListModel = clientListModel; - } - - @Override - public void run() { - Client[] clients = clientUtils.findAllClients(device); - if (clients != null) { - Arrays.sort(clients, new ClientComparator()); - } - clientListModel.removeAllElements(); - for (Client c : clients) { - clientListModel.addElement(c); - } - } - - private static class ClientComparator implements Comparator<Client> { - - @Override - public int compare(Client o1, Client o2) { - String s1 = o1.getClientData().getClientDescription(); - String s2 = o2.getClientData().getClientDescription(); - - if (s1 == null || s2 == null) { - // Not good, didn't get all data? - return (s1 == null) ? -1 : 1; - } - - return s1.compareTo(s2); - } - - } -}
\ No newline at end of file diff --git a/tools/preload2/src/com/android/preload/actions/RunMonkeyAction.java b/tools/preload2/src/com/android/preload/actions/RunMonkeyAction.java deleted file mode 100644 index 29464fc7abdf..000000000000 --- a/tools/preload2/src/com/android/preload/actions/RunMonkeyAction.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * 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. - */ - -package com.android.preload.actions; - -import com.android.ddmlib.IDevice; -import com.android.preload.DeviceUtils; -import com.android.preload.DumpData; -import com.android.preload.DumpTableModel; -import com.android.preload.Main; - -import java.awt.event.ActionEvent; -import java.util.Date; -import java.util.Map; -import java.util.concurrent.TimeUnit; - -import javax.swing.AbstractAction; - -public class RunMonkeyAction extends AbstractAction implements DeviceSpecific { - - private final static String DEFAULT_MONKEY_PACKAGES = - "com.android.calendar,com.android.gallery3d"; - - private IDevice device; - private DumpTableModel dataTableModel; - - public RunMonkeyAction(IDevice device, DumpTableModel dataTableModel) { - super("Run monkey"); - this.device = device; - this.dataTableModel = dataTableModel; - } - - @Override - public void setDevice(IDevice device) { - this.device = device; - } - - @Override - public void actionPerformed(ActionEvent e) { - String packages = Main.getUI().showInputDialog("Please enter packages name to run with" - + " the monkey, or leave empty for default."); - if (packages == null) { - return; - } - if (packages.isEmpty()) { - packages = DEFAULT_MONKEY_PACKAGES; - } - Runnable r = new RunMonkeyRunnable(packages); - if (Main.getUI().isSingleThreaded()) { - r.run(); - } else { - new Thread(r).start(); - } - } - - private class RunMonkeyRunnable implements Runnable { - - private String packages; - private final static int ITERATIONS = 1000; - - public RunMonkeyRunnable(String packages) { - this.packages = packages; - } - - @Override - public void run() { - Main.getUI().showWaitDialog(); - - try { - String pkgs[] = packages.split(","); - - for (String pkg : pkgs) { - Main.getUI().updateWaitDialog("Running monkey on " + pkg); - - try { - // Stop running app. - forceStop(pkg); - - // Little bit of breather here. - try { - Thread.sleep(1000); - } catch (Exception e) { - } - - DeviceUtils.doShell(device, "monkey -p " + pkg + " " + ITERATIONS, 1, - TimeUnit.MINUTES); - - Main.getUI().updateWaitDialog("Retrieving heap data for " + pkg); - Map<String, String> data = Main.findAndGetClassData(device, pkg); - DumpData dumpData = new DumpData(pkg, data, new Date()); - dataTableModel.addData(dumpData); - } catch (Exception e) { - e.printStackTrace(); - } finally { - // Stop running app. - forceStop(pkg); - } - } - } finally { - Main.getUI().hideWaitDialog(); - } - } - - private void forceStop(String packageName) { - // Stop running app. - DeviceUtils.doShell(device, "force-stop " + packageName, 5, TimeUnit.SECONDS); - DeviceUtils.doShell(device, "kill " + packageName, 5, TimeUnit.SECONDS); - DeviceUtils.doShell(device, "kill `pid " + packageName + "`", 5, TimeUnit.SECONDS); - } - } -}
\ No newline at end of file diff --git a/tools/preload2/src/com/android/preload/actions/ScanAllPackagesAction.java b/tools/preload2/src/com/android/preload/actions/ScanAllPackagesAction.java deleted file mode 100644 index d74b8a3f6cfb..000000000000 --- a/tools/preload2/src/com/android/preload/actions/ScanAllPackagesAction.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * 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. - */ - -package com.android.preload.actions; - -import com.android.ddmlib.Client; -import com.android.ddmlib.IDevice; -import com.android.preload.ClientUtils; -import com.android.preload.DumpData; -import com.android.preload.DumpTableModel; -import com.android.preload.Main; - -import java.util.Date; -import java.util.Map; - -public class ScanAllPackagesAction extends AbstractThreadedDeviceSpecificAction { - - private ClientUtils clientUtils; - private DumpTableModel dataTableModel; - - public ScanAllPackagesAction(ClientUtils utils, IDevice device, DumpTableModel dataTableModel) { - super("Scan all packages", device); - this.clientUtils = utils; - this.dataTableModel = dataTableModel; - } - - @Override - public void run() { - Main.getUI().showWaitDialog(); - - try { - Client[] clients = clientUtils.findAllClients(device); - for (Client c : clients) { - String pkg = c.getClientData().getClientDescription(); - Main.getUI().showWaitDialog(); - Main.getUI().updateWaitDialog("Retrieving heap data for " + pkg); - - try { - Map<String, String> data = Main.getClassDataRetriever().getClassData(c); - DumpData dumpData = new DumpData(pkg, data, new Date()); - dataTableModel.addData(dumpData); - } catch (Exception e) { - e.printStackTrace(); - } - } - } finally { - Main.getUI().hideWaitDialog(); - } - } -}
\ No newline at end of file diff --git a/tools/preload2/src/com/android/preload/actions/ScanPackageAction.java b/tools/preload2/src/com/android/preload/actions/ScanPackageAction.java deleted file mode 100644 index 98492bd951bf..000000000000 --- a/tools/preload2/src/com/android/preload/actions/ScanPackageAction.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * 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. - */ - -package com.android.preload.actions; - -import com.android.ddmlib.Client; -import com.android.ddmlib.IDevice; -import com.android.preload.ClientUtils; -import com.android.preload.DumpData; -import com.android.preload.DumpTableModel; -import com.android.preload.Main; - -import java.util.Date; -import java.util.Map; - -public class ScanPackageAction extends AbstractThreadedDeviceSpecificAction { - - private ClientUtils clientUtils; - private DumpTableModel dataTableModel; - - public ScanPackageAction(ClientUtils utils, IDevice device, DumpTableModel dataTableModel) { - super("Scan package", device); - this.clientUtils = utils; - this.dataTableModel = dataTableModel; - } - - @Override - public void run() { - Main.getUI().showWaitDialog(); - - try { - Client client = Main.getUI().getSelectedClient(); - if (client != null) { - work(client); - } else { - Client[] clients = clientUtils.findAllClients(device); - if (clients.length > 0) { - ClientWrapper[] clientWrappers = new ClientWrapper[clients.length]; - for (int i = 0; i < clientWrappers.length; i++) { - clientWrappers[i] = new ClientWrapper(clients[i]); - } - Main.getUI().hideWaitDialog(); - - ClientWrapper ret = Main.getUI().showChoiceDialog("Choose a package to scan", - "Choose package", - clientWrappers); - if (ret != null) { - work(ret.client); - } - } - } - } finally { - Main.getUI().hideWaitDialog(); - } - } - - private void work(Client c) { - String pkg = c.getClientData().getClientDescription(); - Main.getUI().showWaitDialog(); - Main.getUI().updateWaitDialog("Retrieving heap data for " + pkg); - - try { - Map<String, String> data = Main.findAndGetClassData(device, pkg); - DumpData dumpData = new DumpData(pkg, data, new Date()); - dataTableModel.addData(dumpData); - } catch (Exception e) { - e.printStackTrace(); - } - } - - private static class ClientWrapper { - private Client client; - - public ClientWrapper(Client c) { - client = c; - } - - @Override - public String toString() { - return client.getClientData().getClientDescription() + " (pid " - + client.getClientData().getPid() + ")"; - } - } -}
\ No newline at end of file diff --git a/tools/preload2/src/com/android/preload/actions/ShowDataAction.java b/tools/preload2/src/com/android/preload/actions/ShowDataAction.java deleted file mode 100644 index 2bb175f60772..000000000000 --- a/tools/preload2/src/com/android/preload/actions/ShowDataAction.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * 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. - */ - -package com.android.preload.actions; - -import com.android.preload.DumpData; -import com.android.preload.DumpTableModel; -import com.android.preload.Main; - -import java.awt.BorderLayout; -import java.awt.event.ActionEvent; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import javax.swing.AbstractAction; -import javax.swing.JFrame; -import javax.swing.JScrollPane; -import javax.swing.JTextArea; - -public class ShowDataAction extends AbstractAction { - private DumpTableModel dataTableModel; - - public ShowDataAction(DumpTableModel dataTableModel) { - super("Show data"); - this.dataTableModel = dataTableModel; - } - - @Override - public void actionPerformed(ActionEvent e) { - // TODO(agampe): Auto-generated method stub - int selRow = Main.getUI().getSelectedDataTableRow(); - if (selRow != -1) { - DumpData data = dataTableModel.getData().get(selRow); - Map<String, Set<String>> inv = data.invertData(); - - StringBuilder builder = new StringBuilder(); - - // First bootclasspath. - add(builder, "Boot classpath:", inv.get(null)); - - // Now everything else. - for (String k : inv.keySet()) { - if (k != null) { - builder.append("==================\n\n"); - add(builder, k, inv.get(k)); - } - } - - JFrame newFrame = new JFrame(data.getPackageName() + " " + data.getDate()); - newFrame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE); - newFrame.getContentPane().add(new JScrollPane(new JTextArea(builder.toString())), - BorderLayout.CENTER); - newFrame.setSize(800, 600); - newFrame.setLocationRelativeTo(null); - newFrame.setVisible(true); - } - } - - private void add(StringBuilder builder, String head, Set<String> set) { - builder.append(head); - builder.append('\n'); - addSet(builder, set); - builder.append('\n'); - } - - private void addSet(StringBuilder builder, Set<String> set) { - if (set == null) { - builder.append(" NONE\n"); - return; - } - List<String> sorted = new ArrayList<>(set); - Collections.sort(sorted); - for (String s : sorted) { - builder.append(s); - builder.append('\n'); - } - } -}
\ No newline at end of file diff --git a/tools/preload2/src/com/android/preload/actions/WritePreloadedClassesAction.java b/tools/preload2/src/com/android/preload/actions/WritePreloadedClassesAction.java deleted file mode 100644 index 9b97f1168df9..000000000000 --- a/tools/preload2/src/com/android/preload/actions/WritePreloadedClassesAction.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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. - */ - -package com.android.preload.actions; - -import com.android.ddmlib.Client; -import com.android.ddmlib.IDevice; -import com.android.preload.ClientUtils; -import com.android.preload.DeviceUtils; -import com.android.preload.DumpData; -import com.android.preload.DumpTableModel; -import com.android.preload.Main; - -import java.awt.event.ActionEvent; -import java.io.File; -import java.util.Date; -import java.util.Map; - -public class WritePreloadedClassesAction extends AbstractThreadedDeviceSpecificAction { - private File preloadedClassFile; - - public WritePreloadedClassesAction(ClientUtils utils, IDevice device, DumpTableModel dataTableModel) { - super("Write preloaded classes action", device); - } - - @Override - public void actionPerformed(ActionEvent e) { - File[] files = Main.getUI().showOpenDialog(true); - if (files != null && files.length > 0) { - preloadedClassFile = files[0]; - super.actionPerformed(e); - } - } - - @Override - public void run() { - Main.getUI().showWaitDialog(); - try { - // Write the new file with a 5-minute timeout - DeviceUtils.overwritePreloaded(device, preloadedClassFile, 5 * 60); - } catch (Exception e) { - System.err.println(e); - } finally { - Main.getUI().hideWaitDialog(); - } - } -} diff --git a/tools/preload2/src/com/android/preload/classdataretrieval/hprof/GeneralHprofDumpHandler.java b/tools/preload2/src/com/android/preload/classdataretrieval/hprof/GeneralHprofDumpHandler.java deleted file mode 100644 index 8d797ee00128..000000000000 --- a/tools/preload2/src/com/android/preload/classdataretrieval/hprof/GeneralHprofDumpHandler.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * 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. - */ - -package com.android.preload.classdataretrieval.hprof; - -import com.android.ddmlib.Client; -import com.android.ddmlib.ClientData.IHprofDumpHandler; - -import java.util.ArrayList; -import java.util.List; - -public class GeneralHprofDumpHandler implements IHprofDumpHandler { - - private List<IHprofDumpHandler> handlers = new ArrayList<>(); - - public void addHandler(IHprofDumpHandler h) { - synchronized (handlers) { - handlers.add(h); - } - } - - public void removeHandler(IHprofDumpHandler h) { - synchronized (handlers) { - handlers.remove(h); - } - } - - private List<IHprofDumpHandler> getIterationList() { - synchronized (handlers) { - return new ArrayList<>(handlers); - } - } - - @Override - public void onEndFailure(Client arg0, String arg1) { - List<IHprofDumpHandler> iterList = getIterationList(); - for (IHprofDumpHandler h : iterList) { - h.onEndFailure(arg0, arg1); - } - } - - @Override - public void onSuccess(String arg0, Client arg1) { - List<IHprofDumpHandler> iterList = getIterationList(); - for (IHprofDumpHandler h : iterList) { - h.onSuccess(arg0, arg1); - } - } - - @Override - public void onSuccess(byte[] arg0, Client arg1) { - List<IHprofDumpHandler> iterList = getIterationList(); - for (IHprofDumpHandler h : iterList) { - h.onSuccess(arg0, arg1); - } - } - }
\ No newline at end of file diff --git a/tools/preload2/src/com/android/preload/classdataretrieval/hprof/Hprof.java b/tools/preload2/src/com/android/preload/classdataretrieval/hprof/Hprof.java deleted file mode 100644 index 84ec8b7d0fdd..000000000000 --- a/tools/preload2/src/com/android/preload/classdataretrieval/hprof/Hprof.java +++ /dev/null @@ -1,228 +0,0 @@ -/* - * 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. - */ - -package com.android.preload.classdataretrieval.hprof; - -import com.android.ddmlib.Client; -import com.android.ddmlib.ClientData; -import com.android.ddmlib.ClientData.IHprofDumpHandler; -import com.android.preload.classdataretrieval.ClassDataRetriever; -import com.android.preload.ui.NullProgressMonitor; -import com.android.tools.perflib.captures.MemoryMappedFileBuffer; -import com.android.tools.perflib.heap.ClassObj; -import com.android.tools.perflib.heap.Queries; -import com.android.tools.perflib.heap.Snapshot; - -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -public class Hprof implements ClassDataRetriever { - - private static GeneralHprofDumpHandler hprofHandler; - - public static void init() { - synchronized(Hprof.class) { - if (hprofHandler == null) { - ClientData.setHprofDumpHandler(hprofHandler = new GeneralHprofDumpHandler()); - } - } - } - - public static File doHprof(Client client, int timeout) { - GetHprof gh = new GetHprof(client, timeout); - return gh.get(); - } - - /** - * Return a map of class names to class-loader names derived from the hprof dump. - * - * @param hprofLocalFile - */ - public static Map<String, String> analyzeHprof(File hprofLocalFile) throws Exception { - Snapshot snapshot = Snapshot.createSnapshot(new MemoryMappedFileBuffer(hprofLocalFile)); - - Map<String, Set<ClassObj>> classes = Queries.classes(snapshot, null); - Map<String, String> retValue = new HashMap<String, String>(); - for (Map.Entry<String, Set<ClassObj>> e : classes.entrySet()) { - for (ClassObj c : e.getValue()) { - String cl = c.getClassLoader() == null ? null : c.getClassLoader().toString(); - String cName = c.getClassName(); - int aDepth = 0; - while (cName.endsWith("[]")) { - cName = cName.substring(0, cName.length()-2); - aDepth++; - } - String newName = transformPrimitiveClass(cName); - if (aDepth > 0) { - // Need to use kind-a descriptor syntax. If it was transformed, it is primitive. - if (newName.equals(cName)) { - newName = "L" + newName + ";"; - } - for (int i = 0; i < aDepth; i++) { - newName = "[" + newName; - } - } - retValue.put(newName, cl); - } - } - - // Free up memory. - snapshot.dispose(); - - return retValue; - } - - private static Map<String, String> primitiveMapping; - - static { - primitiveMapping = new HashMap<>(); - primitiveMapping.put("boolean", "Z"); - primitiveMapping.put("byte", "B"); - primitiveMapping.put("char", "C"); - primitiveMapping.put("double", "D"); - primitiveMapping.put("float", "F"); - primitiveMapping.put("int", "I"); - primitiveMapping.put("long", "J"); - primitiveMapping.put("short", "S"); - primitiveMapping.put("void", "V"); - } - - private static String transformPrimitiveClass(String name) { - String rep = primitiveMapping.get(name); - if (rep != null) { - return rep; - } - return name; - } - - private static class GetHprof implements IHprofDumpHandler { - - private File target; - private long timeout; - private Client client; - - public GetHprof(Client client, long timeout) { - this.client = client; - this.timeout = timeout; - } - - public File get() { - synchronized (this) { - hprofHandler.addHandler(this); - client.dumpHprof(); - if (target == null) { - try { - wait(timeout); - } catch (Exception e) { - System.out.println(e); - } - } - } - - hprofHandler.removeHandler(this); - return target; - } - - private void wakeUp() { - synchronized (this) { - notifyAll(); - } - } - - @Override - public void onEndFailure(Client arg0, String arg1) { - System.out.println("GetHprof.onEndFailure"); - if (client == arg0) { - wakeUp(); - } - } - - private static File createTargetFile() { - try { - return File.createTempFile("ddms", ".hprof"); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - @Override - public void onSuccess(String arg0, Client arg1) { - System.out.println("GetHprof.onSuccess"); - if (client == arg1) { - try { - target = createTargetFile(); - arg1.getDevice().getSyncService().pullFile(arg0, - target.getAbsoluteFile().toString(), new NullProgressMonitor()); - } catch (Exception e) { - if (target != null) { - target.delete(); - } - e.printStackTrace(); - target = null; - } - wakeUp(); - } - } - - @Override - public void onSuccess(byte[] arg0, Client arg1) { - System.out.println("GetHprof.onSuccess"); - if (client == arg1) { - try { - target = createTargetFile(); - BufferedOutputStream out = - new BufferedOutputStream(new FileOutputStream(target)); - out.write(arg0); - out.close(); - } catch (Exception e) { - if (target != null) { - target.delete(); - } - e.printStackTrace(); - target = null; - } - wakeUp(); - } - } - } - - private int timeout; - - public Hprof(int timeout) { - this.timeout = timeout; - } - - @Override - public Map<String, String> getClassData(Client client) { - File hprofLocalFile = Hprof.doHprof(client, timeout); - if (hprofLocalFile == null) { - throw new RuntimeException("Failed getting dump..."); - } - System.out.println("Dump file is " + hprofLocalFile); - - try { - return analyzeHprof(hprofLocalFile); - } catch (Exception e) { - throw new RuntimeException(e); - } finally { - hprofLocalFile.delete(); - } - } -} diff --git a/tools/preload2/src/com/android/preload/classdataretrieval/jdwp/JDWPClassDataRetriever.java b/tools/preload2/src/com/android/preload/classdataretrieval/jdwp/JDWPClassDataRetriever.java deleted file mode 100644 index dbd4c89b27cf..000000000000 --- a/tools/preload2/src/com/android/preload/classdataretrieval/jdwp/JDWPClassDataRetriever.java +++ /dev/null @@ -1,221 +0,0 @@ -/* - * 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. - */ - -package com.android.preload.classdataretrieval.jdwp; - -import com.android.ddmlib.Client; -import com.android.preload.classdataretrieval.ClassDataRetriever; - -import org.apache.harmony.jpda.tests.framework.jdwp.CommandPacket; -import org.apache.harmony.jpda.tests.framework.jdwp.JDWPCommands; -import org.apache.harmony.jpda.tests.framework.jdwp.JDWPConstants; -import org.apache.harmony.jpda.tests.framework.jdwp.ReplyPacket; -import org.apache.harmony.jpda.tests.jdwp.share.JDWPTestCase; -import org.apache.harmony.jpda.tests.jdwp.share.JDWPUnitDebuggeeWrapper; -import org.apache.harmony.jpda.tests.share.JPDALogWriter; -import org.apache.harmony.jpda.tests.share.JPDATestOptions; - -import java.util.HashMap; -import java.util.Map; - -public class JDWPClassDataRetriever extends JDWPTestCase implements ClassDataRetriever { - - private final Client client; - - public JDWPClassDataRetriever() { - this(null); - } - - public JDWPClassDataRetriever(Client client) { - this.client = client; - } - - - @Override - protected String getDebuggeeClassName() { - return "<unset>"; - } - - @Override - public Map<String, String> getClassData(Client client) { - return new JDWPClassDataRetriever(client).retrieve(); - } - - private Map<String, String> retrieve() { - if (client == null) { - throw new IllegalStateException(); - } - - settings = createTestOptions("localhost:" + String.valueOf(client.getDebuggerListenPort())); - settings.setDebuggeeSuspend("n"); - - logWriter = new JPDALogWriter(System.out, "", false); - - try { - internalSetUp(); - - return retrieveImpl(); - } catch (Exception e) { - e.printStackTrace(); - return null; - } finally { - internalTearDown(); - } - } - - private Map<String, String> retrieveImpl() { - try { - // Suspend the app. - { - CommandPacket packet = new CommandPacket( - JDWPCommands.VirtualMachineCommandSet.CommandSetID, - JDWPCommands.VirtualMachineCommandSet.SuspendCommand); - ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet); - if (reply.getErrorCode() != JDWPConstants.Error.NONE) { - return null; - } - } - - // List all classes. - CommandPacket packet = new CommandPacket( - JDWPCommands.VirtualMachineCommandSet.CommandSetID, - JDWPCommands.VirtualMachineCommandSet.AllClassesCommand); - ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet); - - if (reply.getErrorCode() != JDWPConstants.Error.NONE) { - return null; - } - - int classCount = reply.getNextValueAsInt(); - System.out.println("Runtime reported " + classCount + " classes."); - - Map<Long, String> classes = new HashMap<Long, String>(); - Map<Long, String> arrayClasses = new HashMap<Long, String>(); - - for (int i = 0; i < classCount; i++) { - byte refTypeTag = reply.getNextValueAsByte(); - long typeID = reply.getNextValueAsReferenceTypeID(); - String signature = reply.getNextValueAsString(); - /* int status = */ reply.getNextValueAsInt(); - - switch (refTypeTag) { - case JDWPConstants.TypeTag.CLASS: - case JDWPConstants.TypeTag.INTERFACE: - classes.put(typeID, signature); - break; - - case JDWPConstants.TypeTag.ARRAY: - arrayClasses.put(typeID, signature); - break; - } - } - - Map<String, String> result = new HashMap<String, String>(); - - // Parse all classes. - for (Map.Entry<Long, String> entry : classes.entrySet()) { - long typeID = entry.getKey(); - String signature = entry.getValue(); - - if (!checkClass(typeID, signature, result)) { - System.err.println("Issue investigating " + signature); - } - } - - // For arrays, look at the leaf component type. - for (Map.Entry<Long, String> entry : arrayClasses.entrySet()) { - long typeID = entry.getKey(); - String signature = entry.getValue(); - - if (!checkArrayClass(typeID, signature, result)) { - System.err.println("Issue investigating " + signature); - } - } - - return result; - } finally { - // Resume the app. - { - CommandPacket packet = new CommandPacket( - JDWPCommands.VirtualMachineCommandSet.CommandSetID, - JDWPCommands.VirtualMachineCommandSet.ResumeCommand); - /* ReplyPacket reply = */ debuggeeWrapper.vmMirror.performCommand(packet); - } - } - } - - private boolean checkClass(long typeID, String signature, Map<String, String> result) { - CommandPacket packet = new CommandPacket( - JDWPCommands.ReferenceTypeCommandSet.CommandSetID, - JDWPCommands.ReferenceTypeCommandSet.ClassLoaderCommand); - packet.setNextValueAsReferenceTypeID(typeID); - ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet); - if (reply.getErrorCode() != JDWPConstants.Error.NONE) { - return false; - } - - long classLoaderID = reply.getNextValueAsObjectID(); - - // TODO: Investigate the classloader to have a better string? - String classLoaderString = (classLoaderID == 0) ? null : String.valueOf(classLoaderID); - - result.put(getClassName(signature), classLoaderString); - - return true; - } - - private boolean checkArrayClass(long typeID, String signature, Map<String, String> result) { - // Classloaders of array classes are the same as the component class'. - CommandPacket packet = new CommandPacket( - JDWPCommands.ReferenceTypeCommandSet.CommandSetID, - JDWPCommands.ReferenceTypeCommandSet.ClassLoaderCommand); - packet.setNextValueAsReferenceTypeID(typeID); - ReplyPacket reply = debuggeeWrapper.vmMirror.performCommand(packet); - if (reply.getErrorCode() != JDWPConstants.Error.NONE) { - return false; - } - - long classLoaderID = reply.getNextValueAsObjectID(); - - // TODO: Investigate the classloader to have a better string? - String classLoaderString = (classLoaderID == 0) ? null : String.valueOf(classLoaderID); - - // For array classes, we *need* the signature directly. - result.put(signature, classLoaderString); - - return true; - } - - private static String getClassName(String signature) { - String withoutLAndSemicolon = signature.substring(1, signature.length() - 1); - return withoutLAndSemicolon.replace('/', '.'); - } - - - private static JPDATestOptions createTestOptions(String address) { - JPDATestOptions options = new JPDATestOptions(); - options.setAttachConnectorKind(); - options.setTimeout(1000); - options.setWaitingTime(1000); - options.setTransportAddress(address); - return options; - } - - @Override - protected JDWPUnitDebuggeeWrapper createDebuggeeWrapper() { - return new PreloadDebugeeWrapper(settings, logWriter); - } -} diff --git a/tools/preload2/src/com/android/preload/classdataretrieval/jdwp/PreloadDebugeeWrapper.java b/tools/preload2/src/com/android/preload/classdataretrieval/jdwp/PreloadDebugeeWrapper.java deleted file mode 100644 index b9df6d0aeb93..000000000000 --- a/tools/preload2/src/com/android/preload/classdataretrieval/jdwp/PreloadDebugeeWrapper.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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. - */ - -package com.android.preload.classdataretrieval.jdwp; - -import org.apache.harmony.jpda.tests.framework.LogWriter; -import org.apache.harmony.jpda.tests.jdwp.share.JDWPManualDebuggeeWrapper; -import org.apache.harmony.jpda.tests.share.JPDATestOptions; - -import java.io.IOException; - -public class PreloadDebugeeWrapper extends JDWPManualDebuggeeWrapper { - - public PreloadDebugeeWrapper(JPDATestOptions options, LogWriter writer) { - super(options, writer); - } - - @Override - protected Process launchProcess(String cmdLine) throws IOException { - return null; - } - - @Override - protected void WaitForProcessExit(Process process) { - } - -} diff --git a/tools/preload2/src/com/android/preload/ui/IUI.java b/tools/preload2/src/com/android/preload/ui/IUI.java deleted file mode 100644 index 9371463e9a79..000000000000 --- a/tools/preload2/src/com/android/preload/ui/IUI.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.android.preload.ui; - -import com.android.ddmlib.Client; -import java.io.File; -import java.util.List; -import javax.swing.Action; -import javax.swing.ListModel; -import javax.swing.table.TableModel; - -/** - * UI abstraction for the tool. This allows a graphical mode, command line mode, - * or silent mode. - */ -public interface IUI { - - void prepare(ListModel<Client> clientListModel, TableModel dataTableModel, - List<Action> actions); - - void ready(); - - boolean isSingleThreaded(); - - Client getSelectedClient(); - - int getSelectedDataTableRow(); - - void showWaitDialog(); - - void updateWaitDialog(String s); - - void hideWaitDialog(); - - void showMessageDialog(String s); - - boolean showConfirmDialog(String title, String message); - - String showInputDialog(String message); - - <T> T showChoiceDialog(String title, String message, T[] choices); - - File showSaveDialog(); - - File[] showOpenDialog(boolean multi); - -} diff --git a/tools/preload2/src/com/android/preload/ui/NullProgressMonitor.java b/tools/preload2/src/com/android/preload/ui/NullProgressMonitor.java deleted file mode 100644 index f45aad06ac6b..000000000000 --- a/tools/preload2/src/com/android/preload/ui/NullProgressMonitor.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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. - */ - -package com.android.preload.ui; - -import com.android.ddmlib.SyncService.ISyncProgressMonitor; - -public class NullProgressMonitor implements ISyncProgressMonitor { - - @Override - public void advance(int arg0) {} - - @Override - public boolean isCanceled() { - return false; - } - - @Override - public void start(int arg0) {} - - @Override - public void startSubTask(String arg0) {} - - @Override - public void stop() {} -}
\ No newline at end of file diff --git a/tools/preload2/src/com/android/preload/ui/SequenceUI.java b/tools/preload2/src/com/android/preload/ui/SequenceUI.java deleted file mode 100644 index dc6a4f389b10..000000000000 --- a/tools/preload2/src/com/android/preload/ui/SequenceUI.java +++ /dev/null @@ -1,222 +0,0 @@ -package com.android.preload.ui; - -import com.android.ddmlib.Client; -import com.android.ddmlib.ClientData; -import java.io.File; -import java.util.LinkedList; -import java.util.List; -import javax.swing.Action; -import javax.swing.ListModel; -import javax.swing.table.TableModel; - -public class SequenceUI implements IUI { - - private ListModel<Client> clientListModel; - @SuppressWarnings("unused") - private TableModel dataTableModel; - private List<Action> actions; - - private List<Object> sequence = new LinkedList<>(); - - public SequenceUI() { - } - - @Override - public boolean isSingleThreaded() { - return true; - } - - @Override - public void prepare(ListModel<Client> clientListModel, TableModel dataTableModel, - List<Action> actions) { - this.clientListModel = clientListModel; - this.dataTableModel = dataTableModel; - this.actions = actions; - } - - public SequenceUI action(Action a) { - sequence.add(a); - return this; - } - - public SequenceUI action(Class<? extends Action> actionClass) { - for (Action a : actions) { - if (actionClass.equals(a.getClass())) { - sequence.add(a); - return this; - } - } - throw new IllegalArgumentException("No action of class " + actionClass + " found."); - } - - public SequenceUI confirmYes() { - sequence.add(Boolean.TRUE); - return this; - } - - public SequenceUI confirmNo() { - sequence.add(Boolean.FALSE); - return this; - } - - public SequenceUI input(String input) { - sequence.add(input); - return this; - } - - public SequenceUI input(File... f) { - sequence.add(f); - return this; - } - - public SequenceUI output(File f) { - sequence.add(f); - return this; - } - - public SequenceUI tableRow(int i) { - sequence.add(i); - return this; - } - - private class ClientSelector { - private String pkg; - - public ClientSelector(String pkg) { - this.pkg = pkg; - } - - public Client getClient() { - for (int i = 0; i < clientListModel.getSize(); i++) { - ClientData cd = clientListModel.getElementAt(i).getClientData(); - if (cd != null) { - String s = cd.getClientDescription(); - if (pkg.equals(s)) { - return clientListModel.getElementAt(i); - } - } - } - throw new RuntimeException("Didn't find client " + pkg); - } - } - - public SequenceUI client(String pkg) { - sequence.add(new ClientSelector(pkg)); - return this; - } - - public SequenceUI choice(String pattern) { - sequence.add(pattern); - return this; - } - - @Override - public void ready() { - // Run the actions. - // No iterator or foreach loop as the sequence will be emptied while running. - try { - while (!sequence.isEmpty()) { - Object next = sequence.remove(0); - if (next instanceof Action) { - ((Action)next).actionPerformed(null); - } else { - throw new IllegalStateException("Didn't expect a non-action: " + next); - } - } - } catch (Exception e) { - e.printStackTrace(System.out); - } - - // Now shut down. - System.exit(0); - } - - @Override - public Client getSelectedClient() { - Object next = sequence.remove(0); - if (next instanceof ClientSelector) { - return ((ClientSelector)next).getClient(); - } - throw new IllegalStateException("Unexpected: " + next); - } - - @Override - public int getSelectedDataTableRow() { - Object next = sequence.remove(0); - if (next instanceof Integer) { - return ((Integer)next).intValue(); - } - throw new IllegalStateException("Unexpected: " + next); - } - - @Override - public void showWaitDialog() { - } - - @Override - public void updateWaitDialog(String s) { - System.out.println(s); - } - - @Override - public void hideWaitDialog() { - } - - @Override - public void showMessageDialog(String s) { - System.out.println(s); - } - - @Override - public boolean showConfirmDialog(String title, String message) { - Object next = sequence.remove(0); - if (next instanceof Boolean) { - return ((Boolean)next).booleanValue(); - } - throw new IllegalStateException("Unexpected: " + next); - } - - @Override - public String showInputDialog(String message) { - Object next = sequence.remove(0); - if (next instanceof String) { - return (String)next; - } - throw new IllegalStateException("Unexpected: " + next); - } - - @Override - public <T> T showChoiceDialog(String title, String message, T[] choices) { - Object next = sequence.remove(0); - if (next instanceof String) { - String s = (String)next; - for (T t : choices) { - if (t.toString().contains(s)) { - return t; - } - } - return null; - } - throw new IllegalStateException("Unexpected: " + next); - } - - @Override - public File showSaveDialog() { - Object next = sequence.remove(0); - if (next instanceof File) { - System.out.println(next); - return (File)next; - } - throw new IllegalStateException("Unexpected: " + next); - } - - @Override - public File[] showOpenDialog(boolean multi) { - Object next = sequence.remove(0); - if (next instanceof File[]) { - return (File[])next; - } - throw new IllegalStateException("Unexpected: " + next); - } - -} diff --git a/tools/preload2/src/com/android/preload/ui/SwingUI.java b/tools/preload2/src/com/android/preload/ui/SwingUI.java deleted file mode 100644 index cab3744ad74c..000000000000 --- a/tools/preload2/src/com/android/preload/ui/SwingUI.java +++ /dev/null @@ -1,291 +0,0 @@ -/* - * 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. - */ - -package com.android.preload.ui; - -import com.android.ddmlib.Client; -import com.android.ddmlib.ClientData; - -import java.awt.BorderLayout; -import java.awt.Component; -import java.awt.Dimension; -import java.io.File; -import java.util.List; - -import javax.swing.Action; -import javax.swing.DefaultListCellRenderer; -import javax.swing.JDialog; -import javax.swing.JFileChooser; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JList; -import javax.swing.JOptionPane; -import javax.swing.JProgressBar; -import javax.swing.JScrollPane; -import javax.swing.JTable; -import javax.swing.JToolBar; -import javax.swing.ListModel; -import javax.swing.SwingUtilities; -import javax.swing.table.TableModel; - -public class SwingUI extends JFrame implements IUI { - - private JList<Client> clientList; - private JTable dataTable; - - // Shared file chooser, means the directory is retained. - private JFileChooser jfc; - - public SwingUI() { - super("Preloaded-classes computation"); - } - - @Override - public boolean isSingleThreaded() { - return false; - } - - @Override - public void prepare(ListModel<Client> clientListModel, TableModel dataTableModel, - List<Action> actions) { - getContentPane().add(new JScrollPane(clientList = new JList<Client>(clientListModel)), - BorderLayout.WEST); - clientList.setCellRenderer(new ClientListCellRenderer()); - // clientList.addListSelectionListener(listener); - - dataTable = new JTable(dataTableModel); - getContentPane().add(new JScrollPane(dataTable), BorderLayout.CENTER); - - JToolBar toolbar = new JToolBar(JToolBar.HORIZONTAL); - for (Action a : actions) { - if (a == null) { - toolbar.addSeparator(); - } else { - toolbar.add(a); - } - } - getContentPane().add(toolbar, BorderLayout.PAGE_START); - - setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - setBounds(100, 100, 800, 600); - - setVisible(true); - } - - @Override - public void ready() { - } - - @Override - public Client getSelectedClient() { - return clientList.getSelectedValue(); - } - - @Override - public int getSelectedDataTableRow() { - return dataTable.getSelectedRow(); - } - - private JDialog currentWaitDialog = null; - - @Override - public void showWaitDialog() { - if (currentWaitDialog == null) { - currentWaitDialog = new JDialog(this, "Please wait...", true); - currentWaitDialog.getContentPane().add(new JLabel("Please be patient."), - BorderLayout.CENTER); - JProgressBar progress = new JProgressBar(JProgressBar.HORIZONTAL); - progress.setIndeterminate(true); - currentWaitDialog.getContentPane().add(progress, BorderLayout.SOUTH); - currentWaitDialog.setSize(200, 100); - currentWaitDialog.setLocationRelativeTo(null); - showWaitDialogLater(); - } - } - - private void showWaitDialogLater() { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - if (currentWaitDialog != null) { - currentWaitDialog.setVisible(true); // This is blocking. - } - } - }); - } - - @Override - public void updateWaitDialog(String s) { - if (currentWaitDialog != null) { - ((JLabel) currentWaitDialog.getContentPane().getComponent(0)).setText(s); - Dimension prefSize = currentWaitDialog.getPreferredSize(); - Dimension curSize = currentWaitDialog.getSize(); - if (prefSize.width > curSize.width || prefSize.height > curSize.height) { - currentWaitDialog.setSize(Math.max(prefSize.width, curSize.width), - Math.max(prefSize.height, curSize.height)); - currentWaitDialog.invalidate(); - } - } - } - - @Override - public void hideWaitDialog() { - if (currentWaitDialog != null) { - currentWaitDialog.setVisible(false); - currentWaitDialog = null; - } - } - - @Override - public void showMessageDialog(String s) { - // Hide the wait dialog... - if (currentWaitDialog != null) { - currentWaitDialog.setVisible(false); - } - - try { - JOptionPane.showMessageDialog(this, s); - } finally { - // And reshow it afterwards... - if (currentWaitDialog != null) { - showWaitDialogLater(); - } - } - } - - @Override - public boolean showConfirmDialog(String title, String message) { - // Hide the wait dialog... - if (currentWaitDialog != null) { - currentWaitDialog.setVisible(false); - } - - try { - return JOptionPane.showConfirmDialog(this, title, message, JOptionPane.YES_NO_OPTION) - == JOptionPane.YES_OPTION; - } finally { - // And reshow it afterwards... - if (currentWaitDialog != null) { - showWaitDialogLater(); - } - } - } - - @Override - public String showInputDialog(String message) { - // Hide the wait dialog... - if (currentWaitDialog != null) { - currentWaitDialog.setVisible(false); - } - - try { - return JOptionPane.showInputDialog(message); - } finally { - // And reshow it afterwards... - if (currentWaitDialog != null) { - showWaitDialogLater(); - } - } - } - - @Override - @SuppressWarnings("unchecked") - public <T> T showChoiceDialog(String title, String message, T[] choices) { - // Hide the wait dialog... - if (currentWaitDialog != null) { - currentWaitDialog.setVisible(false); - } - - try{ - return (T)JOptionPane.showInputDialog(this, - title, - message, - JOptionPane.QUESTION_MESSAGE, - null, - choices, - choices[0]); - } finally { - // And reshow it afterwards... - if (currentWaitDialog != null) { - showWaitDialogLater(); - } - } - } - - @Override - public File showSaveDialog() { - // Hide the wait dialog... - if (currentWaitDialog != null) { - currentWaitDialog.setVisible(false); - } - - try{ - if (jfc == null) { - jfc = new JFileChooser(); - } - - int ret = jfc.showSaveDialog(this); - if (ret == JFileChooser.APPROVE_OPTION) { - return jfc.getSelectedFile(); - } else { - return null; - } - } finally { - // And reshow it afterwards... - if (currentWaitDialog != null) { - showWaitDialogLater(); - } - } - } - - @Override - public File[] showOpenDialog(boolean multi) { - // Hide the wait dialog... - if (currentWaitDialog != null) { - currentWaitDialog.setVisible(false); - } - - try{ - if (jfc == null) { - jfc = new JFileChooser(); - } - - jfc.setMultiSelectionEnabled(multi); - int ret = jfc.showOpenDialog(this); - if (ret == JFileChooser.APPROVE_OPTION) { - return jfc.getSelectedFiles(); - } else { - return null; - } - } finally { - // And reshow it afterwards... - if (currentWaitDialog != null) { - showWaitDialogLater(); - } - } - } - - private class ClientListCellRenderer extends DefaultListCellRenderer { - - @Override - public Component getListCellRendererComponent(JList<?> list, Object value, int index, - boolean isSelected, boolean cellHasFocus) { - ClientData cd = ((Client) value).getClientData(); - String s = cd.getClientDescription() + " (pid " + cd.getPid() + ")"; - return super.getListCellRendererComponent(list, s, index, isSelected, cellHasFocus); - } - } -} 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 |