diff options
376 files changed, 10639 insertions, 6370 deletions
diff --git a/Android.bp b/Android.bp index 7693a66fc1f7..7687270fc6f7 100644 --- a/Android.bp +++ b/Android.bp @@ -1417,10 +1417,6 @@ stubs_defaults { srcs_lib: "framework", srcs_lib_whitelist_dirs: frameworks_base_subdirs, srcs_lib_whitelist_pkgs: packages_to_document, - libs: [ - "ext", - "framework", - ], local_sourcepaths: frameworks_base_subdirs, installable: false, annotations_enabled: true, diff --git a/apct-tests/perftests/core/src/android/wm/RecentsAnimationPerfTest.java b/apct-tests/perftests/core/src/android/wm/RecentsAnimationPerfTest.java new file mode 100644 index 000000000000..4e2b281da1b4 --- /dev/null +++ b/apct-tests/perftests/core/src/android/wm/RecentsAnimationPerfTest.java @@ -0,0 +1,276 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.wm; + +import static android.perftests.utils.ManualBenchmarkState.STATS_REPORT_COEFFICIENT_VAR; +import static android.perftests.utils.ManualBenchmarkState.STATS_REPORT_ITERATION; +import static android.perftests.utils.ManualBenchmarkState.STATS_REPORT_MEAN; + +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + +import static org.hamcrest.core.AnyOf.anyOf; +import static org.hamcrest.core.Is.is; + +import android.app.Activity; +import android.app.ActivityTaskManager; +import android.app.IActivityTaskManager; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +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.perftests.utils.StubActivity; +import android.util.Pair; +import android.view.IRecentsAnimationController; +import android.view.IRecentsAnimationRunner; +import android.view.RemoteAnimationTarget; +import android.view.WindowManager; + +import androidx.test.filters.LargeTest; +import androidx.test.rule.ActivityTestRule; +import androidx.test.runner.lifecycle.ActivityLifecycleCallback; +import androidx.test.runner.lifecycle.ActivityLifecycleMonitorRegistry; +import androidx.test.runner.lifecycle.Stage; + +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Assume; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; + +@RunWith(Parameterized.class) +@LargeTest +public class RecentsAnimationPerfTest extends WindowManagerPerfTestBase { + private static Intent sRecentsIntent; + + @Rule + public final PerfManualStatusReporter mPerfStatusReporter = new PerfManualStatusReporter(); + + @Rule + public final ActivityTestRule<StubActivity> mActivityRule = new ActivityTestRule<>( + StubActivity.class, false /* initialTouchMode */, false /* launchActivity */); + + private long mMeasuredTimeNs; + private LifecycleListener mLifecycleListener; + + @Parameterized.Parameter(0) + public int intervalBetweenOperations; + + @Parameterized.Parameters(name = "interval{0}ms") + public static Collection<Object[]> getParameters() { + return Arrays.asList(new Object[][] { + { 0 }, + { 100 }, + { 300 }, + }); + } + + @BeforeClass + public static void setUpClass() { + // Get the permission to invoke startRecentsActivity. + sUiAutomation.adoptShellPermissionIdentity(); + + final Context context = getInstrumentation().getContext(); + final PackageManager pm = context.getPackageManager(); + final ComponentName defaultHome = pm.getHomeActivities(new ArrayList<>()); + + try { + final ComponentName recentsComponent = + ComponentName.unflattenFromString(context.getResources().getString( + com.android.internal.R.string.config_recentsComponentName)); + final int enabledState = pm.getComponentEnabledSetting(recentsComponent); + Assume.assumeThat(enabledState, anyOf( + is(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT), + is(PackageManager.COMPONENT_ENABLED_STATE_ENABLED))); + + final boolean homeIsRecents = + recentsComponent.getPackageName().equals(defaultHome.getPackageName()); + sRecentsIntent = + new Intent().setComponent(homeIsRecents ? defaultHome : recentsComponent); + } catch (Exception e) { + Assume.assumeNoException(e); + } + } + + @AfterClass + public static void tearDownClass() { + sUiAutomation.dropShellPermissionIdentity(); + } + + @Before + @Override + public void setUp() { + super.setUp(); + final Activity testActivity = mActivityRule.launchActivity(null /* intent */); + try { + mActivityRule.runOnUiThread(() -> testActivity.getWindow() + .addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)); + } catch (Throwable ignored) { } + mLifecycleListener = new LifecycleListener(testActivity); + ActivityLifecycleMonitorRegistry.getInstance().addLifecycleCallback(mLifecycleListener); + } + + @After + public void tearDown() { + ActivityLifecycleMonitorRegistry.getInstance().removeLifecycleCallback(mLifecycleListener); + } + + /** Simulate the timing of touch. */ + private void makeInterval() { + SystemClock.sleep(intervalBetweenOperations); + } + + /** + * <pre> + * Steps: + * (1) Start recents activity (only make it visible). + * (2) Finish animation, take turns to execute (a), (b). + * (a) Move recents activity to top. + * ({@link com.android.server.wm.RecentsAnimationController#REORDER_MOVE_TO_TOP}) + * Move test app to top by startActivityFromRecents. + * (b) Cancel (it is similar to swipe a little distance and give up to enter recents). + * ({@link com.android.server.wm.RecentsAnimationController#REORDER_MOVE_TO_ORIGINAL_POSITION}) + * (3) Loop (1). + * </pre> + */ + @Test + @ManualBenchmarkTest( + warmupDurationNs = TIME_1_S_IN_NS, + targetTestDurationNs = TIME_5_S_IN_NS, + statsReportFlags = + STATS_REPORT_ITERATION | STATS_REPORT_MEAN | STATS_REPORT_COEFFICIENT_VAR) + public void testRecentsAnimation() throws Throwable { + final ManualBenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + final IActivityTaskManager atm = ActivityTaskManager.getService(); + + final ArrayList<Pair<String, Boolean>> finishCases = new ArrayList<>(); + // Real launch the recents activity. + finishCases.add(new Pair<>("finishMoveToTop", true)); + // Return to the original top. + finishCases.add(new Pair<>("finishCancel", false)); + + // Ensure startRecentsActivity won't be called before finishing the animation. + final Semaphore recentsSemaphore = new Semaphore(1); + + final int testActivityTaskId = mActivityRule.getActivity().getTaskId(); + final IRecentsAnimationRunner.Stub anim = new IRecentsAnimationRunner.Stub() { + int mIteration; + + @Override + public void onAnimationStart(IRecentsAnimationController controller, + RemoteAnimationTarget[] apps, Rect homeContentInsets, + Rect minimizedHomeBounds) throws RemoteException { + final Pair<String, Boolean> finishCase = finishCases.get(mIteration++ % 2); + final boolean moveRecentsToTop = finishCase.second; + makeInterval(); + + long startTime = SystemClock.elapsedRealtimeNanos(); + controller.finish(moveRecentsToTop, false /* sendUserLeaveHint */); + final long elapsedTimeNsOfFinish = SystemClock.elapsedRealtimeNanos() - startTime; + mMeasuredTimeNs += elapsedTimeNsOfFinish; + state.addExtraResult(finishCase.first, elapsedTimeNsOfFinish); + + if (moveRecentsToTop) { + mLifecycleListener.waitForIdleSync(Stage.STOPPED); + + startTime = SystemClock.elapsedRealtimeNanos(); + atm.startActivityFromRecents(testActivityTaskId, null /* options */); + final long elapsedTimeNs = SystemClock.elapsedRealtimeNanos() - startTime; + mMeasuredTimeNs += elapsedTimeNs; + state.addExtraResult("startFromRecents", elapsedTimeNs); + + mLifecycleListener.waitForIdleSync(Stage.RESUMED); + } + + makeInterval(); + recentsSemaphore.release(); + } + + @Override + public void onAnimationCanceled(boolean deferredWithScreenshot) throws RemoteException { + Assume.assumeNoException( + new AssertionError("onAnimationCanceled should not be called")); + } + }; + + while (state.keepRunning(mMeasuredTimeNs)) { + Assume.assumeTrue(recentsSemaphore.tryAcquire(TIME_5_S_IN_NS, TimeUnit.NANOSECONDS)); + + final long startTime = SystemClock.elapsedRealtimeNanos(); + atm.startRecentsActivity(sRecentsIntent, null /* unused */, anim); + final long elapsedTimeNsOfStart = SystemClock.elapsedRealtimeNanos() - startTime; + mMeasuredTimeNs += elapsedTimeNsOfStart; + state.addExtraResult("start", elapsedTimeNsOfStart); + } + + // Ensure the last round of animation callback is done. + recentsSemaphore.tryAcquire(TIME_5_S_IN_NS, TimeUnit.NANOSECONDS); + recentsSemaphore.release(); + } + + private static class LifecycleListener implements ActivityLifecycleCallback { + private final Activity mTargetActivity; + private Stage mWaitingStage; + private Stage mReceivedStage; + + LifecycleListener(Activity activity) { + mTargetActivity = activity; + } + + void waitForIdleSync(Stage state) { + synchronized (this) { + if (state != mReceivedStage) { + mWaitingStage = state; + try { + wait(TimeUnit.NANOSECONDS.toMillis(TIME_5_S_IN_NS)); + } catch (InterruptedException impossible) { } + } + mWaitingStage = mReceivedStage = null; + } + getInstrumentation().waitForIdleSync(); + } + + @Override + public void onActivityLifecycleChanged(Activity activity, Stage stage) { + if (mTargetActivity != activity) { + return; + } + + synchronized (this) { + mReceivedStage = stage; + if (mWaitingStage == mReceivedStage) { + notifyAll(); + } + } + } + } +} diff --git a/apct-tests/perftests/core/src/android/wm/WindowAddRemovePerfTest.java b/apct-tests/perftests/core/src/android/wm/WindowAddRemovePerfTest.java index a95186916cea..27790e649a26 100644 --- a/apct-tests/perftests/core/src/android/wm/WindowAddRemovePerfTest.java +++ b/apct-tests/perftests/core/src/android/wm/WindowAddRemovePerfTest.java @@ -60,7 +60,7 @@ public class WindowAddRemovePerfTest extends WindowManagerPerfTestBase { } @Test - @ManualBenchmarkTest(warmupDurationNs = WARMUP_DURATION, targetTestDurationNs = TEST_DURATION) + @ManualBenchmarkTest(warmupDurationNs = TIME_1_S_IN_NS, targetTestDurationNs = TIME_5_S_IN_NS) public void testAddRemoveWindow() throws Throwable { new TestWindow().runBenchmark(mPerfStatusReporter.getBenchmarkState()); } diff --git a/apct-tests/perftests/core/src/android/wm/WindowManagerPerfTestBase.java b/apct-tests/perftests/core/src/android/wm/WindowManagerPerfTestBase.java index b2c61688ef59..4864da4b0195 100644 --- a/apct-tests/perftests/core/src/android/wm/WindowManagerPerfTestBase.java +++ b/apct-tests/perftests/core/src/android/wm/WindowManagerPerfTestBase.java @@ -25,8 +25,8 @@ 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; + static final long TIME_1_S_IN_NS = 1 * NANOS_PER_S; + static final long TIME_5_S_IN_NS = 5 * NANOS_PER_S; @Before public void setUp() { diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java index e2ef7a11b3a0..2fe0ee7cab57 100644 --- a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java +++ b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java @@ -530,10 +530,10 @@ public class UserLifecycleTests { */ private void startApp(int userId, String packageName) throws RemoteException { final Context context = InstrumentationRegistry.getContext(); - final WaitResult result = ActivityTaskManager.getService().startActivityAndWait(null, null, - context.getPackageManager().getLaunchIntentForPackage(packageName), - null, null, null, 0, 0, null, null, - userId); + final WaitResult result = ActivityTaskManager.getService().startActivityAndWait(null, + context.getPackageName(), + context.getPackageManager().getLaunchIntentForPackage(packageName), null, null, + null, 0, 0, null, null, userId); attestTrue("User " + userId + " failed to start " + packageName, result.result == ActivityManager.START_SUCCESS); } 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 dd43ae70cc5c..ffe39e8679e1 100644 --- a/apct-tests/perftests/utils/src/android/perftests/utils/ManualBenchmarkState.java +++ b/apct-tests/perftests/utils/src/android/perftests/utils/ManualBenchmarkState.java @@ -16,6 +16,7 @@ package android.perftests.utils; +import android.annotation.IntDef; import android.app.Activity; import android.app.Instrumentation; import android.os.Bundle; @@ -58,6 +59,28 @@ import java.util.concurrent.TimeUnit; public final class ManualBenchmarkState { private static final String TAG = ManualBenchmarkState.class.getSimpleName(); + @IntDef(prefix = {"STATS_REPORT"}, value = { + STATS_REPORT_MEDIAN, + STATS_REPORT_MEAN, + STATS_REPORT_MIN, + STATS_REPORT_MAX, + STATS_REPORT_PERCENTILE90, + STATS_REPORT_PERCENTILE95, + STATS_REPORT_STDDEV, + STATS_REPORT_ITERATION, + }) + public @interface StatsReport {} + + public static final int STATS_REPORT_MEDIAN = 0x00000001; + public static final int STATS_REPORT_MEAN = 0x00000002; + public static final int STATS_REPORT_MIN = 0x00000004; + public static final int STATS_REPORT_MAX = 0x00000008; + public static final int STATS_REPORT_PERCENTILE90 = 0x00000010; + public static final int STATS_REPORT_PERCENTILE95 = 0x00000020; + public static final int STATS_REPORT_STDDEV = 0x00000040; + public static final int STATS_REPORT_COEFFICIENT_VAR = 0x00000080; + public static final int STATS_REPORT_ITERATION = 0x00000100; + // TODO: Tune these values. // warm-up for duration private static final long WARMUP_DURATION_NS = TimeUnit.SECONDS.toNanos(5); @@ -93,6 +116,13 @@ public final class ManualBenchmarkState { // The computation needs double precision, but long int is fine for final reporting. private Stats mStats; + private int mStatsReportFlags = STATS_REPORT_MEDIAN | STATS_REPORT_MEAN + | STATS_REPORT_PERCENTILE90 | STATS_REPORT_PERCENTILE95 | STATS_REPORT_STDDEV; + + private boolean shouldReport(int statsReportFlag) { + return (mStatsReportFlags & statsReportFlag) != 0; + } + void configure(ManualBenchmarkTest testAnnotation) { if (testAnnotation == null) { return; @@ -106,6 +136,10 @@ public final class ManualBenchmarkState { if (targetTestDurationNs >= 0) { mTargetTestDurationNs = targetTestDurationNs; } + final int statsReportFlags = testAnnotation.statsReportFlags(); + if (statsReportFlags >= 0) { + mStatsReportFlags = statsReportFlags; + } } private void beginBenchmark(long warmupDuration, int iterations) { @@ -186,12 +220,35 @@ public final class ManualBenchmarkState { 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()); + private void fillStatus(Bundle status, String key, Stats stats) { + if (shouldReport(STATS_REPORT_ITERATION)) { + status.putLong(key + "_iteration", stats.getSize()); + } + if (shouldReport(STATS_REPORT_MEDIAN)) { + status.putLong(key + "_median", stats.getMedian()); + } + if (shouldReport(STATS_REPORT_MEAN)) { + status.putLong(key + "_mean", Math.round(stats.getMean())); + } + if (shouldReport(STATS_REPORT_MIN)) { + status.putLong(key + "_min", stats.getMin()); + } + if (shouldReport(STATS_REPORT_MAX)) { + status.putLong(key + "_max", stats.getMax()); + } + if (shouldReport(STATS_REPORT_PERCENTILE90)) { + status.putLong(key + "_percentile90", stats.getPercentile90()); + } + if (shouldReport(STATS_REPORT_PERCENTILE95)) { + status.putLong(key + "_percentile95", stats.getPercentile95()); + } + if (shouldReport(STATS_REPORT_STDDEV)) { + status.putLong(key + "_stddev", Math.round(stats.getStandardDeviation())); + } + if (shouldReport(STATS_REPORT_COEFFICIENT_VAR)) { + status.putLong(key + "_cv", + Math.round((100 * stats.getStandardDeviation() / stats.getMean()))); + } } public void sendFullStatusReport(Instrumentation instrumentation, String key) { @@ -204,8 +261,9 @@ public final class ManualBenchmarkState { 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)); + final ArrayList<Long> results = mExtraResults.valueAt(i); + final Stats stats = new Stats(results); + Log.i(TAG, summaryLine(subKey, stats, results)); fillStatus(status, subKey, stats); } } @@ -218,5 +276,6 @@ public final class ManualBenchmarkState { public @interface ManualBenchmarkTest { long warmupDurationNs() default -1; long targetTestDurationNs() default -1; + @StatsReport int statsReportFlags() default -1; } } diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/Stats.java b/apct-tests/perftests/utils/src/android/perftests/utils/Stats.java index 5e50073c0674..f650e810c6de 100644 --- a/apct-tests/perftests/utils/src/android/perftests/utils/Stats.java +++ b/apct-tests/perftests/utils/src/android/perftests/utils/Stats.java @@ -23,6 +23,7 @@ import java.util.List; public class Stats { private long mMedian, mMin, mMax, mPercentile90, mPercentile95; private double mMean, mStandardDeviation; + private final int mSize; /* Calculate stats in constructor. */ public Stats(List<Long> values) { @@ -35,6 +36,7 @@ public class Stats { Collections.sort(values); + mSize = size; mMin = values.get(0); mMax = values.get(values.size() - 1); @@ -56,6 +58,10 @@ public class Stats { mStandardDeviation = Math.sqrt(mStandardDeviation / (double) (size - 1)); } + public int getSize() { + return mSize; + } + public double getMean() { return mMean; } diff --git a/apex/jobscheduler/README_js-mainline.md b/apex/jobscheduler/README_js-mainline.md new file mode 100644 index 000000000000..b5fea5e0a4df --- /dev/null +++ b/apex/jobscheduler/README_js-mainline.md @@ -0,0 +1,51 @@ +# Making Job Scheduler into a Mainline Module + +## TODOs + +See also: +- http://go/moving-js-code-for-mainline +- http://go/jobscheduler-code-dependencies-2019-07 + +- [ ] Move client code + - [ ] Move code + - [ ] Make build file + - [ ] "m jobscheduler-framework" pass + - [ ] "m framework" pass + - [ ] "m service" pass +- [ ] Move proto + - No, couldn't do it, because it's referred to by incidentd_proto +- [ ] Move service + - [X] Move code (done, but it won't compile yet) + - [X] Make build file + - [X] "m service" pass + - [X] "m jobscheduler-service" pass + - To make it pass, jobscheduler-service has to link services.jar too. Many dependencies. +- [ ] Move this into `frameworks/apex/jobscheduler/...`. Currently it's in `frameworks/base/apex/...` +because `frameworks/apex/` is not a part of any git projects. (and also working on multiple +projects is a pain.) + + +## Problems +- Couldn't move dumpsys proto files. They are used by incidentd_proto, which is in the platform + (not updatable). + - One idea is *not* to move the proto files into apex but keep them in the platform. + Then we make sure to extend the proto files in a backward-compat way (which we do anyway) + and always use the latest file from the JS apex. + +- There are a lot of build tasks that use "framework.jar". (Examples: hiddenapi-greylist.txt check, + update-api / public API check and SDK stub (android.jar) creation) + To make the downstream build modules buildable, we need to include js-framework.jar in + framework.jar. However it turned out to be tricky because soong has special logic for "framework" + and "framework.jar". + i.e. Conceptually, we can do it by renaming `framework` to `framework-minus-jobscheduler`, build + `jobscheduler-framework` with `framework-minus-jobscheduler`, and create `framework` by merging + `framework-minus-jobscheduler` and `jobscheduler-framework`. + However it didn't quite work because of the special casing. + +- JS-service uses a lot of other code in `services`, so it needs to link services.core.jar e.g. + - Common system service code, e.g. `com.android.server.SystemService` + - Common utility code, e.g. `FgThread` and `IoThread` + - Other system services such as `DeviceIdleController` and `ActivityManagerService` + - Server side singleton. `AppStateTracker` + - `DeviceIdleController.LocalService`, which is a local service but there's no interface class. + - `XxxInternal` interfaces that are not in the framework side. -> We should be able to move them. diff --git a/apex/jobscheduler/service/Android.bp b/apex/jobscheduler/service/Android.bp new file mode 100644 index 000000000000..ca6dc45a356a --- /dev/null +++ b/apex/jobscheduler/service/Android.bp @@ -0,0 +1,15 @@ +// Job Scheduler Service jar, which will eventually be put in the jobscheduler mainline apex. +// jobscheduler-service needs to be added to PRODUCT_SYSTEM_SERVER_JARS. +java_library { + name: "jobscheduler-service", + installable: true, + + srcs: [ + "java/**/*.java", + ], + + libs: [ + "framework", + "services.core", + ], +} diff --git a/services/core/java/com/android/server/job/GrantedUriPermissions.java b/apex/jobscheduler/service/java/com/android/server/job/GrantedUriPermissions.java index 005b1892f6a6..005b1892f6a6 100644 --- a/services/core/java/com/android/server/job/GrantedUriPermissions.java +++ b/apex/jobscheduler/service/java/com/android/server/job/GrantedUriPermissions.java diff --git a/services/core/java/com/android/server/job/JobCompletedListener.java b/apex/jobscheduler/service/java/com/android/server/job/JobCompletedListener.java index 34ba753b3daa..34ba753b3daa 100644 --- a/services/core/java/com/android/server/job/JobCompletedListener.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobCompletedListener.java diff --git a/services/core/java/com/android/server/job/JobConcurrencyManager.java b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java index bec1947df228..bec1947df228 100644 --- a/services/core/java/com/android/server/job/JobConcurrencyManager.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java diff --git a/services/core/java/com/android/server/job/JobPackageTracker.java b/apex/jobscheduler/service/java/com/android/server/job/JobPackageTracker.java index e28e5bd6c53d..e28e5bd6c53d 100644 --- a/services/core/java/com/android/server/job/JobPackageTracker.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobPackageTracker.java diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java index e44e9022bf5d..e44e9022bf5d 100644 --- a/services/core/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java diff --git a/services/core/java/com/android/server/job/JobSchedulerShellCommand.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java index e3614413121a..e3614413121a 100644 --- a/services/core/java/com/android/server/job/JobSchedulerShellCommand.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java diff --git a/services/core/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java index 7da128f9d3ec..7da128f9d3ec 100644 --- a/services/core/java/com/android/server/job/JobServiceContext.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java diff --git a/services/core/java/com/android/server/job/JobStore.java b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java index d69faf37397c..d69faf37397c 100644 --- a/services/core/java/com/android/server/job/JobStore.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java diff --git a/services/core/java/com/android/server/job/StateChangedListener.java b/apex/jobscheduler/service/java/com/android/server/job/StateChangedListener.java index 87bfc27a715f..87bfc27a715f 100644 --- a/services/core/java/com/android/server/job/StateChangedListener.java +++ b/apex/jobscheduler/service/java/com/android/server/job/StateChangedListener.java diff --git a/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java index b698e5bf4403..b698e5bf4403 100644 --- a/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java diff --git a/services/core/java/com/android/server/job/controllers/BatteryController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java index 46658ad33b85..46658ad33b85 100644 --- a/services/core/java/com/android/server/job/controllers/BatteryController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java diff --git a/services/core/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java index f8cf6ae04950..f8cf6ae04950 100644 --- a/services/core/java/com/android/server/job/controllers/ConnectivityController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java diff --git a/services/core/java/com/android/server/job/controllers/ContentObserverController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java index a775cf5a671c..a775cf5a671c 100644 --- a/services/core/java/com/android/server/job/controllers/ContentObserverController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java diff --git a/services/core/java/com/android/server/job/controllers/DeviceIdleJobsController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java index 127a5c876657..127a5c876657 100644 --- a/services/core/java/com/android/server/job/controllers/DeviceIdleJobsController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java diff --git a/services/core/java/com/android/server/job/controllers/IdleController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java index e3c311f9e327..e3c311f9e327 100644 --- a/services/core/java/com/android/server/job/controllers/IdleController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java index 6f2b33453891..6f2b33453891 100644 --- a/services/core/java/com/android/server/job/controllers/JobStatus.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java diff --git a/services/core/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java index b8cfac4d4206..b8cfac4d4206 100644 --- a/services/core/java/com/android/server/job/controllers/QuotaController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java diff --git a/services/core/java/com/android/server/job/controllers/StateController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java index 51be38be990d..51be38be990d 100644 --- a/services/core/java/com/android/server/job/controllers/StateController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java diff --git a/services/core/java/com/android/server/job/controllers/StorageController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/StorageController.java index 51187dff4d59..51187dff4d59 100644 --- a/services/core/java/com/android/server/job/controllers/StorageController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/StorageController.java diff --git a/services/core/java/com/android/server/job/controllers/TimeController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java index 4c11947212f9..4c11947212f9 100644 --- a/services/core/java/com/android/server/job/controllers/TimeController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java diff --git a/services/core/java/com/android/server/job/controllers/idle/CarIdlenessTracker.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/CarIdlenessTracker.java index 82c33f548c15..82c33f548c15 100644 --- a/services/core/java/com/android/server/job/controllers/idle/CarIdlenessTracker.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/CarIdlenessTracker.java diff --git a/services/core/java/com/android/server/job/controllers/idle/DeviceIdlenessTracker.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/DeviceIdlenessTracker.java index a85bd4066ad3..a85bd4066ad3 100644 --- a/services/core/java/com/android/server/job/controllers/idle/DeviceIdlenessTracker.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/DeviceIdlenessTracker.java diff --git a/services/core/java/com/android/server/job/controllers/idle/IdlenessListener.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/IdlenessListener.java index 7ffd7cd3e2e0..7ffd7cd3e2e0 100644 --- a/services/core/java/com/android/server/job/controllers/idle/IdlenessListener.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/IdlenessListener.java diff --git a/services/core/java/com/android/server/job/controllers/idle/IdlenessTracker.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/IdlenessTracker.java index 09f01c2e4113..09f01c2e4113 100644 --- a/services/core/java/com/android/server/job/controllers/idle/IdlenessTracker.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/IdlenessTracker.java diff --git a/api/current.txt b/api/current.txt index a7b38a5d5b10..870b6ae09d95 100644 --- a/api/current.txt +++ b/api/current.txt @@ -103,6 +103,7 @@ package android { field public static final String PACKAGE_USAGE_STATS = "android.permission.PACKAGE_USAGE_STATS"; field @Deprecated public static final String PERSISTENT_ACTIVITY = "android.permission.PERSISTENT_ACTIVITY"; field @Deprecated public static final String PROCESS_OUTGOING_CALLS = "android.permission.PROCESS_OUTGOING_CALLS"; + field public static final String QUERY_ALL_PACKAGES = "android.permission.QUERY_ALL_PACKAGES"; field public static final String READ_CALENDAR = "android.permission.READ_CALENDAR"; field public static final String READ_CALL_LOG = "android.permission.READ_CALL_LOG"; field public static final String READ_CONTACTS = "android.permission.READ_CONTACTS"; @@ -2810,6 +2811,14 @@ package android.accessibilityservice { method public void onClicked(android.accessibilityservice.AccessibilityButtonController); } + public final class AccessibilityGestureInfo implements android.os.Parcelable { + method public int describeContents(); + method public int getDisplayId(); + method public int getGestureId(); + method public void writeToParcel(android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.accessibilityservice.AccessibilityGestureInfo> CREATOR; + } + public abstract class AccessibilityService extends android.app.Service { ctor public AccessibilityService(); method public final void disableSelf(); @@ -2824,7 +2833,8 @@ package android.accessibilityservice { method public java.util.List<android.view.accessibility.AccessibilityWindowInfo> getWindows(); method public abstract void onAccessibilityEvent(android.view.accessibility.AccessibilityEvent); method public final android.os.IBinder onBind(android.content.Intent); - method protected boolean onGesture(int); + method @Deprecated protected boolean onGesture(int); + method public boolean onGesture(@NonNull android.accessibilityservice.AccessibilityGestureInfo); method public abstract void onInterrupt(); method protected boolean onKeyEvent(android.view.KeyEvent); method protected void onServiceConnected(); @@ -2966,6 +2976,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 +2987,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 { @@ -7165,6 +7177,7 @@ package android.app.assist { method public android.os.Bundle getExtras(); method public int getHeight(); method public String getHint(); + method @Nullable public String getHintIdEntry(); method @Nullable public android.view.ViewStructure.HtmlInfo getHtmlInfo(); method public int getId(); method public String getIdEntry(); @@ -11757,6 +11770,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"; @@ -43991,6 +44007,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); @@ -44168,6 +44185,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(); @@ -44350,6 +44371,7 @@ package android.telephony { method public int getBitErrorRate(); method public int getDbm(); method @IntRange(from=android.telephony.CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN, to=android.telephony.CellSignalStrength.SIGNAL_STRENGTH_GREAT) public int getLevel(); + method public int getRssi(); method public int getTimingAdvance(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellSignalStrengthGsm> CREATOR; @@ -51432,6 +51454,7 @@ package android.view { method public abstract void setFocusable(boolean); method public abstract void setFocused(boolean); method public abstract void setHint(CharSequence); + method public void setHintIdEntry(@NonNull String); method public abstract void setHtmlInfo(@NonNull android.view.ViewStructure.HtmlInfo); method public abstract void setId(int, String, String, String); method public void setImportantForAutofill(int); diff --git a/api/test-current.txt b/api/test-current.txt index b393fccdb366..8e6ff3019b0a 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -32,6 +32,14 @@ package android { } +package android.accessibilityservice { + + public final class AccessibilityGestureInfo implements android.os.Parcelable { + ctor public AccessibilityGestureInfo(int, int); + } + +} + package android.animation { public class ValueAnimator extends android.animation.Animator { @@ -3028,6 +3036,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/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/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 03e3b82c75f7..ea50999e9d56 100644 --- a/config/preloaded-classes +++ b/config/preloaded-classes @@ -563,6 +563,7 @@ android.app.job.JobInfo android.app.job.JobParameters$1 android.app.job.JobParameters android.app.job.JobScheduler +android.app.job.JobSchedulerFrameworkInitializer android.app.job.JobService$1 android.app.job.JobService android.app.job.JobServiceEngine$JobHandler diff --git a/config/preloaded-classes-extra b/config/preloaded-classes-extra index 09f393ad4844..94849fb97433 100644 --- a/config/preloaded-classes-extra +++ b/config/preloaded-classes-extra @@ -1,3 +1,6 @@ +# JobSchedulerFrameworkInitializer must always be preloaded because it registers the job scheduler +# service wrapper to SystemServiceRegistry. +android.app.job.JobSchedulerFrameworkInitializer android.icu.impl.coll.CollationRoot android.icu.impl.IDNA2003 android.icu.impl.number.Parse diff --git a/tools/preload2/src/com/android/preload/ui/NullProgressMonitor.java b/core/java/android/accessibilityservice/AccessibilityGestureInfo.aidl index f45aad06ac6b..2539051f4069 100644 --- a/tools/preload2/src/com/android/preload/ui/NullProgressMonitor.java +++ b/core/java/android/accessibilityservice/AccessibilityGestureInfo.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. @@ -14,26 +14,6 @@ * limitations under the License. */ -package com.android.preload.ui; +package android.accessibilityservice; -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 +parcelable AccessibilityGestureInfo; diff --git a/core/java/android/accessibilityservice/AccessibilityGestureInfo.java b/core/java/android/accessibilityservice/AccessibilityGestureInfo.java new file mode 100644 index 000000000000..dc50a4c6c633 --- /dev/null +++ b/core/java/android/accessibilityservice/AccessibilityGestureInfo.java @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.accessibilityservice; + + +import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_DOWN; +import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_DOWN_AND_LEFT; +import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_DOWN_AND_RIGHT; +import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_DOWN_AND_UP; +import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_LEFT; +import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_LEFT_AND_DOWN; +import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_LEFT_AND_RIGHT; +import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_LEFT_AND_UP; +import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_RIGHT; +import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_RIGHT_AND_DOWN; +import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_RIGHT_AND_LEFT; +import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_RIGHT_AND_UP; +import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_UP; +import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_UP_AND_DOWN; +import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_UP_AND_LEFT; +import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_UP_AND_RIGHT; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.TestApi; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * This class describes the gesture information including gesture id and which display it happens + * on. + * <p> + * <strong>Note:</strong> Accessibility services setting the + * {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE} + * flag can receive gestures. + * + * @see AccessibilityService#onGesture(AccessibilityGestureInfo) + */ + +public final class AccessibilityGestureInfo implements Parcelable { + + /** @hide */ + @IntDef(prefix = { "GESTURE_" }, value = { + GESTURE_SWIPE_UP, + GESTURE_SWIPE_UP_AND_LEFT, + GESTURE_SWIPE_UP_AND_DOWN, + GESTURE_SWIPE_UP_AND_RIGHT, + GESTURE_SWIPE_DOWN, + GESTURE_SWIPE_DOWN_AND_LEFT, + GESTURE_SWIPE_DOWN_AND_UP, + GESTURE_SWIPE_DOWN_AND_RIGHT, + GESTURE_SWIPE_LEFT, + GESTURE_SWIPE_LEFT_AND_UP, + GESTURE_SWIPE_LEFT_AND_RIGHT, + GESTURE_SWIPE_LEFT_AND_DOWN, + GESTURE_SWIPE_RIGHT, + GESTURE_SWIPE_RIGHT_AND_UP, + GESTURE_SWIPE_RIGHT_AND_LEFT, + GESTURE_SWIPE_RIGHT_AND_DOWN + }) + @Retention(RetentionPolicy.SOURCE) + public @interface GestureType {} + + @GestureType + private final int mGestureId; + private final int mDisplayId; + + /** @hide */ + @TestApi + public AccessibilityGestureInfo(int gestureId, int displayId) { + mGestureId = gestureId; + mDisplayId = displayId; + } + + private AccessibilityGestureInfo(@NonNull Parcel parcel) { + mGestureId = parcel.readInt(); + mDisplayId = parcel.readInt(); + } + + /** + * Returns the display id of the received-gesture display, for use with + * {@link android.hardware.display.DisplayManager#getDisplay(int)}. + * + * @return the display id. + */ + public int getDisplayId() { + return mDisplayId; + } + + /** + * Returns performed gesture id. + * + * @return the performed gesture id. + * + */ + @GestureType public int getGestureId() { + return mGestureId; + } + + @Override + public String toString() { + StringBuilder stringBuilder = new StringBuilder("AccessibilityGestureInfo["); + stringBuilder.append("gestureId: ").append(mGestureId); + stringBuilder.append(", "); + stringBuilder.append("displayId: ").append(mDisplayId); + stringBuilder.append(']'); + return stringBuilder.toString(); + } + + /** + * {@inheritDoc} + */ + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeInt(mGestureId); + parcel.writeInt(mDisplayId); + } + + /** + * @see Parcelable.Creator + */ + public static final @NonNull Parcelable.Creator<AccessibilityGestureInfo> CREATOR = + new Parcelable.Creator<AccessibilityGestureInfo>() { + public AccessibilityGestureInfo createFromParcel(Parcel parcel) { + return new AccessibilityGestureInfo(parcel); + } + + public AccessibilityGestureInfo[] newArray(int size) { + return new AccessibilityGestureInfo[size]; + } + }; + +} diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index 4aafa53805e1..827e5408403a 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -309,7 +309,7 @@ public abstract class AccessibilityService extends Service { * Name under which an AccessibilityService component publishes information * about itself. This meta-data must reference an XML resource containing an * <code><{@link android.R.styleable#AccessibilityService accessibility-service}></code> - * tag. This is a a sample XML file configuring an accessibility service: + * tag. This is a sample XML file configuring an accessibility service: * <pre> <accessibility-service * android:accessibilityEventTypes="typeViewClicked|typeViewFocused" * android:packageNames="foo.bar, foo.baz" @@ -381,7 +381,8 @@ public abstract class AccessibilityService extends Service { void onInterrupt(); void onServiceConnected(); void init(int connectionId, IBinder windowToken); - boolean onGesture(int gestureId); + /** The detected gesture information for different displays */ + boolean onGesture(AccessibilityGestureInfo gestureInfo); boolean onKeyEvent(KeyEvent event); /** Magnification changed callbacks for different displays */ void onMagnificationChanged(int displayId, @NonNull Region region, @@ -514,17 +515,18 @@ public abstract class AccessibilityService extends Service { } /** - * Called by the system when the user performs a specific gesture on the - * touch screen. + * Called by {@link #onGesture(AccessibilityGestureInfo)} when the user performs a specific + * gesture on the default display. * * <strong>Note:</strong> To receive gestures an accessibility service must * request that the device is in touch exploration mode by setting the - * {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE} + * {@link AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE} * flag. * * @param gestureId The unique id of the performed gesture. * * @return Whether the gesture was handled. + * @deprecated Override {@link #onGesture(AccessibilityGestureInfo)} instead. * * @see #GESTURE_SWIPE_UP * @see #GESTURE_SWIPE_UP_AND_LEFT @@ -543,11 +545,36 @@ public abstract class AccessibilityService extends Service { * @see #GESTURE_SWIPE_RIGHT_AND_LEFT * @see #GESTURE_SWIPE_RIGHT_AND_DOWN */ + @Deprecated protected boolean onGesture(int gestureId) { return false; } /** + * Called by the system when the user performs a specific gesture on the + * specific touch screen. + *<p> + * <strong>Note:</strong> To receive gestures an accessibility service must + * request that the device is in touch exploration mode by setting the + * {@link AccessibilityServiceInfo#FLAG_REQUEST_TOUCH_EXPLORATION_MODE} + * flag. + *<p> + * <strong>Note:</strong> The default implementation calls {@link #onGesture(int)} when the + * touch screen is default display. + * + * @param gestureInfo The information of gesture. + * + * @return Whether the gesture was handled. + * + */ + public boolean onGesture(@NonNull AccessibilityGestureInfo gestureInfo) { + if (gestureInfo.getDisplayId() == Display.DEFAULT_DISPLAY) { + onGesture(gestureInfo.getGestureId()); + } + return false; + } + + /** * Callback that allows an accessibility service to observe the key events * before they are passed to the rest of the system. This means that the events * are first delivered here before they are passed to the device policy, the @@ -777,8 +804,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); @@ -1673,8 +1700,8 @@ public abstract class AccessibilityService extends Service { } @Override - public boolean onGesture(int gestureId) { - return AccessibilityService.this.onGesture(gestureId); + public boolean onGesture(AccessibilityGestureInfo gestureInfo) { + return AccessibilityService.this.onGesture(gestureInfo); } @Override @@ -1773,8 +1800,9 @@ public abstract class AccessibilityService extends Service { mCaller.sendMessage(message); } - public void onGesture(int gestureId) { - Message message = mCaller.obtainMessageI(DO_ON_GESTURE, gestureId); + @Override + public void onGesture(AccessibilityGestureInfo gestureInfo) { + Message message = mCaller.obtainMessageO(DO_ON_GESTURE, gestureInfo); mCaller.sendMessage(message); } @@ -1887,8 +1915,7 @@ public abstract class AccessibilityService extends Service { case DO_ON_GESTURE: { if (mConnectionId != AccessibilityInteractionClient.NO_ID) { - final int gestureId = message.arg1; - mCallback.onGesture(gestureId); + mCallback.onGesture((AccessibilityGestureInfo) message.obj); } } return; 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/IAccessibilityServiceClient.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl index 1dae4fca111e..407ba59c3297 100644 --- a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl +++ b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl @@ -20,6 +20,7 @@ import android.accessibilityservice.IAccessibilityServiceConnection; import android.graphics.Region; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityWindowInfo; +import android.accessibilityservice.AccessibilityGestureInfo; import android.view.KeyEvent; /** @@ -35,7 +36,7 @@ import android.view.KeyEvent; void onInterrupt(); - void onGesture(int gesture); + void onGesture(in AccessibilityGestureInfo gestureInfo); void clearAccessibilityCache(); diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl index c8e01321812c..1ca07dd6ed23 100644 --- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl +++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl @@ -95,6 +95,8 @@ 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/ActivityOptions.java b/core/java/android/app/ActivityOptions.java index 926044bffdd0..b8d9575f4b0f 100644 --- a/core/java/android/app/ActivityOptions.java +++ b/core/java/android/app/ActivityOptions.java @@ -177,6 +177,13 @@ public class ActivityOptions { private static final String KEY_LAUNCH_DISPLAY_ID = "android.activity.launchDisplayId"; /** + * The id of the display where the caller was on. + * @see #setCallerDisplayId(int) + * @hide + */ + private static final String KEY_CALLER_DISPLAY_ID = "android.activity.callerDisplayId"; + + /** * The windowing mode the activity should be launched into. * @hide */ @@ -269,6 +276,8 @@ public class ActivityOptions { = "android:activity.remoteAnimationAdapter"; /** @hide */ + public static final int ANIM_UNDEFINED = -1; + /** @hide */ public static final int ANIM_NONE = 0; /** @hide */ public static final int ANIM_CUSTOM = 1; @@ -299,7 +308,7 @@ public class ActivityOptions { private String mPackageName; private Rect mLaunchBounds; - private int mAnimationType = ANIM_NONE; + private int mAnimationType = ANIM_UNDEFINED; private int mCustomEnterResId; private int mCustomExitResId; private int mCustomInPlaceResId; @@ -318,6 +327,7 @@ public class ActivityOptions { private int mExitCoordinatorIndex; private PendingIntent mUsageTimeReport; private int mLaunchDisplayId = INVALID_DISPLAY; + private int mCallerDisplayId = INVALID_DISPLAY; @WindowConfiguration.WindowingMode private int mLaunchWindowingMode = WINDOWING_MODE_UNDEFINED; @WindowConfiguration.ActivityType @@ -896,7 +906,7 @@ public class ActivityOptions { Slog.w(TAG, e); } mLaunchBounds = opts.getParcelable(KEY_LAUNCH_BOUNDS); - mAnimationType = opts.getInt(KEY_ANIM_TYPE); + mAnimationType = opts.getInt(KEY_ANIM_TYPE, ANIM_UNDEFINED); switch (mAnimationType) { case ANIM_CUSTOM: mCustomEnterResId = opts.getInt(KEY_ANIM_ENTER_RES_ID, 0); @@ -945,6 +955,7 @@ public class ActivityOptions { } mLockTaskMode = opts.getBoolean(KEY_LOCK_TASK_MODE, false); mLaunchDisplayId = opts.getInt(KEY_LAUNCH_DISPLAY_ID, INVALID_DISPLAY); + mCallerDisplayId = opts.getInt(KEY_CALLER_DISPLAY_ID, INVALID_DISPLAY); mLaunchWindowingMode = opts.getInt(KEY_LAUNCH_WINDOWING_MODE, WINDOWING_MODE_UNDEFINED); mLaunchActivityType = opts.getInt(KEY_LAUNCH_ACTIVITY_TYPE, ACTIVITY_TYPE_UNDEFINED); mLaunchTaskId = opts.getInt(KEY_LAUNCH_TASK_ID, -1); @@ -1204,6 +1215,17 @@ public class ActivityOptions { } /** @hide */ + public int getCallerDisplayId() { + return mCallerDisplayId; + } + + /** @hide */ + public ActivityOptions setCallerDisplayId(int callerDisplayId) { + mCallerDisplayId = callerDisplayId; + return this; + } + + /** @hide */ public int getLaunchWindowingMode() { return mLaunchWindowingMode; } @@ -1447,7 +1469,9 @@ public class ActivityOptions { if (mLaunchBounds != null) { b.putParcelable(KEY_LAUNCH_BOUNDS, mLaunchBounds); } - b.putInt(KEY_ANIM_TYPE, mAnimationType); + if (mAnimationType != ANIM_UNDEFINED) { + b.putInt(KEY_ANIM_TYPE, mAnimationType); + } if (mUsageTimeReport != null) { b.putParcelable(KEY_USAGE_TIME_REPORT, mUsageTimeReport); } @@ -1506,6 +1530,9 @@ public class ActivityOptions { if (mLaunchDisplayId != INVALID_DISPLAY) { b.putInt(KEY_LAUNCH_DISPLAY_ID, mLaunchDisplayId); } + if (mCallerDisplayId != INVALID_DISPLAY) { + b.putInt(KEY_CALLER_DISPLAY_ID, mCallerDisplayId); + } if (mLaunchWindowingMode != WINDOWING_MODE_UNDEFINED) { b.putInt(KEY_LAUNCH_WINDOWING_MODE, mLaunchWindowingMode); } 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/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index fb72e651cebd..d20cc4131261 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -834,9 +834,12 @@ public class AppOpsManager { public static final int OP_ACCESS_ACCESSIBILITY = 88; /** @hide Read the device identifiers (IMEI / MEID, IMSI, SIM / Build serial) */ public static final int OP_READ_DEVICE_IDENTIFIERS = 89; + /** @hide Query all apps on device, regardless of declarations in the calling app manifest */ + public static final int OP_QUERY_ALL_PACKAGES = 90; + /** @hide */ @UnsupportedAppUsage - public static final int _NUM_OP = 90; + public static final int _NUM_OP = 91; /** Access to coarse location information. */ public static final String OPSTR_COARSE_LOCATION = "android:coarse_location"; @@ -1112,6 +1115,8 @@ public class AppOpsManager { public static final String OPSTR_ACCESS_ACCESSIBILITY = "android:access_accessibility"; /** @hide Read device identifiers */ public static final String OPSTR_READ_DEVICE_IDENTIFIERS = "android:read_device_identifiers"; + /** @hide Query all packages on device */ + public static final String OPSTR_QUERY_ALL_PACKAGES = "android:query_all_packages"; // Warning: If an permission is added here it also has to be added to // com.android.packageinstaller.permission.utils.EventLogger @@ -1273,6 +1278,7 @@ public class AppOpsManager { OP_LEGACY_STORAGE, // LEGACY_STORAGE OP_ACCESS_ACCESSIBILITY, // ACCESS_ACCESSIBILITY OP_READ_DEVICE_IDENTIFIERS, // READ_DEVICE_IDENTIFIERS + OP_QUERY_ALL_PACKAGES, // QUERY_ALL_PACKAGES }; /** @@ -1369,6 +1375,7 @@ public class AppOpsManager { OPSTR_LEGACY_STORAGE, OPSTR_ACCESS_ACCESSIBILITY, OPSTR_READ_DEVICE_IDENTIFIERS, + OPSTR_QUERY_ALL_PACKAGES, }; /** @@ -1466,6 +1473,7 @@ public class AppOpsManager { "LEGACY_STORAGE", "ACCESS_ACCESSIBILITY", "READ_DEVICE_IDENTIFIERS", + "QUERY_ALL_PACKAGES", }; /** @@ -1564,6 +1572,7 @@ public class AppOpsManager { null, // no permission for OP_LEGACY_STORAGE null, // no permission for OP_ACCESS_ACCESSIBILITY null, // no direct permission for OP_READ_DEVICE_IDENTIFIERS + null, // no permission for OP_QUERY_ALL_PACKAGES }; /** @@ -1662,6 +1671,7 @@ public class AppOpsManager { null, // LEGACY_STORAGE null, // ACCESS_ACCESSIBILITY null, // READ_DEVICE_IDENTIFIERS + null, // QUERY_ALL_PACKAGES }; /** @@ -1759,6 +1769,7 @@ public class AppOpsManager { false, // LEGACY_STORAGE false, // ACCESS_ACCESSIBILITY false, // READ_DEVICE_IDENTIFIERS + false, // QUERY_ALL_PACKAGES }; /** @@ -1855,6 +1866,7 @@ public class AppOpsManager { AppOpsManager.MODE_DEFAULT, // LEGACY_STORAGE AppOpsManager.MODE_ALLOWED, // ACCESS_ACCESSIBILITY AppOpsManager.MODE_ERRORED, // READ_DEVICE_IDENTIFIERS + AppOpsManager.MODE_DEFAULT, // QUERY_ALL_PACKAGES }; /** @@ -1955,6 +1967,7 @@ public class AppOpsManager { false, // LEGACY_STORAGE false, // ACCESS_ACCESSIBILITY false, // READ_DEVICE_IDENTIFIERS + false, // QUERY_ALL_PACKAGES }; /** diff --git a/core/java/android/app/JobSchedulerImpl.java b/core/java/android/app/JobSchedulerImpl.java index e8770185305c..924a70809747 100644 --- a/core/java/android/app/JobSchedulerImpl.java +++ b/core/java/android/app/JobSchedulerImpl.java @@ -26,6 +26,9 @@ import android.os.RemoteException; import java.util.List; + +// APEX NOTE: Class path referred to by robolectric, so can't move it. + /** * Concrete implementation of the JobScheduler interface * @hide @@ -33,7 +36,7 @@ import java.util.List; public class JobSchedulerImpl extends JobScheduler { IJobScheduler mBinder; - /* package */ JobSchedulerImpl(IJobScheduler binder) { + public JobSchedulerImpl(IJobScheduler binder) { mBinder = binder; } diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 5aa1f8f07f60..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; diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java index 6f7a0607cbdb..4b4a071ecf72 100644 --- a/core/java/android/app/PendingIntent.java +++ b/core/java/android/app/PendingIntent.java @@ -893,6 +893,17 @@ public final class PendingIntent implements Parcelable { String resolvedType = intent != null ? intent.resolveTypeIfNeeded(context.getContentResolver()) : null; + + if (context != null && isActivity()) { + // Set the context display id as preferred for this activity launches, so that it + // can land on caller's display. Or just brought the task to front at the display + // where it was on since it has higher preference. + ActivityOptions activityOptions = options != null ? new ActivityOptions(options) + : ActivityOptions.makeBasic(); + activityOptions.setCallerDisplayId(context.getDisplayId()); + options = activityOptions.toBundle(); + } + return ActivityManager.getService().sendIntentSender( mTarget, mWhitelistToken, code, intent, resolvedType, onFinished != null diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index cfe2cf09ae19..5034b535944c 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -23,8 +23,6 @@ import android.app.admin.DevicePolicyManager; import android.app.admin.IDevicePolicyManager; import android.app.contentsuggestions.ContentSuggestionsManager; import android.app.contentsuggestions.IContentSuggestionsManager; -import android.app.job.IJobScheduler; -import android.app.job.JobScheduler; import android.app.prediction.AppPredictionManager; import android.app.role.RoleControllerManager; import android.app.role.RoleManager; @@ -198,12 +196,15 @@ import com.android.internal.os.IDropBoxManagerService; import com.android.internal.policy.PhoneLayoutInflater; import java.util.Map; +import java.util.function.Function; /** * Manages all of the system services that can be returned by {@link Context#getSystemService}. * Used by {@link ContextImpl}. + * + * @hide */ -final class SystemServiceRegistry { +public final class SystemServiceRegistry { private static final String TAG = "SystemServiceRegistry"; // Service registry information. @@ -979,14 +980,6 @@ final class SystemServiceRegistry { return new NetworkStatsManager(ctx.getOuterContext()); }}); - registerService(Context.JOB_SCHEDULER_SERVICE, JobScheduler.class, - new StaticServiceFetcher<JobScheduler>() { - @Override - public JobScheduler createService() throws ServiceNotFoundException { - IBinder b = ServiceManager.getServiceOrThrow(Context.JOB_SCHEDULER_SERVICE); - return new JobSchedulerImpl(IJobScheduler.Stub.asInterface(b)); - }}); - registerService(Context.PERSISTENT_DATA_BLOCK_SERVICE, PersistentDataBlockManager.class, new StaticServiceFetcher<PersistentDataBlockManager>() { @Override @@ -1324,6 +1317,22 @@ final class SystemServiceRegistry { } /** + * APEX modules will use it to register their service wrapper. + * + * @hide + */ + public static <T> void registerStaticService(String serviceName, Class<T> serviceClass, + Function<IBinder, T> serviceFetcher) { + registerService(serviceName, serviceClass, + new StaticServiceFetcher<T>() { + @Override + public T createService() throws ServiceNotFoundException { + IBinder b = ServiceManager.getServiceOrThrow(serviceName); + return serviceFetcher.apply(b); + }}); + } + + /** * Base interface for classes that fetch services. * These objects must only be created during static initialization. */ diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java index 3935628b707f..2b74b99c8071 100644 --- a/core/java/android/app/UiAutomation.java +++ b/core/java/android/app/UiAutomation.java @@ -16,6 +16,7 @@ package android.app; +import android.accessibilityservice.AccessibilityGestureInfo; import android.accessibilityservice.AccessibilityService.Callbacks; import android.accessibilityservice.AccessibilityService.IAccessibilityServiceClientWrapper; import android.accessibilityservice.AccessibilityServiceInfo; @@ -1232,7 +1233,7 @@ public final class UiAutomation { } @Override - public boolean onGesture(int gestureId) { + public boolean onGesture(AccessibilityGestureInfo gestureInfo) { /* do nothing */ return false; } diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java index 3fca3118c9a0..86e768db3c52 100644 --- a/core/java/android/app/assist/AssistStructure.java +++ b/core/java/android/app/assist/AssistStructure.java @@ -641,6 +641,7 @@ public class AssistStructure implements Parcelable { int mMaxEms = -1; int mMaxLength = -1; @Nullable String mTextIdEntry; + @Nullable String mHintIdEntry; @AutofillImportance int mImportantForAutofill; // POJO used to override some autofill-related values when the node is parcelized. @@ -688,18 +689,19 @@ public class AssistStructure implements Parcelable { static final int FLAGS_HAS_LOCALE_LIST = 0x00010000; static final int FLAGS_ALL_CONTROL = 0xfff00000; - static final int AUTOFILL_FLAGS_HAS_AUTOFILL_VIEW_ID = 0x001; - static final int AUTOFILL_FLAGS_HAS_AUTOFILL_VIRTUAL_VIEW_ID = 0x002; - static final int AUTOFILL_FLAGS_HAS_AUTOFILL_VALUE = 0x004; - static final int AUTOFILL_FLAGS_HAS_AUTOFILL_TYPE = 0x008; - static final int AUTOFILL_FLAGS_HAS_AUTOFILL_HINTS = 0x010; - static final int AUTOFILL_FLAGS_HAS_AUTOFILL_OPTIONS = 0x020; - static final int AUTOFILL_FLAGS_HAS_HTML_INFO = 0x040; - static final int AUTOFILL_FLAGS_HAS_TEXT_ID_ENTRY = 0x080; - static final int AUTOFILL_FLAGS_HAS_MIN_TEXT_EMS = 0x100; - static final int AUTOFILL_FLAGS_HAS_MAX_TEXT_EMS = 0x200; - static final int AUTOFILL_FLAGS_HAS_MAX_TEXT_LENGTH = 0x400; - static final int AUTOFILL_FLAGS_HAS_AUTOFILL_SESSION_ID = 0x800; + static final int AUTOFILL_FLAGS_HAS_AUTOFILL_VIEW_ID = 0x0001; + static final int AUTOFILL_FLAGS_HAS_AUTOFILL_VIRTUAL_VIEW_ID = 0x0002; + static final int AUTOFILL_FLAGS_HAS_AUTOFILL_VALUE = 0x0004; + static final int AUTOFILL_FLAGS_HAS_AUTOFILL_TYPE = 0x0008; + static final int AUTOFILL_FLAGS_HAS_AUTOFILL_HINTS = 0x0010; + static final int AUTOFILL_FLAGS_HAS_AUTOFILL_OPTIONS = 0x0020; + static final int AUTOFILL_FLAGS_HAS_HTML_INFO = 0x0040; + static final int AUTOFILL_FLAGS_HAS_TEXT_ID_ENTRY = 0x0080; + static final int AUTOFILL_FLAGS_HAS_MIN_TEXT_EMS = 0x0100; + static final int AUTOFILL_FLAGS_HAS_MAX_TEXT_EMS = 0x0200; + static final int AUTOFILL_FLAGS_HAS_MAX_TEXT_LENGTH = 0x0400; + static final int AUTOFILL_FLAGS_HAS_AUTOFILL_SESSION_ID = 0x0800; + static final int AUTOFILL_FLAGS_HAS_HINT_ID_ENTRY = 0x1000; int mFlags; int mAutofillFlags; @@ -786,6 +788,9 @@ public class AssistStructure implements Parcelable { if ((autofillFlags & AUTOFILL_FLAGS_HAS_TEXT_ID_ENTRY) != 0) { mTextIdEntry = preader.readString(); } + if ((autofillFlags & AUTOFILL_FLAGS_HAS_HINT_ID_ENTRY) != 0) { + mHintIdEntry = preader.readString(); + } } if ((flags&FLAGS_HAS_LARGE_COORDS) != 0) { mX = in.readInt(); @@ -934,6 +939,9 @@ public class AssistStructure implements Parcelable { if (mTextIdEntry != null) { autofillFlags |= AUTOFILL_FLAGS_HAS_TEXT_ID_ENTRY; } + if (mHintIdEntry != null) { + autofillFlags |= AUTOFILL_FLAGS_HAS_HINT_ID_ENTRY; + } pwriter.writeString(mClassName); @@ -1011,6 +1019,9 @@ public class AssistStructure implements Parcelable { if ((autofillFlags & AUTOFILL_FLAGS_HAS_TEXT_ID_ENTRY) != 0) { pwriter.writeString(mTextIdEntry); } + if ((autofillFlags & AUTOFILL_FLAGS_HAS_HINT_ID_ENTRY) != 0) { + pwriter.writeString(mHintIdEntry); + } } if ((flags&FLAGS_HAS_LARGE_COORDS) != 0) { out.writeInt(mX); @@ -1586,6 +1597,17 @@ public class AssistStructure implements Parcelable { } /** + * Gets the identifier used to set the hint associated with this view. + * + * <p>It's only relevant when the {@link AssistStructure} is used for autofill purposes, + * not for assist purposes. + */ + @Nullable + public String getHintIdEntry() { + return mHintIdEntry; + } + + /** * Return a Bundle containing optional vendor-specific extension information. */ public Bundle getExtras() { @@ -1853,6 +1875,11 @@ public class AssistStructure implements Parcelable { } @Override + public void setHintIdEntry(@NonNull String entryName) { + mNode.mHintIdEntry = Preconditions.checkNotNull(entryName); + } + + @Override public CharSequence getText() { return mNode.mText != null ? mNode.mText.mText : null; } @@ -2266,6 +2293,7 @@ public class AssistStructure implements Parcelable { String hint = node.getHint(); if (hint != null) { Log.i(TAG, prefix + " Hint: " + hint); + Log.i(TAG, prefix + " Resource id: " + node.getHintIdEntry()); } Bundle extras = node.getExtras(); if (extras != null) { diff --git a/core/java/android/app/job/JobSchedulerFrameworkInitializer.java b/core/java/android/app/job/JobSchedulerFrameworkInitializer.java new file mode 100644 index 000000000000..cf2979c96191 --- /dev/null +++ b/core/java/android/app/job/JobSchedulerFrameworkInitializer.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 android.app.job; + +import android.app.JobSchedulerImpl; +import android.app.SystemServiceRegistry; +import android.content.Context; + +/** + * This class needs to be pre-loaded by zygote. This is where the job scheduler service wrapper + * is registered. + * + * @hide + */ +public class JobSchedulerFrameworkInitializer { + static { + SystemServiceRegistry.registerStaticService( + Context.JOB_SCHEDULER_SERVICE, JobScheduler.class, + (b) -> new JobSchedulerImpl(IJobScheduler.Stub.asInterface(b))); + } +} diff --git a/core/java/android/bluetooth/BluetoothCodecConfig.java b/core/java/android/bluetooth/BluetoothCodecConfig.java index 591c418c936e..36f3a1eeba79 100644 --- a/core/java/android/bluetooth/BluetoothCodecConfig.java +++ b/core/java/android/bluetooth/BluetoothCodecConfig.java @@ -441,6 +441,43 @@ public final class BluetoothCodecConfig implements Parcelable { } /** + * Checks whether a value set presented by a bitmask has zero or single bit + * + * @param valueSet the value set presented by a bitmask + * @return true if the valueSet contains zero or single bit, otherwise false. + */ + private static boolean hasSingleBit(int valueSet) { + return (valueSet == 0 || (valueSet & (valueSet - 1)) == 0); + } + + /** + * Checks whether the object contains none or single sample rate. + * + * @return true if the object contains none or single sample rate, otherwise false. + */ + public boolean hasSingleSampleRate() { + return hasSingleBit(mSampleRate); + } + + /** + * Checks whether the object contains none or single bits per sample. + * + * @return true if the object contains none or single bits per sample, otherwise false. + */ + public boolean hasSingleBitsPerSample() { + return hasSingleBit(mBitsPerSample); + } + + /** + * Checks whether the object contains none or single channel mode. + * + * @return true if the object contains none or single channel mode, otherwise false. + */ + public boolean hasSingleChannelMode() { + return hasSingleBit(mChannelMode); + } + + /** * Checks whether the audio feeding parameters are same. * * @param other the codec config to compare against @@ -451,4 +488,58 @@ public final class BluetoothCodecConfig implements Parcelable { && other.mBitsPerSample == mBitsPerSample && other.mChannelMode == mChannelMode); } + + /** + * Checks whether another codec config has the similar feeding parameters. + * Any parameters with NONE value will be considered to be a wildcard matching. + * + * @param other the codec config to compare against + * @return true if the audio feeding parameters are similar, otherwise false. + */ + public boolean similarCodecFeedingParameters(BluetoothCodecConfig other) { + if (other == null || mCodecType != other.mCodecType) { + return false; + } + int sampleRate = other.mSampleRate; + if (mSampleRate == BluetoothCodecConfig.SAMPLE_RATE_NONE + || sampleRate == BluetoothCodecConfig.SAMPLE_RATE_NONE) { + sampleRate = mSampleRate; + } + int bitsPerSample = other.mBitsPerSample; + if (mBitsPerSample == BluetoothCodecConfig.BITS_PER_SAMPLE_NONE + || bitsPerSample == BluetoothCodecConfig.BITS_PER_SAMPLE_NONE) { + bitsPerSample = mBitsPerSample; + } + int channelMode = other.mChannelMode; + if (mChannelMode == BluetoothCodecConfig.CHANNEL_MODE_NONE + || channelMode == BluetoothCodecConfig.CHANNEL_MODE_NONE) { + channelMode = mChannelMode; + } + return sameAudioFeedingParameters(new BluetoothCodecConfig( + mCodecType, /* priority */ 0, sampleRate, bitsPerSample, channelMode, + /* specific1 */ 0, /* specific2 */ 0, /* specific3 */ 0, + /* specific4 */ 0)); + } + + /** + * Checks whether the codec specific parameters are the same. + * + * @param other the codec config to compare against + * @return true if the codec specific parameters are the same, otherwise false. + */ + public boolean sameCodecSpecificParameters(BluetoothCodecConfig other) { + if (other == null && mCodecType != other.mCodecType) { + return false; + } + // Currently we only care about the LDAC Playback Quality at CodecSpecific1 + switch (mCodecType) { + case SOURCE_CODEC_TYPE_LDAC: + if (mCodecSpecific1 != other.mCodecSpecific1) { + return false; + } + // fall through + default: + return true; + } + } } diff --git a/core/java/android/bluetooth/BluetoothCodecStatus.java b/core/java/android/bluetooth/BluetoothCodecStatus.java index 58b6aeae6398..58a764a85bed 100644 --- a/core/java/android/bluetooth/BluetoothCodecStatus.java +++ b/core/java/android/bluetooth/BluetoothCodecStatus.java @@ -89,6 +89,43 @@ public final class BluetoothCodecStatus implements Parcelable { return Arrays.asList(c1).containsAll(Arrays.asList(c2)); } + /** + * Checks whether the codec config matches the selectable capabilities. + * Any parameters of the codec config with NONE value will be considered a wildcard matching. + * + * @param codecConfig the codec config to compare against + * @return true if the codec config matches, otherwise false + */ + public boolean isCodecConfigSelectable(BluetoothCodecConfig codecConfig) { + if (codecConfig == null || !codecConfig.hasSingleSampleRate() + || !codecConfig.hasSingleBitsPerSample() || !codecConfig.hasSingleChannelMode()) { + return false; + } + for (BluetoothCodecConfig selectableConfig : mCodecsSelectableCapabilities) { + if (codecConfig.getCodecType() != selectableConfig.getCodecType()) { + continue; + } + int sampleRate = codecConfig.getSampleRate(); + if ((sampleRate & selectableConfig.getSampleRate()) == 0 + && sampleRate != BluetoothCodecConfig.SAMPLE_RATE_NONE) { + continue; + } + int bitsPerSample = codecConfig.getBitsPerSample(); + if ((bitsPerSample & selectableConfig.getBitsPerSample()) == 0 + && bitsPerSample != BluetoothCodecConfig.BITS_PER_SAMPLE_NONE) { + continue; + } + int channelMode = codecConfig.getChannelMode(); + if ((channelMode & selectableConfig.getChannelMode()) == 0 + && channelMode != BluetoothCodecConfig.CHANNEL_MODE_NONE) { + continue; + } + return true; + } + return false; + } + + @Override public int hashCode() { return Objects.hash(mCodecConfig, mCodecsLocalCapabilities, 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/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 4d8a0c46508e..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>. diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index e690a7fd783b..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; @@ -2445,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 + " " @@ -3538,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)) { @@ -3953,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 @@ -4000,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 */ @@ -6514,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); @@ -6619,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; @@ -7122,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) { @@ -7247,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); } @@ -8257,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/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java index af66dc161343..d8110f33d723 100644 --- a/core/java/android/hardware/biometrics/BiometricManager.java +++ b/core/java/android/hardware/biometrics/BiometricManager.java @@ -129,6 +129,25 @@ public class BiometricManager { } /** + * @hide + * @param userId + * @return + */ + @RequiresPermission(USE_BIOMETRIC_INTERNAL) + public boolean hasEnrolledBiometrics(int userId) { + if (mService != null) { + try { + return mService.hasEnrolledBiometrics(userId); + } catch (RemoteException e) { + Slog.w(TAG, "Remote exception in hasEnrolledBiometrics(): " + e); + return false; + } + } else { + return false; + } + } + + /** * Listens for changes to biometric eligibility on keyguard from user settings. * @param callback * @hide diff --git a/core/java/android/hardware/biometrics/IBiometricService.aidl b/core/java/android/hardware/biometrics/IBiometricService.aidl index 18c14cb835a8..f0a0b2f0235f 100644 --- a/core/java/android/hardware/biometrics/IBiometricService.aidl +++ b/core/java/android/hardware/biometrics/IBiometricService.aidl @@ -42,6 +42,9 @@ interface IBiometricService { // Checks if biometrics can be used. int canAuthenticate(String opPackageName, int userId); + // Checks if any biometrics are enrolled. + boolean hasEnrolledBiometrics(int userId); + // Register callback for when keyguard biometric eligibility changes. void registerEnabledOnKeyguardCallback(IBiometricEnabledOnKeyguardCallback callback); 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/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java index 8355e08b6aa8..2a4576adf192 100644 --- a/core/java/android/os/ParcelFileDescriptor.java +++ b/core/java/android/os/ParcelFileDescriptor.java @@ -581,12 +581,16 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { public static ParcelFileDescriptor fromData(byte[] data, String name) throws IOException { if (data == null) return null; MemoryFile file = new MemoryFile(name, data.length); - if (data.length > 0) { - file.writeBytes(data, 0, 0, data.length); + try { + if (data.length > 0) { + file.writeBytes(data, 0, 0, data.length); + } + file.deactivate(); + FileDescriptor fd = file.getFileDescriptor(); + return fd != null ? ParcelFileDescriptor.dup(fd) : null; + } finally { + file.close(); } - file.deactivate(); - FileDescriptor fd = file.getFileDescriptor(); - return fd != null ? ParcelFileDescriptor.dup(fd) : null; } /** 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/MotionEvent.java b/core/java/android/view/MotionEvent.java index bff3a1dba3f4..bd865c063055 100644 --- a/core/java/android/view/MotionEvent.java +++ b/core/java/android/view/MotionEvent.java @@ -1471,6 +1471,10 @@ public final class MotionEvent extends InputEvent implements Parcelable { @UnsupportedAppUsage private static final int HISTORY_CURRENT = -0x80000000; + // This is essentially the same as native AMOTION_EVENT_INVALID_CURSOR_POSITION as they're all + // NaN and we use isnan() everywhere to check validity. + private static final float INVALID_CURSOR_POSITION = Float.NaN; + private static final int MAX_RECYCLED = 10; private static final Object gRecyclerLock = new Object(); private static int gRecyclerUsed; @@ -1590,6 +1594,12 @@ public final class MotionEvent extends InputEvent implements Parcelable { @CriticalNative private static native float nativeGetYPrecision(long nativePtr); @CriticalNative + private static native float nativeGetXCursorPosition(long nativePtr); + @CriticalNative + private static native float nativeGetYCursorPosition(long nativePtr); + @CriticalNative + private static native void nativeSetCursorPosition(long nativePtr, float x, float y); + @CriticalNative private static native long nativeGetDownTimeNanos(long nativePtr); @CriticalNative private static native void nativeSetDownTimeNanos(long nativePtr, long downTime); @@ -1674,12 +1684,11 @@ public final class MotionEvent extends InputEvent implements Parcelable { float xPrecision, float yPrecision, int deviceId, int edgeFlags, int source, int displayId, int flags) { MotionEvent ev = obtain(); - ev.mNativePtr = nativeInitialize(ev.mNativePtr, - deviceId, source, displayId, action, flags, edgeFlags, metaState, buttonState, - CLASSIFICATION_NONE, 0, 0, xPrecision, yPrecision, + final boolean success = ev.initialize(deviceId, source, displayId, action, flags, edgeFlags, + metaState, buttonState, CLASSIFICATION_NONE, 0, 0, xPrecision, yPrecision, downTime * NS_PER_MS, eventTime * NS_PER_MS, pointerCount, pointerProperties, pointerCoords); - if (ev.mNativePtr == 0) { + if (!success) { Log.e(TAG, "Could not initialize MotionEvent"); ev.recycle(); return null; @@ -1859,8 +1868,7 @@ public final class MotionEvent extends InputEvent implements Parcelable { pc[0].pressure = pressure; pc[0].size = size; - ev.mNativePtr = nativeInitialize(ev.mNativePtr, - deviceId, source, displayId, + ev.initialize(deviceId, source, displayId, action, 0, edgeFlags, metaState, 0 /*buttonState*/, CLASSIFICATION_NONE, 0, 0, xPrecision, yPrecision, downTime * NS_PER_MS, eventTime * NS_PER_MS, @@ -1958,6 +1966,22 @@ public final class MotionEvent extends InputEvent implements Parcelable { return ev; } + private boolean initialize(int deviceId, int source, int displayId, int action, int flags, + int edgeFlags, int metaState, int buttonState, @Classification int classification, + float xOffset, float yOffset, float xPrecision, float yPrecision, + long downTimeNanos, long eventTimeNanos, + int pointerCount, PointerProperties[] pointerIds, PointerCoords[] pointerCoords) { + mNativePtr = nativeInitialize(mNativePtr, deviceId, source, displayId, action, flags, + edgeFlags, metaState, buttonState, classification, xOffset, yOffset, + xPrecision, yPrecision, downTimeNanos, eventTimeNanos, pointerCount, pointerIds, + pointerCoords); + if (mNativePtr == 0) { + return false; + } + updateCursorPosition(); + return true; + } + /** @hide */ @Override @UnsupportedAppUsage @@ -2015,7 +2039,11 @@ public final class MotionEvent extends InputEvent implements Parcelable { /** {@inheritDoc} */ @Override public final void setSource(int source) { + if (source == getSource()) { + return; + } nativeSetSource(mNativePtr, source); + updateCursorPosition(); } /** @hide */ @@ -2670,6 +2698,39 @@ public final class MotionEvent extends InputEvent implements Parcelable { } /** + * Returns the x coordinate of mouse cursor position when this event is + * reported. This value is only valid if {@link #getSource()} returns + * {@link InputDevice#SOURCE_MOUSE}. + * + * @hide + */ + public float getXCursorPosition() { + return nativeGetXCursorPosition(mNativePtr); + } + + /** + * Returns the y coordinate of mouse cursor position when this event is + * reported. This value is only valid if {@link #getSource()} returns + * {@link InputDevice#SOURCE_MOUSE}. + * + * @hide + */ + public float getYCursorPosition() { + return nativeGetYCursorPosition(mNativePtr); + } + + /** + * Sets cursor position to given coordinates. The coordinate in parameters should be after + * offsetting. In other words, the effect of this function is {@link #getXCursorPosition()} and + * {@link #getYCursorPosition()} will return the same value passed in the parameters. + * + * @hide + */ + private void setCursorPosition(float x, float y) { + nativeSetCursorPosition(mNativePtr, x, y); + } + + /** * Returns the number of historical points in this event. These are * movements that have occurred between this event and the previous event. * This only applies to ACTION_MOVE events -- all other actions will have @@ -3305,8 +3366,7 @@ public final class MotionEvent extends InputEvent implements Parcelable { pc[i].x = clamp(pc[i].x, left, right); pc[i].y = clamp(pc[i].y, top, bottom); } - ev.mNativePtr = nativeInitialize(ev.mNativePtr, - nativeGetDeviceId(mNativePtr), nativeGetSource(mNativePtr), + ev.initialize(nativeGetDeviceId(mNativePtr), nativeGetSource(mNativePtr), nativeGetDisplayId(mNativePtr), nativeGetAction(mNativePtr), nativeGetFlags(mNativePtr), nativeGetEdgeFlags(mNativePtr), nativeGetMetaState(mNativePtr), @@ -3399,8 +3459,7 @@ public final class MotionEvent extends InputEvent implements Parcelable { final long eventTimeNanos = nativeGetEventTimeNanos(mNativePtr, historyPos); if (h == 0) { - ev.mNativePtr = nativeInitialize(ev.mNativePtr, - nativeGetDeviceId(mNativePtr), nativeGetSource(mNativePtr), + ev.initialize(nativeGetDeviceId(mNativePtr), nativeGetSource(mNativePtr), nativeGetDisplayId(mNativePtr), newAction, nativeGetFlags(mNativePtr), nativeGetEdgeFlags(mNativePtr), nativeGetMetaState(mNativePtr), @@ -3417,6 +3476,38 @@ public final class MotionEvent extends InputEvent implements Parcelable { } } + /** + * Calculate new cursor position for events from mouse. This is used to split, clamp and inject + * events. + * + * <p>If the source is mouse, it sets cursor position to the centroid of all pointers because + * InputReader maps multiple fingers on a touchpad to locations around cursor position in screen + * coordinates so that the mouse cursor is at the centroid of all pointers. + * + * <p>If the source is not mouse it sets cursor position to NaN. + */ + private void updateCursorPosition() { + if (getSource() != InputDevice.SOURCE_MOUSE) { + setCursorPosition(INVALID_CURSOR_POSITION, INVALID_CURSOR_POSITION); + return; + } + + float x = 0; + float y = 0; + + final int pointerCount = getPointerCount(); + for (int i = 0; i < pointerCount; ++i) { + x += getX(i); + y += getY(i); + } + + // If pointer count is 0, divisions below yield NaN, which is an acceptable result for this + // corner case. + x /= pointerCount; + y /= pointerCount; + setCursorPosition(x, y); + } + @Override public String toString() { StringBuilder msg = new StringBuilder(); diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java index a01e15e7ce46..aa29c5f77602 100644 --- a/core/java/android/view/ThreadedRenderer.java +++ b/core/java/android/view/ThreadedRenderer.java @@ -28,6 +28,7 @@ import android.graphics.Rect; import android.graphics.RenderNode; import android.os.SystemProperties; import android.os.Trace; +import android.util.Log; import android.view.Surface.OutOfResourcesException; import android.view.View.AttachInfo; import android.view.animation.AnimationUtils; @@ -674,11 +675,11 @@ public final class ThreadedRenderer extends HardwareRenderer { int syncResult = syncAndDrawFrame(choreographer.mFrameInfo); if ((syncResult & SYNC_LOST_SURFACE_REWARD_IF_FOUND) != 0) { - setEnabled(false); - attachInfo.mViewRootImpl.mSurface.release(); - // Invalidate since we failed to draw. This should fetch a Surface - // if it is still needed or do nothing if we are no longer drawing - attachInfo.mViewRootImpl.invalidate(); + Log.w("OpenGLRenderer", "Surface lost, forcing relayout"); + // We lost our surface. For a relayout next frame which should give us a new + // surface from WindowManager, which hopefully will work. + attachInfo.mViewRootImpl.mForceNextWindowRelayout = true; + attachInfo.mViewRootImpl.requestLayout(); } if ((syncResult & SYNC_REDRAW_REQUESTED) != 0) { attachInfo.mViewRootImpl.invalidate(); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 69884dc62fb3..e1984894a85a 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -967,6 +967,19 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ static boolean sBrokenInsetsDispatch; + /** + * Prior to Q, calling + * {@link com.android.internal.policy.DecorView#setBackgroundDrawable(Drawable)} + * did not call update the window format so the opacity of the background was not correctly + * applied to the window. Some applications rely on this misbehavior to work properly. + * <p> + * From Q, {@link com.android.internal.policy.DecorView#setBackgroundDrawable(Drawable)} is + * the same as {@link com.android.internal.policy.DecorView#setWindowBackground(Drawable)} + * which updates the window format. + * @hide + */ + protected static boolean sBrokenWindowBackground; + /** @hide */ @IntDef({NOT_FOCUSABLE, FOCUSABLE, FOCUSABLE_AUTO}) @Retention(RetentionPolicy.SOURCE) @@ -5208,6 +5221,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, sBrokenInsetsDispatch = ViewRootImpl.sNewInsetsMode != NEW_INSETS_MODE_FULL || targetSdkVersion < Build.VERSION_CODES.Q; + sBrokenWindowBackground = targetSdkVersion < Build.VERSION_CODES.Q; + sCompatibilityDone = true; } } @@ -8486,7 +8501,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * </ul> * * <p>It's also recommended to set the following properties - the more properties the structure - * has, the higher the changes of an {@link android.service.autofill.AutofillService} properly + * has, the higher the chances of an {@link android.service.autofill.AutofillService} properly * using the structure: * * <ul> diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java index 6efb6f38d118..5f3ce33db19d 100644 --- a/core/java/android/view/ViewStructure.java +++ b/core/java/android/view/ViewStructure.java @@ -228,6 +228,16 @@ public abstract class ViewStructure { public abstract void setHint(CharSequence hint); /** + * Sets the identifier used to set the hint associated with this view. + * + * <p>Should only be set when the node is used for autofill purposes - it will be ignored + * when used for Assist. + */ + public void setHintIdEntry(@NonNull String entryName) { + Preconditions.checkNotNull(entryName); + } + + /** * Retrieve the last {@link #setText(CharSequence)}. */ public abstract CharSequence getText(); diff --git a/core/java/android/view/contentcapture/ViewNode.java b/core/java/android/view/contentcapture/ViewNode.java index 8d6245458d62..0dd2587feed5 100644 --- a/core/java/android/view/contentcapture/ViewNode.java +++ b/core/java/android/view/contentcapture/ViewNode.java @@ -81,6 +81,7 @@ public final class ViewNode extends AssistStructure.ViewNode { private static final long FLAGS_HAS_AUTOFILL_VALUE = 1L << 32; private static final long FLAGS_HAS_AUTOFILL_HINTS = 1L << 33; private static final long FLAGS_HAS_AUTOFILL_OPTIONS = 1L << 34; + private static final long FLAGS_HAS_HINT_ID_ENTRY = 1L << 35; /** Flags used to optimize what's written to the parcel */ private long mFlags; @@ -108,6 +109,7 @@ public final class ViewNode extends AssistStructure.ViewNode { private int mMaxEms = -1; private int mMaxLength = -1; private String mTextIdEntry; + private String mHintIdEntry; private @View.AutofillType int mAutofillType = View.AUTOFILL_TYPE_NONE; private String[] mAutofillHints; private AutofillValue mAutofillValue; @@ -195,6 +197,9 @@ public final class ViewNode extends AssistStructure.ViewNode { if ((nodeFlags & FLAGS_HAS_AUTOFILL_OPTIONS) != 0) { mAutofillOptions = parcel.readCharSequenceArray(); } + if ((nodeFlags & FLAGS_HAS_HINT_ID_ENTRY) != 0) { + mHintIdEntry = parcel.readString(); + } } /** @@ -352,6 +357,11 @@ public final class ViewNode extends AssistStructure.ViewNode { } @Override + public String getHintIdEntry() { + return mHintIdEntry; + } + + @Override public int getTextSelectionStart() { return mText != null ? mText.mTextSelectionStart : -1; } @@ -512,6 +522,9 @@ public final class ViewNode extends AssistStructure.ViewNode { if (mAutofillOptions != null) { nodeFlags |= FLAGS_HAS_AUTOFILL_OPTIONS; } + if (mHintIdEntry != null) { + nodeFlags |= FLAGS_HAS_HINT_ID_ENTRY; + } parcel.writeLong(nodeFlags); if ((nodeFlags & FLAGS_HAS_AUTOFILL_ID) != 0) { @@ -585,6 +598,9 @@ public final class ViewNode extends AssistStructure.ViewNode { if ((nodeFlags & FLAGS_HAS_AUTOFILL_OPTIONS) != 0) { parcel.writeCharSequenceArray(mAutofillOptions); } + if ((nodeFlags & FLAGS_HAS_HINT_ID_ENTRY) != 0) { + parcel.writeString(mHintIdEntry); + } } /** @hide */ @@ -783,7 +799,7 @@ public final class ViewNode extends AssistStructure.ViewNode { } @Override - public void setTextIdEntry(String entryName) { + public void setTextIdEntry(@NonNull String entryName) { mNode.mTextIdEntry = Preconditions.checkNotNull(entryName); } @@ -793,6 +809,11 @@ public final class ViewNode extends AssistStructure.ViewNode { } @Override + public void setHintIdEntry(String entryName) { + mNode.mHintIdEntry = Preconditions.checkNotNull(entryName); + } + + @Override public CharSequence getText() { return mNode.getText(); } diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java index efd5daf0b455..a428feac8806 100644 --- a/core/java/android/widget/NumberPicker.java +++ b/core/java/android/widget/NumberPicker.java @@ -921,10 +921,12 @@ public class NumberPicker extends LinearLayout { if (!mFlingScroller.isFinished()) { mFlingScroller.forceFinished(true); mAdjustScroller.forceFinished(true); + onScrollerFinished(mFlingScroller); onScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE); } else if (!mAdjustScroller.isFinished()) { mFlingScroller.forceFinished(true); mAdjustScroller.forceFinished(true); + onScrollerFinished(mAdjustScroller); } else if (mLastDownEventY < mTopSelectionDividerTop) { postChangeCurrentByOneFromLongPress( false, ViewConfiguration.getLongPressTimeout()); @@ -2556,14 +2558,16 @@ public class NumberPicker extends LinearLayout { } return false; } - case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: { + case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: + case R.id.accessibilityActionScrollDown: { if (NumberPicker.this.isEnabled() && (getWrapSelectorWheel() || getValue() < getMaxValue())) { changeValueByOne(true); return true; } } return false; - case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: { + case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: + case R.id.accessibilityActionScrollUp: { if (NumberPicker.this.isEnabled() && (getWrapSelectorWheel() || getValue() > getMinValue())) { changeValueByOne(false); @@ -2865,10 +2869,13 @@ public class NumberPicker extends LinearLayout { } if (NumberPicker.this.isEnabled()) { if (getWrapSelectorWheel() || getValue() < getMaxValue()) { - info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD); + info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD); + info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_DOWN); } if (getWrapSelectorWheel() || getValue() > getMinValue()) { - info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD); + info.addAction( + AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD); + info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP); } } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index a4844eaa612c..f997d6878a9c 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -918,6 +918,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private boolean mTextSetFromXmlOrResourceId = false; // Resource id used to set the text. private @StringRes int mTextId = Resources.ID_NULL; + // Resource id used to set the hint. + private @StringRes int mHintId = Resources.ID_NULL; // // End of autofill-related attributes @@ -1210,6 +1212,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener break; case com.android.internal.R.styleable.TextView_hint: + mHintId = a.getResourceId(attr, Resources.ID_NULL); hint = a.getText(attr); break; @@ -6446,6 +6449,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener */ @android.view.RemotableViewMethod public final void setHint(@StringRes int resid) { + mHintId = resid; setHint(getContext().getResources().getText(resid)); } @@ -11598,6 +11602,16 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener structure.setMaxTextLength(maxLength); } } + if (mHintId != Resources.ID_NULL) { + try { + structure.setHintIdEntry(getResources().getResourceEntryName(mHintId)); + } catch (Resources.NotFoundException e) { + if (android.view.autofill.Helper.sVerbose) { + Log.v(LOG_TAG, "onProvideAutofillStructure(): cannot set name for hint id " + + mHintId + ": " + e.getMessage()); + } + } + } structure.setHint(getHint()); structure.setInputType(getInputType()); } diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java index ee4666ff368c..8fe23167755a 100644 --- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java +++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java @@ -194,6 +194,38 @@ public final class SystemUiDeviceConfigFlags { public static final String ASSIST_HANDLES_SHOW_WHEN_TAUGHT = "assist_handles_show_when_taught"; /** + * (long) Duration per pixel, in milliseconds, of scrolling text at fast speed. + */ + public static final String ASSIST_TRANSCRIPTION_DURATION_PER_PX_FAST = + "assist_transcription_duration_per_px_fast"; + + /** + * (long) Duration per pixel, in milliseconds, of scrolling text at regular speed. + */ + public static final String ASSIST_TRANSCRIPTION_DURATION_PER_PX_REGULAR = + "assist_transcription_duration_per_px_regular"; + + /** + * (long) Duration, in milliseconds, over which text fades in. + */ + public static final String ASSIST_TRANSCRIPTION_FADE_IN_DURATION = + "assist_transcription_fade_in_duration"; + + /** + * (long) Maximum total duration, in milliseconds, for a given transcription. + */ + public static final String ASSIST_TRANSCRIPTION_MAX_DURATION = + "assist_transcription_max_duration"; + + /** + * (long) Minimum total duration, in milliseconds, for a given transcription. + */ + public static final String ASSIST_TRANSCRIPTION_MIN_DURATION = + "assist_transcription_min_duration"; + + // Flags related to brightline falsing + + /** * (bool) Whether to use the new BrightLineFalsingManager. */ public static final String BRIGHTLINE_FALSING_MANAGER_ENABLED = @@ -282,5 +314,6 @@ public final class SystemUiDeviceConfigFlags { "brightline_falsing_zigzag_y_secondary_deviance"; - private SystemUiDeviceConfigFlags() { } + private SystemUiDeviceConfigFlags() { + } } diff --git a/core/java/com/android/internal/os/KernelWakelockReader.java b/core/java/com/android/internal/os/KernelWakelockReader.java index 3ac58e018a45..e09e0e609380 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,125 @@ 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; + if (useSystemSuspend) { + // Get both kernel and native wakelock stats from SystemSuspend + updateVersion(staleStats); + if (getWakelockStatsFromSystemSuspend(staleStats) == null) { + Slog.w(TAG, "Failed to get wakelock stats from SystemSuspend"); + return null; + } + return removeOldStats(staleStats); + } else { + byte[] buffer = new byte[32*1024]; + int len = 0; + boolean wakeup_sources; + final long startTime = SystemClock.uptimeMillis(); + + final int oldMask = StrictMode.allowThreadDiskReadsMask(); try { - is = new FileInputStream(sWakelockFile); - wakeup_sources = false; - } catch (java.io.FileNotFoundException e) { + FileInputStream is; try { - is = new FileInputStream(sWakeupSourceFile); - wakeup_sources = true; - } catch (java.io.FileNotFoundException e2) { - Slog.wtf(TAG, "neither " + sWakelockFile + " nor " + - sWakeupSourceFile + " exists"); - return null; + 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; + } } - } - int cnt; - while ((cnt = is.read(buffer, len, buffer.length - len)) > 0) { - len += cnt; - } - - is.close(); - } catch (java.io.IOException e) { - Slog.wtf(TAG, "failed to read kernel wakelocks", e); - return null; - } finally { - StrictMode.setThreadPolicyMask(oldMask); - } + int cnt; + while ((cnt = is.read(buffer, len, buffer.length - len)) > 0) { + len += cnt; + } - final long readTime = SystemClock.uptimeMillis() - startTime; - if (readTime > 100) { - Slog.w(TAG, "Reading wakelock stats took " + readTime + "ms"); - } + is.close(); + } catch (java.io.IOException e) { + Slog.wtf(TAG, "failed to read kernel wakelocks", e); + return null; + } finally { + StrictMode.setThreadPolicyMask(oldMask); + } - if (len > 0) { - if (len >= buffer.length) { - Slog.wtf(TAG, "Kernel wake locks exceeded buffer size " + buffer.length); + final long readTime = SystemClock.uptimeMillis() - startTime; + if (readTime > 100) { + Slog.w(TAG, "Reading wakelock stats took " + readTime + "ms"); } - int i; - for (i=0; i<len; i++) { - if (buffer[i] == '\0') { - len = i; - break; + + 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; + } } } - } - - updateVersion(staleStats); - parseProcWakelocks(buffer, len, wakeup_sources, staleStats); + updateVersion(staleStats); + // Get native wakelock stats from SystemSuspend + if (getWakelockStatsFromSystemSuspend(staleStats) == null) { + Slog.w(TAG, "Failed to get Native wakelock stats from SystemSuspend"); + } + // Get kernel wakelock stats + parseProcWakelocks(buffer, len, wakeup_sources, staleStats); + return removeOldStats(staleStats); + } + } + /** + * On success, returns the updated stats from SystemSupend, else returns null. + */ + private KernelWakelockStats getWakelockStatsFromSystemSuspend( + final KernelWakelockStats staleStats) { + WakeLockInfo[] wlStats = null; 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); + return null; } } try { - WakeLockInfo[] wlStats = mSuspendControlService.getWakeLockStats(); - getNativeWakelockStats(wlStats, staleStats); + wlStats = mSuspendControlService.getWakeLockStats(); + updateWakelockStats(wlStats, staleStats); } catch (RemoteException e) { Slog.wtf(TAG, "Failed to obtain wakelock stats from ISuspendControlService", e); + return null; } - return removeOldStats(staleStats); + return 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 599c354f0a1a..981d0bb1cd69 100644 --- a/core/java/com/android/internal/policy/DecorView.java +++ b/core/java/com/android/internal/policy/DecorView.java @@ -989,7 +989,9 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind } else { mBackgroundPadding.setEmpty(); } - drawableChanged(); + if (!View.sBrokenWindowBackground) { + drawableChanged(); + } } } diff --git a/core/java/com/android/internal/policy/DividerSnapAlgorithm.java b/core/java/com/android/internal/policy/DividerSnapAlgorithm.java index c46f86792764..2779be6f9753 100644 --- a/core/java/com/android/internal/policy/DividerSnapAlgorithm.java +++ b/core/java/com/android/internal/policy/DividerSnapAlgorithm.java @@ -297,10 +297,10 @@ public class DividerSnapAlgorithm { private void addNonDismissingTargets(boolean isHorizontalDivision, int topPosition, int bottomPosition, int dividerMax) { - maybeAddTarget(topPosition, topPosition - mInsets.top); + maybeAddTarget(topPosition, topPosition - getStartInset()); addMiddleTarget(isHorizontalDivision); - maybeAddTarget(bottomPosition, dividerMax - mInsets.bottom - - (bottomPosition + mDividerSize)); + maybeAddTarget(bottomPosition, + dividerMax - getEndInset() - (bottomPosition + mDividerSize)); } private void addFixedDivisionTargets(boolean isHorizontalDivision, int dividerMax) { diff --git a/core/java/com/android/internal/widget/MediaNotificationView.java b/core/java/com/android/internal/widget/MediaNotificationView.java index e7d240a1035e..0d87afa42e3e 100644 --- a/core/java/com/android/internal/widget/MediaNotificationView.java +++ b/core/java/com/android/internal/widget/MediaNotificationView.java @@ -26,6 +26,8 @@ import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.RemoteViews; +import java.util.ArrayList; + /** * A TextView that can float around an image on the end. * @@ -42,6 +44,7 @@ public class MediaNotificationView extends FrameLayout { private View mMainColumn; private View mMediaContent; private int mImagePushIn; + private ArrayList<VisibilityChangeListener> mListeners; public MediaNotificationView(Context context) { this(context, null); @@ -168,4 +171,48 @@ public class MediaNotificationView extends FrameLayout { mMainColumn = findViewById(com.android.internal.R.id.notification_main_column); mMediaContent = findViewById(com.android.internal.R.id.notification_media_content); } + + @Override + public void onVisibilityAggregated(boolean isVisible) { + super.onVisibilityAggregated(isVisible); + for (int i = 0; i < mListeners.size(); i++) { + mListeners.get(i).onAggregatedVisibilityChanged(isVisible); + } + } + + /** + * Add a listener to receive updates on the visibility of this view + * + * @param listener The listener to add. + */ + public void addVisibilityListener(VisibilityChangeListener listener) { + if (mListeners == null) { + mListeners = new ArrayList<>(); + } + if (!mListeners.contains(listener)) { + mListeners.add(listener); + } + } + + /** + * Remove the specified listener + * + * @param listener The listener to remove. + */ + public void removeVisibilityListener(VisibilityChangeListener listener) { + if (mListeners != null) { + mListeners.remove(listener); + } + } + + /** + * Interface for receiving updates when the view's visibility changes + */ + public interface VisibilityChangeListener { + /** + * Method called when the visibility of this view has changed + * @param isVisible true if the view is now visible + */ + void onAggregatedVisibilityChanged(boolean isVisible); + } } diff --git a/services/core/java/com/android/server/job/JobSchedulerInternal.java b/core/java/com/android/server/job/JobSchedulerInternal.java index 425ec473bc8e..425ec473bc8e 100644 --- a/services/core/java/com/android/server/job/JobSchedulerInternal.java +++ b/core/java/com/android/server/job/JobSchedulerInternal.java diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp index 8ddbe724217c..f390930e17a5 100644 --- a/core/jni/android_view_MotionEvent.cpp +++ b/core/jni/android_view_MotionEvent.cpp @@ -701,6 +701,21 @@ static jfloat android_view_MotionEvent_nativeGetYPrecision(jlong nativePtr) { return event->getYPrecision(); } +static jfloat android_view_MotionEvent_nativeGetXCursorPosition(jlong nativePtr) { + MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr); + return event->getXCursorPosition(); +} + +static jfloat android_view_MotionEvent_nativeGetYCursorPosition(jlong nativePtr) { + MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr); + return event->getYCursorPosition(); +} + +static void android_view_MotionEvent_nativeSetCursorPosition(jlong nativePtr, jfloat x, jfloat y) { + MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr); + event->setCursorPosition(x, y); +} + static jlong android_view_MotionEvent_nativeGetDownTimeNanos(jlong nativePtr) { MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr); return event->getDownTime(); @@ -871,6 +886,15 @@ static const JNINativeMethod gMotionEventMethods[] = { { "nativeGetYPrecision", "(J)F", (void*)android_view_MotionEvent_nativeGetYPrecision }, + { "nativeGetXCursorPosition", + "(J)F", + (void*)android_view_MotionEvent_nativeGetXCursorPosition }, + { "nativeGetYCursorPosition", + "(J)F", + (void*)android_view_MotionEvent_nativeGetYCursorPosition }, + { "nativeSetCursorPosition", + "(JFF)V", + (void*)android_view_MotionEvent_nativeSetCursorPosition }, { "nativeGetDownTimeNanos", "(J)J", (void*)android_view_MotionEvent_nativeGetDownTimeNanos }, 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 df45ec5e3ae0..6a20484f6faa 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -4599,6 +4599,11 @@ <permission android:name="android.permission.MONITOR_INPUT" android:protectionLevel="signature" /> + <!-- Allows query of any normal app on the device, regardless of manifest declarations. --> + <permission android:name="android.permission.QUERY_ALL_PACKAGES" + android:protectionLevel="normal" /> + <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/> + <application android:process="system" android:persistent="true" android:hasCode="false" @@ -4610,6 +4615,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" diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index be6cdcf3f098..3ea8a778ba9b 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -1743,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 @@ -1977,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 @@ -2477,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..b34c422053df 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1700,6 +1700,17 @@ <!-- Add packages here --> </string-array> + <!-- The set of system packages on device that are queryable regardless of the contents of their + manifest. --> + <string-array name="config_forceQueryablePackages" translatable="false"> + <item>com.android.settings</item> + <!-- Add packages here --> + </string-array> + + <!-- If true, will force all packages on any system partition as queryable regardless of the + contents of their manifest. --> + <bool name="config_forceSystemPackagesQueryable">false</bool> + <!-- 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/strings.xml b/core/res/res/values/strings.xml index 913001c0eb94..40f20ab41b85 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -187,6 +187,9 @@ <string name="notification_channel_wfc">Wi-Fi calling</string> <!-- Telephony notification channel name for a channel containing SIM notifications --> <string name="notification_channel_sim">SIM status</string> + <!-- Telephony notification channel name for a channel containing high priority SIM notifications --> + <string name="notification_channel_sim_high_prio">High priority SIM status</string> + <!-- Displayed to tell the user that peer changed TTY mode --> <string name="peerTtyModeFull">Peer requested TTY Mode FULL</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index d91bbd006318..ac32c95bcd5e 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -653,6 +653,7 @@ <java-symbol type="string" name="notification_channel_voice_mail" /> <java-symbol type="string" name="notification_channel_wfc" /> <java-symbol type="string" name="notification_channel_sim" /> + <java-symbol type="string" name="notification_channel_sim_high_prio" /> <java-symbol type="string" name="SetupCallDefault" /> <java-symbol type="string" name="accept" /> <java-symbol type="string" name="activity_chooser_view_see_all" /> @@ -786,6 +787,8 @@ <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="bool" name="config_forceSystemPackagesQueryable" /> <java-symbol type="string" name="eventTypeAnniversary" /> <java-symbol type="string" name="eventTypeBirthday" /> <java-symbol type="string" name="eventTypeCustom" /> 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/src/android/view/MotionEventTest.java b/core/tests/coretests/src/android/view/MotionEventTest.java index cadf37e0c53c..9d09830c585b 100644 --- a/core/tests/coretests/src/android/view/MotionEventTest.java +++ b/core/tests/coretests/src/android/view/MotionEventTest.java @@ -17,8 +17,11 @@ package android.view; import static android.view.MotionEvent.ACTION_DOWN; +import static android.view.MotionEvent.ACTION_POINTER_DOWN; import static android.view.MotionEvent.TOOL_TYPE_FINGER; +import static junit.framework.Assert.assertTrue; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; @@ -77,4 +80,62 @@ public class MotionEventTest { 0, 0, 0, 0, 0, 0, InputDevice.SOURCE_TOUCHSCREEN, displayId, 0); assertNull(motionEvent); } + + @Test + public void testCalculatesCursorPositionForTouchscreenEvents() { + final MotionEvent event = MotionEvent.obtain(0 /* downTime */, 0 /* eventTime */, + ACTION_DOWN, 30 /* x */, 50 /* y */, 0 /* metaState */); + event.setSource(InputDevice.SOURCE_TOUCHSCREEN); + + assertTrue(Float.isNaN(event.getXCursorPosition())); + assertTrue(Float.isNaN(event.getYCursorPosition())); + } + + @Test + public void testCalculatesCursorPositionForSimpleMouseEvents() { + final MotionEvent event = MotionEvent.obtain(0 /* downTime */, 0 /* eventTime */, + ACTION_DOWN, 30 /* x */, 50 /* y */, 0 /* metaState */); + event.setSource(InputDevice.SOURCE_MOUSE); + + assertEquals(30, event.getXCursorPosition(), 0.1); + assertEquals(50, event.getYCursorPosition(), 0.1); + } + + @Test + public void testCalculatesCursorPositionForSimpleMouseEventsWithOffset() { + final MotionEvent event = MotionEvent.obtain(0 /* downTime */, 0 /* eventTime */, + ACTION_DOWN, 30 /* x */, 50 /* y */, 0 /* metaState */); + event.offsetLocation(10 /* deltaX */, 20 /* deltaY */); + event.setSource(InputDevice.SOURCE_MOUSE); + + assertEquals(40, event.getXCursorPosition(), 0.1); + assertEquals(70, event.getYCursorPosition(), 0.1); + } + + + @Test + public void testCalculatesCursorPositionForMultiTouchMouseEvents() { + final int pointerCount = 2; + final PointerProperties[] properties = new PointerProperties[pointerCount]; + final PointerCoords[] coords = new PointerCoords[pointerCount]; + + for (int i = 0; i < pointerCount; ++i) { + properties[i] = new PointerProperties(); + properties[i].id = i; + properties[i].toolType = MotionEvent.TOOL_TYPE_FINGER; + + coords[i] = new PointerCoords(); + coords[i].x = 20 + i * 20; + coords[i].y = 60 - i * 20; + } + + final MotionEvent event = MotionEvent.obtain(0 /* downTime */, + 0 /* eventTime */, ACTION_POINTER_DOWN, pointerCount, properties, coords, + 0 /* metaState */, 0 /* buttonState */, 1 /* xPrecision */, 1 /* yPrecision */, + 0 /* deviceId */, 0 /* edgeFlags */, InputDevice.SOURCE_MOUSE, + 0 /* flags */); + + assertEquals(30, event.getXCursorPosition(), 0.1); + assertEquals(50, event.getYCursorPosition(), 0.1); + } } diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java index adaae5cdba8d..682416c58c72 100644 --- a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java +++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java @@ -132,6 +132,8 @@ 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; } 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..dc9208de7198 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,7 +266,7 @@ public class KernelWakelockReaderTest extends TestCase { assertTrue(staleStats.containsKey("WakeLock2")); entry = staleStats.get("WakeLock2"); assertEquals(20, entry.mCount); - assertEquals(2000, entry.mTotalTime); + assertEquals(2000 * 1000, entry.mTotalTime); // Micro seconds } // -------------------- Aggregate Wakelock Stats Tests -------------------- @@ -313,7 +308,7 @@ public class KernelWakelockReaderTest extends TestCase { byte[] buffer = new byte[0]; WakeLockInfo[] wlStats = new WakeLockInfo[1]; - wlStats[0] = createWakeLockInfo("WakeLock", 10, 1000); + wlStats[0] = createWakeLockInfo("WakeLock", 10, 1000); // Milliseconds readKernelWakelockStats(staleStats, buffer, wlStats); @@ -323,7 +318,7 @@ public class KernelWakelockReaderTest extends TestCase { KernelWakelockStats.Entry entry = staleStats.get("WakeLock"); assertEquals(10, entry.mCount); - assertEquals(1000, entry.mTotalTime); + assertEquals(1000 * 1000, entry.mTotalTime); // Microseconds } @SmallTest @@ -334,7 +329,7 @@ public class KernelWakelockReaderTest extends TestCase { .addLine("WakeLock1", 34, 123) // Milliseconds .getBytes(); WakeLockInfo[] wlStats = new WakeLockInfo[1]; - wlStats[0] = createWakeLockInfo("WakeLock2", 10, 1000); + wlStats[0] = createWakeLockInfo("WakeLock2", 10, 1000); // Milliseconds readKernelWakelockStats(staleStats, buffer, wlStats); @@ -348,7 +343,7 @@ public class KernelWakelockReaderTest extends TestCase { assertTrue(staleStats.containsKey("WakeLock2")); KernelWakelockStats.Entry entry2 = staleStats.get("WakeLock2"); assertEquals(10, entry2.mCount); - assertEquals(1000, entry2.mTotalTime); + assertEquals(1000 * 1000, entry2.mTotalTime); // Microseconds } @SmallTest @@ -360,8 +355,8 @@ public class KernelWakelockReaderTest extends TestCase { .addLine("WakeLock2", 46, 345) // Milliseconds .getBytes(); WakeLockInfo[] wlStats = new WakeLockInfo[2]; - wlStats[0] = createWakeLockInfo("WakeLock3", 10, 1000); - wlStats[1] = createWakeLockInfo("WakeLock4", 20, 2000); + wlStats[0] = createWakeLockInfo("WakeLock3", 10, 1000); // Milliseconds + wlStats[1] = createWakeLockInfo("WakeLock4", 20, 2000); // Milliseconds readKernelWakelockStats(staleStats, buffer, wlStats); @@ -382,18 +377,18 @@ public class KernelWakelockReaderTest extends TestCase { KernelWakelockStats.Entry entry3 = staleStats.get("WakeLock3"); assertEquals(10, entry3.mCount); - assertEquals(1000, entry3.mTotalTime); + assertEquals(1000 * 1000, entry3.mTotalTime); // Microseconds KernelWakelockStats.Entry entry4 = staleStats.get("WakeLock4"); assertEquals(20, entry4.mCount); - assertEquals(2000, entry4.mTotalTime); + assertEquals(2000 * 1000, entry4.mTotalTime); // Microseconds buffer = new ProcFileBuilder() .addLine("WakeLock1", 45, 789) // Milliseconds .addLine("WakeLock1", 56, 123) // Milliseconds .getBytes(); wlStats = new WakeLockInfo[1]; - wlStats[0] = createWakeLockInfo("WakeLock4", 40, 4000); + wlStats[0] = createWakeLockInfo("WakeLock4", 40, 4000); // Milliseconds readKernelWakelockStats(staleStats, buffer, wlStats); @@ -411,6 +406,6 @@ public class KernelWakelockReaderTest extends TestCase { entry2 = staleStats.get("WakeLock4"); assertEquals(40, entry2.mCount); - assertEquals(4000, entry4.mTotalTime); + assertEquals(4000 * 1000, entry4.mTotalTime); // Microseconds } } diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index f01b1bfa780d..54ac0bd9ff75 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -454,7 +454,7 @@ void SkiaCanvas::drawPaint(const SkPaint& paint) { // Canvas draw operations: Geometry // ---------------------------------------------------------------------------- -void SkiaCanvas::drawPoints(const float* points, int count, const SkPaint& paint, +void SkiaCanvas::drawPoints(const float* points, int count, const Paint& paint, SkCanvas::PointMode mode) { if (CC_UNLIKELY(count < 2 || paint.nothingToDraw())) return; // convert the floats into SkPoints @@ -464,109 +464,142 @@ void SkiaCanvas::drawPoints(const float* points, int count, const SkPaint& paint pts[i].set(points[0], points[1]); points += 2; } - mCanvas->drawPoints(mode, count, pts.get(), *filterPaint(paint)); + + apply_looper(&paint, [&](const SkPaint& p) { + mCanvas->drawPoints(mode, count, pts.get(), p); + }); } -void SkiaCanvas::drawPoint(float x, float y, const SkPaint& paint) { - mCanvas->drawPoint(x, y, *filterPaint(paint)); +void SkiaCanvas::drawPoint(float x, float y, const Paint& paint) { + apply_looper(&paint, [&](const SkPaint& p) { + mCanvas->drawPoint(x, y, p); + }); } -void SkiaCanvas::drawPoints(const float* points, int count, const SkPaint& paint) { - this->drawPoints(points, count, *filterPaint(paint), SkCanvas::kPoints_PointMode); +void SkiaCanvas::drawPoints(const float* points, int count, const Paint& paint) { + this->drawPoints(points, count, paint, SkCanvas::kPoints_PointMode); } void SkiaCanvas::drawLine(float startX, float startY, float stopX, float stopY, - const SkPaint& paint) { - mCanvas->drawLine(startX, startY, stopX, stopY, *filterPaint(paint)); + const Paint& paint) { + apply_looper(&paint, [&](const SkPaint& p) { + mCanvas->drawLine(startX, startY, stopX, stopY, p); + }); } -void SkiaCanvas::drawLines(const float* points, int count, const SkPaint& paint) { +void SkiaCanvas::drawLines(const float* points, int count, const Paint& paint) { if (CC_UNLIKELY(count < 4 || paint.nothingToDraw())) return; - this->drawPoints(points, count, *filterPaint(paint), SkCanvas::kLines_PointMode); + this->drawPoints(points, count, paint, SkCanvas::kLines_PointMode); } -void SkiaCanvas::drawRect(float left, float top, float right, float bottom, const SkPaint& paint) { +void SkiaCanvas::drawRect(float left, float top, float right, float bottom, const Paint& paint) { if (CC_UNLIKELY(paint.nothingToDraw())) return; - mCanvas->drawRect({left, top, right, bottom}, *filterPaint(paint)); + apply_looper(&paint, [&](const SkPaint& p) { + mCanvas->drawRect({left, top, right, bottom}, p); + }); } -void SkiaCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) { +void SkiaCanvas::drawRegion(const SkRegion& region, const Paint& paint) { if (CC_UNLIKELY(paint.nothingToDraw())) return; - mCanvas->drawRegion(region, *filterPaint(paint)); + apply_looper(&paint, [&](const SkPaint& p) { + mCanvas->drawRegion(region, p); + }); } void SkiaCanvas::drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, - const SkPaint& paint) { + const Paint& paint) { if (CC_UNLIKELY(paint.nothingToDraw())) return; SkRect rect = SkRect::MakeLTRB(left, top, right, bottom); - mCanvas->drawRoundRect(rect, rx, ry, *filterPaint(paint)); + apply_looper(&paint, [&](const SkPaint& p) { + mCanvas->drawRoundRect(rect, rx, ry, p); + }); } void SkiaCanvas::drawDoubleRoundRect(const SkRRect& outer, const SkRRect& inner, - const SkPaint& paint) { - mCanvas->drawDRRect(outer, inner, *filterPaint(paint)); + const Paint& paint) { + apply_looper(&paint, [&](const SkPaint& p) { + mCanvas->drawDRRect(outer, inner, p); + }); } -void SkiaCanvas::drawCircle(float x, float y, float radius, const SkPaint& paint) { +void SkiaCanvas::drawCircle(float x, float y, float radius, const Paint& paint) { if (CC_UNLIKELY(radius <= 0 || paint.nothingToDraw())) return; - mCanvas->drawCircle(x, y, radius, *filterPaint(paint)); + apply_looper(&paint, [&](const SkPaint& p) { + mCanvas->drawCircle(x, y, radius, p); + }); } -void SkiaCanvas::drawOval(float left, float top, float right, float bottom, const SkPaint& paint) { +void SkiaCanvas::drawOval(float left, float top, float right, float bottom, const Paint& paint) { if (CC_UNLIKELY(paint.nothingToDraw())) return; SkRect oval = SkRect::MakeLTRB(left, top, right, bottom); - mCanvas->drawOval(oval, *filterPaint(paint)); + apply_looper(&paint, [&](const SkPaint& p) { + mCanvas->drawOval(oval, p); + }); } void SkiaCanvas::drawArc(float left, float top, float right, float bottom, float startAngle, - float sweepAngle, bool useCenter, const SkPaint& paint) { + float sweepAngle, bool useCenter, const Paint& paint) { if (CC_UNLIKELY(paint.nothingToDraw())) return; SkRect arc = SkRect::MakeLTRB(left, top, right, bottom); - if (fabs(sweepAngle) >= 360.0f) { - mCanvas->drawOval(arc, *filterPaint(paint)); - } else { - mCanvas->drawArc(arc, startAngle, sweepAngle, useCenter, *filterPaint(paint)); - } + apply_looper(&paint, [&](const SkPaint& p) { + if (fabs(sweepAngle) >= 360.0f) { + mCanvas->drawOval(arc, p); + } else { + mCanvas->drawArc(arc, startAngle, sweepAngle, useCenter, p); + } + }); } -void SkiaCanvas::drawPath(const SkPath& path, const SkPaint& paint) { +void SkiaCanvas::drawPath(const SkPath& path, const Paint& paint) { if (CC_UNLIKELY(paint.nothingToDraw())) return; if (CC_UNLIKELY(path.isEmpty() && (!path.isInverseFillType()))) { return; } - mCanvas->drawPath(path, *filterPaint(paint)); + apply_looper(&paint, [&](const SkPaint& p) { + mCanvas->drawPath(path, p); + }); } -void SkiaCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint) { - mCanvas->drawVertices(vertices, mode, *filterPaint(paint)); +void SkiaCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, const Paint& paint) { + apply_looper(&paint, [&](const SkPaint& p) { + mCanvas->drawVertices(vertices, mode, p); + }); } // ---------------------------------------------------------------------------- // Canvas draw operations: Bitmaps // ---------------------------------------------------------------------------- -void SkiaCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) { - mCanvas->drawImage(bitmap.makeImage(), left, top, filterPaint(paint)); +void SkiaCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) { + auto image = bitmap.makeImage(); + apply_looper(paint, [&](const SkPaint& p) { + mCanvas->drawImage(image, left, top, &p); + }); } -void SkiaCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) { +void SkiaCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const Paint* paint) { + auto image = bitmap.makeImage(); SkAutoCanvasRestore acr(mCanvas, true); mCanvas->concat(matrix); - mCanvas->drawImage(bitmap.makeImage(), 0, 0, filterPaint(paint)); + apply_looper(paint, [&](const SkPaint& p) { + mCanvas->drawImage(image, 0, 0, &p); + }); } void SkiaCanvas::drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, float srcRight, float srcBottom, float dstLeft, float dstTop, float dstRight, - float dstBottom, const SkPaint* paint) { + float dstBottom, const Paint* paint) { + auto image = bitmap.makeImage(); SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom); SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom); - mCanvas->drawImageRect(bitmap.makeImage(), srcRect, dstRect, filterPaint(paint), - SkCanvas::kFast_SrcRectConstraint); + apply_looper(paint, [&](const SkPaint& p) { + mCanvas->drawImageRect(image, srcRect, dstRect, &p, SkCanvas::kFast_SrcRectConstraint); + }); } void SkiaCanvas::drawBitmapMesh(Bitmap& bitmap, int meshWidth, int meshHeight, - const float* vertices, const int* colors, const SkPaint* paint) { + const float* vertices, const int* colors, const Paint* paint) { const int ptCount = (meshWidth + 1) * (meshHeight + 1); const int indexCount = meshWidth * meshHeight * 6; uint32_t flags = SkVertices::kHasTexCoords_BuilderFlag; @@ -640,20 +673,20 @@ void SkiaCanvas::drawBitmapMesh(Bitmap& bitmap, int meshWidth, int meshHeight, #endif // cons-up a shader for the bitmap - PaintCoW paintCoW(paint); - SkPaint& tmpPaint = paintCoW.writeable(); - - sk_sp<SkImage> image = bitmap.makeImage(); - sk_sp<SkShader> shader = image->makeShader(); - tmpPaint.setShader(std::move(shader)); - - mCanvas->drawVertices(builder.detach(), SkBlendMode::kModulate, - *filterPaint(std::move(paintCoW))); + Paint pnt; + if (paint) { + pnt = *paint; + } + pnt.setShader(bitmap.makeImage()->makeShader()); + auto v = builder.detach(); + apply_looper(&pnt, [&](const SkPaint& p) { + mCanvas->drawVertices(v, SkBlendMode::kModulate, p); + }); } void SkiaCanvas::drawNinePatch(Bitmap& bitmap, const Res_png_9patch& chunk, float dstLeft, float dstTop, float dstRight, float dstBottom, - const SkPaint* paint) { + const Paint* paint) { SkCanvas::Lattice lattice; NinePatchUtils::SetLatticeDivs(&lattice, chunk, bitmap.width(), bitmap.height()); @@ -674,8 +707,10 @@ void SkiaCanvas::drawNinePatch(Bitmap& bitmap, const Res_png_9patch& chunk, floa lattice.fBounds = nullptr; SkRect dst = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom); - - mCanvas->drawImageLattice(bitmap.makeImage().get(), lattice, dst, filterPaint(paint)); + auto image = bitmap.makeImage(); + apply_looper(paint, [&](const SkPaint& p) { + mCanvas->drawImageLattice(image.get(), lattice, dst, &p); + }); } double SkiaCanvas::drawAnimatedImage(AnimatedImageDrawable* imgDrawable) { diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h index 799a89158298..ec83a1961eff 100644 --- a/libs/hwui/SkiaCanvas.h +++ b/libs/hwui/SkiaCanvas.h @@ -22,8 +22,10 @@ #include "RenderNode.h" #include "VectorDrawable.h" #include "hwui/Canvas.h" +#include "hwui/Paint.h" #include <SkCanvas.h> +#include "src/core/SkArenaAlloc.h" #include <cassert> #include <optional> @@ -99,39 +101,39 @@ public: virtual void drawColor(int color, SkBlendMode mode) override; virtual void drawPaint(const SkPaint& paint) override; - virtual void drawPoint(float x, float y, const SkPaint& paint) override; - virtual void drawPoints(const float* points, int count, const SkPaint& paint) override; + virtual void drawPoint(float x, float y, const Paint& paint) override; + virtual void drawPoints(const float* points, int count, const Paint& paint) override; virtual void drawLine(float startX, float startY, float stopX, float stopY, - const SkPaint& paint) override; - virtual void drawLines(const float* points, int count, const SkPaint& paint) override; + const Paint& paint) override; + virtual void drawLines(const float* points, int count, const Paint& paint) override; virtual void drawRect(float left, float top, float right, float bottom, - const SkPaint& paint) override; - virtual void drawRegion(const SkRegion& region, const SkPaint& paint) override; + const Paint& paint) override; + virtual void drawRegion(const SkRegion& region, const Paint& paint) override; virtual void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, - const SkPaint& paint) override; + const Paint& paint) override; virtual void drawDoubleRoundRect(const SkRRect& outer, const SkRRect& inner, - const SkPaint& paint) override; + const Paint& paint) override; - virtual void drawCircle(float x, float y, float radius, const SkPaint& paint) override; + virtual void drawCircle(float x, float y, float radius, const Paint& paint) override; virtual void drawOval(float left, float top, float right, float bottom, - const SkPaint& paint) override; + const Paint& paint) override; virtual void drawArc(float left, float top, float right, float bottom, float startAngle, - float sweepAngle, bool useCenter, const SkPaint& paint) override; - virtual void drawPath(const SkPath& path, const SkPaint& paint) override; - virtual void drawVertices(const SkVertices*, SkBlendMode, const SkPaint& paint) override; + float sweepAngle, bool useCenter, const Paint& paint) override; + virtual void drawPath(const SkPath& path, const Paint& paint) override; + virtual void drawVertices(const SkVertices*, SkBlendMode, const Paint& paint) override; - virtual void drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) override; - virtual void drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) override; + virtual void drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) override; + virtual void drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const Paint* paint) override; virtual void drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, float srcRight, float srcBottom, float dstLeft, float dstTop, float dstRight, - float dstBottom, const SkPaint* paint) override; + float dstBottom, const Paint* paint) override; virtual void drawBitmapMesh(Bitmap& bitmap, int meshWidth, int meshHeight, const float* vertices, const int* colors, - const SkPaint* paint) override; + const Paint* paint) override; virtual void drawNinePatch(Bitmap& bitmap, const android::Res_png_9patch& chunk, float dstLeft, float dstTop, float dstRight, float dstBottom, - const SkPaint* paint) override; + const Paint* paint) override; virtual double drawAnimatedImage(AnimatedImageDrawable* imgDrawable) override; virtual bool drawTextAbsolutePos() const override { return true; } @@ -208,6 +210,35 @@ protected: */ PaintCoW&& filterPaint(PaintCoW&& paint) const; + template <typename Proc> void apply_looper(const Paint* paint, Proc proc) { + SkPaint skp; + SkDrawLooper* looper = nullptr; + if (paint) { + skp = *filterPaint(paint); + looper = paint->getLooper(); + } + if (looper) { + SkSTArenaAlloc<256> alloc; + SkDrawLooper::Context* ctx = looper->makeContext(&alloc); + if (ctx) { + SkDrawLooper::Context::Info info; + for (;;) { + SkPaint p = skp; + if (!ctx->next(&info, &p)) { + break; + } + mCanvas->save(); + mCanvas->translate(info.fTranslate.fX, info.fTranslate.fY); + proc(p); + mCanvas->restore(); + } + } + } else { + proc(skp); + } + } + + private: struct SaveRec { int saveCount; @@ -222,7 +253,7 @@ private: void recordClip(const T&, SkClipOp); void applyPersistentClips(size_t clipStartIndex); - void drawPoints(const float* points, int count, const SkPaint& paint, SkCanvas::PointMode mode); + void drawPoints(const float* points, int count, const Paint& paint, SkCanvas::PointMode mode); class Clip; diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp index 89ad1b99c6b6..f91d178f01f3 100644 --- a/libs/hwui/VectorDrawable.cpp +++ b/libs/hwui/VectorDrawable.cpp @@ -17,6 +17,7 @@ #include "VectorDrawable.h" #include <utils/Log.h> +#include "hwui/Paint.h" #include "PathParser.h" #include "SkColorFilter.h" #include "SkImageInfo.h" @@ -458,8 +459,12 @@ void Tree::drawStaging(Canvas* outCanvas) { mStagingCache.dirty = false; } - SkPaint paint; - getPaintFor(&paint, mStagingProperties); + SkPaint skp; + getPaintFor(&skp, mStagingProperties); + Paint paint; + paint.setFilterQuality(skp.getFilterQuality()); + paint.setColorFilter(skp.refColorFilter()); + paint.setAlpha(skp.getAlpha()); outCanvas->drawBitmap(*mStagingCache.bitmap, 0, 0, mStagingCache.bitmap->width(), mStagingCache.bitmap->height(), mStagingProperties.getBounds().left(), mStagingProperties.getBounds().top(), diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp index a48c86028262..b98ffca84dfc 100644 --- a/libs/hwui/hwui/Canvas.cpp +++ b/libs/hwui/hwui/Canvas.cpp @@ -34,7 +34,7 @@ Canvas* Canvas::create_recording_canvas(int width, int height, uirenderer::Rende } static inline void drawStroke(SkScalar left, SkScalar right, SkScalar top, SkScalar thickness, - const SkPaint& paint, Canvas* canvas) { + const Paint& paint, Canvas* canvas) { const SkScalar strokeWidth = fmax(thickness, 1.0f); const SkScalar bottom = top + strokeWidth; canvas->drawRect(left, top, right, bottom, paint); @@ -182,7 +182,7 @@ void Canvas::drawText(const uint16_t* text, int textSize, int start, int count, void Canvas::drawDoubleRoundRectXY(float outerLeft, float outerTop, float outerRight, float outerBottom, float outerRx, float outerRy, float innerLeft, float innerTop, float innerRight, float innerBottom, float innerRx, - float innerRy, const SkPaint& paint) { + float innerRy, const Paint& paint) { if (CC_UNLIKELY(paint.nothingToDraw())) return; SkRect outer = SkRect::MakeLTRB(outerLeft, outerTop, outerRight, outerBottom); SkRect inner = SkRect::MakeLTRB(innerLeft, innerTop, innerRight, innerBottom); @@ -198,7 +198,7 @@ void Canvas::drawDoubleRoundRectXY(float outerLeft, float outerTop, float outerR void Canvas::drawDoubleRoundRectRadii(float outerLeft, float outerTop, float outerRight, float outerBottom, const float* outerRadii, float innerLeft, float innerTop, float innerRight, float innerBottom, - const float* innerRadii, const SkPaint& paint) { + const float* innerRadii, const Paint& paint) { static_assert(sizeof(SkVector) == sizeof(float) * 2); if (CC_UNLIKELY(paint.nothingToDraw())) return; SkRect outer = SkRect::MakeLTRB(outerLeft, outerTop, outerRight, outerBottom); diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h index ee4fa1d689ef..b90a4a3d68e6 100644 --- a/libs/hwui/hwui/Canvas.h +++ b/libs/hwui/hwui/Canvas.h @@ -217,37 +217,37 @@ public: virtual void drawPaint(const SkPaint& paint) = 0; // Geometry - virtual void drawPoint(float x, float y, const SkPaint& paint) = 0; - virtual void drawPoints(const float* points, int floatCount, const SkPaint& paint) = 0; + virtual void drawPoint(float x, float y, const Paint& paint) = 0; + virtual void drawPoints(const float* points, int floatCount, const Paint& paint) = 0; virtual void drawLine(float startX, float startY, float stopX, float stopY, - const SkPaint& paint) = 0; - virtual void drawLines(const float* points, int floatCount, const SkPaint& paint) = 0; + const Paint& paint) = 0; + virtual void drawLines(const float* points, int floatCount, const Paint& paint) = 0; virtual void drawRect(float left, float top, float right, float bottom, - const SkPaint& paint) = 0; - virtual void drawRegion(const SkRegion& region, const SkPaint& paint) = 0; + const Paint& paint) = 0; + virtual void drawRegion(const SkRegion& region, const Paint& paint) = 0; virtual void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, - const SkPaint& paint) = 0; + const Paint& paint) = 0; virtual void drawDoubleRoundRect(const SkRRect& outer, const SkRRect& inner, - const SkPaint& paint) = 0; - virtual void drawCircle(float x, float y, float radius, const SkPaint& paint) = 0; + const Paint& paint) = 0; + virtual void drawCircle(float x, float y, float radius, const Paint& paint) = 0; virtual void drawOval(float left, float top, float right, float bottom, - const SkPaint& paint) = 0; + const Paint& paint) = 0; virtual void drawArc(float left, float top, float right, float bottom, float startAngle, - float sweepAngle, bool useCenter, const SkPaint& paint) = 0; - virtual void drawPath(const SkPath& path, const SkPaint& paint) = 0; - virtual void drawVertices(const SkVertices*, SkBlendMode, const SkPaint& paint) = 0; + float sweepAngle, bool useCenter, const Paint& paint) = 0; + virtual void drawPath(const SkPath& path, const Paint& paint) = 0; + virtual void drawVertices(const SkVertices*, SkBlendMode, const Paint& paint) = 0; // Bitmap-based - virtual void drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) = 0; - virtual void drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) = 0; + virtual void drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) = 0; + virtual void drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const Paint* paint) = 0; virtual void drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, float srcRight, float srcBottom, float dstLeft, float dstTop, float dstRight, - float dstBottom, const SkPaint* paint) = 0; + float dstBottom, const Paint* paint) = 0; virtual void drawBitmapMesh(Bitmap& bitmap, int meshWidth, int meshHeight, - const float* vertices, const int* colors, const SkPaint* paint) = 0; + const float* vertices, const int* colors, const Paint* paint) = 0; virtual void drawNinePatch(Bitmap& bitmap, const android::Res_png_9patch& chunk, float dstLeft, float dstTop, float dstRight, float dstBottom, - const SkPaint* paint) = 0; + const Paint* paint) = 0; virtual double drawAnimatedImage(AnimatedImageDrawable* imgDrawable) = 0; virtual void drawPicture(const SkPicture& picture) = 0; @@ -281,12 +281,12 @@ public: void drawDoubleRoundRectXY(float outerLeft, float outerTop, float outerRight, float outerBottom, float outerRx, float outerRy, float innerLeft, float innerTop, float innerRight, float innerBottom, float innerRx, - float innerRy, const SkPaint& paint); + float innerRy, const Paint& paint); void drawDoubleRoundRectRadii(float outerLeft, float outerTop, float outerRight, float outerBottom, const float* outerRadii, float innerLeft, float innerTop, float innerRight, float innerBottom, - const float* innerRadii, const SkPaint& paint); + const float* innerRadii, const Paint& paint); static int GetApiLevel() { return sApiLevel; } diff --git a/libs/hwui/hwui/Paint.h b/libs/hwui/hwui/Paint.h index 9b2fa9df1e8f..281ecd27d780 100644 --- a/libs/hwui/hwui/Paint.h +++ b/libs/hwui/hwui/Paint.h @@ -21,6 +21,7 @@ #include <cutils/compiler.h> +#include <SkDrawLooper.h> #include <SkFont.h> #include <SkPaint.h> #include <string> @@ -58,12 +59,17 @@ public: SkFont& getSkFont() { return mFont; } const SkFont& getSkFont() const { return mFont; } + SkDrawLooper* getLooper() const { return mLooper.get(); } + void setLooper(sk_sp<SkDrawLooper> looper) { mLooper = std::move(looper); } + // These shadow the methods on SkPaint, but we need to so we can keep related // attributes in-sync. void reset(); void setAntiAlias(bool); + bool nothingToDraw() const { return !mLooper && SkPaint::nothingToDraw(); } + // End method shadowing void setLetterSpacing(float letterSpacing) { mLetterSpacing = letterSpacing; } @@ -146,6 +152,7 @@ public: private: SkFont mFont; + sk_sp<SkDrawLooper> mLooper; float mLetterSpacing = 0; float mWordSpacing = 0; diff --git a/libs/hwui/hwui/PaintImpl.cpp b/libs/hwui/hwui/PaintImpl.cpp index 2f2d575bca29..fa2674fc2f5e 100644 --- a/libs/hwui/hwui/PaintImpl.cpp +++ b/libs/hwui/hwui/PaintImpl.cpp @@ -34,6 +34,7 @@ Paint::Paint() Paint::Paint(const Paint& paint) : SkPaint(paint) , mFont(paint.mFont) + , mLooper(paint.mLooper) , mLetterSpacing(paint.mLetterSpacing) , mWordSpacing(paint.mWordSpacing) , mFontFeatureSettings(paint.mFontFeatureSettings) @@ -52,6 +53,7 @@ Paint::~Paint() {} Paint& Paint::operator=(const Paint& other) { SkPaint::operator=(other); mFont = other.mFont; + mLooper = other.mLooper; mLetterSpacing = other.mLetterSpacing; mWordSpacing = other.mWordSpacing; mFontFeatureSettings = other.mFontFeatureSettings; @@ -69,6 +71,7 @@ Paint& Paint::operator=(const Paint& other) { bool operator==(const Paint& a, const Paint& b) { return static_cast<const SkPaint&>(a) == static_cast<const SkPaint&>(b) && a.mFont == b.mFont && + a.mLooper == b.mLooper && a.mLetterSpacing == b.mLetterSpacing && a.mWordSpacing == b.mWordSpacing && a.mFontFeatureSettings == b.mFontFeatureSettings && a.mMinikinLocaleListId == b.mMinikinLocaleListId && @@ -83,6 +86,7 @@ void Paint::reset() { mFont = SkFont(); mFont.setEdging(SkFont::Edging::kAlias); + mLooper.reset(); mStrikeThru = false; mUnderline = false; diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp index 96b17e1d7975..b0173846e582 100644 --- a/libs/hwui/pipeline/skia/LayerDrawable.cpp +++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp @@ -33,6 +33,10 @@ void LayerDrawable::onDraw(SkCanvas* canvas) { } } +static inline SkScalar isIntegerAligned(SkScalar x) { + return fabsf(roundf(x) - x) <= NON_ZERO_EPSILON; +} + // Disable filtering when there is no scaling in screen coordinates and the corners have the same // fraction (for translate) or zero fraction (for any other rect-to-rect transform). static bool shouldFilterRect(const SkMatrix& matrix, const SkRect& srcRect, const SkRect& dstRect) { @@ -62,10 +66,10 @@ static bool shouldFilterRect(const SkMatrix& matrix, const SkRect& srcRect, cons if (requiresIntegerTranslate) { // Device rect and source rect should be integer aligned to ensure there's no difference // in how nearest-neighbor sampling is resolved. - return !(MathUtils::isZero(SkScalarFraction(srcRect.x())) && - MathUtils::isZero(SkScalarFraction(srcRect.y())) && - MathUtils::isZero(SkScalarFraction(dstDevRect.x())) && - MathUtils::isZero(SkScalarFraction(dstDevRect.y()))); + return !(isIntegerAligned(srcRect.x()) && + isIntegerAligned(srcRect.y()) && + isIntegerAligned(dstDevRect.x()) && + isIntegerAligned(dstDevRect.y())); } else { // As long as src and device rects are translated by the same fractional amount, // filtering won't be needed diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp index 38ef131bedef..0db5133037b0 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp @@ -15,7 +15,7 @@ */ #include "SkiaRecordingCanvas.h" - +#include "hwui/Paint.h" #include <SkImagePriv.h> #include "CanvasTransform.h" #ifdef __ANDROID__ // Layoutlib does not support Layers @@ -197,9 +197,37 @@ SkiaCanvas::PaintCoW&& SkiaRecordingCanvas::filterBitmap(PaintCoW&& paint) { return filterPaint(std::move(paint)); } -void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) { +static SkDrawLooper* get_looper(const Paint* paint) { + return paint ? paint->getLooper() : nullptr; +} + +template <typename Proc> +void applyLooper(SkDrawLooper* looper, const SkPaint& paint, Proc proc) { + if (looper) { + SkSTArenaAlloc<256> alloc; + SkDrawLooper::Context* ctx = looper->makeContext(&alloc); + if (ctx) { + SkDrawLooper::Context::Info info; + for (;;) { + SkPaint p = paint; + if (!ctx->next(&info, &p)) { + break; + } + proc(info.fTranslate.fX, info.fTranslate.fY, p); + } + } + } else { + proc(0, 0, paint); + } +} + +void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) { sk_sp<SkImage> image = bitmap.makeImage(); - mRecorder.drawImage(image, left, top, filterBitmap(paint), bitmap.palette()); + + applyLooper(get_looper(paint), *filterBitmap(paint), [&](SkScalar x, SkScalar y, const SkPaint& p) { + mRecorder.drawImage(image, left + x, top + y, &p, bitmap.palette()); + }); + // if image->unique() is true, then mRecorder.drawImage failed for some reason. It also means // it is not safe to store a raw SkImage pointer, because the image object will be destroyed // when this function ends. @@ -208,12 +236,16 @@ void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, cons } } -void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) { +void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const Paint* paint) { SkAutoCanvasRestore acr(&mRecorder, true); concat(matrix); sk_sp<SkImage> image = bitmap.makeImage(); - mRecorder.drawImage(image, 0, 0, filterBitmap(paint), bitmap.palette()); + + applyLooper(get_looper(paint), *filterBitmap(paint), [&](SkScalar x, SkScalar y, const SkPaint& p) { + mRecorder.drawImage(image, x, y, &p, bitmap.palette()); + }); + if (!bitmap.isImmutable() && image.get() && !image->unique()) { mDisplayList->mMutableImages.push_back(image.get()); } @@ -221,13 +253,17 @@ void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, con void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, float srcRight, float srcBottom, float dstLeft, float dstTop, float dstRight, - float dstBottom, const SkPaint* paint) { + float dstBottom, const Paint* paint) { SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom); SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom); sk_sp<SkImage> image = bitmap.makeImage(); - mRecorder.drawImageRect(image, srcRect, dstRect, filterBitmap(paint), - SkCanvas::kFast_SrcRectConstraint, bitmap.palette()); + + applyLooper(get_looper(paint), *filterBitmap(paint), [&](SkScalar x, SkScalar y, const SkPaint& p) { + mRecorder.drawImageRect(image, srcRect, dstRect.makeOffset(x, y), &p, + SkCanvas::kFast_SrcRectConstraint, bitmap.palette()); + }); + if (!bitmap.isImmutable() && image.get() && !image->unique() && !srcRect.isEmpty() && !dstRect.isEmpty()) { mDisplayList->mMutableImages.push_back(image.get()); @@ -236,7 +272,7 @@ void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop void SkiaRecordingCanvas::drawNinePatch(Bitmap& bitmap, const Res_png_9patch& chunk, float dstLeft, float dstTop, float dstRight, float dstBottom, - const SkPaint* paint) { + const Paint* paint) { SkCanvas::Lattice lattice; NinePatchUtils::SetLatticeDivs(&lattice, chunk, bitmap.width(), bitmap.height()); @@ -264,8 +300,11 @@ void SkiaRecordingCanvas::drawNinePatch(Bitmap& bitmap, const Res_png_9patch& ch filteredPaint.writeable().setFilterQuality(kLow_SkFilterQuality); } sk_sp<SkImage> image = bitmap.makeImage(); - mRecorder.drawImageLattice(image, lattice, dst, filterBitmap(std::move(filteredPaint)), - bitmap.palette()); + + applyLooper(get_looper(paint), *filterBitmap(paint), [&](SkScalar x, SkScalar y, const SkPaint& p) { + mRecorder.drawImageLattice(image, lattice, dst.makeOffset(x, y), &p, bitmap.palette()); + }); + if (!bitmap.isImmutable() && image.get() && !image->unique() && !dst.isEmpty()) { mDisplayList->mMutableImages.push_back(image.get()); } diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h index c42cea33211e..bd5274c94e75 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h @@ -45,14 +45,14 @@ public: virtual uirenderer::DisplayList* finishRecording() override; - virtual void drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) override; - virtual void drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) override; + virtual void drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) override; + virtual void drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const Paint* paint) override; virtual void drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop, float srcRight, float srcBottom, float dstLeft, float dstTop, float dstRight, - float dstBottom, const SkPaint* paint) override; + float dstBottom, const Paint* paint) override; virtual void drawNinePatch(Bitmap& hwuiBitmap, const android::Res_png_9patch& chunk, float dstLeft, float dstTop, float dstRight, float dstBottom, - const SkPaint* paint) override; + const Paint* paint) override; virtual double drawAnimatedImage(AnimatedImageDrawable* animatedImage) override; virtual void drawRoundRect(uirenderer::CanvasPropertyPrimitive* left, diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index d97c5ed4c7f5..d19351bf1db9 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -450,20 +450,38 @@ void CanvasContext::draw() { waitOnFences(); bool requireSwap = false; + int error = OK; bool didSwap = mRenderPipeline->swapBuffers(frame, drew, windowDirty, mCurrentFrameInfo, &requireSwap); mIsDirty = false; if (requireSwap) { - if (!didSwap) { // some error happened + bool didDraw = true; + // Handle any swapchain errors + error = mNativeSurface->getAndClearError(); + if (error == TIMED_OUT) { + // Try again + mRenderThread.postFrameCallback(this); + // But since this frame didn't happen, we need to mark full damage in the swap + // history + didDraw = false; + + } else if (error != OK || !didSwap) { + // Unknown error, abandon the surface setSurface(nullptr); + didDraw = false; } + SwapHistory& swap = mSwapHistory.next(); - swap.damage = windowDirty; + if (didDraw) { + swap.damage = windowDirty; + } else { + swap.damage = SkRect::MakeWH(INT_MAX, INT_MAX); + } swap.swapCompletedTime = systemTime(SYSTEM_TIME_MONOTONIC); swap.vsyncTime = mRenderThread.timeLord().latestVsync(); - if (mNativeSurface.get()) { + if (didDraw) { int durationUs; nsecs_t dequeueStart = mNativeSurface->getLastDequeueStartTime(); if (dequeueStart < mCurrentFrameInfo->get(FrameInfoIndex::SyncStart)) { diff --git a/libs/hwui/renderthread/ReliableSurface.cpp b/libs/hwui/renderthread/ReliableSurface.cpp index ad1fc4921781..a44b80457218 100644 --- a/libs/hwui/renderthread/ReliableSurface.cpp +++ b/libs/hwui/renderthread/ReliableSurface.cpp @@ -87,21 +87,21 @@ void ReliableSurface::perform(int operation, va_list args) { } int ReliableSurface::reserveNext() { + if constexpr (DISABLE_BUFFER_PREFETCH) { + return OK; + } { std::lock_guard _lock{mMutex}; if (mReservedBuffer) { ALOGW("reserveNext called but there was already a buffer reserved?"); return OK; } - if (mInErrorState) { + if (mBufferQueueState != OK) { return UNKNOWN_ERROR; } if (mHasDequeuedBuffer) { return OK; } - if constexpr (DISABLE_BUFFER_PREFETCH) { - return OK; - } } // TODO: Update this to better handle when requested dimensions have changed @@ -165,10 +165,11 @@ int ReliableSurface::dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd) { } } + int result = callProtected(mSurface, dequeueBuffer, buffer, fenceFd); if (result != OK) { ALOGW("dequeueBuffer failed, error = %d; switching to fallback", result); - *buffer = acquireFallbackBuffer(); + *buffer = acquireFallbackBuffer(result); *fenceFd = -1; return *buffer ? OK : INVALID_OPERATION; } else { @@ -201,9 +202,9 @@ bool ReliableSurface::isFallbackBuffer(const ANativeWindowBuffer* windowBuffer) return windowBuffer == scratchBuffer; } -ANativeWindowBuffer* ReliableSurface::acquireFallbackBuffer() { +ANativeWindowBuffer* ReliableSurface::acquireFallbackBuffer(int error) { std::lock_guard _lock{mMutex}; - mInErrorState = true; + mBufferQueueState = error; if (mScratchBuffer) { return AHardwareBuffer_to_ANativeWindowBuffer(mScratchBuffer.get()); diff --git a/libs/hwui/renderthread/ReliableSurface.h b/libs/hwui/renderthread/ReliableSurface.h index 0bfc72ef61cb..41fc35eca9f7 100644 --- a/libs/hwui/renderthread/ReliableSurface.h +++ b/libs/hwui/renderthread/ReliableSurface.h @@ -43,6 +43,12 @@ public: uint64_t getNextFrameNumber() const { return mSurface->getNextFrameNumber(); } + int getAndClearError() { + int ret = mBufferQueueState; + mBufferQueueState = OK; + return ret; + } + private: const sp<Surface> mSurface; @@ -55,10 +61,10 @@ private: ANativeWindowBuffer* mReservedBuffer = nullptr; base::unique_fd mReservedFenceFd; bool mHasDequeuedBuffer = false; - bool mInErrorState = false; + int mBufferQueueState = OK; bool isFallbackBuffer(const ANativeWindowBuffer* windowBuffer) const; - ANativeWindowBuffer* acquireFallbackBuffer(); + ANativeWindowBuffer* acquireFallbackBuffer(int error); void clearReservedBuffer(); void perform(int operation, va_list args); diff --git a/libs/hwui/tests/common/scenes/BitmapShaders.cpp b/libs/hwui/tests/common/scenes/BitmapShaders.cpp index 400196b203b9..c4067af388e3 100644 --- a/libs/hwui/tests/common/scenes/BitmapShaders.cpp +++ b/libs/hwui/tests/common/scenes/BitmapShaders.cpp @@ -15,6 +15,7 @@ */ #include <SkImagePriv.h> +#include "hwui/Paint.h" #include "TestSceneBase.h" #include "tests/common/BitmapAllocationTestUtils.h" #include "utils/Color.h" @@ -43,7 +44,7 @@ public: skCanvas.drawRect(SkRect::MakeXYWH(100, 100, 100, 100), skPaint); }); - SkPaint paint; + Paint paint; sk_sp<SkImage> image = hwuiBitmap->makeImage(); sk_sp<SkShader> repeatShader = image->makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat); diff --git a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp index 659926bae06f..3d0a2b2474bf 100644 --- a/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp +++ b/libs/hwui/tests/common/scenes/HwBitmapInCompositeShader.cpp @@ -65,7 +65,7 @@ public: sk_sp<SkShader> compositeShader( SkShaders::Blend(SkBlendMode::kDstATop, hardwareShader, gradientShader)); - SkPaint paint; + Paint paint; paint.setShader(std::move(compositeShader)); canvas.drawRoundRect(0, 0, 400, 200, 10.0f, 10.0f, paint); } diff --git a/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp b/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp index c6b60aaec9c5..a9449b62a1f8 100644 --- a/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp +++ b/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp @@ -49,7 +49,7 @@ class ListOfFadedTextAnimation : public TestListViewSceneBase { SkMatrix matrix; matrix.setScale(1, length); matrix.postRotate(-90); - SkPaint fadingPaint; + Paint fadingPaint; fadingPaint.setShader(s->makeWithLocalMatrix(matrix)); fadingPaint.setBlendMode(SkBlendMode::kDstOut); canvas.drawRect(0, 0, length, itemHeight, fadingPaint); diff --git a/libs/hwui/tests/common/scenes/ListViewAnimation.cpp b/libs/hwui/tests/common/scenes/ListViewAnimation.cpp index fb7e34dfa513..d031923a112b 100644 --- a/libs/hwui/tests/common/scenes/ListViewAnimation.cpp +++ b/libs/hwui/tests/common/scenes/ListViewAnimation.cpp @@ -79,7 +79,7 @@ class ListViewAnimation : public TestListViewSceneBase { static sk_sp<Bitmap> filledBox(createBoxBitmap(true)); static sk_sp<Bitmap> strokedBox(createBoxBitmap(false)); // TODO: switch to using round rect clipping, once merging correctly handles that - SkPaint roundRectPaint; + Paint roundRectPaint; roundRectPaint.setAntiAlias(true); roundRectPaint.setColor(Color::White); canvas.drawRoundRect(0, 0, itemWidth, itemHeight, dp(6), dp(6), roundRectPaint); diff --git a/libs/hwui/tests/common/scenes/OvalAnimation.cpp b/libs/hwui/tests/common/scenes/OvalAnimation.cpp index 4ff868b9d068..402c1ece2146 100644 --- a/libs/hwui/tests/common/scenes/OvalAnimation.cpp +++ b/libs/hwui/tests/common/scenes/OvalAnimation.cpp @@ -28,7 +28,7 @@ public: void createContent(int width, int height, Canvas& canvas) override { canvas.drawColor(Color::White, SkBlendMode::kSrcOver); card = TestUtils::createNode(0, 0, 200, 200, [](RenderProperties& props, Canvas& canvas) { - SkPaint paint; + Paint paint; paint.setAntiAlias(true); paint.setColor(Color::Black); canvas.drawOval(0, 0, 200, 200, paint); diff --git a/libs/hwui/tests/common/scenes/RectGridAnimation.cpp b/libs/hwui/tests/common/scenes/RectGridAnimation.cpp index 6a3b6a57b28a..d5ecfaff4f0c 100644 --- a/libs/hwui/tests/common/scenes/RectGridAnimation.cpp +++ b/libs/hwui/tests/common/scenes/RectGridAnimation.cpp @@ -41,7 +41,7 @@ public: } } - SkPaint paint; + Paint paint; paint.setColor(0xff00ffff); canvas.drawRegion(region, paint); }); diff --git a/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp b/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp index 02dd42ff2ae8..97bfba34c790 100644 --- a/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp +++ b/libs/hwui/tests/common/scenes/SaveLayerAnimation.cpp @@ -45,7 +45,7 @@ public: canvas.save(SaveFlags::MatrixClip); canvas.translate(0, 400); canvas.saveLayerAlpha(100, 100, 300, 300, 128, SaveFlags::Flags(0)); // unclipped - SkPaint paint; + Paint paint; paint.setAntiAlias(true); paint.setColor(Color::Green_700); canvas.drawCircle(200, 200, 200, paint); diff --git a/libs/hwui/tests/common/scenes/ShapeAnimation.cpp b/libs/hwui/tests/common/scenes/ShapeAnimation.cpp index d189a9379c33..70a1557dcf6a 100644 --- a/libs/hwui/tests/common/scenes/ShapeAnimation.cpp +++ b/libs/hwui/tests/common/scenes/ShapeAnimation.cpp @@ -30,14 +30,14 @@ public: void createContent(int width, int height, Canvas& canvas) override { card = TestUtils::createNode( 0, 0, width, height, [width](RenderProperties& props, Canvas& canvas) { - std::function<void(Canvas&, float, const SkPaint&)> ops[] = { - [](Canvas& canvas, float size, const SkPaint& paint) { + std::function<void(Canvas&, float, const Paint&)> ops[] = { + [](Canvas& canvas, float size, const Paint& paint) { canvas.drawArc(0, 0, size, size, 50, 189, true, paint); }, - [](Canvas& canvas, float size, const SkPaint& paint) { + [](Canvas& canvas, float size, const Paint& paint) { canvas.drawOval(0, 0, size, size, paint); }, - [](Canvas& canvas, float size, const SkPaint& paint) { + [](Canvas& canvas, float size, const Paint& paint) { SkPath diamondPath; diamondPath.moveTo(size / 2, 0); diamondPath.lineTo(size, size / 2); @@ -46,18 +46,18 @@ public: diamondPath.close(); canvas.drawPath(diamondPath, paint); }, - [](Canvas& canvas, float size, const SkPaint& paint) { + [](Canvas& canvas, float size, const Paint& paint) { float data[] = {0, 0, size, size, 0, size, size, 0}; canvas.drawLines(data, sizeof(data) / sizeof(float), paint); }, - [](Canvas& canvas, float size, const SkPaint& paint) { + [](Canvas& canvas, float size, const Paint& paint) { float data[] = {0, 0, size, size, 0, size, size, 0}; canvas.drawPoints(data, sizeof(data) / sizeof(float), paint); }, - [](Canvas& canvas, float size, const SkPaint& paint) { + [](Canvas& canvas, float size, const Paint& paint) { canvas.drawRect(0, 0, size, size, paint); }, - [](Canvas& canvas, float size, const SkPaint& paint) { + [](Canvas& canvas, float size, const Paint& paint) { float rad = size / 4; canvas.drawRoundRect(0, 0, size, size, rad, rad, paint); }}; @@ -66,7 +66,7 @@ public: // each combination of strokeWidth + style gets a column int outerCount = canvas.save(SaveFlags::MatrixClip); - SkPaint paint; + Paint paint; paint.setAntiAlias(true); SkPaint::Style styles[] = {SkPaint::kStroke_Style, SkPaint::kFill_Style, SkPaint::kStrokeAndFill_Style}; diff --git a/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp b/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp index e60bd5fae198..a0bc5aa245d5 100644 --- a/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp +++ b/libs/hwui/tests/common/scenes/SimpleColorMatrixAnimation.cpp @@ -52,7 +52,7 @@ private: return TestUtils::createNode( x, y, x + width, y + height, [width, height](RenderProperties& props, Canvas& canvas) { - SkPaint paint; + Paint paint; // Simple scale/translate case where R, G, and B are all treated equivalently SkColorMatrix cm; cm.setScale(1.1f, 1.1f, 1.1f, 0.5f); diff --git a/libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp b/libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp index 8bd804e75674..57a260c8d234 100644 --- a/libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp +++ b/libs/hwui/tests/common/scenes/SimpleGradientAnimation.cpp @@ -51,7 +51,7 @@ private: [width, height](RenderProperties& props, Canvas& canvas) { float pos[] = {0, 1}; SkPoint pts[] = {SkPoint::Make(0, 0), SkPoint::Make(width, height)}; - SkPaint paint; + Paint paint; // overdraw several times to emphasize shader cost for (int i = 0; i < 10; i++) { // use i%2 start position to pick 2 color combo with black in it diff --git a/libs/hwui/tests/common/scenes/TestSceneBase.h b/libs/hwui/tests/common/scenes/TestSceneBase.h index 6f76a502ae3e..24d35857c60d 100644 --- a/libs/hwui/tests/common/scenes/TestSceneBase.h +++ b/libs/hwui/tests/common/scenes/TestSceneBase.h @@ -17,6 +17,7 @@ #pragma once #include "hwui/Canvas.h" +#include "hwui/Paint.h" #include "RenderNode.h" #include "tests/common/TestContext.h" #include "tests/common/TestScene.h" diff --git a/libs/hwui/tests/common/scenes/TvApp.cpp b/libs/hwui/tests/common/scenes/TvApp.cpp index 76f02888f994..bac887053d2f 100644 --- a/libs/hwui/tests/common/scenes/TvApp.cpp +++ b/libs/hwui/tests/common/scenes/TvApp.cpp @@ -217,7 +217,7 @@ private: std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas( image->stagingProperties().getWidth(), image->stagingProperties().getHeight(), image.get())); - SkPaint paint; + Paint paint; sk_sp<SkColorFilter> filter( SkColorFilters::Blend((curFrame % 150) << 24, SkBlendMode::kSrcATop)); paint.setColorFilter(filter); diff --git a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp index 70423a70157b..4ce6c32470ea 100644 --- a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp +++ b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp @@ -18,6 +18,7 @@ #include "DisplayList.h" #include "hwui/Canvas.h" +#include "hwui/Paint.h" #include "pipeline/skia/SkiaDisplayList.h" #include "tests/common/TestUtils.h" @@ -93,7 +94,7 @@ void BM_DisplayListCanvas_record_simpleBitmapView(benchmark::State& benchState) std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(100, 100)); delete canvas->finishRecording(); - SkPaint rectPaint; + Paint rectPaint; sk_sp<Bitmap> iconBitmap(TestUtils::createBitmap(80, 80)); while (benchState.KeepRunning()) { diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp index e70378bd15a5..3632be06c45f 100644 --- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp +++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp @@ -24,6 +24,7 @@ #include "DamageAccumulator.h" #include "FatalTestCanvas.h" #include "IContextFactory.h" +#include "hwui/Paint.h" #include "RecordingCanvas.h" #include "SkiaCanvas.h" #include "pipeline/skia/SkiaDisplayList.h" @@ -59,7 +60,7 @@ TEST(RenderNodeDrawable, create) { namespace { static void drawOrderedRect(Canvas* canvas, uint8_t expectedDrawOrder) { - SkPaint paint; + Paint paint; // order put in blue channel, transparent so overlapped content doesn't get rejected paint.setColor(SkColorSetARGB(1, 0, 0, expectedDrawOrder)); canvas->drawRect(0, 0, 100, 100, paint); @@ -211,7 +212,7 @@ TEST(RenderNodeDrawable, saveLayerClipAndMatrixRestore) { ASSERT_EQ(SkRect::MakeLTRB(0, 0, 400, 800), getRecorderClipBounds(recorder)); EXPECT_TRUE(getRecorderMatrix(recorder).isIdentity()); - SkPaint paint; + Paint paint; paint.setAntiAlias(true); paint.setColor(SK_ColorGREEN); recorder.drawRect(0.0f, 400.0f, 400.0f, 800.0f, paint); @@ -291,7 +292,7 @@ RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorder) { properties.setTranslationX(SCROLL_X); properties.setTranslationY(SCROLL_Y); - SkPaint paint; + Paint paint; paint.setColor(SK_ColorWHITE); canvas.drawRect(0, 0, 100, 100, paint); }, @@ -302,7 +303,7 @@ RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorder) { [](RenderProperties& properties, SkiaRecordingCanvas& canvas) { properties.setProjectBackwards(true); properties.setClipToBounds(false); - SkPaint paint; + Paint paint; paint.setColor(SK_ColorDKGRAY); canvas.drawRect(-10, -10, 60, 60, paint); }, @@ -310,7 +311,7 @@ RENDERTHREAD_TEST(RenderNodeDrawable, projectionReorder) { auto child = TestUtils::createSkiaNode( 0, 50, 100, 100, [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) { - SkPaint paint; + Paint paint; paint.setColor(SK_ColorBLUE); canvas.drawRect(0, 0, 100, 50, paint); canvas.drawRenderNode(projectingRipple.get()); @@ -375,14 +376,14 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, emptyReceiver) { [](RenderProperties& properties, SkiaRecordingCanvas& canvas) { properties.setProjectBackwards(true); properties.setClipToBounds(false); - SkPaint paint; + Paint paint; canvas.drawRect(0, 0, 100, 100, paint); }, "P"); auto child = TestUtils::createSkiaNode( 0, 0, 100, 100, [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) { - SkPaint paint; + Paint paint; canvas.drawRect(0, 0, 100, 100, paint); canvas.drawRenderNode(projectingRipple.get()); }, @@ -483,7 +484,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, projectionHwLayer) { properties.setTranslationX(SCROLL_X); properties.setTranslationY(SCROLL_Y); - canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, SkPaint()); + canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, Paint()); }, "B"); // B auto projectingRipple = TestUtils::createSkiaNode( @@ -491,14 +492,14 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, projectionHwLayer) { [](RenderProperties& properties, SkiaRecordingCanvas& canvas) { properties.setProjectBackwards(true); properties.setClipToBounds(false); - canvas.drawOval(100, 100, 300, 300, SkPaint()); // drawn mostly out of layer bounds + canvas.drawOval(100, 100, 300, 300, Paint()); // drawn mostly out of layer bounds }, "R"); // R auto child = TestUtils::createSkiaNode( 100, 100, 300, 300, [&projectingRipple](RenderProperties& properties, SkiaRecordingCanvas& canvas) { canvas.drawRenderNode(projectingRipple.get()); - canvas.drawArc(0, 0, LAYER_WIDTH, LAYER_HEIGHT, 0.0f, 280.0f, true, SkPaint()); + canvas.drawArc(0, 0, LAYER_WIDTH, LAYER_HEIGHT, 0.0f, 280.0f, true, Paint()); }, "C"); // C auto parent = TestUtils::createSkiaNode( @@ -578,7 +579,7 @@ RENDERTHREAD_TEST(RenderNodeDrawable, projectionChildScroll) { 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, [](RenderProperties& properties, SkiaRecordingCanvas& canvas) { properties.setProjectionReceiver(true); - canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, SkPaint()); + canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, Paint()); }, "B"); // B auto projectingRipple = TestUtils::createSkiaNode( @@ -591,7 +592,7 @@ RENDERTHREAD_TEST(RenderNodeDrawable, projectionChildScroll) { properties.setTranslationY(SCROLL_Y); properties.setProjectBackwards(true); properties.setClipToBounds(false); - canvas.drawOval(0, 0, 200, 200, SkPaint()); + canvas.drawOval(0, 0, 200, 200, Paint()); }, "R"); // R auto child = TestUtils::createSkiaNode( @@ -946,7 +947,7 @@ RENDERTHREAD_TEST(RenderNodeDrawable, simple) { [](RenderProperties& props, SkiaRecordingCanvas& canvas) { sk_sp<Bitmap> bitmap(TestUtils::createBitmap(25, 25)); canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, - SkPaint()); + Paint()); canvas.drawBitmap(*bitmap, 10, 10, nullptr); }); @@ -1022,7 +1023,7 @@ TEST(RenderNodeDrawable, renderNode) { auto child = TestUtils::createSkiaNode( 10, 10, 110, 110, [](RenderProperties& props, SkiaRecordingCanvas& canvas) { - SkPaint paint; + Paint paint; paint.setColor(SK_ColorWHITE); canvas.drawRect(0, 0, 100, 100, paint); }); @@ -1030,7 +1031,7 @@ TEST(RenderNodeDrawable, renderNode) { auto parent = TestUtils::createSkiaNode( 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, [&child](RenderProperties& props, SkiaRecordingCanvas& canvas) { - SkPaint paint; + Paint paint; paint.setColor(SK_ColorDKGRAY); canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, paint); @@ -1065,7 +1066,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, layerComposeQuality) { auto layerNode = TestUtils::createSkiaNode( 0, 0, LAYER_WIDTH, LAYER_HEIGHT, [](RenderProperties& properties, SkiaRecordingCanvas& canvas) { - canvas.drawPaint(SkPaint()); + canvas.drawPaint(Paint()); }); layerNode->animatorProperties().mutateLayerProperties().setType(LayerType::RenderLayer); diff --git a/libs/hwui/tests/unit/SkiaCanvasTests.cpp b/libs/hwui/tests/unit/SkiaCanvasTests.cpp index 2ed1b25efc71..fcc64fdd0be6 100644 --- a/libs/hwui/tests/unit/SkiaCanvasTests.cpp +++ b/libs/hwui/tests/unit/SkiaCanvasTests.cpp @@ -16,6 +16,7 @@ #include "tests/common/TestUtils.h" +#include <hwui/Paint.h> #include <SkBlurDrawLooper.h> #include <SkCanvasStateUtils.h> #include <SkPicture.h> @@ -32,7 +33,7 @@ TEST(SkiaCanvas, drawShadowLayer) { // clear to white canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrc); - SkPaint paint; + Paint paint; // it is transparent to ensure that we still draw the rect since it has a looper paint.setColor(SK_ColorTRANSPARENT); // this is how view's shadow layers are implemented diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp index 7b76bd80de4d..958baa78deab 100644 --- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp +++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp @@ -23,6 +23,7 @@ #include "AnimationContext.h" #include "DamageAccumulator.h" #include "IContextFactory.h" +#include "hwui/Paint.h" #include "SkiaCanvas.h" #include "pipeline/skia/SkiaDisplayList.h" #include "pipeline/skia/SkiaOpenGLPipeline.h" @@ -96,7 +97,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, testOnPrepareTree) { RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckOpaque) { auto halfGreenNode = TestUtils::createSkiaNode( 0, 0, 2, 2, [](RenderProperties& props, SkiaRecordingCanvas& bottomHalfGreenCanvas) { - SkPaint greenPaint; + Paint greenPaint; greenPaint.setColor(SK_ColorGREEN); greenPaint.setStyle(SkPaint::kFill_Style); bottomHalfGreenCanvas.drawRect(0, 1, 2, 2, greenPaint); @@ -294,7 +295,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, deferRenderNodeScene) { }; std::vector<sp<RenderNode>> nodes; - SkPaint transparentPaint; + Paint transparentPaint; transparentPaint.setAlpha(128); // backdrop diff --git a/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp b/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp index 635429dea359..eec25c6bd40d 100644 --- a/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp +++ b/libs/hwui/tests/unit/SkiaRenderPropertiesTests.cpp @@ -24,6 +24,7 @@ #include "DamageAccumulator.h" #include "FatalTestCanvas.h" #include "IContextFactory.h" +#include "hwui/Paint.h" #include "SkiaCanvas.h" #include "pipeline/skia/SkiaDisplayList.h" #include "pipeline/skia/SkiaPipeline.h" @@ -60,7 +61,7 @@ static void testProperty(std::function<void(RenderProperties&)> propSetupCallbac 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, [propSetupCallback](RenderProperties& props, SkiaRecordingCanvas& canvas) { propSetupCallback(props); - SkPaint paint; + Paint paint; paint.setColor(SK_ColorWHITE); canvas.drawRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, paint); }); diff --git a/libs/hwui/utils/PaintUtils.h b/libs/hwui/utils/PaintUtils.h index ebf2343c5518..e2fdf2fcb5a5 100644 --- a/libs/hwui/utils/PaintUtils.h +++ b/libs/hwui/utils/PaintUtils.h @@ -67,29 +67,6 @@ public: return (filter->getFlags() & SkColorFilter::kAlphaUnchanged_Flag) == 0; } - struct TextShadow { - SkScalar radius; - float dx; - float dy; - SkColor color; - }; - - static inline bool getTextShadow(const SkPaint* paint, TextShadow* textShadow) { - SkDrawLooper::BlurShadowRec blur; - if (paint && paint->getLooper() && paint->getLooper()->asABlurShadow(&blur)) { - if (textShadow) { - textShadow->radius = Blur::convertSigmaToRadius(blur.fSigma); - textShadow->dx = blur.fOffset.fX; - textShadow->dy = blur.fOffset.fY; - textShadow->color = blur.fColor; - } - return true; - } - return false; - } - - static inline bool hasTextShadow(const SkPaint* paint) { return getTextShadow(paint, nullptr); } - static inline SkBlendMode getBlendModeDirect(const SkPaint* paint) { return paint ? paint->getBlendMode() : SkBlendMode::kSrcOver; } 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/DynamicSystemInstallationService/Android.bp b/packages/DynamicSystemInstallationService/Android.bp new file mode 100644 index 000000000000..f1a18ae2a53f --- /dev/null +++ b/packages/DynamicSystemInstallationService/Android.bp @@ -0,0 +1,14 @@ +android_app { + name: "DynamicSystemInstallationService", + + srcs: ["src/**/*.java"], + resource_dirs: ["res"], + + certificate: "platform", + privileged: true, + platform_apis: true, + + optimize: { + enabled: false, + }, +} diff --git a/packages/DynamicSystemInstallationService/Android.mk b/packages/DynamicSystemInstallationService/Android.mk deleted file mode 100644 index 16aca1bd07c5..000000000000 --- a/packages/DynamicSystemInstallationService/Android.mk +++ /dev/null @@ -1,19 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := optional - -LOCAL_SRC_FILES := $(call all-java-files-under, src) -LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, res) - -LOCAL_USE_AAPT2 := true - -LOCAL_PACKAGE_NAME := DynamicSystemInstallationService -LOCAL_CERTIFICATE := platform -LOCAL_PRIVILEGED_MODULE := true -LOCAL_PRIVATE_PLATFORM_APIS := true - -LOCAL_PROGUARD_ENABLED := disabled - - -include $(BUILD_PACKAGE) diff --git a/packages/InputDevices/Android.bp b/packages/InputDevices/Android.bp new file mode 100644 index 000000000000..7532aea23615 --- /dev/null +++ b/packages/InputDevices/Android.bp @@ -0,0 +1,42 @@ +// Copyright (C) 2012 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: "InputDevices", + + srcs: [ + "**/*.java", + ":validate_input_devices_keymaps", + ], + + resource_dirs: ["res"], + + sdk_version: "current", + certificate: "platform", + privileged: true, +} + +// Validate all key maps. +// Produces an empty srcjar that is used as an input to InputDevices to make sure +// the check runs for platform builds. +genrule { + name: "validate_input_devices_keymaps", + tools: [ + "validatekeymaps", + "soong_zip", + ], + srcs: ["res/raw/*.kcm"], + out: ["validate_input_devices_keymaps.srcjar"], + cmd: "$(location validatekeymaps) -q $(in) && $(location soong_zip) -o $(out)", +} diff --git a/packages/InputDevices/Android.mk b/packages/InputDevices/Android.mk deleted file mode 100644 index 80803fd94ff1..000000000000 --- a/packages/InputDevices/Android.mk +++ /dev/null @@ -1,50 +0,0 @@ -# Copyright (C) 2012 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) -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := optional - -LOCAL_SRC_FILES := $(call all-subdir-java-files) - -LOCAL_JAVA_LIBRARIES := - -LOCAL_PACKAGE_NAME := InputDevices -LOCAL_SDK_VERSION := current -LOCAL_CERTIFICATE := platform -LOCAL_PRIVILEGED_MODULE := true - -include $(BUILD_PACKAGE) - -# Validate all key maps. -include $(CLEAR_VARS) - -LOCAL_MODULE := validate_input_devices_keymaps -intermediates := $(call intermediates-dir-for,ETC,$(LOCAL_MODULE),,COMMON) -LOCAL_BUILT_MODULE := $(intermediates)/stamp - -validatekeymaps := $(HOST_OUT_EXECUTABLES)/validatekeymaps$(HOST_EXECUTABLE_SUFFIX) -input_devices_keymaps := $(wildcard $(LOCAL_PATH)/res/raw/*.kcm) -$(LOCAL_BUILT_MODULE): PRIVATE_VALIDATEKEYMAPS := $(validatekeymaps) -$(LOCAL_BUILT_MODULE) : $(input_devices_keymaps) | $(validatekeymaps) - $(hide) $(PRIVATE_VALIDATEKEYMAPS) -q $^ - $(hide) mkdir -p $(dir $@) && touch $@ - -# Run validatekeymaps unconditionally for platform build. -droidcore : $(LOCAL_BUILT_MODULE) - -# Reset temp vars. -validatekeymaps := -input_devices_keymaps := diff --git a/packages/MtpDocumentsProvider/Android.bp b/packages/MtpDocumentsProvider/Android.bp new file mode 100644 index 000000000000..3dafa2649af6 --- /dev/null +++ b/packages/MtpDocumentsProvider/Android.bp @@ -0,0 +1,11 @@ +android_app { + name: "MtpDocumentsProvider", + + srcs: ["src/**/*.java"], + platform_apis: true, + certificate: "media", + privileged: true, + optimize: { + proguard_flags_files: ["proguard.flags"], + }, +} diff --git a/packages/MtpDocumentsProvider/Android.mk b/packages/MtpDocumentsProvider/Android.mk deleted file mode 100644 index 2d62a07566bf..000000000000 --- a/packages/MtpDocumentsProvider/Android.mk +++ /dev/null @@ -1,18 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := optional -LOCAL_SRC_FILES := $(call all-java-files-under, src) -LOCAL_PACKAGE_NAME := MtpDocumentsProvider -LOCAL_PRIVATE_PLATFORM_APIS := true -LOCAL_CERTIFICATE := media -LOCAL_PRIVILEGED_MODULE := true -LOCAL_PROGUARD_FLAG_FILES := proguard.flags - -# Only enable asserts on userdebug/eng builds -ifneq (,$(filter userdebug eng, $(TARGET_BUILD_VARIANT))) -LOCAL_JACK_FLAGS += -D jack.assert.policy=always -endif - -include $(BUILD_PACKAGE) -include $(call all-makefiles-under, $(LOCAL_PATH)) diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml index beb1ac546949..2734f4e28632 100644 --- a/packages/SettingsLib/res/values-es-rUS/strings.xml +++ b/packages/SettingsLib/res/values-es-rUS/strings.xml @@ -141,7 +141,7 @@ <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Aplicaciones eliminadas"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Aplicaciones y usuarios eliminados"</string> <string name="data_usage_ota" msgid="5377889154805560860">"Actualizaciones del sistema"</string> - <string name="tether_settings_title_usb" msgid="6688416425801386511">"Conexión USB"</string> + <string name="tether_settings_title_usb" msgid="6688416425801386511">"Conexión a red por USB"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"Hotspot portátil"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Conexión Bluetooth"</string> <string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Compartir conexión"</string> diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml index 238eba58e0a5..3a20d04d7ab2 100644 --- a/packages/SettingsLib/res/values-hi/strings.xml +++ b/packages/SettingsLib/res/values-hi/strings.xml @@ -46,7 +46,7 @@ <string name="wifi_limited_connection" msgid="7717855024753201527">"सीमित कनेक्शन"</string> <string name="wifi_status_no_internet" msgid="5784710974669608361">"इंटरनेट कनेक्शन नहीं है"</string> <string name="wifi_status_sign_in_required" msgid="123517180404752756">"साइन इन करना ज़रूरी है"</string> - <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"एक्सेस पॉइंट फ़िलहाल भरा हुआ है"</string> + <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"ऐक्सेस पॉइंट फ़िलहाल भरा हुआ है"</string> <string name="connected_via_carrier" msgid="7583780074526041912">"%1$s के ज़रिए कनेक्ट"</string> <string name="available_via_carrier" msgid="1469036129740799053">"%1$s के ज़रिए उपलब्ध"</string> <string name="osu_opening_provider" msgid="5488997661548640424">"<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g> खोला जा रहा है"</string> @@ -68,7 +68,7 @@ <string name="bluetooth_pairing" msgid="1426882272690346242">"युग्मित कर रहा है…"</string> <string name="bluetooth_connected_no_headset" msgid="616068069034994802">"जुड़ गया (फ़ोन के ऑडियो को छोड़कर)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string> <string name="bluetooth_connected_no_a2dp" msgid="3736431800395923868">"जुड़ गया (मीडिया ऑडियो को छोड़कर)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string> - <string name="bluetooth_connected_no_map" msgid="3200033913678466453">"जुड़ गया (मैसेज का एक्सेस नहीं)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string> + <string name="bluetooth_connected_no_map" msgid="3200033913678466453">"जुड़ गया (मैसेज का ऐक्सेस नहीं)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string> <string name="bluetooth_connected_no_headset_no_a2dp" msgid="2047403011284187056">"जुड़ गया (फ़ोन या मीडिया ऑडियो को छोड़कर)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string> <string name="bluetooth_connected_battery_level" msgid="5162924691231307748">"जुड़ गया, बैटरी का लेवल <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string> <string name="bluetooth_connected_no_headset_battery_level" msgid="1610296229139400266">"जुड़ गया (फ़ोन के ऑडियो को छोड़कर), बैटरी का लेवल <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string> @@ -88,7 +88,7 @@ <string name="bluetooth_profile_pbap_summary" msgid="6605229608108852198">"संपर्क साझाकरण के लिए उपयोग करें"</string> <string name="bluetooth_profile_pan_nap" msgid="8429049285027482959">"इंटरनेट कनेक्शन साझाकरण"</string> <string name="bluetooth_profile_map" msgid="1019763341565580450">"लेख संदेश"</string> - <string name="bluetooth_profile_sap" msgid="5764222021851283125">"सिम एक्सेस"</string> + <string name="bluetooth_profile_sap" msgid="5764222021851283125">"सिम ऐक्सेस"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="5444517801472820055">"HD ऑडियो: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="8510588052415438887">"HD ऑडियो"</string> <string name="bluetooth_profile_hearing_aid" msgid="6680721080542444257">"सुनने में मदद करने वाले डिवाइस"</string> @@ -200,7 +200,7 @@ <string name="development_settings_not_available" msgid="4308569041701535607">"यह उपयोगकर्ता, डेवलपर के लिए सेटिंग और टूल का इस्तेमाल नहीं कर सकता"</string> <string name="vpn_settings_not_available" msgid="956841430176985598">"VPN सेटिंग इस उपयोगकर्ता के लिए उपलब्ध नहीं हैं"</string> <string name="tethering_settings_not_available" msgid="6765770438438291012">"टेदरिंग सेटिंग इस उपयोगकर्ता के लिए उपलब्ध नहीं हैं"</string> - <string name="apn_settings_not_available" msgid="7873729032165324000">"एक्सेस पॉइंट के नाम की सेटिंग इस उपयोगकर्ता के लिए मौजूद नहीं हैं"</string> + <string name="apn_settings_not_available" msgid="7873729032165324000">"ऐक्सेस पॉइंट के नाम की सेटिंग इस उपयोगकर्ता के लिए मौजूद नहीं हैं"</string> <string name="enable_adb" msgid="7982306934419797485">"USB डीबग करना"</string> <string name="enable_adb_summary" msgid="4881186971746056635">"डीबग मोड जब USB कनेक्ट किया गया हो"</string> <string name="clear_adb_keys" msgid="4038889221503122743">"USB डीबग करने की मंज़ूरी रद्द करें"</string> @@ -414,7 +414,7 @@ <string name="disabled" msgid="9206776641295849915">"बंद किया गया"</string> <string name="external_source_trusted" msgid="2707996266575928037">"अनुमति है"</string> <string name="external_source_untrusted" msgid="2677442511837596726">"अनुमति नहीं है"</string> - <string name="install_other_apps" msgid="6986686991775883017">"अनजान ऐप्लिकेशन इंस्टॉल करने का एक्सेस"</string> + <string name="install_other_apps" msgid="6986686991775883017">"अनजान ऐप्लिकेशन इंस्टॉल करने का ऐक्सेस"</string> <string name="home" msgid="3256884684164448244">"सेटिंग का होम पेज"</string> <string-array name="battery_labels"> <item msgid="8494684293649631252">"0%"</item> diff --git a/packages/SettingsLib/tests/Android.mk b/packages/SettingsLib/tests/Android.mk deleted file mode 100644 index 333c41d33f40..000000000000 --- a/packages/SettingsLib/tests/Android.mk +++ /dev/null @@ -1,19 +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. - -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) - -# Include all makefiles in subdirectories -include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java index b11df4890e98..7d56868d0ce8 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java @@ -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); } @@ -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); diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java index c9bb83c0b986..a7b44443fc9c 100644 --- a/packages/Shell/src/com/android/shell/BugreportProgressService.java +++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java @@ -350,18 +350,19 @@ public class BugreportProgressService extends Service { private final class BugreportCallbackImpl extends BugreportCallback { - private final int mId; private final BugreportInfo mInfo; - BugreportCallbackImpl(String name, int id) { - mId = id; + BugreportCallbackImpl(String name) { // pid not used in this workflow, so setting default = 0 - mInfo = new BugreportInfo(mContext, mId, 0 /* pid */, name, + mInfo = new BugreportInfo(mContext, 0 /* pid */, name, 100 /* max progress*/); } @Override public void onProgress(float progress) { + if (progress == 0) { + trackInfoWithId(); + } checkProgressUpdated(mInfo, (int) progress); } @@ -369,17 +370,34 @@ public class BugreportProgressService extends Service { // Logging errors and removing progress notification for now. @Override public void onError(@BugreportErrorCode int errorCode) { - stopProgress(mId); + trackInfoWithId(); + stopProgress(mInfo.id); Log.e(TAG, "Bugreport API callback onError() errorCode = " + errorCode); return; } @Override public void onFinished() { + trackInfoWithId(); // Stop running on foreground, otherwise share notification cannot be dismissed. - onBugreportFinished(mId); + onBugreportFinished(mInfo.id); stopSelfWhenDone(); } + + /** + * Reads bugreport id and links it to the bugreport info to track the bugreport's + * progress/completion/error. id is incremented in dumpstate code. This function is called + * when dumpstate calls one of the callback functions (onProgress, onFinished, onError) + * after the id has been incremented. + */ + private void trackInfoWithId() { + final int id = SystemProperties.getInt(PROPERTY_LAST_ID, 1); + if (mBugreportInfos.get(id) == null) { + mInfo.id = id; + mBugreportInfos.put(mInfo.id, mInfo); + } + return; + } } /** @@ -539,12 +557,17 @@ public class BugreportProgressService extends Service { } } + private String getBugreportName() { + String buildId = SystemProperties.get("ro.build.id", "UNKNOWN_BUILD"); + String deviceName = SystemProperties.get("ro.product.name", "UNKNOWN_DEVICE"); + String currentTimestamp = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date()); + return String.format("bugreport-%s-%s-%s", deviceName, buildId, currentTimestamp); + } + private void startBugreportAPI(Intent intent) { mUsingBugreportApi = true; - String bugreportName = "bugreport-" + new SimpleDateFormat("yyyy-MM-dd-HH-mm").format( - new Date()); + String bugreportName = getBugreportName(); - // TODO(b/126862297): Make file naming same as dumpstate triggered bugreports ParcelFileDescriptor bugreportFd = createReadWriteFile(BUGREPORT_DIR, bugreportName + ".zip"); if (bugreportFd == null) { @@ -565,19 +588,15 @@ public class BugreportProgressService extends Service { mBugreportManager = (BugreportManager) mContext.getSystemService( Context.BUGREPORT_SERVICE); final Executor executor = ActivityThread.currentActivityThread().getExecutor(); - // TODO(b/123617758): This id should come from dumpstate. - // 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; + Log.i(TAG, "bugreport type = " + bugreportType + " bugreport file fd: " + bugreportFd + " screenshot file fd: " + screenshotFd); - BugreportCallbackImpl bugreportCallback = new BugreportCallbackImpl(bugreportName, id); + BugreportCallbackImpl bugreportCallback = new BugreportCallbackImpl(bugreportName); try { mBugreportManager.startBugreport(bugreportFd, screenshotFd, new BugreportParams(bugreportType), executor, bugreportCallback); - mBugreportInfos.put(bugreportCallback.mInfo.id, bugreportCallback.mInfo); } catch (RuntimeException e) { Log.i(TAG, "error in generating bugreports: ", e); // The binder call didn't go through successfully, so need to close the fds. @@ -1811,7 +1830,7 @@ public class BugreportProgressService extends Service { /** * Sequential, user-friendly id used to identify the bugreport. */ - final int id; + int id; /** * {@code pid} of the {@code dumpstate} process generating the bugreport. @@ -1903,8 +1922,15 @@ public class BugreportProgressService extends Service { * Constructor for tracked bugreports - typically called upon receiving BUGREPORT_STARTED. */ BugreportInfo(Context context, int id, int pid, String name, int max) { - this.context = context; + this(context, pid, name, max); this.id = id; + } + + /** + * Constructor for tracked bugreports - typically called upon receiving BUGREPORT_REQUESTED. + */ + BugreportInfo(Context context, int pid, String name, int max) { + this.context = context; this.pid = pid; this.name = name; this.max = this.realMax = max; diff --git a/packages/SimAppDialog/Android.bp b/packages/SimAppDialog/Android.bp new file mode 100644 index 000000000000..ff26710fa2e1 --- /dev/null +++ b/packages/SimAppDialog/Android.bp @@ -0,0 +1,15 @@ +android_app { + name: "SimAppDialog", + + srcs: ["src/**/*.java"], + + platform_apis: true, + certificate: "platform", + + static_libs: [ + "androidx.legacy_legacy-support-v4", + "setup-wizard-lib", + ], + + resource_dirs: ["res"], +} diff --git a/packages/SimAppDialog/Android.mk b/packages/SimAppDialog/Android.mk deleted file mode 100644 index 991e3330e00a..000000000000 --- a/packages/SimAppDialog/Android.mk +++ /dev/null @@ -1,19 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := optional - -LOCAL_SRC_FILES := $(call all-java-files-under, src) - -LOCAL_PACKAGE_NAME := SimAppDialog -LOCAL_PRIVATE_PLATFORM_APIS := true -LOCAL_CERTIFICATE := platform - - -LOCAL_STATIC_ANDROID_LIBRARIES := \ - androidx.legacy_legacy-support-v4 - -LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res -include frameworks/opt/setupwizard/library/common-platform-deprecated.mk - -include $(BUILD_PACKAGE) 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/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml index f124d89070bf..6becd21984b9 100644 --- a/packages/SystemUI/res/values/attrs.xml +++ b/packages/SystemUI/res/values/attrs.xml @@ -75,14 +75,15 @@ <attr name="horizontalSpacing" format="dimension" /> </declare-styleable> - <!-- Theme for icons in the status bar (light/dark). background/fillColor is used for dual tone - icons like wifi and signal, and singleToneColor is used for icons with only one tone. + <!-- Theme for icons in the status/nav bar (light/dark). background/fillColor is used for dual + tone icons like wifi and signal, and singleToneColor is used for icons with only one tone. Contract: Pixel with fillColor blended over backgroundColor blended over translucent should equal to singleToneColor blended over translucent. --> <declare-styleable name="TonedIcon"> <attr name="backgroundColor" format="integer" /> <attr name="fillColor" format="integer" /> <attr name="singleToneColor" format="integer" /> + <attr name="homeHandleColor" format="integer" /> </declare-styleable> <declare-styleable name="StatusBarWindowView_Layout"> diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index e7a1a660abc2..61816f60d0ba 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -116,6 +116,9 @@ <!-- The color of the navigation bar icons. Need to be in sync with ic_sysbar_* --> <color name="navigation_bar_icon_color">#E5FFFFFF</color> + <color name="navigation_bar_home_handle_light_color">#EBffffff</color> + <color name="navigation_bar_home_handle_dark_color">#99000000</color> + <!-- The shadow color for light navigation bar icons. --> <color name="nav_key_button_shadow_color">#30000000</color> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 2f1770a39f02..6374191c4d7b 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -366,11 +366,13 @@ <item name="backgroundColor">@color/light_mode_icon_color_dual_tone_background</item> <item name="fillColor">@color/light_mode_icon_color_dual_tone_fill</item> <item name="singleToneColor">@color/light_mode_icon_color_single_tone</item> + <item name="homeHandleColor">@color/navigation_bar_home_handle_light_color</item> </style> <style name="DualToneDarkTheme"> <item name="backgroundColor">@color/dark_mode_icon_color_dual_tone_background</item> <item name="fillColor">@color/dark_mode_icon_color_dual_tone_fill</item> <item name="singleToneColor">@color/dark_mode_icon_color_single_tone</item> + <item name="homeHandleColor">@color/navigation_bar_home_handle_dark_color</item> </style> <style name="QSHeaderDarkTheme"> <item name="backgroundColor">@color/dark_mode_qs_icon_color_dual_tone_background</item> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskKeyLruCache.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskKeyLruCache.java index b2c79a4c0a32..e106c657109d 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskKeyLruCache.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/TaskKeyLruCache.java @@ -52,7 +52,11 @@ public class TaskKeyLruCache<V> extends TaskKeyCache<V> { if (mEvictionCallback != null) { mEvictionCallback.onEntryEvicted(mKeys.get(taskId)); } - mKeys.remove(taskId); + + // Only remove from mKeys on cache remove, not a cache update. + if (newV == null) { + mKeys.remove(taskId); + } } }; } 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..c732584eddfc 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,18 +80,25 @@ 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; + // The notification panel is expanded and interactive (either locked or unlocked), and the + // quick settings is not expanded + public static final int SYSUI_STATE_QUICK_SETTINGS_EXPANDED = 1 << 11; @Retention(RetentionPolicy.SOURCE) @IntDef({SYSUI_STATE_SCREEN_PINNING, SYSUI_STATE_NAV_BAR_HIDDEN, SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED, + SYSUI_STATE_QUICK_SETTINGS_EXPANDED, SYSUI_STATE_BOUNCER_SHOWING, SYSUI_STATE_A11Y_BUTTON_CLICKABLE, SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE, 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,8 +107,10 @@ 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_QUICK_SETTINGS_EXPANDED) != 0 ? "qs_visible" : ""); str.add((flags & SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING) != 0 ? "keygrd_visible" : ""); str.add((flags & SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED) != 0 ? "keygrd_occluded" : ""); @@ -150,10 +159,13 @@ public class QuickStepContract { * disabled. */ public static boolean isAssistantGestureDisabled(int sysuiStateFlags) { - // Disable when in screen pinning, immersive, the bouncer is showing + // Disable when in quick settings, 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 + | SYSUI_STATE_QUICK_SETTINGS_EXPANDED; 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/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java index 4ad262fee520..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; diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index fd618b05f348..4e7b15715243 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -1783,13 +1783,15 @@ 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, user has enrolled and enabled auth via setting. - * Not considering encryption or lock down state. */ - public boolean isUnlockWithFacePossible(int userId) { + public boolean isFaceAuthEnabledForUser(int userId) { return mFaceManager != null && mFaceManager.isHardwareDetected() - && !isFaceDisabled(userId) && mFaceManager.hasEnrolledTemplates(userId) && mFaceSettingEnabledForUser.get(userId); } diff --git a/packages/SystemUI/src/com/android/systemui/CornerHandleView.java b/packages/SystemUI/src/com/android/systemui/CornerHandleView.java index 54a36355decb..a94952c5bc19 100644 --- a/packages/SystemUI/src/com/android/systemui/CornerHandleView.java +++ b/packages/SystemUI/src/com/android/systemui/CornerHandleView.java @@ -107,7 +107,9 @@ public class CornerHandleView extends View { mPaint.setColor((int) ArgbEvaluator.getInstance().evaluate(darkIntensity, mLightColor, mDarkColor)); - invalidate(); + if (getVisibility() == VISIBLE) { + invalidate(); + } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/DumpController.kt b/packages/SystemUI/src/com/android/systemui/DumpController.kt index 646abb5c2c5a..65f1abd863c7 100644 --- a/packages/SystemUI/src/com/android/systemui/DumpController.kt +++ b/packages/SystemUI/src/com/android/systemui/DumpController.kt @@ -16,6 +16,7 @@ package com.android.systemui +import android.util.ArraySet import android.util.Log import androidx.annotation.GuardedBy import com.android.internal.util.Preconditions @@ -39,38 +40,39 @@ class DumpController @Inject constructor() : Dumpable { } @GuardedBy("listeners") - private val listeners = mutableListOf<WeakReference<Dumpable>>() + private val listeners = mutableListOf<RegisteredDumpable>() val numListeners: Int get() = listeners.size /** - * Adds a [Dumpable] listener to be dumped. It will only be added if it is not already tracked. + * Adds a [Dumpable] dumpable to be dumped. * - * @param listener the [Dumpable] to be added + * @param tag a string tag to associate with this dumpable. Tags must be globally unique; this + * method will throw if the same tag has already been registered. Tags can be used to + * filter output when debugging. + * @param dumpable the [Dumpable] to be added */ - fun addListener(listener: Dumpable) { - Preconditions.checkNotNull(listener, "The listener to be added cannot be null") - if (DEBUG) Log.v(TAG, "*** register callback for $listener") + fun registerDumpable(tag: String, dumpable: Dumpable) { + Preconditions.checkNotNull(dumpable, "The dumpable to be added cannot be null") + if (DEBUG) Log.v(TAG, "*** register callback for $dumpable") synchronized<Unit>(listeners) { - if (listeners.any { it.get() == listener }) { - if (DEBUG) { - Log.e(TAG, "Object tried to add another callback") - } + if (listeners.any { it.tag == tag }) { + throw IllegalArgumentException("Duplicate dumpable tag registered: $tag") } else { - listeners.add(WeakReference(listener)) + listeners.add(RegisteredDumpable(tag, WeakReference(dumpable))) } } } /** - * Removes a listener from the list of elements to be dumped. + * Removes a dumpable from the list of elements to be dumped. * - * @param listener the [Dumpable] to be removed. + * @param dumpable the [Dumpable] to be removed. */ - fun removeListener(listener: Dumpable) { - if (DEBUG) Log.v(TAG, "*** unregister callback for $listener") + fun unregisterDumpable(dumpable: Dumpable) { + if (DEBUG) Log.v(TAG, "*** unregister callback for $dumpable") synchronized(listeners) { - listeners.removeAll { it.get() == listener || it.get() == null } + listeners.removeAll { it.dumpable.get() == dumpable || it.dumpable.get() == null } } } @@ -79,8 +81,22 @@ class DumpController @Inject constructor() : Dumpable { */ override fun dump(fd: FileDescriptor?, pw: PrintWriter, args: Array<String>?) { pw.println("DumpController state:") + + val filter = if (args != null && args.size >= 3 && + args[0] == "dependency" && args[1] == "DumpController") { + ArraySet(args[2].split(',').map { it.toLowerCase() }) + } else { + null + } + synchronized(listeners) { - listeners.forEach { it.get()?.dump(fd, pw, args) } + listeners.forEach { + if (filter == null || filter.contains(it.tag.toLowerCase())) { + it.dumpable.get()?.dump(fd, pw, args) + } + } } } + + data class RegisteredDumpable(val tag: String, val dumpable: WeakReference<Dumpable>) } diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java index c9d4957494e4..bd91333100bd 100644 --- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java +++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java @@ -47,7 +47,7 @@ public class ImageWallpaper extends WallpaperService { // This is to avoid destroying then recreating render context in a very short time. private static final int DELAY_FINISH_RENDERING = 1000; private static final int INTERVAL_WAIT_FOR_RENDERING = 100; - private static final int PATIENCE_WAIT_FOR_RENDERING = 5; + private static final int PATIENCE_WAIT_FOR_RENDERING = 10; private HandlerThread mWorker; @Override @@ -124,10 +124,10 @@ public class ImageWallpaper extends WallpaperService { @Override public void onAmbientModeChanged(boolean inAmbientMode, long animationDuration) { - long duration = mNeedTransition || animationDuration != 0 ? animationDuration : 0; + if (!mNeedTransition) return; mWorker.getThreadHandler().post( - () -> mRenderer.updateAmbientMode(inAmbientMode, duration)); - if (inAmbientMode && duration == 0) { + () -> mRenderer.updateAmbientMode(inAmbientMode, animationDuration)); + if (inAmbientMode && animationDuration == 0) { // This means that we are transiting from home to aod, to avoid // race condition between window visibility and transition, // we don't return until the transition is finished. See b/136643341. diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java index 002d4f34be11..75956545432e 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleBehaviorController.java @@ -127,7 +127,7 @@ public final class AssistHandleBehaviorController implements AssistHandleCallbac SystemUiDeviceConfigFlags.ASSIST_HANDLES_BEHAVIOR_MODE, null)); } }); - Dependency.get(DumpController.class).addListener(this); + Dependency.get(DumpController.class).registerDumpable(TAG, this); } @Override // AssistHandleCallbacks 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/brightline/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java index cee01a4d3048..ce82bbfb1512 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java @@ -50,6 +50,8 @@ public class BrightLineFalsingManager implements FalsingManager { private boolean mSessionStarted; private MetricsLogger mMetricsLogger; private int mIsFalseTouchCalls; + private boolean mShowingAod; + private boolean mScreenOn; private final ExecutorService mBackgroundExecutor = Executors.newSingleThreadExecutor(); @@ -105,10 +107,12 @@ public class BrightLineFalsingManager implements FalsingManager { } private void sessionStart() { - logDebug("Starting Session"); - mSessionStarted = true; - registerSensors(); - mClassifiers.forEach(FalsingClassifier::onSessionStarted); + if (!mSessionStarted && !mShowingAod && mScreenOn) { + logDebug("Starting Session"); + mSessionStarted = true; + registerSensors(); + mClassifiers.forEach(FalsingClassifier::onSessionStarted); + } } private void sessionEnd() { @@ -172,6 +176,7 @@ public class BrightLineFalsingManager implements FalsingManager { mMetricsLogger.histogram(FALSING_SUCCESS, mIsFalseTouchCalls); mIsFalseTouchCalls = 0; } + sessionEnd(); } @Override @@ -180,6 +185,7 @@ public class BrightLineFalsingManager implements FalsingManager { @Override public void setShowingAod(boolean showingAod) { + mShowingAod = showingAod; if (showingAod) { sessionEnd(); } else { @@ -264,7 +270,7 @@ public class BrightLineFalsingManager implements FalsingManager { @Override public void onScreenOnFromTouch() { - sessionStart(); + onScreenTurningOn(); } @Override @@ -286,11 +292,13 @@ public class BrightLineFalsingManager implements FalsingManager { @Override public void onScreenTurningOn() { + mScreenOn = true; sessionStart(); } @Override public void onScreenOff() { + mScreenOn = false; sessionEnd(); } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java index ae6dac59b2f5..07dd2cd77043 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java @@ -77,8 +77,10 @@ public interface DozeHost { interface Callback { /** * Called when a high priority notification is added. + * @param onPulseSuppressedListener A listener that is invoked if the pulse is being + * supressed. */ - default void onNotificationAlerted() {} + default void onNotificationAlerted(Runnable onPulseSuppressedListener) {} /** * Called when battery state or power save mode changes. diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java index 8ef01e8d608e..2ca85c074a89 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java @@ -106,22 +106,31 @@ public class DozeTriggers implements DozeMachine.Part { mDockManager = dockManager; } - private void onNotification() { + private void onNotification(Runnable onPulseSuppressedListener) { if (DozeMachine.DEBUG) { Log.d(TAG, "requestNotificationPulse"); } if (!sWakeDisplaySensorState) { Log.d(TAG, "Wake display false. Pulse denied."); + runIfNotNull(onPulseSuppressedListener); return; } mNotificationPulseTime = SystemClock.elapsedRealtime(); if (!mConfig.pulseOnNotificationEnabled(UserHandle.USER_CURRENT)) { + runIfNotNull(onPulseSuppressedListener); return; } - requestPulse(DozeLog.PULSE_REASON_NOTIFICATION, false /* performedProxCheck */); + requestPulse(DozeLog.PULSE_REASON_NOTIFICATION, false /* performedProxCheck */, + onPulseSuppressedListener); DozeLog.traceNotificationPulse(mContext); } + private static void runIfNotNull(Runnable runnable) { + if (runnable != null) { + runnable.run(); + } + } + private void proximityCheckThenCall(IntConsumer callback, boolean alreadyPerformedProxCheck, int reason) { @@ -158,10 +167,11 @@ public class DozeTriggers implements DozeMachine.Part { if (isWakeDisplay) { onWakeScreen(wakeEvent, mMachine.isExecutingTransition() ? null : mMachine.getState()); } else if (isLongPress) { - requestPulse(pulseReason, sensorPerformedProxCheck); + requestPulse(pulseReason, sensorPerformedProxCheck, null /* onPulseSupressedListener */); } else if (isWakeLockScreen) { if (wakeEvent) { - requestPulse(pulseReason, sensorPerformedProxCheck); + requestPulse(pulseReason, sensorPerformedProxCheck, + null /* onPulseSupressedListener */); } } else { proximityCheckThenCall((result) -> { @@ -340,7 +350,8 @@ public class DozeTriggers implements DozeMachine.Part { } } - private void requestPulse(final int reason, boolean performedProxCheck) { + private void requestPulse(final int reason, boolean performedProxCheck, + Runnable onPulseSuppressedListener) { Assert.isMainThread(); mDozeHost.extendPulse(reason); @@ -357,6 +368,7 @@ public class DozeTriggers implements DozeMachine.Part { DozeLog.tracePulseDropped(mContext, mPulsePending, mMachine.getState(), mDozeHost.isPulsingBlocked()); } + runIfNotNull(onPulseSuppressedListener); return; } @@ -365,6 +377,7 @@ public class DozeTriggers implements DozeMachine.Part { if (result == ProximityCheck.RESULT_NEAR) { // in pocket, abort pulse mPulsePending = false; + runIfNotNull(onPulseSuppressedListener); } else { // not in pocket, continue pulsing continuePulseRequest(reason); @@ -482,7 +495,8 @@ public class DozeTriggers implements DozeMachine.Part { public void onReceive(Context context, Intent intent) { if (PULSE_ACTION.equals(intent.getAction())) { if (DozeMachine.DEBUG) Log.d(TAG, "Received pulse intent"); - requestPulse(DozeLog.PULSE_REASON_INTENT, false /* performedProxCheck */); + requestPulse(DozeLog.PULSE_REASON_INTENT, false, /* performedProxCheck */ + null /* onPulseSupressedListener */); } if (UiModeManager.ACTION_ENTER_CAR_MODE.equals(intent.getAction())) { mMachine.requestState(DozeMachine.State.FINISH); @@ -532,8 +546,8 @@ public class DozeTriggers implements DozeMachine.Part { private DozeHost.Callback mHostCallback = new DozeHost.Callback() { @Override - public void onNotificationAlerted() { - onNotification(); + public void onNotificationAlerted(Runnable onPulseSuppressedListener) { + onNotification(onPulseSuppressedListener); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java index 45e97b38d87e..b154e66a846e 100644 --- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java +++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java @@ -65,7 +65,7 @@ class ImageRevealHelper { @Override public void onAnimationStart(Animator animation) { if (mRevealListener != null) { - mRevealListener.onRevealStart(); + mRevealListener.onRevealStart(true /* animate */); } } }); @@ -73,7 +73,7 @@ class ImageRevealHelper { private void animate() { mAnimator.cancel(); - mAnimator.setFloatValues(mReveal, !mAwake ? MIN_REVEAL : MAX_REVEAL); + mAnimator.setFloatValues(mReveal, mAwake ? MAX_REVEAL : MIN_REVEAL); mAnimator.start(); } @@ -84,12 +84,11 @@ class ImageRevealHelper { void updateAwake(boolean awake, long duration) { mAwake = awake; mAnimator.setDuration(duration); - if (!mAwake && duration == 0) { - // We are transiting from home to aod, - // since main thread is waiting for rendering finished, we only need draw - // the last state directly, which is a black screen. - mReveal = MIN_REVEAL; - mRevealListener.onRevealStart(); + if (duration == 0) { + // We are transiting from home to aod or aod to home directly, + // we don't need to do transition in these cases. + mReveal = mAwake ? MAX_REVEAL : MIN_REVEAL; + mRevealListener.onRevealStart(false /* animate */); mRevealListener.onRevealStateChanged(); mRevealListener.onRevealEnd(); } else { @@ -110,7 +109,7 @@ class ImageRevealHelper { /** * Called back while reveal starts. */ - void onRevealStart(); + void onRevealStart(boolean animate); /** * Called back while reveal ends. diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java index 93d8dd6146a6..7b22a49fc88a 100644 --- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java +++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java @@ -24,6 +24,7 @@ import static android.opengl.GLES20.glViewport; import android.app.WallpaperManager; import android.content.Context; +import android.content.res.Configuration; import android.graphics.Bitmap; import android.graphics.Rect; import android.util.Log; @@ -70,7 +71,14 @@ public class ImageWallpaperRenderer implements GLWallpaperRenderer, DisplayInfo displayInfo = new DisplayInfo(); WindowManager wm = context.getSystemService(WindowManager.class); wm.getDefaultDisplay().getDisplayInfo(displayInfo); - mScissor = new Rect(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight); + + // We only do transition in portrait currently, b/137962047. + int orientation = context.getResources().getConfiguration().orientation; + if (orientation == Configuration.ORIENTATION_PORTRAIT) { + mScissor = new Rect(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight); + } else { + mScissor = new Rect(0, 0, displayInfo.logicalHeight, displayInfo.logicalWidth); + } mProxy = proxy; mProgram = new ImageGLProgram(context); @@ -179,20 +187,24 @@ public class ImageWallpaperRenderer implements GLWallpaperRenderer, } @Override - public void onRevealStart() { - mScissorMode = true; - // Use current display area of texture. - mWallpaper.adjustTextureCoordinates(mSurfaceSize, mScissor, mXOffset, mYOffset); + public void onRevealStart(boolean animate) { + if (animate) { + mScissorMode = true; + // Use current display area of texture. + mWallpaper.adjustTextureCoordinates(mSurfaceSize, mScissor, mXOffset, mYOffset); + } mProxy.preRender(); } @Override public void onRevealEnd() { - mScissorMode = false; - // reset texture coordinates to use full texture. - mWallpaper.adjustTextureCoordinates(null, null, 0, 0); - // We need draw full texture back before finishing render. - mProxy.requestRender(); + if (mScissorMode) { + mScissorMode = false; + // reset texture coordinates to use full texture. + mWallpaper.adjustTextureCoordinates(null, null, 0, 0); + // We need draw full texture back before finishing render. + mProxy.requestRender(); + } mProxy.postRender(); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index 89aa96db7a92..ae83567ed159 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -187,7 +187,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne if (mBrightnessMirrorController != null) { mBrightnessMirrorController.addCallback(this); } - if (mDumpController != null) mDumpController.addListener(this); + if (mDumpController != null) mDumpController.registerDumpable(getDumpableTag(), this); } @Override @@ -202,10 +202,14 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne if (mBrightnessMirrorController != null) { mBrightnessMirrorController.removeCallback(this); } - if (mDumpController != null) mDumpController.removeListener(this); + if (mDumpController != null) mDumpController.unregisterDumpable(this); super.onDetachedFromWindow(); } + protected String getDumpableTag() { + return TAG; + } + @Override public void onTilesChanged() { setTiles(mHost.getTiles()); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java index fed59a526bab..b27bf32b9382 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java @@ -109,7 +109,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D defaultFactory.setHost(this); mQsFactories.add(defaultFactory); pluginManager.addPluginListener(this, QSFactory.class, true); - mDumpController.addListener(this); + mDumpController.registerDumpable(TAG, this); mainHandler.post(() -> { // This is technically a hack to avoid circular dependency of @@ -131,7 +131,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D mTunerService.removeTunable(this); mServices.destroy(); mPluginManager.removePluginListener(this); - mDumpController.removeListener(this); + mDumpController.unregisterDumpable(this); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java index 73f6fc5ea2f4..f1e9c8714776 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java @@ -96,6 +96,11 @@ public class QuickQSPanel extends QSPanel { Dependency.get(TunerService.class).removeTunable(mNumTiles); } + @Override + protected String getDumpableTag() { + return TAG; + } + public void setQSPanelAndHeader(QSPanel fullPanel, View header) { mFullPanel = fullPanel; } 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/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java index 4ccd0cd3353b..99682fcfccbf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java @@ -374,6 +374,10 @@ public class NotificationShelf extends ActivatableNotificationView implements clipTransientViews(); setClipTopAmount(clipTopAmount); + boolean isHidden = getViewState().hidden || clipTopAmount >= getIntrinsicHeight(); + if (mShowNotificationShelf) { + setVisibility(isHidden ? View.INVISIBLE : View.VISIBLE); + } setBackgroundTop(backgroundTop); setFirstElementRoundness(firstElementRoundness); mShelfIcons.setSpeedBumpIndex(mAmbientState.getSpeedBumpIndex()); 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/ActivityLaunchAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java index 0d9f4e7b909d..91d47077fc31 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java @@ -153,6 +153,7 @@ public class ActivityLaunchAnimator { if (primary == null) { setAnimationPending(false); invokeCallback(iRemoteAnimationFinishedCallback); + mNotificationPanel.collapse(false /* delayed */, 1.0f /* speedUpFactor */); return; } 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 f7e5bcf4769c..2f67f90a115e 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") { @@ -98,7 +100,9 @@ class NotificationWakeUpCoordinator @Inject constructor( } } + private var collapsedEnoughToHide: Boolean = false lateinit var iconAreaController : NotificationIconAreaController + var pulsing: Boolean = false set(value) { field = value @@ -120,7 +124,6 @@ class NotificationWakeUpCoordinator @Inject constructor( } } } - /** * True if we can show pulsing heads up notifications */ @@ -132,6 +135,10 @@ class NotificationWakeUpCoordinator @Inject constructor( // We also allow pulsing on the lock screen! 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 } @@ -140,6 +147,17 @@ class NotificationWakeUpCoordinator @Inject constructor( mHeadsUpManagerPhone.addListener(this) statusBarStateController.addCallback(this) mDozeParameters = DozeParameters.getInstance(mContext) + addListener(object : WakeUpListener { + override fun onFullyHiddenChanged(isFullyHidden: Boolean) { + if (isFullyHidden && mNotificationsVisibleForExpansion) { + // When the notification becomes fully invisible, let's make sure our expansion + // flag also changes. This can happen if the bouncer shows when dragging down + // and then the screen turning off, where we don't reset this state. + setNotificationsVisibleForExpansion(visible = false, animate = false, + increaseSpeed = false) + } + } + }); } fun setStackScroller(stackScroller: NotificationStackScrollLayout) { @@ -247,6 +265,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; 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..fe8854168288 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,9 +173,13 @@ 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; + private boolean mPulseSupressed; public NotificationEntry(StatusBarNotification n) { this(n, null); @@ -224,6 +228,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; } @@ -900,6 +916,14 @@ public final class NotificationEntry { mOnSensitiveChangedListener = listener; } + public boolean isPulseSuppressed() { + return mPulseSupressed; + } + + public void setPulseSuppressed(boolean suppressed) { + mPulseSupressed = suppressed; + } + /** Information about a suggestion that is being edited. */ public static class EditedSuggestionInfo { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt index 8e6822770694..782aad17345e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt @@ -23,6 +23,7 @@ import android.app.NotificationChannel.DEFAULT_CHANNEL_ID import android.app.NotificationChannelGroup import android.app.NotificationManager.IMPORTANCE_NONE import android.content.Context +import android.content.DialogInterface import android.graphics.Color import android.graphics.PixelFormat import android.graphics.drawable.Drawable @@ -171,7 +172,6 @@ class ChannelEditorDialogController @Inject constructor( private fun done() { resetState() dialog.dismiss() - onFinishListener?.onChannelEditorDialogFinished() } private fun resetState() { @@ -261,6 +261,11 @@ class ChannelEditorDialogController @Inject constructor( dialog.apply { setContentView(R.layout.notif_half_shelf) setCanceledOnTouchOutside(true) + setOnDismissListener(object : DialogInterface.OnDismissListener { + override fun onDismiss(dialog: DialogInterface?) { + onFinishListener?.onChannelEditorDialogFinished() + } + }) findViewById<ChannelEditorListView>(R.id.half_shelf_container).apply { controller = this@ChannelEditorDialogController appIcon = this@ChannelEditorDialogController.appIcon 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/row/wrapper/NotificationMediaTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java index 20e8b733ce6a..38f9bff2a63f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java @@ -39,6 +39,7 @@ import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.internal.widget.MediaNotificationView; import com.android.systemui.Dependency; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.TransformableView; @@ -67,6 +68,7 @@ public class NotificationMediaTemplateViewWrapper extends NotificationTemplateVi private View mSeekBarView; private Context mContext; private MetricsLogger mMetricsLogger; + private boolean mIsViewVisible; @VisibleForTesting protected SeekBar.OnSeekBarChangeListener mSeekListener = @@ -88,11 +90,33 @@ public class NotificationMediaTemplateViewWrapper extends NotificationTemplateVi } }; + MediaNotificationView.VisibilityChangeListener mVisibilityListener = + new MediaNotificationView.VisibilityChangeListener() { + @Override + public void onAggregatedVisibilityChanged(boolean isVisible) { + mIsViewVisible = isVisible; + if (isVisible) { + // Restart timer if we're currently playing and didn't already have one going + PlaybackState state = mMediaController.getPlaybackState(); + if (state != null && state.getState() == PlaybackState.STATE_PLAYING + && mSeekBarTimer == null && mSeekBarView != null + && mSeekBarView.getVisibility() != View.GONE) { + startTimer(); + } + } else { + clearTimer(); + } + } + }; + private MediaController.Callback mMediaCallback = new MediaController.Callback() { @Override public void onSessionDestroyed() { clearTimer(); mMediaController.unregisterCallback(this); + if (mView instanceof MediaNotificationView) { + ((MediaNotificationView) mView).removeVisibilityListener(mVisibilityListener); + } } @Override @@ -126,10 +150,16 @@ public class NotificationMediaTemplateViewWrapper extends NotificationTemplateVi mContext = ctx; mMediaManager = Dependency.get(NotificationMediaManager.class); mMetricsLogger = Dependency.get(MetricsLogger.class); + + if (mView instanceof MediaNotificationView) { + MediaNotificationView mediaView = (MediaNotificationView) mView; + mediaView.addVisibilityListener(mVisibilityListener); + } } private void resolveViews() { mActions = mView.findViewById(com.android.internal.R.id.media_actions); + mIsViewVisible = mView.isShown(); final MediaSession.Token token = mRow.getEntry().notification.getNotification().extras .getParcelable(Notification.EXTRA_MEDIA_SESSION); @@ -208,18 +238,19 @@ public class NotificationMediaTemplateViewWrapper extends NotificationTemplateVi private void startTimer() { clearTimer(); - mSeekBarTimer = new Timer(true /* isDaemon */); - mSeekBarTimer.schedule(new TimerTask() { - @Override - public void run() { - mHandler.post(mOnUpdateTimerTick); - } - }, 0, PROGRESS_UPDATE_INTERVAL); + if (mIsViewVisible) { + mSeekBarTimer = new Timer(true /* isDaemon */); + mSeekBarTimer.schedule(new TimerTask() { + @Override + public void run() { + mHandler.post(mOnUpdateTimerTick); + } + }, 0, PROGRESS_UPDATE_INTERVAL); + } } private void clearTimer() { if (mSeekBarTimer != null) { - // TODO: also trigger this when the notification panel is collapsed mSeekBarTimer.cancel(); mSeekBarTimer.purge(); mSeekBarTimer = null; 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 04cbb130c243..1a418665dd2e 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 @@ -411,6 +411,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd outline.setRoundRect(mBackgroundAnimationRect, MathUtils.lerp(mCornerRadius / 2.0f, mCornerRadius, xProgress)); + outline.setAlpha(1.0f - mAmbientState.getHideAmount()); } else { ViewOutlineProvider.BACKGROUND.getOutline(view, outline); } @@ -499,6 +500,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd private boolean mAnimateBottomOnLayout; private float mLastSentAppear; private float mLastSentExpandedHeight; + private boolean mWillExpand; @Inject public NotificationStackScrollLayout( @@ -1043,6 +1045,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd requestChildrenUpdate(); updateFirstAndLastBackgroundViews(); updateAlgorithmLayoutMinHeight(); + updateOwnTranslationZ(); } @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) @@ -4406,6 +4409,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd mStateAnimator.setShadeExpanded(isExpanded); mSwipeHelper.setIsExpanded(isExpanded); if (changed) { + mWillExpand = false; if (!mIsExpanded) { mGroupManager.collapseAllGroups(); mExpandHelper.cancelImmediately(); @@ -4774,6 +4778,20 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd updateAlgorithmHeightAndPadding(); updateBackgroundDimming(); requestChildrenUpdate(); + updateOwnTranslationZ(); + } + + private void updateOwnTranslationZ() { + // Since we are clipping to the outline we need to make sure that the shadows aren't + // clipped when pulsing + float ownTranslationZ = 0; + if (mKeyguardBypassController.getBypassEnabled() && mAmbientState.isHiddenAtAll()) { + ExpandableView firstChildNotGone = getFirstChildNotGone(); + if (firstChildNotGone != null && firstChildNotGone.showingPulsing()) { + ownTranslationZ = firstChildNotGone.getTranslationZ(); + } + } + setTranslationZ(ownTranslationZ); } private void updateVisibility() { @@ -5054,7 +5072,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(); @@ -5075,6 +5093,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; @@ -5675,6 +5698,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) { @@ -5745,7 +5770,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; } @@ -5803,9 +5828,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/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java index 7655056ea60b..ef8048487022 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 @@ -161,6 +161,7 @@ public class StackScrollAlgorithm { : 0; float clipStart = 0; int childCount = algorithmState.visibleChildren.size(); + boolean firstHeadsUp = true; for (int i = 0; i < childCount; i++) { ExpandableView child = algorithmState.visibleChildren.get(i); ExpandableViewState state = child.getViewState(); @@ -173,7 +174,7 @@ public class StackScrollAlgorithm { boolean isHeadsUp = (child instanceof ExpandableNotificationRow) && ((ExpandableNotificationRow) child).isPinned(); if (mClipNotificationScrollToTop - && (!state.inShelf || isHeadsUp) + && (!state.inShelf || (isHeadsUp && !firstHeadsUp)) && newYTranslation < clipStart) { // The previous view is overlapping on top, clip! float overlapAmount = clipStart - newYTranslation; @@ -181,7 +182,9 @@ public class StackScrollAlgorithm { } else { state.clipTopAmount = 0; } - + if (isHeadsUp) { + firstHeadsUp = false; + } if (!child.isTransparent()) { // Only update the previous values if we are not transparent, // otherwise we would clip to a transparent view. 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 94cd2cdaa9d3..41c6a7ba7848 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java @@ -445,14 +445,13 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback { if (!mUpdateMonitor.isDeviceInteractive()) { if (!mStatusBarKeyguardViewManager.isShowing()) { return bypass ? MODE_WAKE_AND_UNLOCK : MODE_ONLY_WAKE; - } else if (mDozeScrimController.isPulsing() && unlockingAllowed) { + } else if (!unlockingAllowed) { + return bypass ? MODE_SHOW_BOUNCER : MODE_NONE; + } else if (mDozeScrimController.isPulsing()) { // Let's not wake-up to lock screen when not bypassing, otherwise the notification // would move as the user tried to tap it. return bypass ? MODE_WAKE_AND_UNLOCK_PULSING : MODE_NONE; } else { - if (!(mDozeScrimController.isPulsing() && !unlockingAllowed)) { - Log.wtf(TAG, "Face somehow arrived when the device was not interactive"); - } if (bypass) { // Wake-up fading out nicely return MODE_WAKE_AND_UNLOCK_PULSING; @@ -530,7 +529,8 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback { mStatusBar.notifyBiometricAuthModeChanged(); } - private final WakefulnessLifecycle.Observer mWakefulnessObserver = + @VisibleForTesting + final WakefulnessLifecycle.Observer mWakefulnessObserver = new WakefulnessLifecycle.Observer() { @Override public void onFinishedWakingUp() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java index 539bc7bcb3f6..d22ad71594b9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.phone; import static com.android.systemui.Interpolators.ALPHA_IN; import static com.android.systemui.Interpolators.ALPHA_OUT; +import static com.android.systemui.Interpolators.LINEAR; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -33,7 +34,7 @@ import java.util.ArrayList; */ public class ButtonDispatcher { private final static int FADE_DURATION_IN = 150; - private final static int FADE_DURATION_OUT = 100; + private final static int FADE_DURATION_OUT = 1000; private final ArrayList<View> mViews = new ArrayList<>(); @@ -178,7 +179,7 @@ public class ButtonDispatcher { setVisibility(View.VISIBLE); mFadeAnimator = ValueAnimator.ofFloat(getAlpha(), alpha); mFadeAnimator.setDuration(duration); - mFadeAnimator.setInterpolator(getAlpha() < alpha ? ALPHA_IN : ALPHA_OUT); + mFadeAnimator.setInterpolator(LINEAR); mFadeAnimator.addListener(mFadeListener); mFadeAnimator.addUpdateListener(mAlphaListener); mFadeAnimator.start(); 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 b13af8bea784..06a2225ed0bf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java @@ -323,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; } } @@ -349,10 +359,9 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange .setDuration(233) .start(); } + return true; } - updateClickability(); - - return true; + return false; } private boolean canBlockUpdates() { @@ -440,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..081e29381a65 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -21,7 +21,9 @@ import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED; 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_QUICK_SETTINGS_EXPANDED; 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; @@ -706,7 +708,7 @@ public class NavigationBarView extends FrameLayout implements } } - public void onPanelExpandedChange() { + public void onStatusBarPanelStateChanged() { updateSlippery(); updateSystemUiStateFlags(); } @@ -719,9 +721,13 @@ 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); + mOverviewProxyService.setSystemUiStateFlag(SYSUI_STATE_QUICK_SETTINGS_EXPANDED, + mPanelView.isInSettings(), displayId); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationHandle.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationHandle.java index 0fe12943614c..4f7af58094ec 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationHandle.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationHandle.java @@ -53,8 +53,8 @@ public class NavigationHandle extends View implements ButtonInterface { final int dualToneLightTheme = Utils.getThemeAttr(context, R.attr.lightIconTheme); Context lightContext = new ContextThemeWrapper(context, dualToneLightTheme); Context darkContext = new ContextThemeWrapper(context, dualToneDarkTheme); - mLightColor = Utils.getColorAttrDefaultColor(lightContext, R.attr.singleToneColor); - mDarkColor = Utils.getColorAttrDefaultColor(darkContext, R.attr.singleToneColor); + mLightColor = Utils.getColorAttrDefaultColor(lightContext, R.attr.homeHandleColor); + mDarkColor = Utils.getColorAttrDefaultColor(darkContext, R.attr.homeHandleColor); mPaint.setAntiAlias(true); setFocusable(false); } 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 21de8a59836e..ba3406999388 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java @@ -78,6 +78,7 @@ public class NotificationIconAreaController implements DarkReceiver, private int mAodIconTint; private boolean mFullyHidden; private boolean mAodIconsVisible; + private boolean mIsPulsing; public NotificationIconAreaController(Context context, StatusBar statusBar, StatusBarStateController statusBarStateController, @@ -265,7 +266,9 @@ public class NotificationIconAreaController implements DarkReceiver, if (!showAmbient && entry.shouldSuppressStatusBar()) { return false; } - if (hidePulsing && entry.showingPulsing()) { + if (hidePulsing && entry.showingPulsing() + && (!mWakeUpCoordinator.getNotificationsFullyHidden() + || !entry.isPulseSuppressed())) { return false; } return true; 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 901afab4a6f7..d0626ed780e2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -40,6 +40,7 @@ import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.Region; import android.os.PowerManager; +import android.os.SystemClock; import android.util.AttributeSet; import android.util.Log; import android.util.MathUtils; @@ -139,6 +140,12 @@ public class NotificationPanelView extends PanelView implements private static final int CAP_HEIGHT = 1456; private static final int FONT_HEIGHT = 2163; + /** + * Maximum time before which we will expand the panel even for slow motions when getting a + * touch passed over from launcher. + */ + private static final int MAX_TIME_TO_OPEN_WHEN_FLINGING_FROM_LAUNCHER = 300; + static final String COUNTER_PANEL_OPEN = "panel_open"; static final String COUNTER_PANEL_OPEN_QS = "panel_open_qs"; private static final String COUNTER_PANEL_OPEN_PEEK = "panel_open_peek"; @@ -374,6 +381,8 @@ public class NotificationPanelView extends PanelView implements private boolean mHeadsUpPinnedMode; private float mKeyguardHeadsUpShowingAmount = 0.0f; private boolean mShowingKeyguardHeadsUp; + private boolean mAllowExpandForSmallExpansion; + private Runnable mExpandAfterLayoutRunnable; @Inject public NotificationPanelView(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs, @@ -671,6 +680,10 @@ public class NotificationPanelView extends PanelView implements } updateMaxHeadsUpTranslation(); updateGestureExclusionRect(); + if (mExpandAfterLayoutRunnable != null) { + mExpandAfterLayoutRunnable.run(); + mExpandAfterLayoutRunnable = null; + } } private void updateGestureExclusionRect() { @@ -808,8 +821,7 @@ public class NotificationPanelView extends PanelView implements if (suppressedSummary) { continue; } - if (!mLockscreenUserManager.shouldShowOnKeyguard( - row.getStatusBarNotification())) { + if (!mLockscreenUserManager.shouldShowOnKeyguard(row.getEntry())) { continue; } if (row.isRemoved()) { @@ -1070,6 +1082,8 @@ public class NotificationPanelView extends PanelView implements mDownY = event.getY(); mCollapsedOnDown = isFullyCollapsed(); mListenForHeadsUp = mCollapsedOnDown && mHeadsUpManager.hasPinnedHeadsUp(); + mAllowExpandForSmallExpansion = mExpectingSynthesizedDown; + mTouchSlopExceededBeforeDown = mExpectingSynthesizedDown; if (mExpectingSynthesizedDown) { mLastEventSynthesizedDown = true; } else { @@ -1128,6 +1142,20 @@ public class NotificationPanelView extends PanelView implements } @Override + protected boolean shouldExpandWhenNotFlinging() { + if (super.shouldExpandWhenNotFlinging()) { + return true; + } + if (mAllowExpandForSmallExpansion) { + // When we get a touch that came over from launcher, the velocity isn't always correct + // Let's err on expanding if the gesture has been reasonably slow + long timeSinceDown = SystemClock.uptimeMillis() - mDownTime; + return timeSinceDown <= MAX_TIME_TO_OPEN_WHEN_FLINGING_FROM_LAUNCHER; + } + return false; + } + + @Override protected float getOpeningHeight() { return mNotificationStackScroller.getOpeningHeight(); } @@ -1283,6 +1311,7 @@ public class NotificationPanelView extends PanelView implements } mExpectingSynthesizedDown = true; onTrackingStarted(); + updatePanelExpanded(); } /** @@ -1299,10 +1328,19 @@ public class NotificationPanelView extends PanelView implements * * @param velocity unit is in px / millis */ - public void stopWaitingForOpenPanelGesture(float velocity) { + public void stopWaitingForOpenPanelGesture(final float velocity) { if (mExpectingSynthesizedDown) { mExpectingSynthesizedDown = false; - fling(velocity > 1f ? 1000f * velocity : 0, true /* animate */); + maybeVibrateOnOpening(); + Runnable runnable = () -> fling(velocity > 1f ? 1000f * velocity : 0, + true /* expand */); + if (mStatusBar.getStatusBarWindow().getHeight() + != mStatusBar.getStatusBarHeight()) { + // The panel is already expanded to its full size, let's expand directly + runnable.run(); + } else { + mExpandAfterLayoutRunnable = runnable; + } onTrackingStopped(false); } } @@ -1565,9 +1603,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 @@ -1610,7 +1654,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(); @@ -1639,7 +1683,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 @@ -1722,8 +1766,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) { @@ -1923,7 +1967,8 @@ public class NotificationPanelView extends PanelView implements * @return Whether we should intercept a gesture to open Quick Settings. */ private boolean shouldQuickSettingsIntercept(float x, float y, float yDiff) { - if (!mQsExpansionEnabled || mCollapsedOnDown) { + if (!mQsExpansionEnabled || mCollapsedOnDown + || (mKeyguardShowing && mKeyguardBypassController.getBypassEnabled())) { return false; } View header = mKeyguardShowing || mQs == null ? mKeyguardStatusBar : mQs.getHeader(); @@ -2034,7 +2079,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); @@ -3379,24 +3424,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..31600e391f34 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; @@ -61,14 +61,15 @@ public abstract class PanelView extends FrameLayout { private static final int INITIAL_OPENING_PEEK_DURATION = 200; private static final int PEEK_ANIMATION_DURATION = 360; private static final int NO_FIXED_DURATION = -1; - private long mDownTime; + protected long mDownTime; + protected boolean mTouchSlopExceededBeforeDown; private float mMinExpandHeight; private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger(); private boolean mPanelUpdateWhenAnimatorEnds; 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)); @@ -322,7 +323,7 @@ public abstract class PanelView extends FrameLayout { if (!mGestureWaitForTouchSlop || (mHeightAnimator != null && !mHintAnimationRunning) || mPeekAnimator != null) { mTouchSlopExceeded = (mHeightAnimator != null && !mHintAnimationRunning) - || mPeekAnimator != null; + || mPeekAnimator != null || mTouchSlopExceededBeforeDown; cancelHeightAnimator(); cancelPeek(); onTrackingStarted(); @@ -408,9 +409,7 @@ public abstract class PanelView extends FrameLayout { runPeekAnimation(INITIAL_OPENING_PEEK_DURATION, getOpeningHeight(), false /* collapseWhenFinished */); notifyBarPanelExpansionChanged(); - if (mVibrateOnOpening) { - mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK); - } + maybeVibrateOnOpening(); //TODO: keyguard opens QS a different way; log that too? @@ -425,6 +424,12 @@ public abstract class PanelView extends FrameLayout { rot); } + protected void maybeVibrateOnOpening() { + if (mVibrateOnOpening) { + mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK); + } + } + protected abstract float getOpeningHeight(); /** @@ -576,7 +581,7 @@ public abstract class PanelView extends FrameLayout { mInitialTouchY = y; mInitialTouchX = x; mTouchStartedInEmptyArea = !isInContentBounds(x, y); - mTouchSlopExceeded = false; + mTouchSlopExceeded = mTouchSlopExceededBeforeDown; mJustPeeked = false; mMotionAborted = false; mPanelClosedOnDown = isFullyCollapsed(); @@ -679,12 +684,16 @@ public abstract class PanelView extends FrameLayout { return true; } if (Math.abs(vectorVel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) { - return getExpandedFraction() > 0.5f; + return shouldExpandWhenNotFlinging(); } else { return vel > 0; } } + protected boolean shouldExpandWhenNotFlinging() { + return getExpandedFraction() > 0.5f; + } + /** * @param x the final x-coordinate when the finger was lifted * @param y the final y-coordinate when the finger was lifted @@ -1173,13 +1182,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/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java index 660810fe7eb9..8efd952e67c5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java @@ -46,7 +46,6 @@ import com.android.systemui.R; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver; import com.android.systemui.statusbar.CommandQueue; -import com.android.systemui.statusbar.policy.HeadsUpManager; import java.util.Objects; @@ -279,7 +278,7 @@ public class PhoneStatusBarView extends PanelBar { super.panelExpansionChanged(frac, expanded); updateScrimFraction(); if ((frac == 0 || frac == 1) && mBar.getNavigationBarView() != null) { - mBar.getNavigationBarView().onPanelExpandedChange(); + mBar.getNavigationBarView().onStatusBarPanelStateChanged(); } } 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 9f1ec4d6894a..f15b60123a80 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -489,9 +489,12 @@ public class StatusBar extends SystemUI implements DemoMode, WallpaperInfo info = wallpaperManager.getWallpaperInfo(UserHandle.USER_CURRENT); final boolean deviceSupportsAodWallpaper = mContext.getResources().getBoolean( com.android.internal.R.bool.config_dozeSupportsAodWallpaper); + final boolean imageWallpaperInAmbient = + !DozeParameters.getInstance(mContext).getDisplayNeedsBlanking(); // If WallpaperInfo is null, it must be ImageWallpaper. final boolean supportsAmbientMode = deviceSupportsAodWallpaper - && (info == null || info.supportsAmbientMode()); + && ((info == null && imageWallpaperInAmbient) + || (info != null && info.supportsAmbientMode())); mStatusBarWindowController.setWallpaperSupportsAmbientMode(supportsAmbientMode); mScrimController.setWallpaperSupportsAmbientMode(supportsAmbientMode); @@ -813,6 +816,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 @@ -1508,6 +1512,9 @@ public class StatusBar extends SystemUI implements DemoMode, mNotificationPanel.setStatusAccessibilityImportance(expanded ? View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS : View.IMPORTANT_FOR_ACCESSIBILITY_AUTO); + if (getNavigationBarView() != null) { + getNavigationBarView().onStatusBarPanelStateChanged(); + } } public boolean isWakeUpComingFromTouch() { @@ -1578,7 +1585,8 @@ public class StatusBar extends SystemUI implements DemoMode, public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) { mEntryManager.updateNotifications(); if (isDozing() && isHeadsUp) { - mDozeServiceHost.fireNotificationPulse(); + entry.setPulseSuppressed(false); + mDozeServiceHost.fireNotificationPulse(entry); if (mPulsing) { mDozeScrimController.cancelPendingPulseTimeout(); } @@ -1802,6 +1810,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 */, @@ -1939,7 +1949,6 @@ public class StatusBar extends SystemUI implements DemoMode, if (start) { mNotificationPanel.startWaitingForOpenPanelGesture(); - setPanelExpanded(true); } else { mNotificationPanel.stopWaitingForOpenPanelGesture(velocity); } @@ -2388,6 +2397,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); } @@ -3200,12 +3213,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); @@ -3213,7 +3227,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); } /** @@ -3563,6 +3577,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(); @@ -3920,9 +3937,13 @@ public class StatusBar extends SystemUI implements DemoMode, } } - public void fireNotificationPulse() { + public void fireNotificationPulse(NotificationEntry entry) { + Runnable pulseSupressedListener = () -> { + entry.setPulseSuppressed(true); + mNotificationIconAreaController.updateAodNotificationIcons(); + }; for (Callback callback : mCallbacks) { - callback.onNotificationAlerted(); + callback.onNotificationAlerted(pulseSupressedListener); } } 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/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java index 4ddd0e9962ad..d9a9f7cbc2a8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java @@ -51,12 +51,13 @@ import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; import com.google.android.collect.Lists; + import java.io.FileDescriptor; import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.lang.reflect.Field; - import java.util.ArrayList; + import javax.inject.Inject; import javax.inject.Singleton; @@ -352,7 +353,7 @@ public class StatusBarWindowController implements Callback, Dumpable, Configurat } private void applyForceStatusBarVisibleFlag(State state) { - if (state.forceStatusBarVisible) { + if (state.forceStatusBarVisible || state.forcePluginOpen) { mLpChanged.privateFlags |= WindowManager .LayoutParams.PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT; } else { 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 a71fcdbd9914..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); @@ -110,8 +111,8 @@ public class UnlockMethodCache { /** * If there are faces enrolled and user enabled face auth on keyguard. */ - public boolean isUnlockingWithFacePossible() { - return mIsUnlockingWithFacePossible; + public boolean isFaceAuthEnabled() { + return mFaceAuthEnabled; } private void update(boolean updateAlways) { @@ -122,16 +123,16 @@ public class UnlockMethodCache { || (Build.IS_DEBUGGABLE && DEBUG_AUTH_WITH_ADB && mDebugUnlocked); boolean trustManaged = mKeyguardUpdateMonitor.getUserTrustIsManaged(user); boolean trusted = mKeyguardUpdateMonitor.getUserHasTrust(user); - boolean isUnlockingWithFacePossible = mKeyguardUpdateMonitor.isUnlockWithFacePossible(user); + boolean faceAuthEnabled = mKeyguardUpdateMonitor.isFaceAuthEnabledForUser(user); boolean changed = secure != mSecure || canSkipBouncer != mCanSkipBouncer || trustManaged != mTrustManaged - || mIsUnlockingWithFacePossible != isUnlockingWithFacePossible; + || mFaceAuthEnabled != faceAuthEnabled; if (changed || updateAlways) { mSecure = secure; mCanSkipBouncer = canSkipBouncer; mTrusted = trusted; mTrustManaged = trustManaged; - mIsUnlockingWithFacePossible = isUnlockingWithFacePossible; + mFaceAuthEnabled = faceAuthEnabled; notifyListeners(); } Trace.endSection(); @@ -143,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/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java index 3f3e1e32a951..a6b5b38fd728 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java @@ -43,6 +43,7 @@ import android.os.Looper; import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.UserHandle; import android.os.VibrationEffect; import android.os.Vibrator; import android.provider.Settings; @@ -275,14 +276,14 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa } public boolean areCaptionsEnabled() { - int currentValue = Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Secure.ODI_CAPTIONS_ENABLED, 0); + int currentValue = Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.ODI_CAPTIONS_ENABLED, 0, UserHandle.USER_CURRENT); return currentValue == 1; } public void setCaptionsEnabled(boolean isEnabled) { - Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Secure.ODI_CAPTIONS_ENABLED, isEnabled ? 1 : 0); + Settings.Secure.putIntForUser(mContext.getContentResolver(), + Settings.Secure.ODI_CAPTIONS_ENABLED, isEnabled ? 1 : 0, UserHandle.USER_CURRENT); } @Override diff --git a/packages/SystemUI/tests/src/com/android/systemui/DumpControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/DumpControllerTest.kt index cca35ca39606..d921d5832a01 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/DumpControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/DumpControllerTest.kt @@ -18,7 +18,6 @@ package com.android.systemui import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest -import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -37,6 +36,7 @@ class DumpControllerTest : SysuiTestCase() { private lateinit var controller: DumpController @Mock private lateinit var callback1: Dumpable @Mock private lateinit var callback2: Dumpable + @Mock private lateinit var callback3: Dumpable @Mock private lateinit var fd: FileDescriptor @Mock private lateinit var pw: PrintWriter private val args = emptyArray<String>() @@ -46,40 +46,81 @@ class DumpControllerTest : SysuiTestCase() { MockitoAnnotations.initMocks(this) controller = DumpController() -// Debug.waitForDebugger() } - @Test + @Test(expected = IllegalArgumentException::class) fun testListenerOnlyAddedOnce() { + controller.registerDumpable("cb1", callback1) + controller.registerDumpable("cb1", callback2) + } + + @Test + fun testListenersCalledOnDump() { controller.apply { - addListener(callback1) - addListener(callback1) + registerDumpable("cb1", callback1) + registerDumpable("cb2", callback2) } - assertEquals(1, controller.numListeners) controller.dump(fd, pw, args) + verify(callback1 /* only once */).dump(fd, pw, args) + verify(callback2 /* only once */).dump(fd, pw, args) } @Test - fun testListenersCalledOnDump() { + fun testListenersAreFiltered() { + controller.apply { + registerDumpable("cb1", callback1) + registerDumpable("cb2", callback2) + registerDumpable("cb3", callback3) + } + + val args = arrayOf("dependency", "DumpController", "cb3,cb1") + controller.dump(fd, pw, args) + + verify(callback1 /* only once */).dump(fd, pw, args) + verify(callback2, never()).dump(fd, pw, args) + verify(callback3 /* only once */).dump(fd, pw, args) + } + + @Test + fun testFiltersAreNotCaseSensitive() { + controller.apply { + registerDumpable("cb1", callback1) + registerDumpable("cb2", callback2) + registerDumpable("cb3", callback3) + } + + val args = arrayOf("dependency", "DumpController", "CB3") + controller.dump(fd, pw, args) + + verify(callback1, never()).dump(fd, pw, args) + verify(callback2, never()).dump(fd, pw, args) + verify(callback3 /* only once */).dump(fd, pw, args) + } + + @Test + fun testFiltersAreIgnoredIfPrecedingArgsDontMatch() { controller.apply { - addListener(callback1) - addListener(callback2) + registerDumpable("cb1", callback1) + registerDumpable("cb2", callback2) + registerDumpable("cb3", callback3) } + val args = arrayOf("", "", "cb2") controller.dump(fd, pw, args) verify(callback1 /* only once */).dump(fd, pw, args) verify(callback2 /* only once */).dump(fd, pw, args) + verify(callback3 /* only once */).dump(fd, pw, args) } @Test fun testRemoveListener() { controller.apply { - addListener(callback1) - addListener(callback2) - removeListener(callback1) + registerDumpable("cb1", callback1) + registerDumpable("cb2", callback2) + unregisterDumpable(callback1) } controller.dump(fd, pw, args) diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java index eb8ef09d0635..d4642238d8fd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java @@ -95,13 +95,13 @@ public class DozeTriggersTest extends SysuiTestCase { mTriggers.transitionTo(DozeMachine.State.INITIALIZED, DozeMachine.State.DOZE); clearInvocations(mMachine); - mHost.callback.onNotificationAlerted(); + mHost.callback.onNotificationAlerted(null /* pulseSuppressedListener */); mSensors.getMockProximitySensor().sendProximityResult(false); /* Near */ verify(mMachine, never()).requestState(any()); verify(mMachine, never()).requestPulse(anyInt()); - mHost.callback.onNotificationAlerted(); + mHost.callback.onNotificationAlerted(null /* pulseSuppressedListener */); mSensors.getMockProximitySensor().sendProximityResult(true); /* Far */ verify(mMachine).requestPulse(anyInt()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/recents/model/TaskKeyLruCacheTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/recents/model/TaskKeyLruCacheTest.java new file mode 100644 index 000000000000..eb71dd6ee677 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/recents/model/TaskKeyLruCacheTest.java @@ -0,0 +1,106 @@ +/* + * 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.shared.recents.model; + + +import static junit.framework.TestCase.assertEquals; +import static junit.framework.TestCase.assertNull; + +import android.test.suitebuilder.annotation.SmallTest; + +import com.android.systemui.SysuiTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@SmallTest +@RunWith(JUnit4.class) +public class TaskKeyLruCacheTest extends SysuiTestCase { + private static int sCacheSize = 3; + private static int sIdTask1 = 1; + private static int sIdTask2 = 2; + private static int sIdTask3 = 3; + private static int sIdUser1 = 1; + + TaskKeyCache<Integer> mCache = new TaskKeyLruCache<>(sCacheSize, null); + Task.TaskKey mKey1; + Task.TaskKey mKey2; + Task.TaskKey mKey3; + + @Before + public void setup() { + mKey1 = new Task.TaskKey(sIdTask1, 0, null, null, sIdUser1, System.currentTimeMillis()); + mKey2 = new Task.TaskKey(sIdTask2, 0, null, null, sIdUser1, System.currentTimeMillis()); + mKey3 = new Task.TaskKey(sIdTask3, 0, null, null, sIdUser1, System.currentTimeMillis()); + } + + @Test + public void addSingleItem_get_success() { + mCache.put(mKey1, 1); + + assertEquals(1, (int) mCache.get(mKey1)); + } + + @Test + public void addSingleItem_getUninsertedItem_returnsNull() { + mCache.put(mKey1, 1); + + assertNull(mCache.get(mKey2)); + } + + @Test + public void emptyCache_get_returnsNull() { + assertNull(mCache.get(mKey1)); + } + + @Test + public void updateItem_get_returnsSecond() { + mCache.put(mKey1, 1); + mCache.put(mKey1, 2); + + assertEquals(2, (int) mCache.get(mKey1)); + assertEquals(1, mCache.mKeys.size()); + } + + @Test + public void fillCache_put_evictsOldest() { + mCache.put(mKey1, 1); + mCache.put(mKey2, 2); + mCache.put(mKey3, 3); + Task.TaskKey key4 = new Task.TaskKey(sIdTask3 + 1, 0, + null, null, sIdUser1, System.currentTimeMillis()); + mCache.put(key4, 4); + + assertNull(mCache.get(mKey1)); + assertEquals(3, mCache.mKeys.size()); + assertEquals(mKey2, mCache.mKeys.valueAt(0)); + } + + @Test + public void fillCache_remove_success() { + mCache.put(mKey1, 1); + mCache.put(mKey2, 2); + mCache.put(mKey3, 3); + + mCache.remove(mKey2); + + assertNull(mCache.get(mKey2)); + assertEquals(2, mCache.mKeys.size()); + } +} 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..fd676111b1da 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); @@ -161,6 +161,7 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { @Test public void onBiometricAuthenticated_whenFace_andBypass_encrypted_showBouncer() { + reset(mUpdateMonitor); when(mKeyguardBypassController.getBypassEnabled()).thenReturn(true); mBiometricUnlockController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager); @@ -168,11 +169,18 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT, BiometricSourceType.FACE); + // Wake up before showing the bouncer + verify(mStatusBarKeyguardViewManager, never()).showBouncer(eq(false)); + mBiometricUnlockController.mWakefulnessObserver.onFinishedWakingUp(); + verify(mStatusBarKeyguardViewManager).showBouncer(eq(false)); + assertThat(mBiometricUnlockController.getMode()) + .isEqualTo(BiometricUnlockController.MODE_SHOW_BOUNCER); } @Test public void onBiometricAuthenticated_whenFace_noBypass_encrypted_doNothing() { + reset(mUpdateMonitor); mBiometricUnlockController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager); when(mUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(false); @@ -181,6 +189,8 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean()); verify(mStatusBarKeyguardViewManager, never()).animateCollapsePanels(anyFloat()); + assertThat(mBiometricUnlockController.getMode()) + .isEqualTo(BiometricUnlockController.MODE_NONE); } @Test 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/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 2db7f0e108b4..d923bedd15c1 100644 --- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java @@ -23,6 +23,7 @@ import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLEAR_ACCE import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK; import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK; +import android.accessibilityservice.AccessibilityGestureInfo; import android.accessibilityservice.AccessibilityServiceInfo; import android.accessibilityservice.IAccessibilityServiceClient; import android.accessibilityservice.IAccessibilityServiceConnection; @@ -89,7 +90,7 @@ 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; @@ -167,9 +168,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 @@ -715,6 +717,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) @@ -1159,9 +1165,9 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ } } - public void notifyGesture(int gestureId) { + public void notifyGesture(AccessibilityGestureInfo gestureInfo) { mInvocationHandler.obtainMessage(InvocationHandler.MSG_ON_GESTURE, - gestureId, 0).sendToTarget(); + gestureInfo).sendToTarget(); } public void notifyClearAccessibilityNodeInfoCache() { @@ -1250,13 +1256,13 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ } } - private void notifyGestureInternal(int gestureId) { + private void notifyGestureInternal(AccessibilityGestureInfo gestureInfo) { final IAccessibilityServiceClient listener = getServiceInterfaceSafely(); if (listener != null) { try { - listener.onGesture(gestureId); + listener.onGesture(gestureInfo); } catch (RemoteException re) { - Slog.e(LOG_TAG, "Error during sending gesture " + gestureId + Slog.e(LOG_TAG, "Error during sending gesture " + gestureInfo + " to " + mService, re); } } @@ -1451,8 +1457,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ final int type = message.what; switch (type) { case MSG_ON_GESTURE: { - final int gestureId = message.arg1; - notifyGestureInternal(gestureId); + notifyGestureInternal((AccessibilityGestureInfo) message.obj); } break; case MSG_CLEAR_ACCESSIBILITY_CACHE: { diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java index d7670112d55c..b4ac92f0cf55 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java @@ -16,21 +16,15 @@ package com.android.server.accessibility; +import android.accessibilityservice.AccessibilityGestureInfo; import android.accessibilityservice.AccessibilityService; import android.content.Context; -import android.gesture.Gesture; import android.gesture.GesturePoint; -import android.gesture.GestureStore; -import android.gesture.GestureStroke; -import android.gesture.Prediction; import android.graphics.PointF; import android.util.Slog; import android.util.TypedValue; import android.view.GestureDetector; import android.view.MotionEvent; -import android.view.ViewConfiguration; - -import com.android.internal.R; import java.util.ArrayList; @@ -125,11 +119,11 @@ class AccessibilityGestureDetector extends GestureDetector.SimpleOnGestureListen /** * Called when an event stream is recognized as a gesture. * - * @param gestureId ID of the gesture that was recognized. + * @param gestureInfo Information about the gesture. * * @return true if the event is consumed, else false */ - boolean onGestureCompleted(int gestureId); + boolean onGestureCompleted(AccessibilityGestureInfo gestureInfo); /** * Called when the system has decided an event stream doesn't match any @@ -562,6 +556,7 @@ class AccessibilityGestureDetector extends GestureDetector.SimpleOnGestureListen private boolean recognizeGesturePath(MotionEvent event, int policyFlags, ArrayList<PointF> path) { + final int displayId = event.getDisplayId(); if (path.size() == 2) { PointF start = path.get(0); PointF end = path.get(1); @@ -571,13 +566,21 @@ class AccessibilityGestureDetector extends GestureDetector.SimpleOnGestureListen int direction = toDirection(dX, dY); switch (direction) { case LEFT: - return mListener.onGestureCompleted(AccessibilityService.GESTURE_SWIPE_LEFT); + return mListener.onGestureCompleted( + new AccessibilityGestureInfo(AccessibilityService.GESTURE_SWIPE_LEFT, + displayId)); case RIGHT: - return mListener.onGestureCompleted(AccessibilityService.GESTURE_SWIPE_RIGHT); + return mListener.onGestureCompleted( + new AccessibilityGestureInfo(AccessibilityService.GESTURE_SWIPE_RIGHT, + displayId)); case UP: - return mListener.onGestureCompleted(AccessibilityService.GESTURE_SWIPE_UP); + return mListener.onGestureCompleted( + new AccessibilityGestureInfo(AccessibilityService.GESTURE_SWIPE_UP, + displayId)); case DOWN: - return mListener.onGestureCompleted(AccessibilityService.GESTURE_SWIPE_DOWN); + return mListener.onGestureCompleted( + new AccessibilityGestureInfo(AccessibilityService.GESTURE_SWIPE_DOWN, + displayId)); default: // Do nothing. } @@ -596,7 +599,8 @@ class AccessibilityGestureDetector extends GestureDetector.SimpleOnGestureListen int segmentDirection0 = toDirection(dX0, dY0); int segmentDirection1 = toDirection(dX1, dY1); int gestureId = DIRECTIONS_TO_GESTURE_ID[segmentDirection0][segmentDirection1]; - return mListener.onGestureCompleted(gestureId); + return mListener.onGestureCompleted( + new AccessibilityGestureInfo(gestureId, displayId)); } // else if (path.size() < 2 || 3 < path.size()) then no gesture recognized. return mListener.onGestureCancelled(event, policyFlags); 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 a2204518e762..814853694347 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -27,6 +27,7 @@ import static com.android.internal.util.FunctionalUtils.ignoreRemoteException; import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; import android.Manifest; +import android.accessibilityservice.AccessibilityGestureInfo; import android.accessibilityservice.AccessibilityService; import android.accessibilityservice.AccessibilityServiceInfo; import android.accessibilityservice.IAccessibilityServiceClient; @@ -210,7 +211,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private KeyEventDispatcher mKeyEventDispatcher; - private MotionEventInjector mMotionEventInjector; + private SparseArray<MotionEventInjector> mMotionEventInjectors; private FingerprintGestureDispatcher mFingerprintGestureDispatcher; @@ -815,11 +816,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } - boolean onGesture(int gestureId) { + boolean onGesture(AccessibilityGestureInfo gestureInfo) { synchronized (mLock) { - boolean handled = notifyGestureLocked(gestureId, false); + boolean handled = notifyGestureLocked(gestureInfo, false); if (!handled) { - handled = notifyGestureLocked(gestureId, true); + handled = notifyGestureLocked(gestureInfo, true); } return handled; } @@ -860,30 +861,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; } /** @@ -1010,7 +1015,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } - private boolean notifyGestureLocked(int gestureId, boolean isDefault) { + private boolean notifyGestureLocked(AccessibilityGestureInfo gestureInfo, boolean isDefault) { // TODO: Now we are giving the gestures to the last enabled // service that can handle them which is the last one // in our list since we write the last enabled as the @@ -1024,7 +1029,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub for (int i = state.mBoundServices.size() - 1; i >= 0; i--) { AccessibilityServiceConnection service = state.mBoundServices.get(i); if (service.mRequestTouchExplorationMode && service.mIsDefault == isDefault) { - service.notifyGesture(gestureId); + service.notifyGesture(gestureInfo); return true; } } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java index 02306c02fa6f..961168a46c7d 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java @@ -238,7 +238,6 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect return (userState != null) ? userState.getSoftKeyboardShowMode() : 0; } - @Override public boolean isAccessibilityButtonAvailable() { synchronized (mLock) { @@ -354,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/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java index 7920bbb7b179..380e853820ff 100644 --- a/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java +++ b/services/accessibility/java/com/android/server/accessibility/TouchExplorer.java @@ -20,6 +20,7 @@ import static android.view.MotionEvent.INVALID_POINTER_ID; import static com.android.server.accessibility.TouchState.ALL_POINTER_ID_BITS; +import android.accessibilityservice.AccessibilityGestureInfo; import android.content.Context; import android.graphics.Point; import android.os.Handler; @@ -356,14 +357,14 @@ class TouchExplorer extends BaseEventStreamTransformation } @Override - public boolean onGestureCompleted(int gestureId) { + public boolean onGestureCompleted(AccessibilityGestureInfo gestureInfo) { if (!mState.isGestureDetecting()) { return false; } endGestureDetection(true); - mAms.onGesture(gestureId); + mAms.onGesture(gestureInfo); return true; } 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/core/Android.bp b/services/core/Android.bp index 9855e4ea339a..474dbfe49d70 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -13,12 +13,14 @@ java_library_static { }, srcs: [ "java/**/*.java", + ":platformcompat_aidl", ":dumpstate_aidl", ":idmap2_aidl", ":installd_aidl", ":storaged_aidl", ":vold_aidl", ":gsiservice_aidl", + ":platform-compat-config", "java/com/android/server/EventLogTags.logtags", "java/com/android/server/am/EventLogTags.logtags", "java/com/android/server/policy/EventLogTags.logtags", @@ -80,3 +82,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", +} diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java index ddfc3a631be6..a30371876955 100644 --- a/services/core/java/com/android/server/DeviceIdleController.java +++ b/services/core/java/com/android/server/DeviceIdleController.java @@ -1742,6 +1742,12 @@ public class DeviceIdleController extends SystemService return mConstants; } + + /** Returns the current elapsed realtime in milliseconds. */ + long getElapsedRealtime() { + return SystemClock.elapsedRealtime(); + } + LocationManager getLocationManager() { if (mLocationManager == null) { mLocationManager = mContext.getSystemService(LocationManager.class); @@ -2023,7 +2029,7 @@ public class DeviceIdleController extends SystemService private void unregisterDeviceIdleConstraintInternal(IDeviceIdleConstraint constraint) { synchronized (this) { - // Artifically force the constraint to inactive to unblock anything waiting for it. + // Artificially force the constraint to inactive to unblock anything waiting for it. onConstraintStateChangedLocked(constraint, /* active= */ false); // Let the constraint know that we are not listening to it any more. @@ -2746,9 +2752,18 @@ public class DeviceIdleController extends SystemService mState = STATE_QUICK_DOZE_DELAY; // Make sure any motion sensing or locating is stopped. resetIdleManagementLocked(); - // Wait a small amount of time in case something (eg: background service from - // recently closed app) needs to finish running. - scheduleAlarmLocked(mConstants.QUICK_DOZE_DELAY_TIMEOUT, false); + if (isUpcomingAlarmClock()) { + // If there's an upcoming AlarmClock alarm, we won't go into idle, so + // setting a wakeup alarm before the upcoming alarm is futile. Set the quick + // doze alarm to after the upcoming AlarmClock alarm. + scheduleAlarmLocked( + mAlarmManager.getNextWakeFromIdleTime() - mInjector.getElapsedRealtime() + + mConstants.QUICK_DOZE_DELAY_TIMEOUT, false); + } else { + // Wait a small amount of time in case something (eg: background service from + // recently closed app) needs to finish running. + scheduleAlarmLocked(mConstants.QUICK_DOZE_DELAY_TIMEOUT, false); + } EventLogTags.writeDeviceIdle(mState, "no activity"); } else if (mState == STATE_ACTIVE) { mState = STATE_INACTIVE; @@ -2758,7 +2773,16 @@ public class DeviceIdleController extends SystemService if (shouldUseIdleTimeoutFactorLocked()) { delay = (long) (mPreIdleFactor * delay); } - scheduleAlarmLocked(delay, false); + if (isUpcomingAlarmClock()) { + // If there's an upcoming AlarmClock alarm, we won't go into idle, so + // setting a wakeup alarm before the upcoming alarm is futile. Set the idle + // alarm to after the upcoming AlarmClock alarm. + scheduleAlarmLocked( + mAlarmManager.getNextWakeFromIdleTime() - mInjector.getElapsedRealtime() + + delay, false); + } else { + scheduleAlarmLocked(delay, false); + } EventLogTags.writeDeviceIdle(mState, "no activity"); } } @@ -2906,13 +2930,21 @@ public class DeviceIdleController extends SystemService return mState; } + /** + * Returns true if there's an upcoming AlarmClock alarm that is soon enough to prevent the + * device from going into idle. + */ + private boolean isUpcomingAlarmClock() { + return mInjector.getElapsedRealtime() + mConstants.MIN_TIME_TO_ALARM + >= mAlarmManager.getNextWakeFromIdleTime(); + } + @VisibleForTesting void stepIdleStateLocked(String reason) { if (DEBUG) Slog.d(TAG, "stepIdleStateLocked: mState=" + mState); EventLogTags.writeDeviceIdleStep(); - final long now = SystemClock.elapsedRealtime(); - if ((now+mConstants.MIN_TIME_TO_ALARM) > mAlarmManager.getNextWakeFromIdleTime()) { + if (isUpcomingAlarmClock()) { // Whoops, there is an upcoming alarm. We don't actually want to go idle. if (mState != STATE_ACTIVE) { mActiveReason = ACTIVE_REASON_ALARM; diff --git a/services/core/java/com/android/server/DynamicSystemService.java b/services/core/java/com/android/server/DynamicSystemService.java index f92d0e0ff6f1..173d5b053309 100644 --- a/services/core/java/com/android/server/DynamicSystemService.java +++ b/services/core/java/com/android/server/DynamicSystemService.java @@ -21,6 +21,7 @@ import android.content.pm.PackageManager; import android.gsi.GsiInstallParams; import android.gsi.GsiProgress; import android.gsi.IGsiService; +import android.gsi.IGsid; import android.os.Environment; import android.os.IBinder; import android.os.IBinder.DeathRecipient; @@ -61,7 +62,9 @@ public class DynamicSystemService extends IDynamicSystemService.Stub implements * re-initialized in this case. */ binder.linkToDeath(recipient, 0); - return IGsiService.Stub.asInterface(binder); + + IGsid gsid = IGsid.Stub.asInterface(binder); + return gsid.getClient(); } /** implements DeathRecipient */ @@ -159,7 +162,7 @@ public class DynamicSystemService extends IDynamicSystemService.Stub implements isInUse = getGsiService().isGsiRunning(); } finally { if (!gsidWasRunning && !isInUse) { - SystemProperties.set("ctl.stop", "gsid"); + mGsiService = null; } } diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index f7e825eecc12..e66e596d5038 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -1027,12 +1027,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { log(str); } mLocalLog.log(str); - // for service state updates, don't notify clients when subId is invalid. This prevents - // us from sending incorrect notifications like b/133140128 - // In the future, we can remove this logic for every notification here and add a - // callback so listeners know when their PhoneStateListener's subId becomes invalid, but - // for now we use the simplest fix. - if (validatePhoneId(phoneId) && SubscriptionManager.isValidSubscriptionId(subId)) { + if (validatePhoneId(phoneId)) { mServiceState[phoneId] = state; for (Record r : mRecords) { @@ -1064,8 +1059,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } } else { - log("notifyServiceStateForSubscriber: INVALID phoneId=" + phoneId - + " or subId=" + subId); + log("notifyServiceStateForSubscriber: INVALID phoneId=" + phoneId); } handleRemoveListLocked(); } 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 268e813c12dd..f3264e2be565 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -7901,11 +7901,12 @@ public class ActivityManagerService extends IActivityManager.Stub } void reportGlobalUsageEventLocked(int event) { - mUsageStatsService.reportEvent("android", mUserController.getCurrentUserId(), event); + mUsageStatsService.reportEvent(Event.DEVICE_EVENT_PACKAGE_NAME, + mUserController.getCurrentUserId(), event); int[] profiles = mUserController.getCurrentProfileIds(); if (profiles != null) { for (int i = profiles.length - 1; i >= 0; i--) { - mUsageStatsService.reportEvent((String)null, profiles[i], event); + mUsageStatsService.reportEvent(Event.DEVICE_EVENT_PACKAGE_NAME, profiles[i], event); } } } diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index 33070dc6b757..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; @@ -98,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; @@ -116,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; @@ -922,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(); diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index 770cb3da3362..97e529361a46 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -34,6 +34,7 @@ import static android.os.Process.THREAD_GROUP_BG_NONINTERACTIVE; import static android.os.Process.THREAD_GROUP_DEFAULT; import static android.os.Process.THREAD_GROUP_RESTRICTED; import static android.os.Process.THREAD_GROUP_TOP_APP; +import static android.os.Process.THREAD_PRIORITY_DISPLAY; import static android.os.Process.setProcessGroup; import static android.os.Process.setThreadPriority; import static android.os.Process.setThreadScheduler; @@ -1815,7 +1816,6 @@ public final class OomAdjuster { if (app.renderThreadTid != 0) { setThreadScheduler(app.renderThreadTid, SCHED_OTHER, 0); - setThreadPriority(app.renderThreadTid, -4); } } catch (IllegalArgumentException e) { Slog.w(TAG, @@ -1827,9 +1827,10 @@ public final class OomAdjuster { } else { // Reset priority for top app UI and render threads setThreadPriority(app.pid, 0); - if (app.renderThreadTid != 0) { - setThreadPriority(app.renderThreadTid, 0); - } + } + + if (app.renderThreadTid != 0) { + setThreadPriority(app.renderThreadTid, THREAD_PRIORITY_DISPLAY); } } } catch (Exception e) { diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java index bd3cd5439b32..af2f24f959b4 100644 --- a/services/core/java/com/android/server/biometrics/BiometricService.java +++ b/services/core/java/com/android/server/biometrics/BiometricService.java @@ -789,6 +789,23 @@ public class BiometricService extends SystemService { return error; } + @Override + public boolean hasEnrolledBiometrics(int userId) { + checkInternalPermission(); + + final long ident = Binder.clearCallingIdentity(); + try { + for (int i = 0; i < mAuthenticators.size(); i++) { + if (mAuthenticators.get(i).mAuthenticator.hasEnrolledTemplates(userId)) { + return true; + } + } + } finally { + Binder.restoreCallingIdentity(ident); + } + return false; + } + @Override // Binder call public void registerEnabledOnKeyguardCallback(IBiometricEnabledOnKeyguardCallback callback) throws RemoteException { diff --git a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java index 20eb6180832c..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 @@ -912,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..a7065216f6a3 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. @@ -343,16 +344,19 @@ public class FaceService extends BiometricServiceBase { @Override // Binder call public int revokeChallenge(IBinder token) { checkPermission(MANAGE_BIOMETRIC); - // TODO(b/137106905): Schedule binder calls in FaceService to avoid deadlocks. - if (getCurrentClient() == null) { - // if we aren't handling any other HIDL calls (mCurrentClient == null), revoke the - // challenge right away. - return startRevokeChallenge(token); - } else { - // postpone revoking the challenge until we finish processing the current HIDL call. - mRevokeChallengePending = true; - return Status.OK; - } + mHandler.post(() -> { + // TODO(b/137106905): Schedule binder calls in FaceService to avoid deadlocks. + if (getCurrentClient() == null) { + // if we aren't handling any other HIDL calls (mCurrentClient == null), revoke + // the challenge right away. + startRevokeChallenge(token); + } else { + // postpone revoking the challenge until we finish processing the current HIDL + // call. + mRevokeChallengePending = true; + } + }); + return Status.OK; } @Override // Binder call @@ -368,7 +372,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 +614,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 +670,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 2a866f390283..6f32beea66d3 100644 --- a/services/core/java/com/android/server/compat/CompatChange.java +++ b/services/core/java/com/android/server/compat/CompatChange.java @@ -20,6 +20,8 @@ import android.annotation.Nullable; import android.compat.annotation.EnabledAfter; import android.content.pm.ApplicationInfo; +import com.android.server.compat.config.Change; + import java.util.HashMap; import java.util.Map; @@ -60,6 +62,16 @@ public final class CompatChange { mDisabled = disabled; } + /** + * @param change an object generated by services/core/xsd/platform-compat-config.xsd + */ + public CompatChange(Change change) { + mChangeId = change.getId(); + mName = change.getName(); + mEnableAfterTargetSdk = change.getEnableAfterTargetSdk(); + mDisabled = change.getDisabled(); + } + long getId() { return mChangeId; } diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java index bcf1d80781a6..044e41789bb2 100644 --- a/services/core/java/com/android/server/compat/CompatConfig.java +++ b/services/core/java/com/android/server/compat/CompatConfig.java @@ -17,13 +17,27 @@ package com.android.server.compat; import android.content.pm.ApplicationInfo; +import android.os.Environment; import android.text.TextUtils; import android.util.LongArray; import android.util.LongSparseArray; +import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.server.compat.config.Change; +import com.android.server.compat.config.XmlParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintWriter; + +import javax.xml.datatype.DatatypeConfigurationException; /** * This class maintains state relating to platform compatibility changes. * @@ -32,7 +46,12 @@ import com.android.internal.annotations.VisibleForTesting; */ public final class CompatConfig { - private static final CompatConfig sInstance = new CompatConfig(); + private static final String TAG = "CompatConfig"; + private static final String CONFIG_FILE_SUFFIX = "platform_compat_config.xml"; + + private static final CompatConfig sInstance = new CompatConfig().initConfigFromLib( + Environment.buildPath( + Environment.getRootDirectory(), "etc", "sysconfig")); @GuardedBy("mChanges") private final LongSparseArray<CompatChange> mChanges = new LongSparseArray<>(); @@ -169,4 +188,47 @@ public final class CompatConfig { 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()); + } + } + } + + CompatConfig initConfigFromLib(File libraryDir) { + if (!libraryDir.exists() || !libraryDir.isDirectory()) { + Slog.e(TAG, "No directory " + libraryDir + ", skipping"); + return this; + } + for (File f : libraryDir.listFiles()) { + //TODO(b/138222363): Handle duplicate ids across config files. + if (f.getPath().endsWith(CONFIG_FILE_SUFFIX)) { + readConfig(f); + } + } + return this; + } + + private void readConfig(File configFile) { + try (InputStream in = new BufferedInputStream(new FileInputStream(configFile))) { + for (Change change : XmlParser.read(in).getCompatChange()) { + Slog.w(TAG, "Adding: " + change.toString()); + addChange(new CompatChange(change)); + } + } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) { + Slog.e(TAG, "Encountered an error while reading/parsing compat config file", e); + } + } + } 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/display/ColorFade.java b/services/core/java/com/android/server/display/ColorFade.java index 7be1b8a3d371..c46fc20b3cc8 100644 --- a/services/core/java/com/android/server/display/ColorFade.java +++ b/services/core/java/com/android/server/display/ColorFade.java @@ -29,6 +29,7 @@ import android.opengl.GLES11Ext; import android.opengl.GLES20; import android.os.IBinder; import android.util.Slog; +import android.view.Display; import android.view.DisplayInfo; import android.view.Surface; import android.view.Surface.OutOfResourcesException; @@ -72,6 +73,9 @@ final class ColorFade { // See code for details. private static final int DEJANK_FRAMES = 3; + private static final int EGL_GL_COLORSPACE_KHR = 0x309D; + private static final int EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT = 0x3490; + private final int mDisplayId; // Set to true when the animation context has been fully prepared. @@ -93,6 +97,7 @@ final class ColorFade { private EGLSurface mEglSurface; private boolean mSurfaceVisible; private float mSurfaceAlpha; + private boolean mIsWideColor; // Texture names. We only use one texture, which contains the screenshot. private final int[] mTexNames = new int[1]; @@ -482,6 +487,8 @@ final class ColorFade { return false; } + mIsWideColor = SurfaceControl.getActiveColorMode(token) + == Display.COLOR_MODE_DISPLAY_P3; SurfaceControl.screenshot(token, s); st.updateTexImage(); st.getTransformMatrix(mTexMatrix); @@ -608,8 +615,16 @@ final class ColorFade { private boolean createEglSurface() { if (mEglSurface == null) { int[] eglSurfaceAttribList = new int[] { + EGL14.EGL_NONE, + EGL14.EGL_NONE, EGL14.EGL_NONE }; + + // If the current display is in wide color, then so is the screenshot. + if (mIsWideColor) { + eglSurfaceAttribList[0] = EGL_GL_COLORSPACE_KHR; + eglSurfaceAttribList[1] = EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT; + } // turn our SurfaceControl into a Surface mEglSurface = EGL14.eglCreateWindowSurface(mEglDisplay, mEglConfig, mSurface, eglSurfaceAttribList, 0); diff --git a/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java b/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java index 2c0cacdd26e5..52cede2121bd 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java @@ -290,8 +290,7 @@ final class HdmiCecKeycode { new KeycodeEntry(KeyEvent.KEYCODE_CHANNEL_UP, CEC_KEYCODE_CHANNEL_UP), new KeycodeEntry(KeyEvent.KEYCODE_CHANNEL_DOWN, CEC_KEYCODE_CHANNEL_DOWN), new KeycodeEntry(KeyEvent.KEYCODE_LAST_CHANNEL, CEC_KEYCODE_PREVIOUS_CHANNEL), - // No Android keycode defined for CEC_KEYCODE_SOUND_SELECT - new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_SOUND_SELECT), + new KeycodeEntry(KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK, CEC_KEYCODE_SOUND_SELECT), new KeycodeEntry(KeyEvent.KEYCODE_TV_INPUT, CEC_KEYCODE_INPUT_SELECT), new KeycodeEntry(KeyEvent.KEYCODE_INFO, CEC_KEYCODE_DISPLAY_INFORMATION), // No Android keycode defined for CEC_KEYCODE_HELP 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/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index 433ce811c8d7..9510db09aa25 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -446,7 +446,7 @@ public class LockSettingsService extends ILockSettings.Stub { public boolean hasEnrolledBiometrics(int userId) { BiometricManager bm = mContext.getSystemService(BiometricManager.class); - return bm.canAuthenticate(userId) == BiometricManager.BIOMETRIC_SUCCESS; + return bm.hasEnrolledBiometrics(userId); } public int binderGetCallingUid() { 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/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/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index d30895efbdc3..217c0bdceede 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -473,6 +473,8 @@ public class NotificationManagerService extends SystemService { private MetricsLogger mMetricsLogger; private TriPredicate<String, Integer, String> mAllowedManagedServicePackages; + private final SavePolicyFileRunnable mSavePolicyFile = new SavePolicyFileRunnable(); + private static class Archive { final int mBufferSize; final ArrayDeque<StatusBarNotification> mBuffer; @@ -666,7 +668,14 @@ public class NotificationManagerService extends SystemService { @VisibleForTesting protected void handleSavePolicyFile() { - IoThread.getHandler().post(() -> { + if (!IoThread.getHandler().hasCallbacks(mSavePolicyFile)) { + IoThread.getHandler().post(mSavePolicyFile); + } + } + + private final class SavePolicyFileRunnable implements Runnable { + @Override + public void run() { if (DBG) Slog.d(TAG, "handleSavePolicyFile"); synchronized (mPolicyFile) { final FileOutputStream stream; @@ -686,7 +695,7 @@ public class NotificationManagerService extends SystemService { } } BackupManager.dataChanged(getContext().getPackageName()); - }); + } } private void writePolicyXml(OutputStream stream, boolean forBackup, int userId) @@ -1845,6 +1854,7 @@ public class NotificationManagerService extends SystemService { } if (properties.getKeyset() .contains(SystemUiDeviceConfigFlags.NAS_DEFAULT_SERVICE)) { + mAssistants.allowAdjustmentType(Adjustment.KEY_IMPORTANCE); mAssistants.resetDefaultAssistantsIfNecessary(); } }); 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/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java new file mode 100644 index 000000000000..ab8cc5374ec9 --- /dev/null +++ b/services/core/java/com/android/server/pm/AppsFilter.java @@ -0,0 +1,372 @@ +/* + * 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.Manifest; +import android.annotation.Nullable; +import android.app.AppOpsManager; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.PackageParser; +import android.os.Build; +import android.os.Process; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.permission.IPermissionManager; +import android.provider.DeviceConfig; +import android.util.ArraySet; +import android.util.Slog; +import android.util.SparseArray; + +import com.android.internal.R; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * The entity responsible for filtering visibility between apps based on declarations in their + * manifests. + */ +class AppsFilter { + + private static final String TAG = PackageManagerService.TAG; + /** + * This contains a list of packages that are implicitly queryable because another app explicitly + * interacted with it. For example, if application A starts a service in application B, + * application B is implicitly allowed to query for application A; regardless of any manifest + * entries. + */ + private final SparseArray<HashMap<String, ArrayList<String>>> mImplicitlyQueryable = + new SparseArray<>(); + + /** + * A mapping from the set of packages that query other packages via package name to the + * list of packages that they can see. + */ + private final HashMap<String, List<String>> mQueriesViaPackage = new HashMap<>(); + + /** + * A mapping from the set of packages that query others via intent to the list + * of packages that the intents resolve to. + */ + private final HashMap<String, List<String>> mQueriesViaIntent = new HashMap<>(); + + /** + * A set of packages that are always queryable by any package, regardless of their manifest + * content. + */ + private final HashSet<String> mForceQueryable; + /** + * A set of packages that are always queryable by any package, regardless of their manifest + * content. + */ + private final Set<String> mForceQueryableByDevice; + + /** True if all system apps should be made queryable by default. */ + private final boolean mSystemAppsQueryable; + + private final IPermissionManager mPermissionManager; + + private final AppOpsManager mAppOpsManager; + private final ConfigProvider mConfigProvider; + + AppsFilter(ConfigProvider configProvider, IPermissionManager permissionManager, + AppOpsManager appOpsManager, String[] forceQueryableWhitelist, + boolean systemAppsQueryable) { + mConfigProvider = configProvider; + mAppOpsManager = appOpsManager; + final HashSet<String> forceQueryableByDeviceSet = new HashSet<>(); + Collections.addAll(forceQueryableByDeviceSet, forceQueryableWhitelist); + this.mForceQueryableByDevice = Collections.unmodifiableSet(forceQueryableByDeviceSet); + this.mForceQueryable = new HashSet<>(); + mPermissionManager = permissionManager; + mSystemAppsQueryable = systemAppsQueryable; + } + + public static AppsFilter create(Context context) { + final boolean forceSystemAppsQueryable = + context.getResources().getBoolean(R.bool.config_forceSystemPackagesQueryable); + final ConfigProvider configProvider = () -> DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE, + "package_query_filtering_enabled", + false); + final String[] forcedQueryablePackageNames; + if (forceSystemAppsQueryable) { + // all system apps already queryable, no need to read and parse individual exceptions + forcedQueryablePackageNames = new String[]{}; + } else { + forcedQueryablePackageNames = + context.getResources().getStringArray(R.array.config_forceQueryablePackages); + for (int i = 0; i < forcedQueryablePackageNames.length; i++) { + forcedQueryablePackageNames[i] = forcedQueryablePackageNames[i].intern(); + } + } + IPermissionManager permissionmgr = + (IPermissionManager) ServiceManager.getService("permissionmgr"); + return new AppsFilter(configProvider, permissionmgr, + context.getSystemService(AppOpsManager.class), forcedQueryablePackageNames, + forceSystemAppsQueryable); + } + + /** Returns true if the querying package may query for the potential target package */ + private static boolean canQuery(PackageParser.Package querying, + PackageParser.Package potentialTarget) { + if (querying.mQueriesIntents == null) { + return false; + } + for (Intent intent : querying.mQueriesIntents) { + for (PackageParser.Activity activity : potentialTarget.activities) { + if (activity.intents != null) { + for (PackageParser.ActivityIntentInfo filter : activity.intents) { + if (matches(intent, filter)) { + return true; + } + } + } + } + } + return false; + } + + /** Returns true if the given intent matches the given filter. */ + private static boolean matches(Intent intent, PackageParser.ActivityIntentInfo filter) { + return filter.match(intent.getAction(), intent.getType(), intent.getScheme(), + intent.getData(), intent.getCategories(), "AppsFilter") > 0; + } + + /** + * Marks that a package initiated an interaction with another package, granting visibility of + * the prior from the former. + * + * @param initiatingPackage the package initiating the interaction + * @param targetPackage the package being interacted with and thus gaining visibility of the + * initiating package. + * @param userId the user in which this interaction was taking place + */ + private void markAppInteraction( + PackageSetting initiatingPackage, PackageSetting targetPackage, int userId) { + HashMap<String, ArrayList<String>> currentUser = mImplicitlyQueryable.get(userId); + if (currentUser == null) { + currentUser = new HashMap<>(); + mImplicitlyQueryable.put(userId, currentUser); + } + if (!currentUser.containsKey(targetPackage.pkg.packageName)) { + currentUser.put(targetPackage.pkg.packageName, new ArrayList<>()); + } + currentUser.get(targetPackage.pkg.packageName).add(initiatingPackage.pkg.packageName); + } + + /** + * Adds a package that should be considered when filtering visibility between apps. + * + * @param newPkg the new package being added + * @param existing all other packages currently on the device. + */ + public void addPackage(PackageParser.Package newPkg, + Map<String, PackageParser.Package> existing) { + // let's re-evaluate the ability of already added packages to see this new package + if (newPkg.mForceQueryable + || (mSystemAppsQueryable && (newPkg.isSystem() || newPkg.isUpdatedSystemApp()))) { + mForceQueryable.add(newPkg.packageName); + } else { + for (String packageName : mQueriesViaIntent.keySet()) { + if (packageName == newPkg.packageName) { + continue; + } + final PackageParser.Package existingPackage = existing.get(packageName); + if (canQuery(existingPackage, newPkg)) { + mQueriesViaIntent.get(packageName).add(newPkg.packageName); + } + } + } + // if the new package declares them, let's evaluate its ability to see existing packages + mQueriesViaIntent.put(newPkg.packageName, new ArrayList<>()); + for (PackageParser.Package existingPackage : existing.values()) { + if (existingPackage.packageName == newPkg.packageName) { + continue; + } + if (existingPackage.mForceQueryable + || (mSystemAppsQueryable + && (newPkg.isSystem() || newPkg.isUpdatedSystemApp()))) { + continue; + } + if (canQuery(newPkg, existingPackage)) { + mQueriesViaIntent.get(newPkg.packageName).add(existingPackage.packageName); + } + } + final ArrayList<String> queriesPackages = new ArrayList<>( + newPkg.mQueriesPackages == null ? 0 : newPkg.mQueriesPackages.size()); + if (newPkg.mQueriesPackages != null) { + queriesPackages.addAll(newPkg.mQueriesPackages); + } + mQueriesViaPackage.put(newPkg.packageName, queriesPackages); + } + + /** + * Removes a package for consideration when filtering visibility between apps. + * + * @param packageName the name of the package being removed. + */ + public void removePackage(String packageName) { + mForceQueryable.remove(packageName); + + for (int i = 0; i < mImplicitlyQueryable.size(); i++) { + mImplicitlyQueryable.valueAt(i).remove(packageName); + for (ArrayList<String> initiators : mImplicitlyQueryable.valueAt(i).values()) { + initiators.remove(packageName); + } + } + + mQueriesViaIntent.remove(packageName); + for (List<String> declarators : mQueriesViaIntent.values()) { + declarators.remove(packageName); + } + + mQueriesViaPackage.remove(packageName); + } + + /** + * Returns true if the calling package should not be able to see the target package, false if no + * filtering should be done. + * + * @param callingUid the uid of the caller attempting to access a package + * @param callingSetting the setting attempting to access a package or null if it could not be + * found + * @param targetPkgSetting the package being accessed + * @param userId the user in which this access is being attempted + */ + public boolean shouldFilterApplication(int callingUid, @Nullable SettingBase callingSetting, + PackageSetting targetPkgSetting, int userId) { + if (callingUid < Process.FIRST_APPLICATION_UID) { + return false; + } + if (callingSetting == null) { + Slog.wtf(TAG, "No setting found for non system uid " + callingUid); + return true; + } + PackageSetting callingPkgSetting = null; + if (callingSetting instanceof PackageSetting) { + callingPkgSetting = (PackageSetting) callingSetting; + if (!shouldFilterApplicationInternal(callingPkgSetting, targetPkgSetting, + userId)) { + // TODO: actually base this on a start / launch (not just a query) + markAppInteraction(callingPkgSetting, targetPkgSetting, userId); + return false; + } + } else if (callingSetting instanceof SharedUserSetting) { + final ArraySet<PackageSetting> packageSettings = + ((SharedUserSetting) callingSetting).packages; + if (packageSettings != null && packageSettings.size() > 0) { + for (PackageSetting packageSetting : packageSettings) { + if (!shouldFilterApplicationInternal(packageSetting, targetPkgSetting, + userId)) { + // TODO: actually base this on a start / launch (not just a query) + markAppInteraction(packageSetting, targetPkgSetting, userId); + return false; + } + if (callingPkgSetting == null && packageSetting.pkg != null) { + callingPkgSetting = packageSetting; + } + } + } else { + return true; + } + } + if (callingPkgSetting == null) { + Slog.wtf(TAG, "What... " + callingSetting); + return true; + } + final int mode = mAppOpsManager + .checkOpNoThrow(AppOpsManager.OP_QUERY_ALL_PACKAGES, callingUid, + callingPkgSetting.pkg.packageName); + switch (mode) { + case AppOpsManager.MODE_DEFAULT: + // if default, let's rely on remote feature toggle to determine whether to + // actually filter + return mConfigProvider.isEnabled(); + case AppOpsManager.MODE_ALLOWED: + // explicitly allowed to see all packages, don't filter + return false; + case AppOpsManager.MODE_ERRORED: + // deny / error: let's log so developer can audit usages + Slog.i(TAG, callingPkgSetting.pkg.packageName + + " blocked from accessing " + targetPkgSetting.pkg.packageName); + case AppOpsManager.MODE_IGNORED: + // fall through + default: + return true; + } + } + + private boolean shouldFilterApplicationInternal( + PackageSetting callingPkgSetting, PackageSetting targetPkgSetting, int userId) { + final String callingName = callingPkgSetting.pkg.packageName; + final String targetName = targetPkgSetting.pkg.packageName; + if (callingPkgSetting.pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.R) { + return false; + } + if (isImplicitlyQueryableSystemApp(targetPkgSetting)) { + return false; + } + if (targetPkgSetting.pkg.mForceQueryable) { + return false; + } + if (mForceQueryable.contains(targetName)) { + return false; + } + if (mQueriesViaPackage.containsKey(callingName) + && mQueriesViaPackage.get(callingName).contains( + targetName)) { + // the calling package has explicitly declared the target package; allow + return false; + } else if (mQueriesViaIntent.containsKey(callingName) + && mQueriesViaIntent.get(callingName).contains(targetName)) { + return false; + } + if (mImplicitlyQueryable.get(userId) != null + && mImplicitlyQueryable.get(userId).containsKey(callingName) + && mImplicitlyQueryable.get(userId).get(callingName).contains(targetName)) { + return false; + } + try { + if (mPermissionManager.checkPermission( + Manifest.permission.QUERY_ALL_PACKAGES, callingName, userId) + == PackageManager.PERMISSION_GRANTED) { + return false; + } + } catch (RemoteException e) { + return true; + } + return true; + } + + private boolean isImplicitlyQueryableSystemApp(PackageSetting targetPkgSetting) { + return targetPkgSetting.isSystem() && (mSystemAppsQueryable + || mForceQueryableByDevice.contains(targetPkgSetting.pkg.packageName)); + } + + public interface ConfigProvider { + boolean isEnabled(); + } + +} diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java index 2b33aced7151..984f22f01465 100644 --- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java +++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java @@ -345,40 +345,55 @@ public class BackgroundDexOptService extends JobService { private int optimizePackages(PackageManagerService pm, ArraySet<String> pkgs, long lowStorageThreshold, boolean isForPrimaryDex) { ArraySet<String> updatedPackages = new ArraySet<>(); - Set<String> unusedPackages = pm.getUnusedPackages(mDowngradeUnusedAppsThresholdInMillis); - boolean hadSomeLowSpaceFailure = false; - Log.d(TAG, "Unsused Packages " + String.join(",", unusedPackages)); - // Only downgrade apps when space is low on device. - // Threshold is selected above the lowStorageThreshold so that we can pro-actively clean - // up disk before user hits the actual lowStorageThreshold. - final long lowStorageThresholdForDowngrade = LOW_THRESHOLD_MULTIPLIER_FOR_DOWNGRADE * - lowStorageThreshold; - boolean shouldDowngrade = shouldDowngrade(lowStorageThresholdForDowngrade); - Log.d(TAG, "Should Downgrade " + shouldDowngrade); - boolean dex_opt_performed = false; - for (String pkg : pkgs) { - int abort_code = abortIdleOptimizations(lowStorageThreshold); - if (abort_code == OPTIMIZE_ABORT_BY_JOB_SCHEDULER) { - return abort_code; - } - // Downgrade unused packages. - if (unusedPackages.contains(pkg) && shouldDowngrade) { - dex_opt_performed = downgradePackage(pm, pkg, isForPrimaryDex); - } else { - if (abort_code == OPTIMIZE_ABORT_NO_SPACE_LEFT) { - // can't dexopt because of low space. - hadSomeLowSpaceFailure = true; - continue; + + try { + // Only downgrade apps when space is low on device. + // Threshold is selected above the lowStorageThreshold so that we can pro-actively clean + // up disk before user hits the actual lowStorageThreshold. + final long lowStorageThresholdForDowngrade = LOW_THRESHOLD_MULTIPLIER_FOR_DOWNGRADE + * lowStorageThreshold; + boolean shouldDowngrade = shouldDowngrade(lowStorageThresholdForDowngrade); + Log.d(TAG, "Should Downgrade " + shouldDowngrade); + if (shouldDowngrade) { + Set<String> unusedPackages = + pm.getUnusedPackages(mDowngradeUnusedAppsThresholdInMillis); + Log.d(TAG, "Unsused Packages " + String.join(",", unusedPackages)); + + for (String pkg : unusedPackages) { + int abortCode = abortIdleOptimizations(/*lowStorageThreshold*/ -1); + if (abortCode != OPTIMIZE_CONTINUE) { + // Should be aborted by the scheduler. + return abortCode; + } + if (downgradePackage(pm, pkg, isForPrimaryDex)) { + updatedPackages.add(pkg); + } + } + + if (!unusedPackages.isEmpty()) { + pkgs = new ArraySet<>(pkgs); + pkgs.removeAll(unusedPackages); } - dex_opt_performed = optimizePackage(pm, pkg, isForPrimaryDex); } - if (dex_opt_performed) { - updatedPackages.add(pkg); + + for (String pkg : pkgs) { + int abortCode = abortIdleOptimizations(lowStorageThreshold); + if (abortCode != OPTIMIZE_CONTINUE) { + // Either aborted by the scheduler or no space left. + return abortCode; + } + + boolean dexOptPerformed = optimizePackage(pm, pkg, isForPrimaryDex); + if (dexOptPerformed) { + updatedPackages.add(pkg); + } } - } - notifyPinService(updatedPackages); - return hadSomeLowSpaceFailure ? OPTIMIZE_ABORT_NO_SPACE_LEFT : OPTIMIZE_PROCESSED; + return OPTIMIZE_PROCESSED; + } finally { + // Always let the pinner service know about changes. + notifyPinService(updatedPackages); + } } diff --git a/services/core/java/com/android/server/pm/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java index 8b9af7a435e4..8f38026b365f 100644 --- a/services/core/java/com/android/server/pm/InstantAppRegistry.java +++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java @@ -112,7 +112,7 @@ class InstantAppRegistry { private final CookiePersistence mCookiePersistence; /** State for uninstalled instant apps */ - @GuardedBy("mService.mPackages") + @GuardedBy("mService.mLock") private SparseArray<List<UninstalledInstantAppState>> mUninstalledInstantApps; /** @@ -121,11 +121,11 @@ class InstantAppRegistry { * The value is a set of instant app UIDs. * UserID -> TargetAppId -> InstantAppId */ - @GuardedBy("mService.mPackages") + @GuardedBy("mService.mLock") private SparseArray<SparseArray<SparseBooleanArray>> mInstantGrants; /** The set of all installed instant apps. UserID -> AppID */ - @GuardedBy("mService.mPackages") + @GuardedBy("mService.mLock") private SparseArray<SparseBooleanArray> mInstalledInstantAppUids; public InstantAppRegistry(PackageManagerService service) { @@ -133,7 +133,7 @@ class InstantAppRegistry { mCookiePersistence = new CookiePersistence(BackgroundThread.getHandler().getLooper()); } - @GuardedBy("mService.mPackages") + @GuardedBy("mService.mLock") public byte[] getInstantAppCookieLPw(@NonNull String packageName, @UserIdInt int userId) { // Only installed packages can get their own cookie @@ -157,7 +157,7 @@ class InstantAppRegistry { return null; } - @GuardedBy("mService.mPackages") + @GuardedBy("mService.mLock") public boolean setInstantAppCookieLPw(@NonNull String packageName, @Nullable byte[] cookie, @UserIdInt int userId) { if (cookie != null && cookie.length > 0) { @@ -182,7 +182,7 @@ class InstantAppRegistry { private void persistInstantApplicationCookie(@Nullable byte[] cookie, @NonNull String packageName, @NonNull File cookieFile, @UserIdInt int userId) { - synchronized (mService.mPackages) { + synchronized (mService.mLock) { File appDir = getInstantApplicationDir(packageName, userId); if (!appDir.exists() && !appDir.mkdirs()) { Slog.e(LOG_TAG, "Cannot create instant app cookie directory"); @@ -250,7 +250,7 @@ class InstantAppRegistry { } - @GuardedBy("mService.mPackages") + @GuardedBy("mService.mLock") public @Nullable List<InstantAppInfo> getInstantAppsLPr(@UserIdInt int userId) { List<InstantAppInfo> installedApps = getInstalledInstantApplicationsLPr(userId); List<InstantAppInfo> uninstalledApps = getUninstalledInstantApplicationsLPr(userId); @@ -263,7 +263,7 @@ class InstantAppRegistry { return uninstalledApps; } - @GuardedBy("mService.mPackages") + @GuardedBy("mService.mLock") public void onPackageInstalledLPw(@NonNull PackageParser.Package pkg, @NonNull int[] userIds) { PackageSetting ps = (PackageSetting) pkg.mExtras; if (ps == null) { @@ -334,7 +334,7 @@ class InstantAppRegistry { } } - @GuardedBy("mService.mPackages") + @GuardedBy("mService.mLock") public void onPackageUninstalledLPw(@NonNull PackageParser.Package pkg, @NonNull int[] userIds) { PackageSetting ps = (PackageSetting) pkg.mExtras; @@ -360,7 +360,7 @@ class InstantAppRegistry { } } - @GuardedBy("mService.mPackages") + @GuardedBy("mService.mLock") public void onUserRemovedLPw(int userId) { if (mUninstalledInstantApps != null) { mUninstalledInstantApps.remove(userId); @@ -399,7 +399,7 @@ class InstantAppRegistry { return instantGrantList.get(instantAppId); } - @GuardedBy("mService.mPackages") + @GuardedBy("mService.mLock") public void grantInstantAccessLPw(@UserIdInt int userId, @Nullable Intent intent, int targetAppId, int instantAppId) { if (mInstalledInstantAppUids == null) { @@ -434,7 +434,7 @@ class InstantAppRegistry { instantGrantList.put(instantAppId, true /*granted*/); } - @GuardedBy("mService.mPackages") + @GuardedBy("mService.mLock") public void addInstantAppLPw(@UserIdInt int userId, int instantAppId) { if (mInstalledInstantAppUids == null) { mInstalledInstantAppUids = new SparseArray<>(); @@ -447,7 +447,7 @@ class InstantAppRegistry { instantAppList.put(instantAppId, true /*installed*/); } - @GuardedBy("mService.mPackages") + @GuardedBy("mService.mLock") private void removeInstantAppLPw(@UserIdInt int userId, int instantAppId) { // remove from the installed list if (mInstalledInstantAppUids == null) { @@ -473,7 +473,7 @@ class InstantAppRegistry { } } - @GuardedBy("mService.mPackages") + @GuardedBy("mService.mLock") private void removeAppLPw(@UserIdInt int userId, int targetAppId) { // remove from the installed list if (mInstantGrants == null) { @@ -486,7 +486,7 @@ class InstantAppRegistry { targetAppList.delete(targetAppId); } - @GuardedBy("mService.mPackages") + @GuardedBy("mService.mLock") private void addUninstalledInstantAppLPw(@NonNull PackageParser.Package pkg, @UserIdInt int userId) { InstantAppInfo uninstalledApp = createInstantAppInfoForPackage( @@ -541,13 +541,13 @@ class InstantAppRegistry { } } - @GuardedBy("mService.mPackages") + @GuardedBy("mService.mLock") boolean hasInstantApplicationMetadataLPr(String packageName, int userId) { return hasUninstalledInstantAppStateLPr(packageName, userId) || hasInstantAppMetadataLPr(packageName, userId); } - @GuardedBy("mService.mPackages") + @GuardedBy("mService.mLock") public void deleteInstantApplicationMetadataLPw(@NonNull String packageName, @UserIdInt int userId) { removeUninstalledInstantAppStateLPw((UninstalledInstantAppState state) -> @@ -564,7 +564,7 @@ class InstantAppRegistry { } } - @GuardedBy("mService.mPackages") + @GuardedBy("mService.mLock") private void removeUninstalledInstantAppStateLPw( @NonNull Predicate<UninstalledInstantAppState> criteria, @UserIdInt int userId) { if (mUninstalledInstantApps == null) { @@ -592,7 +592,7 @@ class InstantAppRegistry { } } - @GuardedBy("mService.mPackages") + @GuardedBy("mService.mLock") private boolean hasUninstalledInstantAppStateLPr(String packageName, @UserIdInt int userId) { if (mUninstalledInstantApps == null) { return false; @@ -685,7 +685,7 @@ class InstantAppRegistry { final long now = System.currentTimeMillis(); // Prune first installed instant apps - synchronized (mService.mPackages) { + synchronized (mService.mLock) { allUsers = PackageManagerService.sUserManager.getUserIds(); final int packageCount = mService.mPackages.size(); @@ -768,7 +768,7 @@ class InstantAppRegistry { } // Prune uninstalled instant apps - synchronized (mService.mPackages) { + synchronized (mService.mLock) { // TODO: Track last used time for uninstalled instant apps for better pruning for (int userId : UserManagerService.getInstance().getUserIds()) { // Prune in-memory state @@ -811,7 +811,7 @@ class InstantAppRegistry { return false; } - @GuardedBy("mService.mPackages") + @GuardedBy("mService.mLock") private @Nullable List<InstantAppInfo> getInstalledInstantApplicationsLPr( @UserIdInt int userId) { List<InstantAppInfo> result = null; @@ -866,7 +866,7 @@ class InstantAppRegistry { } } - @GuardedBy("mService.mPackages") + @GuardedBy("mService.mLock") private @Nullable List<InstantAppInfo> getUninstalledInstantApplicationsLPr( @UserIdInt int userId) { List<UninstalledInstantAppState> uninstalledAppStates = @@ -939,7 +939,7 @@ class InstantAppRegistry { return uninstalledAppState.mInstantAppInfo; } - @GuardedBy("mService.mPackages") + @GuardedBy("mService.mLock") private @Nullable List<UninstalledInstantAppState> getUninstalledInstantAppStatesLPr( @UserIdInt int userId) { List<UninstalledInstantAppState> uninstalledAppStates = null; diff --git a/services/core/java/com/android/server/pm/InstructionSets.java b/services/core/java/com/android/server/pm/InstructionSets.java index f326f1d20c46..ec48713b0874 100644 --- a/services/core/java/com/android/server/pm/InstructionSets.java +++ b/services/core/java/com/android/server/pm/InstructionSets.java @@ -22,11 +22,11 @@ import android.os.SystemProperties; import android.text.TextUtils; import android.util.ArraySet; +import dalvik.system.VMRuntime; + import java.util.ArrayList; import java.util.List; -import dalvik.system.VMRuntime; - /** * Provides various methods for obtaining and converting of instruction sets. * @@ -113,12 +113,15 @@ public class InstructionSets { return allInstructionSets; } - public static String getPrimaryInstructionSet(ApplicationInfo info) { - if (info.primaryCpuAbi == null) { + /** + * Calculates the primary instruction set based on the computed Abis of a given package. + */ + public static String getPrimaryInstructionSet(PackageAbiHelper.Abis abis) { + if (abis.primary == null) { return getPreferredInstructionSet(); } - return VMRuntime.getInstructionSet(info.primaryCpuAbi); + return VMRuntime.getInstructionSet(abis.primary); } } diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java index e5a2e777a796..d49ecdda679d 100644 --- a/services/core/java/com/android/server/pm/OtaDexoptService.java +++ b/services/core/java/com/android/server/pm/OtaDexoptService.java @@ -120,7 +120,7 @@ public class OtaDexoptService extends IOtaDexopt.Stub { } final List<PackageParser.Package> important; final List<PackageParser.Package> others; - synchronized (mPackageManagerService.mPackages) { + synchronized (mPackageManagerService.mLock) { // Important: the packages we need to run with ab-ota compiler-reason. important = PackageManagerServiceUtils.getPackagesForDexopt( mPackageManagerService.mPackages.values(), mPackageManagerService, 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..6f46564068d9 --- /dev/null +++ b/services/core/java/com/android/server/pm/PackageAbiHelper.java @@ -0,0 +1,135 @@ +/* + * 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 android.util.Pair; + +import com.android.internal.annotations.VisibleForTesting; + +import java.io.File; +import java.util.Set; + +@VisibleForTesting +interface PackageAbiHelper { + /** + * Derive and get the location of native libraries for the given package, + * which varies depending on where and how the package was installed. + */ + NativeLibraryPaths getNativeLibraryPaths( + PackageParser.Package pkg, File appLib32InstallDir); + + /** + * Calculate the abis 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. + */ + Abis getBundledAppAbis(PackageParser.Package pkg); + + /** + * Derive the ABI of a non-system package located at {@code pkg}. This information + * is derived purely on the basis of the contents of {@code pkg} and {@code cpuAbiOverride}. + * + * If {@code extractLibs} is true, native libraries are extracted from the app if required. + */ + Pair<Abis, NativeLibraryPaths> derivePackageAbi( + PackageParser.Package pkg, String cpuAbiOverride, boolean extractLibs) + throws PackageManagerException; + + /** + * Calculates adjusted 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 scannedPackage} 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. + * + * @return the calculated primary abi that should be set for all non-specified packages + * belonging to the shared user. + */ + @Nullable + String getAdjustedAbiForSharedUser( + Set<PackageSetting> packagesForUser, PackageParser.Package scannedPackage); + + /** + * The native library paths and related properties that should be set on a + * {@link android.content.pm.PackageParser.Package}. + */ + final class NativeLibraryPaths { + public final String nativeLibraryRootDir; + public final boolean nativeLibraryRootRequiresIsa; + public final String nativeLibraryDir; + public final String secondaryNativeLibraryDir; + + @VisibleForTesting + NativeLibraryPaths(String nativeLibraryRootDir, + boolean nativeLibraryRootRequiresIsa, String nativeLibraryDir, + String secondaryNativeLibraryDir) { + this.nativeLibraryRootDir = nativeLibraryRootDir; + this.nativeLibraryRootRequiresIsa = nativeLibraryRootRequiresIsa; + this.nativeLibraryDir = nativeLibraryDir; + this.secondaryNativeLibraryDir = secondaryNativeLibraryDir; + } + + public void applyTo(PackageParser.Package pkg) { + pkg.applicationInfo.nativeLibraryRootDir = nativeLibraryRootDir; + pkg.applicationInfo.nativeLibraryRootRequiresIsa = nativeLibraryRootRequiresIsa; + pkg.applicationInfo.nativeLibraryDir = nativeLibraryDir; + pkg.applicationInfo.secondaryNativeLibraryDir = secondaryNativeLibraryDir; + } + } + + /** + * The primary and secondary ABIs that should be set on a package and its package setting. + */ + final class Abis { + public final String primary; + public final String secondary; + + @VisibleForTesting + Abis(String primary, String secondary) { + this.primary = primary; + this.secondary = secondary; + } + + Abis(PackageParser.Package pkg) { + this(pkg.applicationInfo.primaryCpuAbi, pkg.applicationInfo.secondaryCpuAbi); + } + + public void applyTo(PackageParser.Package pkg) { + pkg.applicationInfo.primaryCpuAbi = primary; + pkg.applicationInfo.secondaryCpuAbi = secondary; + } + public void applyTo(PackageSetting pkgSetting) { + // 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 derived. + if (pkgSetting != null) { + pkgSetting.primaryCpuAbiString = primary; + pkgSetting.secondaryCpuAbiString = secondary; + } + } + } +} 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..1d3d24c27041 --- /dev/null +++ b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java @@ -0,0 +1,528 @@ +/* + * 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.Pair; +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.Set; + +final class PackageAbiHelperImpl implements PackageAbiHelper { + + 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); + } + } + } + + @Override + public NativeLibraryPaths getNativeLibraryPaths( + PackageParser.Package pkg, File appLib32InstallDir) { + return getNativeLibraryPaths(new Abis(pkg), appLib32InstallDir, pkg.codePath, + pkg.applicationInfo.sourceDir, pkg.applicationInfo.isSystemApp(), + pkg.applicationInfo.isUpdatedSystemApp()); + } + + private static NativeLibraryPaths getNativeLibraryPaths(final Abis abis, + final File appLib32InstallDir, final String codePath, final String sourceDir, + final boolean isSystemApp, final boolean isUpdatedSystemApp) { + final File codeFile = new File(codePath); + final boolean bundledApp = isSystemApp && !isUpdatedSystemApp; + + final String nativeLibraryRootDir; + final boolean nativeLibraryRootRequiresIsa; + final String nativeLibraryDir; + final String secondaryNativeLibraryDir; + + 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(sourceDir); + final boolean is64Bit = VMRuntime.is64BitInstructionSet( + getPrimaryInstructionSet(abis)); + + // 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; + nativeLibraryRootDir = Environment.buildPath(new File(apkRoot), libDir, + apkName).getAbsolutePath(); + + if (abis.secondary != null) { + final String secondaryLibDir = is64Bit ? LIB_DIR_NAME : LIB64_DIR_NAME; + secondaryNativeLibraryDir = Environment.buildPath(new File(apkRoot), + secondaryLibDir, apkName).getAbsolutePath(); + } else { + secondaryNativeLibraryDir = null; + } + } else { + final String apkName = deriveCodePathName(codePath); + nativeLibraryRootDir = new File(appLib32InstallDir, apkName) + .getAbsolutePath(); + secondaryNativeLibraryDir = null; + } + + nativeLibraryRootRequiresIsa = false; + nativeLibraryDir = nativeLibraryRootDir; + } else { + // Cluster install + nativeLibraryRootDir = new File(codeFile, LIB_DIR_NAME).getAbsolutePath(); + nativeLibraryRootRequiresIsa = true; + + nativeLibraryDir = new File(nativeLibraryRootDir, + getPrimaryInstructionSet(abis)).getAbsolutePath(); + + if (abis.secondary != null) { + secondaryNativeLibraryDir = new File(nativeLibraryRootDir, + VMRuntime.getInstructionSet(abis.secondary)).getAbsolutePath(); + } else { + secondaryNativeLibraryDir = null; + } + } + return new NativeLibraryPaths(nativeLibraryRootDir, nativeLibraryRootRequiresIsa, + nativeLibraryDir, secondaryNativeLibraryDir); + } + + @Override + public Abis getBundledAppAbis(PackageParser.Package pkg) { + 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); + final Abis abis = getBundledAppAbi(pkg, apkRoot, apkName); + return abis; + } + + /** + * 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 Abis getBundledAppAbi(PackageParser.Package pkg, String apkRoot, String apkName) { + final File codeFile = new File(pkg.codePath); + + final boolean has64BitLibs; + final boolean has32BitLibs; + + final String primaryCpuAbi; + final String secondaryCpuAbi; + 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. + + primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[0]; + secondaryCpuAbi = null; + } else if (has32BitLibs && !has64BitLibs) { + // The package has 32 bit libs but not 64 bit libs. Its primary + // ABI should be 32 bit. + + primaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0]; + 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())) { + primaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[0]; + secondaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0]; + } else { + primaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0]; + secondaryCpuAbi = Build.SUPPORTED_64_BIT_ABIS[0]; + } + } else { + primaryCpuAbi = null; + secondaryCpuAbi = null; + } + return new Abis(primaryCpuAbi, secondaryCpuAbi); + } + + @Override + public Pair<Abis, NativeLibraryPaths> 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. + final NativeLibraryPaths initialLibraryPaths = getNativeLibraryPaths(new Abis(pkg), + PackageManagerService.sAppLib32InstallDir, pkg.codePath, + pkg.applicationInfo.sourceDir, pkg.applicationInfo.isSystemApp(), + pkg.applicationInfo.isUpdatedSystemApp()); + + // 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 = initialLibraryPaths.nativeLibraryRootDir; + final boolean useIsaSpecificSubdirs = initialLibraryPaths.nativeLibraryRootRequiresIsa; + + String primaryCpuAbi = null; + String secondaryCpuAbi = null; + + 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. + primaryCpuAbi = null; + 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"); + } + 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) { + secondaryCpuAbi = primaryCpuAbi; + primaryCpuAbi = abi; + } else { + secondaryCpuAbi = abi; + } + } else { + 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"); + } + primaryCpuAbi = abiList[copyRet]; + } else if (copyRet == PackageManager.NO_NATIVE_LIBRARIES + && cpuAbiOverride != null) { + primaryCpuAbi = cpuAbiOverride; + } else if (needsRenderScriptOverride) { + 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. + + final Abis abis = new Abis(primaryCpuAbi, secondaryCpuAbi); + return new Pair<>(abis, + getNativeLibraryPaths(abis, PackageManagerService.sAppLib32InstallDir, + pkg.codePath, pkg.applicationInfo.sourceDir, + pkg.applicationInfo.isSystemApp(), + pkg.applicationInfo.isUpdatedSystemApp())); + } + + /** + * 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 String getAdjustedAbiForSharedUser( + Set<PackageSetting> packagesForUser, PackageParser.Package scannedPackage) { + 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)) { + continue; + } + if (ps.primaryCpuAbiString == null) { + continue; + } + + final String instructionSet = + VMRuntime.getInstructionSet(ps.primaryCpuAbiString); + if (requiredInstructionSet != null && !requiredInstructionSet.equals(instructionSet)) { + // 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) { + return null; + } + final 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; + } else { + // requirer == null implies that we're updating all ABIs in the set to + // match scannedPackage. + adjustedAbi = scannedPackage.applicationInfo.primaryCpuAbi; + } + return adjustedAbi; + } +} diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index dfdefe12f072..8960dfb7106c 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; @@ -314,6 +313,8 @@ import com.android.server.pm.permission.BasePermission; import com.android.server.pm.permission.PermissionManagerService; import com.android.server.pm.permission.PermissionManagerServiceInternal; import com.android.server.pm.permission.PermissionsState; +import com.android.server.policy.PermissionPolicyInternal; +import com.android.server.policy.PermissionPolicyInternal.OnInitializedCallback; import com.android.server.security.VerityUtils; import com.android.server.storage.DeviceStorageMonitorInternal; import com.android.server.utils.TimingsTraceAndSlog; @@ -429,7 +430,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; @@ -641,7 +642,7 @@ public class PackageManagerService extends IPackageManager.Stub final boolean mIsPreNMR1Upgrade; final boolean mIsPreQUpgrade; - @GuardedBy("mPackages") + @GuardedBy("mLock") private boolean mDexOptDialogShown; // Used for privilege escalation. MUST NOT BE CALLED WITH mPackages @@ -653,7 +654,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"); // ---------------------------------------------------------------- @@ -665,15 +667,18 @@ public class PackageManagerService extends IPackageManager.Stub // ---------------------------------------------------------------- - // Keys are String (package name), values are Package. This also serves - // as the lock for the global state. Methods that must be called with - // this lock held have the prefix "LP". - @GuardedBy("mPackages") + // Lock for global state used when modifying package state or settings. + // Methods that must be called with this lock held have + // the suffix "Locked". Some methods may use the legacy the suffix "LP" + final Object mLock; + + // Keys are String (package name), values are Package. + @GuardedBy("mLock") final ArrayMap<String, PackageParser.Package> mPackages = new ArrayMap<>(); // Keys are isolated uids and values are the uid of the application // that created the isolated proccess. - @GuardedBy("mPackages") + @GuardedBy("mLock") final SparseIntArray mIsolatedOwners = new SparseIntArray(); /** @@ -692,7 +697,7 @@ public class PackageManagerService extends IPackageManager.Stub */ boolean mPromoteSystemApps; - @GuardedBy("mPackages") + @GuardedBy("mLock") final Settings mSettings; /** @@ -702,7 +707,7 @@ public class PackageManagerService extends IPackageManager.Stub * * @see PackageFreezer */ - @GuardedBy("mPackages") + @GuardedBy("mLock") final ArraySet<String> mFrozenPackages = new ArraySet<>(); final ProtectedPackages mProtectedPackages; @@ -719,31 +724,57 @@ public class PackageManagerService extends IPackageManager.Stub private final InstantAppRegistry mInstantAppRegistry; - @GuardedBy("mPackages") + @GuardedBy("mLock") int mChangedPackagesSequenceNumber; /** * List of changed [installed, removed or updated] packages. * mapping from user id -> sequence number -> package name */ - @GuardedBy("mPackages") + @GuardedBy("mLock") final SparseArray<SparseArray<String>> mChangedPackages = new SparseArray<>(); /** * The sequence number of the last change to a package. * mapping from user id -> package name -> sequence number */ - @GuardedBy("mPackages") + @GuardedBy("mLock") final SparseArray<Map<String, Integer>> mChangedPackagesSequenceNumbers = new SparseArray<>(); - @GuardedBy("mPackages") + @GuardedBy("mLock") final private ArraySet<PackageListObserver> mPackageListObservers = new ArraySet<>(); - @GuardedBy("mPackages") + @GuardedBy("mLock") private final SparseIntArray mDefaultPermissionsGrantedUsers = new SparseIntArray(); private final ModuleInfoProvider mModuleInfoProvider; 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; + } + } + + private final AppsFilter mAppsFilter; + class PackageParserCallback implements PackageParser.Callback { @Override public final boolean hasFeature(String feature) { return PackageManagerService.this.hasSystemFeature(feature, 0); @@ -813,7 +844,7 @@ public class PackageManagerService extends IPackageManager.Stub String[] getStaticOverlayPaths(String targetPackageName, String targetPath) { List<PackageParser.Package> overlayPackages; synchronized (mInstallLock) { - synchronized (mPackages) { + synchronized (mLock) { overlayPackages = getStaticOverlayPackages( mPackages.values(), targetPackageName); } @@ -837,7 +868,7 @@ public class PackageManagerService extends IPackageManager.Stub List<PackageParser.Package> mOverlayPackages = null; void findStaticOverlayPackages() { - synchronized (mPackages) { + synchronized (mLock) { for (PackageParser.Package p : mPackages.values()) { if (p.mOverlayIsStatic) { if (mOverlayPackages == null) { @@ -1021,7 +1052,7 @@ public class PackageManagerService extends IPackageManager.Stub PackageParser.ActivityIntentInfo filter = filters.get(m); domainsSet.addAll(filter.getHostsList()); } - synchronized (mPackages) { + synchronized (mLock) { if (mSettings.createIntentFilterVerificationIfNeededLPw( packageName, domainsSet) != null) { scheduleWriteSettingsLocked(); @@ -1089,7 +1120,7 @@ public class PackageManagerService extends IPackageManager.Stub final String packageName = ivs.getPackageName(); IntentFilterVerificationInfo ivi; - synchronized (mPackages) { + synchronized (mLock) { ivi = mSettings.getIntentFilterVerificationLPr(packageName); } if (ivi == null) { @@ -1098,7 +1129,7 @@ public class PackageManagerService extends IPackageManager.Stub return; } - synchronized (mPackages) { + synchronized (mLock) { if (verified) { ivi.setStatus(INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS); } else { @@ -1210,7 +1241,7 @@ public class PackageManagerService extends IPackageManager.Stub IntentFilterVerificationState ivs = new IntentFilterVerificationState( verifierUid, userId, packageName); ivs.setPendingState(); - synchronized (mPackages) { + synchronized (mLock) { mIntentFilterVerificationStates.append(verificationId, ivs); mCurrentIntentFilterVerifications.add(verificationId); } @@ -1423,7 +1454,7 @@ public class PackageManagerService extends IPackageManager.Stub int size = 0; int uids[]; Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); - synchronized (mPackages) { + synchronized (mLock) { size = mPendingBroadcasts.size(); if (size <= 0) { // Nothing to be done. Just return @@ -1531,7 +1562,7 @@ public class PackageManagerService extends IPackageManager.Stub } break; case WRITE_SETTINGS: { Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); - synchronized (mPackages) { + synchronized (mLock) { removeMessages(WRITE_SETTINGS); removeMessages(WRITE_PACKAGE_RESTRICTIONS); mSettings.writeLPr(); @@ -1541,7 +1572,7 @@ public class PackageManagerService extends IPackageManager.Stub } break; case WRITE_PACKAGE_RESTRICTIONS: { Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); - synchronized (mPackages) { + synchronized (mLock) { removeMessages(WRITE_PACKAGE_RESTRICTIONS); for (int userId : mDirtyUsers) { mSettings.writePackageRestrictionsLPr(userId); @@ -1552,7 +1583,7 @@ public class PackageManagerService extends IPackageManager.Stub } break; case WRITE_PACKAGE_LIST: { Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); - synchronized (mPackages) { + synchronized (mLock) { removeMessages(WRITE_PACKAGE_LIST); mSettings.writePackageListLPr(msg.arg1); } @@ -1789,7 +1820,7 @@ public class PackageManagerService extends IPackageManager.Stub res.pkg, callingUid); } - synchronized (mPackages) { + synchronized (mLock) { mInstantAppRegistry.onPackageInstalledLPw(res.pkg, res.newUsers); } @@ -1950,7 +1981,7 @@ public class PackageManagerService extends IPackageManager.Stub if (packageIsBrowser(packageName, userId)) { // If this browser is restored from user's backup, do not clear // default-browser state for this user - synchronized (mPackages) { + synchronized (mLock) { final PackageSetting pkgSetting = mSettings.mPackages.get(packageName); if (pkgSetting.getInstallReason(userId) != PackageManager.INSTALL_REASON_DEVICE_RESTORE) { @@ -2007,8 +2038,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); } @@ -2030,7 +2061,8 @@ public class PackageManagerService extends IPackageManager.Stub for (String packageName : packages) { PackageSetting setting = mSettings.mPackages.get(packageName); - if (setting != null && filterAppAccessLPr(setting, callingUid, callingUserId)) { + if (setting != null + && shouldFilterApplicationLocked(setting, callingUid, callingUserId)) { notifyInstallObserver(packageName); } } @@ -2078,6 +2110,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) { @@ -2130,7 +2163,7 @@ public class PackageManagerService extends IPackageManager.Stub } // Remove any apps installed on the forgotten volume - synchronized (mPackages) { + synchronized (mLock) { final List<PackageSetting> packages = mSettings.getVolumePackagesLPr(fsUuid); for (PackageSetting ps : packages) { Slog.d(TAG, "Destroying " + ps.name + " because volume was forgotten"); @@ -2206,9 +2239,10 @@ public class PackageManagerService extends IPackageManager.Stub boolean factoryTest, boolean onlyCore) { // Self-check for initial settings. PackageManagerServiceCompilerMapping.checkProperties(); + final Object packageLock = new Object(); PackageManagerService m = new PackageManagerService(context, installer, - factoryTest, onlyCore); + factoryTest, onlyCore, packageLock); m.enableSystemUserPackages(); ServiceManager.addService("package", m); final PackageManagerNative pmn = m.new PackageManagerNative(); @@ -2241,7 +2275,7 @@ public class PackageManagerService extends IPackageManager.Stub List<String> allAps = queryHelper.queryApps(0, /* systemAppsOnly */ false, UserHandle.SYSTEM); final int allAppsSize = allAps.size(); - synchronized (mPackages) { + synchronized (mLock) { for (int i = 0; i < allAppsSize; i++) { String pName = allAps.get(i); PackageSetting pkgSetting = mSettings.mPackages.get(pName); @@ -2300,11 +2334,12 @@ public class PackageManagerService extends IPackageManager.Stub } public PackageManagerService(Context context, Installer installer, boolean factoryTest, - boolean onlyCore) { + boolean onlyCore, Object packageLock) { final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing", Trace.TRACE_TAG_PACKAGE_MANAGER); t.traceBegin("create package manager"); - LockGuard.installLock(mPackages, LockGuard.INDEX_PACKAGES); + mLock = packageLock; + LockGuard.installLock(mLock, LockGuard.INDEX_PACKAGES); EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START, SystemClock.uptimeMillis()); @@ -2313,7 +2348,6 @@ public class PackageManagerService extends IPackageManager.Stub } mContext = context; - mFactoryTest = factoryTest; mOnlyCore = onlyCore; mMetrics = new DisplayMetrics(); @@ -2323,23 +2357,28 @@ public class PackageManagerService extends IPackageManager.Stub t.traceBegin("createSubComponents"); // CHECKSTYLE:OFF IndentationCheck synchronized (mInstallLock) { - synchronized (mPackages) { + synchronized (mLock) { // Expose private service for system components to use. LocalServices.addService( PackageManagerInternal.class, new PackageManagerInternalImpl()); sUserManager = new UserManagerService(context, this, - new UserDataPreparer(mInstaller, mInstallLock, mContext, mOnlyCore), mPackages); + new UserDataPreparer(mInstaller, mInstallLock, mContext, mOnlyCore), + mLock); mComponentResolver = new ComponentResolver(sUserManager, LocalServices.getService(PackageManagerInternal.class), - mPackages); + mLock); mPermissionManager = PermissionManagerService.create(context, - mPackages /*externalLock*/); + mLock /*externalLock*/); mPermissionManagerService = (IPermissionManager) ServiceManager.getService("permissionmgr"); mSettings = new Settings(Environment.getDataDirectory(), - mPermissionManager.getPermissionSettings(), mPackages); + mPermissionManager.getPermissionSettings(), mLock); } } + + // 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(); @@ -2397,10 +2436,12 @@ public class PackageManagerService extends IPackageManager.Stub mProtectedPackages = new ProtectedPackages(mContext); mApexManager = ApexManager.create(context); + mAppsFilter = AppsFilter.create(context); + // CHECKSTYLE:OFF IndentationCheck synchronized (mInstallLock) { // writer - synchronized (mPackages) { + synchronized (mLock) { mHandlerThread = new ServiceThread(TAG, Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/); mHandlerThread.start(); @@ -3048,7 +3089,9 @@ 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*/); + applyAdjustedAbiToSharedUser(setting, null /*scannedPackage*/, + mInjector.getAbiHelper().getAdjustedAbiForSharedUser( + 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); @@ -3131,7 +3174,7 @@ public class PackageManagerService extends IPackageManager.Stub int count = 0; for (String pkgName : deferPackages) { PackageParser.Package pkg = null; - synchronized (mPackages) { + synchronized (mLock) { PackageSetting ps = mSettings.getPackageLPr(pkgName); if (ps != null && ps.getInstalled(UserHandle.USER_SYSTEM)) { pkg = ps.pkg; @@ -3278,7 +3321,7 @@ public class PackageManagerService extends IPackageManager.Stub MetricsLogger.histogram(null, "ota_package_manager_init_time", (int) (SystemClock.uptimeMillis() - startTime)); } - } // synchronized (mPackages) + } // synchronized (mLock) } // synchronized (mInstallLock) // CHECKSTYLE:ON IndentationCheck @@ -3294,7 +3337,7 @@ public class PackageManagerService extends IPackageManager.Stub // The initial scanning above does many calls into installd while // holding the mPackages lock, but we're mostly interested in yelling // once we have a booted system. - mInstaller.setWarnIfHeld(mPackages); + mInstaller.setWarnIfHeld(mLock); PackageParser.readConfigUseRoundIcon(mContext.getResources()); @@ -3376,7 +3419,7 @@ public class PackageManagerService extends IPackageManager.Stub try (PackageFreezer freezer = freezePackage(stubPkg.packageName, "setEnabledSetting")) { pkg = installStubPackageLI(stubPkg, parseFlags, 0 /*scanFlags*/); - synchronized (mPackages) { + synchronized (mLock) { prepareAppDataAfterInstallLIF(pkg); try { updateSharedLibrariesLocked(pkg, null, mPackages); @@ -3390,7 +3433,7 @@ public class PackageManagerService extends IPackageManager.Stub // Whoops! Something went very wrong; roll back to the stub and disable the package try (PackageFreezer freezer = freezePackage(stubPkg.packageName, "setEnabledSetting")) { - synchronized (mPackages) { + synchronized (mLock) { // NOTE: Ensure the system package is enabled; even for a compressed stub. // If we don't, installing the system package fails during scan enableSystemPackageLPw(stubPkg); @@ -3403,7 +3446,7 @@ public class PackageManagerService extends IPackageManager.Stub Slog.wtf(TAG, "Failed to restore system package:" + stubPkg.packageName, pme); } finally { // Disable the package; the stub by itself is not runnable - synchronized (mPackages) { + synchronized (mLock) { final PackageSetting stubPs = mSettings.mPackages.get(stubPkg.packageName); if (stubPs != null) { stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, @@ -3433,7 +3476,7 @@ public class PackageManagerService extends IPackageManager.Stub if (scanFile == null) { throw new PackageManagerException("Unable to decompress stub at " + stubPkg.codePath); } - synchronized (mPackages) { + synchronized (mLock) { mSettings.disableSystemPackageLPw(stubPkg.packageName, true /*replaced*/); } removePackageLI(stubPkg, true /*chatty*/); @@ -3510,7 +3553,7 @@ public class PackageManagerService extends IPackageManager.Stub return dstCodePath; } - @GuardedBy("mPackages") + @GuardedBy("mLock") private void updateInstantAppInstallerLocked(String modifiedPackage) { // we're only interested in updating the installer appliction when 1) it's not // already set or 2) the modified package is the installer @@ -3631,7 +3674,7 @@ public class PackageManagerService extends IPackageManager.Stub } private @NonNull String getRequiredSharedLibraryLPr(String name, int version) { - synchronized (mPackages) { + synchronized (mLock) { SharedLibraryInfo libraryInfo = getSharedLibraryInfoLPr(name, version); if (libraryInfo == null) { throw new IllegalStateException("Missing required shared library:" + name); @@ -3731,7 +3774,7 @@ public class PackageManagerService extends IPackageManager.Stub if (getInstantAppPackageName(Binder.getCallingUid()) != null) { return null; } - synchronized (mPackages) { + synchronized (mLock) { final Pair<ComponentName, String> instantAppResolver = getInstantAppResolverLPr(); if (instantAppResolver == null) { return null; @@ -3796,7 +3839,7 @@ public class PackageManagerService extends IPackageManager.Stub return null; } - @GuardedBy("mPackages") + @GuardedBy("mLock") private @Nullable ActivityInfo getInstantAppInstallerLPr() { String[] orderedActions = Build.IS_ENG ? new String[]{ @@ -3863,7 +3906,7 @@ public class PackageManagerService extends IPackageManager.Stub return matches.get(0).getComponentInfo().getComponentName(); } - @GuardedBy("mPackages") + @GuardedBy("mLock") private void primeDomainVerificationsLPw(int userId) { if (DEBUG_DOMAIN_VERIFICATION) { Slog.d(TAG, "Priming domain verifications in user " + userId); @@ -3993,7 +4036,7 @@ public class PackageManagerService extends IPackageManager.Stub // and 2) ephemeral apps that have explicitly interacted with it // * Ephemeral apps can only see their own data and exposed installed apps // * Holding a signature permission allows seeing instant apps - if (filterAppAccessLPr(ps, callingUid, userId)) { + if (shouldFilterApplicationLocked(ps, callingUid, userId)) { return null; } @@ -4058,9 +4101,9 @@ public class PackageManagerService extends IPackageManager.Stub throw new SecurityException("Instant applications don't have access to this method"); } final boolean userKeyUnlocked = StorageManager.isUserKeyUnlocked(userId); - synchronized (mPackages) { + synchronized (mLock) { final PackageSetting ps = mSettings.mPackages.get(packageName); - if (ps == null || filterAppAccessLPr(ps, callingUid, userId)) { + if (ps == null || shouldFilterApplicationLocked(ps, callingUid, userId)) { throw new SecurityException("Package " + packageName + " was not found!"); } @@ -4089,11 +4132,11 @@ public class PackageManagerService extends IPackageManager.Stub final int callingUid = Binder.getCallingUid(); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, false /*checkShell*/, "is package available"); - synchronized (mPackages) { + synchronized (mLock) { PackageParser.Package p = mPackages.get(packageName); if (p != null) { final PackageSetting ps = (PackageSetting) p.mExtras; - if (filterAppAccessLPr(ps, callingUid, userId)) { + if (shouldFilterApplicationLocked(ps, callingUid, userId)) { return false; } if (ps != null) { @@ -4134,7 +4177,7 @@ public class PackageManagerService extends IPackageManager.Stub false /* requireFullPermission */, false /* checkShell */, "get package info"); // reader - synchronized (mPackages) { + synchronized (mLock) { // Normalize package name to handle renamed packages and static libs packageName = resolveInternalPackageNameLPr(packageName, versionCode); @@ -4150,7 +4193,7 @@ public class PackageManagerService extends IPackageManager.Stub if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) { return null; } - if (filterAppAccessLPr(ps, filterCallingUid, userId)) { + if (shouldFilterApplicationLocked(ps, filterCallingUid, userId)) { return null; } return generatePackageInfo(ps, flags, userId); @@ -4168,7 +4211,7 @@ public class PackageManagerService extends IPackageManager.Stub if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) { return null; } - if (ps != null && filterAppAccessLPr(ps, filterCallingUid, userId)) { + if (ps != null && shouldFilterApplicationLocked(ps, filterCallingUid, userId)) { return null; } return generatePackageInfo((PackageSetting)p.mExtras, flags, userId); @@ -4179,7 +4222,7 @@ public class PackageManagerService extends IPackageManager.Stub if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) { return null; } - if (filterAppAccessLPr(ps, filterCallingUid, userId)) { + if (shouldFilterApplicationLocked(ps, filterCallingUid, userId)) { return null; } return generatePackageInfo(ps, flags, userId); @@ -4250,8 +4293,8 @@ public class PackageManagerService extends IPackageManager.Stub * * @see #canViewInstantApps(int, int) */ - @GuardedBy("mPackages") - private boolean filterAppAccessLPr(@Nullable PackageSetting ps, int callingUid, + @GuardedBy("mLock") + private boolean shouldFilterApplicationLocked(@Nullable PackageSetting ps, int callingUid, @Nullable ComponentName component, @ComponentType int componentType, int userId) { // if we're in an isolated process, get the real calling UID if (Process.isIsolated(callingUid)) { @@ -4302,18 +4345,21 @@ public class PackageManagerService extends IPackageManager.Stub return !mInstantAppRegistry.isInstantAccessGranted( userId, UserHandle.getAppId(callingUid), ps.appId); } - return false; + int appId = UserHandle.getAppId(callingUid); + final SettingBase callingPs = mSettings.getSettingLPr(appId); + return mAppsFilter.shouldFilterApplication(callingUid, callingPs, ps, userId); } /** - * @see #filterAppAccessLPr(PackageSetting, int, ComponentName, int, int) + * @see #shouldFilterApplicationLocked(PackageSetting, int, ComponentName, int, int) */ - @GuardedBy("mPackages") - private boolean filterAppAccessLPr(@Nullable PackageSetting ps, int callingUid, int userId) { - return filterAppAccessLPr(ps, callingUid, null, TYPE_UNKNOWN, userId); + @GuardedBy("mLock") + private boolean shouldFilterApplicationLocked( + @Nullable PackageSetting ps, int callingUid, int userId) { + return shouldFilterApplicationLocked(ps, callingUid, null, TYPE_UNKNOWN, userId); } - @GuardedBy("mPackages") + @GuardedBy("mLock") private boolean filterSharedLibPackageLPr(@Nullable PackageSetting ps, int uid, int userId, int flags) { // Callers can access only the libs they depend on, otherwise they need to explicitly @@ -4376,7 +4422,7 @@ public class PackageManagerService extends IPackageManager.Stub } final String[] out = new String[names.length]; // reader - synchronized (mPackages) { + synchronized (mLock) { final int callingUserId = UserHandle.getUserId(callingUid); final boolean canViewInstantApps = canViewInstantApps(callingUid, callingUserId); for (int i=names.length-1; i>=0; i--) { @@ -4403,7 +4449,7 @@ public class PackageManagerService extends IPackageManager.Stub } final String[] out = new String[names.length]; // reader - synchronized (mPackages) { + synchronized (mLock) { final int callingUserId = UserHandle.getUserId(callingUid); final boolean canViewInstantApps = canViewInstantApps(callingUid, callingUserId); for (int i=names.length-1; i>=0; i--) { @@ -4433,11 +4479,11 @@ public class PackageManagerService extends IPackageManager.Stub false /*requireFullPermission*/, false /*checkShell*/, "getPackageUid"); // reader - synchronized (mPackages) { + synchronized (mLock) { final PackageParser.Package p = mPackages.get(packageName); if (p != null && p.isMatch(flags)) { PackageSetting ps = (PackageSetting) p.mExtras; - if (filterAppAccessLPr(ps, callingUid, userId)) { + if (shouldFilterApplicationLocked(ps, callingUid, userId)) { return -1; } return UserHandle.getUid(userId, p.applicationInfo.uid); @@ -4445,7 +4491,7 @@ public class PackageManagerService extends IPackageManager.Stub if ((flags & MATCH_KNOWN_PACKAGES) != 0) { final PackageSetting ps = mSettings.mPackages.get(packageName); if (ps != null && ps.isMatch(flags) - && !filterAppAccessLPr(ps, callingUid, userId)) { + && !shouldFilterApplicationLocked(ps, callingUid, userId)) { return UserHandle.getUid(userId, ps.appId); } } @@ -4463,11 +4509,11 @@ public class PackageManagerService extends IPackageManager.Stub false /*requireFullPermission*/, false /*checkShell*/, "getPackageGids"); // reader - synchronized (mPackages) { + synchronized (mLock) { final PackageParser.Package p = mPackages.get(packageName); if (p != null && p.isMatch(flags)) { PackageSetting ps = (PackageSetting) p.mExtras; - if (filterAppAccessLPr(ps, callingUid, userId)) { + if (shouldFilterApplicationLocked(ps, callingUid, userId)) { return null; } // TODO: Shouldn't this be checking for package installed state for userId and @@ -4477,7 +4523,7 @@ public class PackageManagerService extends IPackageManager.Stub if ((flags & MATCH_KNOWN_PACKAGES) != 0) { final PackageSetting ps = mSettings.mPackages.get(packageName); if (ps != null && ps.isMatch(flags) - && !filterAppAccessLPr(ps, callingUid, userId)) { + && !shouldFilterApplicationLocked(ps, callingUid, userId)) { return ps.getPermissionsState().computeGids(userId); } } @@ -4497,7 +4543,7 @@ public class PackageManagerService extends IPackageManager.Stub return null; } - @GuardedBy("mPackages") + @GuardedBy("mLock") private ApplicationInfo generateApplicationInfoFromSettingsLPw(String packageName, int flags, int filterCallingUid, int userId) { if (!sUserManager.exists(userId)) return null; @@ -4506,7 +4552,7 @@ public class PackageManagerService extends IPackageManager.Stub if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) { return null; } - if (filterAppAccessLPr(ps, filterCallingUid, userId)) { + if (shouldFilterApplicationLocked(ps, filterCallingUid, userId)) { return null; } if (ps.pkg == null) { @@ -4549,7 +4595,7 @@ public class PackageManagerService extends IPackageManager.Stub } // writer - synchronized (mPackages) { + synchronized (mLock) { // Normalize package name to handle renamed packages and static libs packageName = resolveInternalPackageNameLPr(packageName, PackageManager.VERSION_CODE_HIGHEST); @@ -4564,7 +4610,7 @@ public class PackageManagerService extends IPackageManager.Stub if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) { return null; } - if (filterAppAccessLPr(ps, filterCallingUid, userId)) { + if (shouldFilterApplicationLocked(ps, filterCallingUid, userId)) { return null; } // Note: isEnabledLP() does not apply here - always return info @@ -4587,7 +4633,7 @@ public class PackageManagerService extends IPackageManager.Stub return null; } - @GuardedBy("mPackages") + @GuardedBy("mLock") private String normalizePackageNameLPr(String packageName) { String normalizedPackageName = mSettings.getRenamedPackageLPr(packageName); return normalizedPackageName != null ? normalizedPackageName : packageName; @@ -4742,7 +4788,7 @@ public class PackageManagerService extends IPackageManager.Stub List<VersionedPackage> packagesToDelete = null; final long now = System.currentTimeMillis(); - synchronized (mPackages) { + synchronized (mLock) { final int[] allUsers = sUserManager.getUserIds(); final int libCount = mSharedLibraries.size(); for (int i = 0; i < libCount; i++) { @@ -4980,14 +5026,15 @@ public class PackageManagerService extends IPackageManager.Stub false /* requireFullPermission */, false /* checkShell */, "get activity info"); } - synchronized (mPackages) { + synchronized (mLock) { PackageParser.Activity a = mComponentResolver.getActivity(component); if (DEBUG_PACKAGE_INFO) Log.v(TAG, "getActivityInfo " + component + ": " + a); if (a != null && mSettings.isEnabledAndMatchLPr(a.info, flags, userId)) { PackageSetting ps = mSettings.mPackages.get(component.getPackageName()); if (ps == null) return null; - if (filterAppAccessLPr(ps, filterCallingUid, component, TYPE_ACTIVITY, userId)) { + if (shouldFilterApplicationLocked( + ps, filterCallingUid, component, TYPE_ACTIVITY, userId)) { return null; } return PackageParser.generateActivityInfo( @@ -5020,7 +5067,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public boolean activitySupportsIntent(ComponentName component, Intent intent, String resolvedType) { - synchronized (mPackages) { + synchronized (mLock) { if (component.equals(mResolveComponentName)) { // The resolver supports EVERYTHING! return true; @@ -5035,7 +5082,8 @@ public class PackageManagerService extends IPackageManager.Stub if (ps == null) { return false; } - if (filterAppAccessLPr(ps, callingUid, component, TYPE_ACTIVITY, callingUserId)) { + if (shouldFilterApplicationLocked( + ps, callingUid, component, TYPE_ACTIVITY, callingUserId)) { return false; } for (int i=0; i<a.intents.size(); i++) { @@ -5055,14 +5103,15 @@ public class PackageManagerService extends IPackageManager.Stub flags = updateFlagsForComponent(flags, userId, component); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */, false /* checkShell */, "get receiver info"); - synchronized (mPackages) { + synchronized (mLock) { PackageParser.Activity a = mComponentResolver.getReceiver(component); if (DEBUG_PACKAGE_INFO) Log.v( TAG, "getReceiverInfo " + component + ": " + a); if (a != null && mSettings.isEnabledAndMatchLPr(a.info, flags, userId)) { PackageSetting ps = mSettings.mPackages.get(component.getPackageName()); if (ps == null) return null; - if (filterAppAccessLPr(ps, callingUid, component, TYPE_RECEIVER, userId)) { + if (shouldFilterApplicationLocked( + ps, callingUid, component, TYPE_RECEIVER, userId)) { return null; } return PackageParser.generateActivityInfo( @@ -5096,7 +5145,7 @@ public class PackageManagerService extends IPackageManager.Stub || mContext.checkCallingOrSelfPermission( Manifest.permission.ACCESS_SHARED_LIBRARIES) == PERMISSION_GRANTED; - synchronized (mPackages) { + synchronized (mLock) { List<SharedLibraryInfo> result = null; final int libCount = mSharedLibraries.size(); @@ -5165,7 +5214,7 @@ public class PackageManagerService extends IPackageManager.Stub return null; } - synchronized (mPackages) { + synchronized (mLock) { List<SharedLibraryInfo> result = null; int libraryCount = mSharedLibraries.size(); @@ -5214,7 +5263,7 @@ public class PackageManagerService extends IPackageManager.Stub } } - @GuardedBy("mPackages") + @GuardedBy("mLock") private List<VersionedPackage> getPackagesUsingSharedLibraryLPr( SharedLibraryInfo libInfo, int flags, int userId) { List<VersionedPackage> versionedPackages = null; @@ -5269,14 +5318,15 @@ public class PackageManagerService extends IPackageManager.Stub flags = updateFlagsForComponent(flags, userId, component); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */, false /* checkShell */, "get service info"); - synchronized (mPackages) { + synchronized (mLock) { PackageParser.Service s = mComponentResolver.getService(component); if (DEBUG_PACKAGE_INFO) Log.v( TAG, "getServiceInfo " + component + ": " + s); if (s != null && mSettings.isEnabledAndMatchLPr(s.info, flags, userId)) { PackageSetting ps = mSettings.mPackages.get(component.getPackageName()); if (ps == null) return null; - if (filterAppAccessLPr(ps, callingUid, component, TYPE_SERVICE, userId)) { + if (shouldFilterApplicationLocked( + ps, callingUid, component, TYPE_SERVICE, userId)) { return null; } return PackageParser.generateServiceInfo( @@ -5293,14 +5343,15 @@ public class PackageManagerService extends IPackageManager.Stub flags = updateFlagsForComponent(flags, userId, component); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */, false /* checkShell */, "get provider info"); - synchronized (mPackages) { + synchronized (mLock) { PackageParser.Provider p = mComponentResolver.getProvider(component); if (DEBUG_PACKAGE_INFO) Log.v( TAG, "getProviderInfo " + component + ": " + p); if (p != null && mSettings.isEnabledAndMatchLPr(p.info, flags, userId)) { PackageSetting ps = mSettings.mPackages.get(component.getPackageName()); if (ps == null) return null; - if (filterAppAccessLPr(ps, callingUid, component, TYPE_PROVIDER, userId)) { + if (shouldFilterApplicationLocked( + ps, callingUid, component, TYPE_PROVIDER, userId)) { return null; } return PackageParser.generateProviderInfo( @@ -5323,7 +5374,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public String[] getSystemSharedLibraryNames() { // allow instant applications - synchronized (mPackages) { + synchronized (mLock) { Set<String> libs = null; final int libCount = mSharedLibraries.size(); for (int i = 0; i < libCount; i++) { @@ -5367,7 +5418,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public @NonNull String getServicesSystemSharedLibraryPackageName() { // allow instant applications - synchronized (mPackages) { + synchronized (mLock) { return mServicesSystemSharedLibraryPackageName; } } @@ -5375,12 +5426,12 @@ public class PackageManagerService extends IPackageManager.Stub @Override public @NonNull String getSharedSystemSharedLibraryPackageName() { // allow instant applications - synchronized (mPackages) { + synchronized (mLock) { return mSharedSystemSharedLibraryPackageName; } } - @GuardedBy("mPackages") + @GuardedBy("mLock") private void updateSequenceNumberLP(PackageSetting pkgSetting, int[] userList) { for (int i = userList.length - 1; i >= 0; --i) { final int userId = userList[i]; @@ -5413,7 +5464,7 @@ public class PackageManagerService extends IPackageManager.Stub if (getInstantAppPackageName(Binder.getCallingUid()) != null) { return null; } - synchronized (mPackages) { + synchronized (mLock) { if (sequenceNumber >= mChangedPackagesSequenceNumber) { return null; } @@ -5487,13 +5538,13 @@ public class PackageManagerService extends IPackageManager.Stub @Override public String getPermissionControllerPackageName() { - synchronized (mPackages) { + synchronized (mLock) { return mRequiredPermissionControllerPackage; } } String getPackageInstallerPackageName() { - synchronized (mPackages) { + synchronized (mLock) { return mRequiredInstallerPackage; } } @@ -5561,7 +5612,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public int checkSignatures(String pkg1, String pkg2) { - synchronized (mPackages) { + synchronized (mLock) { final PackageParser.Package p1 = mPackages.get(pkg1); final PackageParser.Package p2 = mPackages.get(pkg2); if (p1 == null || p1.mExtras == null @@ -5572,8 +5623,8 @@ public class PackageManagerService extends IPackageManager.Stub final int callingUserId = UserHandle.getUserId(callingUid); final PackageSetting ps1 = (PackageSetting) p1.mExtras; final PackageSetting ps2 = (PackageSetting) p2.mExtras; - if (filterAppAccessLPr(ps1, callingUid, callingUserId) - || filterAppAccessLPr(ps2, callingUid, callingUserId)) { + if (shouldFilterApplicationLocked(ps1, callingUid, callingUserId) + || shouldFilterApplicationLocked(ps2, callingUid, callingUserId)) { return PackageManager.SIGNATURE_UNKNOWN_PACKAGE; } return compareSignatures(p1.mSigningDetails.signatures, p2.mSigningDetails.signatures); @@ -5589,7 +5640,7 @@ public class PackageManagerService extends IPackageManager.Stub final int appId1 = UserHandle.getAppId(uid1); final int appId2 = UserHandle.getAppId(uid2); // reader - synchronized (mPackages) { + synchronized (mLock) { Signature[] s1; Signature[] s2; Object obj = mSettings.getSettingLPr(appId1); @@ -5601,7 +5652,7 @@ public class PackageManagerService extends IPackageManager.Stub s1 = ((SharedUserSetting)obj).signatures.mSigningDetails.signatures; } else if (obj instanceof PackageSetting) { final PackageSetting ps = (PackageSetting) obj; - if (filterAppAccessLPr(ps, callingUid, callingUserId)) { + if (shouldFilterApplicationLocked(ps, callingUid, callingUserId)) { return PackageManager.SIGNATURE_UNKNOWN_PACKAGE; } s1 = ps.signatures.mSigningDetails.signatures; @@ -5620,7 +5671,7 @@ public class PackageManagerService extends IPackageManager.Stub s2 = ((SharedUserSetting)obj).signatures.mSigningDetails.signatures; } else if (obj instanceof PackageSetting) { final PackageSetting ps = (PackageSetting) obj; - if (filterAppAccessLPr(ps, callingUid, callingUserId)) { + if (shouldFilterApplicationLocked(ps, callingUid, callingUserId)) { return PackageManager.SIGNATURE_UNKNOWN_PACKAGE; } s2 = ps.signatures.mSigningDetails.signatures; @@ -5638,7 +5689,7 @@ public class PackageManagerService extends IPackageManager.Stub public boolean hasSigningCertificate( String packageName, byte[] certificate, @PackageManager.CertificateInputType int type) { - synchronized (mPackages) { + synchronized (mLock) { final PackageParser.Package p = mPackages.get(packageName); if (p == null || p.mExtras == null) { return false; @@ -5646,7 +5697,7 @@ public class PackageManagerService extends IPackageManager.Stub final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); final PackageSetting ps = (PackageSetting) p.mExtras; - if (filterAppAccessLPr(ps, callingUid, callingUserId)) { + if (shouldFilterApplicationLocked(ps, callingUid, callingUserId)) { return false; } switch (type) { @@ -5668,7 +5719,7 @@ public class PackageManagerService extends IPackageManager.Stub // Map to base uids. final int appId = UserHandle.getAppId(uid); // reader - synchronized (mPackages) { + synchronized (mLock) { final PackageParser.SigningDetails signingDetails; final Object obj = mSettings.getSettingLPr(appId); if (obj != null) { @@ -5680,7 +5731,7 @@ public class PackageManagerService extends IPackageManager.Stub signingDetails = ((SharedUserSetting)obj).signatures.mSigningDetails; } else if (obj instanceof PackageSetting) { final PackageSetting ps = (PackageSetting) obj; - if (filterAppAccessLPr(ps, callingUid, callingUserId)) { + if (shouldFilterApplicationLocked(ps, callingUid, callingUserId)) { return false; } signingDetails = ps.signatures.mSigningDetails; @@ -5726,7 +5777,7 @@ public class PackageManagerService extends IPackageManager.Stub public List<String> getAllPackages() { final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); - synchronized (mPackages) { + synchronized (mLock) { if (canViewInstantApps(callingUid, callingUserId)) { return new ArrayList<>(mPackages.keySet()); } @@ -5775,7 +5826,7 @@ public class PackageManagerService extends IPackageManager.Stub final int userId = UserHandle.getUserId(uid); final int appId = UserHandle.getAppId(uid); // reader - synchronized (mPackages) { + synchronized (mLock) { final Object obj = mSettings.getSettingLPr(appId); if (obj instanceof SharedUserSetting) { if (isCallerInstantApp) { @@ -5797,7 +5848,8 @@ public class PackageManagerService extends IPackageManager.Stub return res; } else if (obj instanceof PackageSetting) { final PackageSetting ps = (PackageSetting) obj; - if (ps.getInstalled(userId) && !filterAppAccessLPr(ps, callingUid, userId)) { + if (ps.getInstalled(userId) + && !shouldFilterApplicationLocked(ps, callingUid, userId)) { return new String[]{ps.name}; } } @@ -5812,14 +5864,15 @@ public class PackageManagerService extends IPackageManager.Stub return null; } final int appId = UserHandle.getAppId(uid); - synchronized (mPackages) { + synchronized (mLock) { final Object obj = mSettings.getSettingLPr(appId); if (obj instanceof SharedUserSetting) { final SharedUserSetting sus = (SharedUserSetting) obj; return sus.name + ":" + sus.userId; } else if (obj instanceof PackageSetting) { final PackageSetting ps = (PackageSetting) obj; - if (filterAppAccessLPr(ps, callingUid, UserHandle.getUserId(callingUid))) { + if (shouldFilterApplicationLocked( + ps, callingUid, UserHandle.getUserId(callingUid))) { return null; } return ps.name; @@ -5838,7 +5891,7 @@ public class PackageManagerService extends IPackageManager.Stub return null; } final String[] names = new String[uids.length]; - synchronized (mPackages) { + synchronized (mLock) { for (int i = uids.length - 1; i >= 0; i--) { final int appId = UserHandle.getAppId(uids[i]); final Object obj = mSettings.getSettingLPr(appId); @@ -5847,7 +5900,8 @@ public class PackageManagerService extends IPackageManager.Stub names[i] = "shared:" + sus.name; } else if (obj instanceof PackageSetting) { final PackageSetting ps = (PackageSetting) obj; - if (filterAppAccessLPr(ps, callingUid, UserHandle.getUserId(callingUid))) { + if (shouldFilterApplicationLocked( + ps, callingUid, UserHandle.getUserId(callingUid))) { names[i] = null; } else { names[i] = ps.name; @@ -5869,7 +5923,7 @@ public class PackageManagerService extends IPackageManager.Stub return -1; } // reader - synchronized (mPackages) { + synchronized (mLock) { SharedUserSetting suid; try { suid = mSettings.getSharedUserLPw(sharedUserName, 0, 0, false); @@ -5890,14 +5944,15 @@ public class PackageManagerService extends IPackageManager.Stub return 0; } final int appId = UserHandle.getAppId(uid); - synchronized (mPackages) { + synchronized (mLock) { final Object obj = mSettings.getSettingLPr(appId); if (obj instanceof SharedUserSetting) { final SharedUserSetting sus = (SharedUserSetting) obj; return sus.pkgFlags; } else if (obj instanceof PackageSetting) { final PackageSetting ps = (PackageSetting) obj; - if (filterAppAccessLPr(ps, callingUid, UserHandle.getUserId(callingUid))) { + if (shouldFilterApplicationLocked( + ps, callingUid, UserHandle.getUserId(callingUid))) { return 0; } return ps.pkgFlags; @@ -5913,14 +5968,15 @@ public class PackageManagerService extends IPackageManager.Stub return 0; } final int appId = UserHandle.getAppId(uid); - synchronized (mPackages) { + synchronized (mLock) { final Object obj = mSettings.getSettingLPr(appId); if (obj instanceof SharedUserSetting) { final SharedUserSetting sus = (SharedUserSetting) obj; return sus.pkgPrivateFlags; } else if (obj instanceof PackageSetting) { final PackageSetting ps = (PackageSetting) obj; - if (filterAppAccessLPr(ps, callingUid, UserHandle.getUserId(callingUid))) { + if (shouldFilterApplicationLocked( + ps, callingUid, UserHandle.getUserId(callingUid))) { return 0; } return ps.pkgPrivateFlags; @@ -5936,7 +5992,7 @@ public class PackageManagerService extends IPackageManager.Stub } final int appId = UserHandle.getAppId(uid); // reader - synchronized (mPackages) { + synchronized (mLock) { final Object obj = mSettings.getSettingLPr(appId); if (obj instanceof SharedUserSetting) { final SharedUserSetting sus = (SharedUserSetting) obj; @@ -6017,7 +6073,7 @@ public class PackageManagerService extends IPackageManager.Stub 0, userId, intent, callingUid, false /*includeInstantApps*/); final List<ResolveInfo> query = queryIntentActivitiesInternal(intent, resolvedType, flags, userId); - synchronized (mPackages) { + synchronized (mLock) { return findPersistentPreferredActivityLP(intent, resolvedType, flags, query, false, userId); } @@ -6104,7 +6160,7 @@ public class PackageManagerService extends IPackageManager.Stub } // Deny ephemeral apps if the user chose _ALWAYS or _ALWAYS_ASK for intent resolution. // Or if there's already an ephemeral app installed that handles the action - synchronized (mPackages) { + synchronized (mLock) { final int count = (resolvedActivities == null ? 0 : resolvedActivities.size()); for (int n = 0; n < count; n++) { final ResolveInfo info = resolvedActivities.get(n); @@ -6246,7 +6302,7 @@ public class PackageManagerService extends IPackageManager.Stub return true; } - @GuardedBy("mPackages") + @GuardedBy("mLock") private ResolveInfo findPersistentPreferredActivityLP(Intent intent, String resolvedType, int flags, List<ResolveInfo> query, boolean debug, int userId) { final int N = query.size(); @@ -6316,7 +6372,7 @@ public class PackageManagerService extends IPackageManager.Stub ResolveInfo findPreferredActivityNotLocked(Intent intent, String resolvedType, int flags, List<ResolveInfo> query, int priority, boolean always, boolean removeMatches, boolean debug, int userId) { - if (Thread.holdsLock(mPackages)) { + if (Thread.holdsLock(mLock)) { Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName() + " is holding mPackages", new Throwable()); } @@ -6331,7 +6387,7 @@ public class PackageManagerService extends IPackageManager.Stub flags, userId, intent, callingUid, false /*includeInstantApps*/); intent = updateIntentForResolve(intent); // writer - synchronized (mPackages) { + synchronized (mLock) { // Try to find a matching persistent preferred activity. ResolveInfo pri = findPersistentPreferredActivityLP(intent, resolvedType, flags, query, debug, userId); @@ -6535,7 +6591,7 @@ public class PackageManagerService extends IPackageManager.Stub // cross-profile app linking works only towards the parent. final int callingUid = Binder.getCallingUid(); final UserInfo parent = getProfileParent(sourceUserId); - synchronized(mPackages) { + synchronized (mLock) { int flags = updateFlagsForResolve(0, parent.id, intent, callingUid, false /*includeInstantApps*/); CrossProfileDomainInfo xpDomainInfo = getCrossProfileDomainPreferredLpr( @@ -6582,7 +6638,7 @@ public class PackageManagerService extends IPackageManager.Stub * instant, returns {@code null}. */ private String getInstantAppPackageName(int callingUid) { - synchronized (mPackages) { + synchronized (mLock) { // If the caller is an isolated app use the owner's uid for the lookup. if (Process.isIsolated(callingUid)) { callingUid = mIsolatedOwners.get(callingUid); @@ -6673,7 +6729,7 @@ public class PackageManagerService extends IPackageManager.Stub boolean sortResult = false; boolean addInstant = false; List<ResolveInfo> result; - synchronized (mPackages) { + synchronized (mLock) { if (pkgName == null) { List<CrossProfileIntentFilter> matchingFilters = getMatchingCrossProfileIntentFilters(intent, resolvedType, userId); @@ -7112,7 +7168,7 @@ public class PackageManagerService extends IPackageManager.Stub final ArrayList<ResolveInfo> neverList = new ArrayList<>(); final ArrayList<ResolveInfo> matchAllList = new ArrayList<>(); - synchronized (mPackages) { + synchronized (mLock) { final int count = candidates.size(); // First, try to use linked apps. Partition the candidates into four lists: // one for the final results, one for the "do not use ever", one for "undefined status" @@ -7631,7 +7687,7 @@ public class PackageManagerService extends IPackageManager.Stub } // reader - synchronized (mPackages) { + synchronized (mLock) { String pkgName = intent.getPackage(); if (pkgName == null) { final List<ResolveInfo> result = @@ -7735,7 +7791,7 @@ public class PackageManagerService extends IPackageManager.Stub } // reader - synchronized (mPackages) { + synchronized (mLock) { String pkgName = intent.getPackage(); if (pkgName == null) { return applyPostServiceResolutionFilter( @@ -7853,7 +7909,7 @@ public class PackageManagerService extends IPackageManager.Stub } // reader - synchronized (mPackages) { + synchronized (mLock) { String pkgName = intent.getPackage(); if (pkgName == null) { return applyPostContentProviderResolutionFilter( @@ -7931,7 +7987,7 @@ public class PackageManagerService extends IPackageManager.Stub "get installed packages"); // writer - synchronized (mPackages) { + synchronized (mLock) { ArrayList<PackageInfo> list; if (listUninstalled) { list = new ArrayList<>(mSettings.mPackages.size()); @@ -7939,7 +7995,7 @@ public class PackageManagerService extends IPackageManager.Stub if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) { continue; } - if (filterAppAccessLPr(ps, callingUid, userId)) { + if (shouldFilterApplicationLocked(ps, callingUid, userId)) { continue; } final PackageInfo pi = generatePackageInfo(ps, flags, userId); @@ -7954,7 +8010,7 @@ public class PackageManagerService extends IPackageManager.Stub if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) { continue; } - if (filterAppAccessLPr(ps, callingUid, userId)) { + if (shouldFilterApplicationLocked(ps, callingUid, userId)) { continue; } final PackageInfo pi = generatePackageInfo((PackageSetting) @@ -8028,7 +8084,7 @@ public class PackageManagerService extends IPackageManager.Stub final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0; // writer - synchronized (mPackages) { + synchronized (mLock) { ArrayList<PackageInfo> list = new ArrayList<>(); boolean[] tmpBools = new boolean[permissions.length]; if (listUninstalled) { @@ -8074,7 +8130,7 @@ public class PackageManagerService extends IPackageManager.Stub "get installed application info"); // writer - synchronized (mPackages) { + synchronized (mLock) { ArrayList<ApplicationInfo> list; if (listUninstalled) { list = new ArrayList<>(mSettings.mPackages.size()); @@ -8088,7 +8144,7 @@ public class PackageManagerService extends IPackageManager.Stub if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) { continue; } - if (filterAppAccessLPr(ps, callingUid, userId)) { + if (shouldFilterApplicationLocked(ps, callingUid, userId)) { continue; } ai = PackageParser.generateApplicationInfo(ps.pkg, effectiveFlags, @@ -8114,7 +8170,7 @@ public class PackageManagerService extends IPackageManager.Stub if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId, flags)) { continue; } - if (filterAppAccessLPr(ps, callingUid, userId)) { + if (shouldFilterApplicationLocked(ps, callingUid, userId)) { continue; } ApplicationInfo ai = PackageParser.generateApplicationInfo(p, flags, @@ -8143,7 +8199,7 @@ public class PackageManagerService extends IPackageManager.Stub mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, false /* checkShell */, "getEphemeralApplications"); - synchronized (mPackages) { + synchronized (mLock) { List<InstantAppInfo> instantApps = mInstantAppRegistry .getInstantAppsLPr(userId); if (instantApps != null) { @@ -8162,7 +8218,7 @@ public class PackageManagerService extends IPackageManager.Stub return false; } - synchronized (mPackages) { + synchronized (mLock) { int callingUid = Binder.getCallingUid(); if (Process.isIsolated(callingUid)) { callingUid = mIsolatedOwners.get(callingUid); @@ -8194,7 +8250,7 @@ public class PackageManagerService extends IPackageManager.Stub if (!isCallerSameApp(packageName, Binder.getCallingUid())) { return null; } - synchronized (mPackages) { + synchronized (mLock) { return mInstantAppRegistry.getInstantAppCookieLPw( packageName, userId); } @@ -8212,7 +8268,7 @@ public class PackageManagerService extends IPackageManager.Stub if (!isCallerSameApp(packageName, Binder.getCallingUid())) { return false; } - synchronized (mPackages) { + synchronized (mLock) { return mInstantAppRegistry.setInstantAppCookieLPw( packageName, cookie, userId); } @@ -8232,7 +8288,7 @@ public class PackageManagerService extends IPackageManager.Stub true /* requireFullPermission */, false /* checkShell */, "getInstantAppIcon"); - synchronized (mPackages) { + synchronized (mLock) { return mInstantAppRegistry.getInstantAppIconLPw( packageName, userId); } @@ -8256,7 +8312,7 @@ public class PackageManagerService extends IPackageManager.Stub final ArrayList<ApplicationInfo> finalList = new ArrayList<>(); // reader - synchronized (mPackages) { + synchronized (mLock) { final Iterator<PackageParser.Package> i = mPackages.values().iterator(); final int userId = UserHandle.getCallingUserId(); while (i.hasNext()) { @@ -8302,11 +8358,11 @@ public class PackageManagerService extends IPackageManager.Stub if (!mSettings.isEnabledAndMatchLPr(providerInfo, flags, userId)) { return null; } - synchronized (mPackages) { + synchronized (mLock) { final PackageSetting ps = mSettings.mPackages.get(providerInfo.packageName); final ComponentName component = new ComponentName(providerInfo.packageName, providerInfo.name); - if (filterAppAccessLPr(ps, callingUid, component, TYPE_PROVIDER, userId)) { + if (shouldFilterApplicationLocked(ps, callingUid, component, TYPE_PROVIDER, userId)) { return null; } return providerInfo; @@ -8337,7 +8393,7 @@ public class PackageManagerService extends IPackageManager.Stub final List<ProviderInfo> matchList = mComponentResolver.queryProviders(processName, metaDataKey, uid, flags, userId); final int listSize = (matchList == null ? 0 : matchList.size()); - synchronized (mPackages) { + synchronized (mLock) { for (int i = 0; i < listSize; i++) { final ProviderInfo providerInfo = matchList.get(i); if (!mSettings.isEnabledAndMatchLPr(providerInfo, flags, userId)) { @@ -8346,7 +8402,8 @@ public class PackageManagerService extends IPackageManager.Stub final PackageSetting ps = mSettings.mPackages.get(providerInfo.packageName); final ComponentName component = new ComponentName(providerInfo.packageName, providerInfo.name); - if (filterAppAccessLPr(ps, callingUid, component, TYPE_PROVIDER, userId)) { + if (shouldFilterApplicationLocked( + ps, callingUid, component, TYPE_PROVIDER, userId)) { continue; } if (finalList == null) { @@ -8367,12 +8424,13 @@ public class PackageManagerService extends IPackageManager.Stub @Override public InstrumentationInfo getInstrumentationInfo(ComponentName component, int flags) { // reader - synchronized (mPackages) { + synchronized (mLock) { final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); final PackageSetting ps = mSettings.mPackages.get(component.getPackageName()); if (ps == null) return null; - if (filterAppAccessLPr(ps, callingUid, component, TYPE_UNKNOWN, callingUserId)) { + if (shouldFilterApplicationLocked( + ps, callingUid, component, TYPE_UNKNOWN, callingUserId)) { return null; } final PackageParser.Instrumentation i = mInstrumentation.get(component); @@ -8386,7 +8444,7 @@ public class PackageManagerService extends IPackageManager.Stub final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); final PackageSetting ps = mSettings.mPackages.get(targetPackage); - if (filterAppAccessLPr(ps, callingUid, callingUserId)) { + if (shouldFilterApplicationLocked(ps, callingUid, callingUserId)) { return ParceledListSlice.emptyList(); } return new ParceledListSlice<>(queryInstrumentationInternal(targetPackage, flags)); @@ -8397,7 +8455,7 @@ public class PackageManagerService extends IPackageManager.Stub ArrayList<InstrumentationInfo> finalList = new ArrayList<>(); // reader - synchronized (mPackages) { + synchronized (mLock) { final Iterator<PackageParser.Instrumentation> i = mInstrumentation.values().iterator(); while (i.hasNext()) { final PackageParser.Instrumentation p = i.next(); @@ -8562,7 +8620,7 @@ public class PackageManagerService extends IPackageManager.Stub * Traces a package scan. * @see #scanPackageLI(File, int, int, long, UserHandle) */ - @GuardedBy({"mInstallLock", "mPackages"}) + @GuardedBy({"mInstallLock", "mLock"}) private PackageParser.Package scanPackageTracedLI(File scanFile, final int parseFlags, int scanFlags, long currentTime, UserHandle user) throws PackageManagerException { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage [" + scanFile.toString() + "]"); @@ -8577,7 +8635,7 @@ public class PackageManagerService extends IPackageManager.Stub * Scans a package and returns the newly parsed package. * Returns {@code null} in case of errors and the error code is stored in mLastScanError */ - @GuardedBy({"mInstallLock", "mPackages"}) + @GuardedBy({"mInstallLock", "mLock"}) private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags, long currentTime, UserHandle user) throws PackageManagerException { if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile); @@ -8609,7 +8667,7 @@ public class PackageManagerService extends IPackageManager.Stub * Scans a package and returns the newly parsed package. * @throws PackageManagerException on a parse error. */ - @GuardedBy({"mInstallLock", "mPackages"}) + @GuardedBy({"mInstallLock", "mLock"}) private PackageParser.Package scanPackageChildLI(PackageParser.Package pkg, final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime, @Nullable UserHandle user) @@ -8705,7 +8763,7 @@ public class PackageManagerService extends IPackageManager.Stub * structures and the package is made available to the rest of the system. * <p>NOTE: The return value should be removed. It's the passed in package object. */ - @GuardedBy({"mInstallLock", "mPackages"}) + @GuardedBy({"mInstallLock", "mLock"}) private PackageParser.Package addForInitLI(PackageParser.Package pkg, @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime, @Nullable UserHandle user) @@ -8730,7 +8788,7 @@ public class PackageManagerService extends IPackageManager.Stub pkg.setApplicationInfoBaseResourcePath(pkg.baseCodePath); pkg.setApplicationInfoSplitResourcePaths(pkg.splitCodePaths); - synchronized (mPackages) { + synchronized (mLock) { renamedPkgName = mSettings.getRenamedPackageLPr(pkg.mRealPackage); final String realPkgName = getRealPackageName(pkg, renamedPkgName); if (realPkgName != null) { @@ -8791,7 +8849,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); } @@ -8810,7 +8869,7 @@ public class PackageManagerService extends IPackageManager.Stub // /data. Switch back to the application on /system. // It's safe to assume the application on /system will correctly scan. If not, // there won't be a working copy of the application. - synchronized (mPackages) { + synchronized (mLock) { // just remove the loaded entries from package lists mPackages.remove(pkgSetting.name); } @@ -8825,7 +8884,7 @@ public class PackageManagerService extends IPackageManager.Stub pkgSetting.codePathString, pkgSetting.resourcePathString, getAppDexInstructionSets(pkgSetting)); args.cleanUpResourcesLI(); - synchronized (mPackages) { + synchronized (mLock) { mSettings.enableSystemPackageLPw(pkgSetting.name); } } @@ -8914,7 +8973,7 @@ public class PackageManagerService extends IPackageManager.Stub final ScanResult scanResult = scanPackageNewLI(pkg, parseFlags, scanFlags | SCAN_UPDATE_SIGNATURE, currentTime, user); if (scanResult.success) { - synchronized (mPackages) { + synchronized (mLock) { boolean appIdCreated = false; try { final String pkgName = scanResult.pkgSetting.name; @@ -8940,7 +8999,7 @@ public class PackageManagerService extends IPackageManager.Stub } if (shouldHideSystemApp) { - synchronized (mPackages) { + synchronized (mLock) { mSettings.disableSystemPackageLPw(pkg.packageName, true); } } @@ -9011,7 +9070,7 @@ public class PackageManagerService extends IPackageManager.Stub } if (doTrim) { final boolean dexOptDialogShown; - synchronized (mPackages) { + synchronized (mLock) { dexOptDialogShown = mDexOptDialogShown; } if (!isFirstBoot() && dexOptDialogShown) { @@ -9052,7 +9111,7 @@ public class PackageManagerService extends IPackageManager.Stub } List<PackageParser.Package> pkgs; - synchronized (mPackages) { + synchronized (mLock) { pkgs = PackageManagerServiceUtils.getPackagesForDexopt(mPackages.values(), this); } @@ -9180,7 +9239,7 @@ public class PackageManagerService extends IPackageManager.Stub numberOfPackagesVisited, numberOfPackagesToDexopt), true); } catch (RemoteException e) { } - synchronized (mPackages) { + synchronized (mLock) { mDexOptDialogShown = true; } } @@ -9233,7 +9292,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public void notifyPackageUse(String packageName, int reason) { - synchronized (mPackages) { + synchronized (mLock) { final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); if (getInstantAppPackageName(callingUid) != null) { @@ -9249,7 +9308,7 @@ public class PackageManagerService extends IPackageManager.Stub } } - @GuardedBy("mPackages") + @GuardedBy("mLock") private void notifyPackageUseLocked(String packageName, int reason) { final PackageParser.Package p = mPackages.get(packageName); if (p == null) { @@ -9336,7 +9395,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public boolean compileLayouts(String packageName) { PackageParser.Package pkg; - synchronized (mPackages) { + synchronized (mLock) { pkg = mPackages.get(packageName); if (pkg == null) { return false; @@ -9383,7 +9442,7 @@ public class PackageManagerService extends IPackageManager.Stub // if the package can now be considered up to date for the given filter. private int performDexOptInternal(DexoptOptions options) { PackageParser.Package p; - synchronized (mPackages) { + synchronized (mLock) { p = mPackages.get(options.getPackageName()); if (p == null) { // Package could not be found. Report failure. @@ -9404,7 +9463,7 @@ public class PackageManagerService extends IPackageManager.Stub public ArraySet<String> getOptimizablePackages() { ArraySet<String> pkgs = new ArraySet<>(); - synchronized (mPackages) { + synchronized (mLock) { for (PackageParser.Package p : mPackages.values()) { if (PackageDexOptimizer.canOptimizePackage(p)) { pkgs.add(p.packageName); @@ -9439,7 +9498,7 @@ public class PackageManagerService extends IPackageManager.Stub options.getFlags() | DexoptOptions.DEXOPT_AS_SHARED_LIBRARY); for (SharedLibraryInfo info : deps) { PackageParser.Package depPackage = null; - synchronized (mPackages) { + synchronized (mLock) { depPackage = mPackages.get(info.getPackageName()); } if (depPackage != null) { @@ -9527,7 +9586,7 @@ public class PackageManagerService extends IPackageManager.Stub List<SharedLibraryInfo> deps = findSharedLibraries(pkg); if (!deps.isEmpty()) { ArrayList<PackageParser.Package> retValue = new ArrayList<>(); - synchronized (mPackages) { + synchronized (mLock) { for (SharedLibraryInfo info : deps) { PackageParser.Package depPackage = mPackages.get(info.getPackageName()); if (depPackage != null) { @@ -9591,7 +9650,7 @@ public class PackageManagerService extends IPackageManager.Stub @Nullable private PackageSetting getSharedLibLatestVersionSetting(@NonNull ScanResult scanResult) { PackageSetting sharedLibPackage = null; - synchronized (mPackages) { + synchronized (mLock) { final SharedLibraryInfo latestSharedLibraVersionLPr = getLatestSharedLibraVersionLPr(scanResult.pkgSetting.pkg); if (latestSharedLibraVersionLPr != null) { @@ -9609,7 +9668,7 @@ public class PackageManagerService extends IPackageManager.Stub PackageWatchdog.getInstance(mContext).writeNow(); // This is the last chance to write out pending restriction settings - synchronized (mPackages) { + synchronized (mLock) { if (mHandler.hasMessages(WRITE_PACKAGE_RESTRICTIONS)) { mHandler.removeMessages(WRITE_PACKAGE_RESTRICTIONS); for (int userId : mDirtyUsers) { @@ -9623,7 +9682,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public void dumpProfiles(String packageName) { PackageParser.Package pkg; - synchronized (mPackages) { + synchronized (mLock) { pkg = mPackages.get(packageName); if (pkg == null) { throw new IllegalArgumentException("Unknown package: " + packageName); @@ -9649,7 +9708,7 @@ public class PackageManagerService extends IPackageManager.Stub enforceSystemOrRoot("forceDexOpt"); PackageParser.Package pkg; - synchronized (mPackages) { + synchronized (mLock) { pkg = mPackages.get(packageName); if (pkg == null) { throw new IllegalArgumentException("Unknown package: " + packageName); @@ -9674,7 +9733,7 @@ public class PackageManagerService extends IPackageManager.Stub } } - @GuardedBy("mPackages") + @GuardedBy("mLock") private boolean verifyPackageUpdateLPr(PackageSetting oldPkg, PackageParser.Package newPkg) { if ((oldPkg.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) { Slog.w(TAG, "Unable to update from " + oldPkg.name @@ -9725,7 +9784,7 @@ public class PackageManagerService extends IPackageManager.Stub private void clearAppDataLeafLIF(PackageParser.Package pkg, int userId, int flags) { final PackageSetting ps; - synchronized (mPackages) { + synchronized (mLock) { ps = mSettings.mPackages.get(pkg.packageName); } for (int realUserId : resolveUserIds(userId)) { @@ -9753,7 +9812,7 @@ public class PackageManagerService extends IPackageManager.Stub private void destroyAppDataLeafLIF(PackageParser.Package pkg, int userId, int flags) { final PackageSetting ps; - synchronized (mPackages) { + synchronized (mLock) { ps = mSettings.mPackages.get(pkg.packageName); } for (int realUserId : resolveUserIds(userId)) { @@ -9820,7 +9879,7 @@ public class PackageManagerService extends IPackageManager.Stub } } - @GuardedBy("mPackages") + @GuardedBy("mLock") private void applyDefiningSharedLibraryUpdateLocked( PackageParser.Package pkg, SharedLibraryInfo libInfo, BiConsumer<SharedLibraryInfo, SharedLibraryInfo> action) { @@ -9849,7 +9908,7 @@ public class PackageManagerService extends IPackageManager.Stub } } - @GuardedBy("mPackages") + @GuardedBy("mLock") private void addSharedLibraryLPr(PackageParser.Package pkg, Set<String> usesLibraryFiles, SharedLibraryInfo libInfo, PackageParser.Package changingLib) { if (libInfo.getPath() != null) { @@ -9878,7 +9937,7 @@ public class PackageManagerService extends IPackageManager.Stub } } - @GuardedBy("mPackages") + @GuardedBy("mLock") private void updateSharedLibrariesLocked(PackageParser.Package pkg, PackageParser.Package changingLib, Map<String, PackageParser.Package> availablePackages) throws PackageManagerException { @@ -9940,7 +9999,7 @@ public class PackageManagerService extends IPackageManager.Stub } } - @GuardedBy("mPackages") + @GuardedBy("mLock") private static ArrayList<SharedLibraryInfo> collectSharedLibraryInfos( @NonNull List<String> requestedLibraries, @Nullable long[] requiredVersions, @Nullable String[][] requiredCertDigests, @@ -10050,7 +10109,7 @@ public class PackageManagerService extends IPackageManager.Stub return false; } - @GuardedBy("mPackages") + @GuardedBy("mLock") private ArrayList<PackageParser.Package> updateAllSharedLibrariesLocked( PackageParser.Package updatedPkg, Map<String, PackageParser.Package> availablePackages) { @@ -10110,7 +10169,7 @@ public class PackageManagerService extends IPackageManager.Stub return resultList; } - @GuardedBy({"mInstallLock", "mPackages"}) + @GuardedBy({"mInstallLock", "mLock"}) private List<ScanResult> scanPackageTracedLI(PackageParser.Package pkg, final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime, @Nullable UserHandle user) throws PackageManagerException { @@ -10151,7 +10210,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 */ @@ -10190,7 +10250,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 */ @@ -10325,7 +10386,7 @@ public class PackageManagerService extends IPackageManager.Stub // TODO(b/72378145) Fix this exemption. Force signature apps // to whitelist their privileged permissions just like other // priv-apps. - synchronized (mPackages) { + synchronized (mLock) { PackageSetting platformPkgSetting = mSettings.mPackages.get("android"); if ((compareSignatures(platformPkgSetting.signatures.mSigningDetails.signatures, pkg.mSigningDetails.signatures) != PackageManager.SIGNATURE_MATCH)) { @@ -10342,7 +10403,7 @@ public class PackageManagerService extends IPackageManager.Stub // the results / removing app data needs to be moved up a level to the callers of this // method. Also, we need to solve the problem of potentially creating a new shared user // setting. That can probably be done later and patch things up after the fact. - @GuardedBy({"mInstallLock", "mPackages"}) + @GuardedBy({"mInstallLock", "mLock"}) private ScanResult scanPackageNewLI(@NonNull PackageParser.Package pkg, final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime, @Nullable UserHandle user) throws PackageManagerException { @@ -10363,7 +10424,7 @@ public class PackageManagerService extends IPackageManager.Stub } scanFlags = adjustScanFlags(scanFlags, pkgSetting, disabledPkgSetting, user, pkg); - synchronized (mPackages) { + synchronized (mLock) { applyPolicy(pkg, parseFlags, scanFlags, mPlatformPackage); assertPackageIsValid(pkg, parseFlags, scanFlags); @@ -10383,7 +10444,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); } } @@ -10424,7 +10485,7 @@ public class PackageManagerService extends IPackageManager.Stub * This needs to be fixed so, once we get to this point, no errors are * possible and the system is not left in an inconsistent state. */ - @GuardedBy({"mPackages", "mInstallLock"}) + @GuardedBy({"mLock", "mInstallLock"}) private void commitReconciledScanResultLocked(@NonNull ReconciledPackage reconciledPkg) { final ScanResult result = reconciledPkg.scanResult; final ScanRequest request = result.request; @@ -10512,7 +10573,7 @@ public class PackageManagerService extends IPackageManager.Stub if ((scanFlags & SCAN_CHECK_ONLY) != 0) { if (oldPkgSetting != null) { - synchronized (mPackages) { + synchronized (mLock) { mSettings.mPackages.put(oldPkgSetting.name, oldPkgSetting); } } @@ -10553,7 +10614,7 @@ public class PackageManagerService extends IPackageManager.Stub * <p>An original package must be signed identically and it must have the same * shared user [if any]. */ - @GuardedBy("mPackages") + @GuardedBy("mLock") private @Nullable PackageSetting getOriginalPackageLocked(@NonNull PackageParser.Package pkg, @Nullable String renamedPkgName) { if (!isPackageRenamed(pkg, renamedPkgName)) { @@ -10604,20 +10665,70 @@ public class PackageManagerService extends IPackageManager.Stub } /** + * Applies the adjusted ABI calculated by + * {@link PackageAbiHelper#getAdjustedAbiForSharedUser(Set, PackageParser.Package)} to all + * relevant packages and settings. + * @param sharedUserSetting The {@code SharedUserSetting} to adjust + * @param scannedPackage the package being scanned or null + * @param adjustedAbi the adjusted ABI calculated by {@link PackageAbiHelper} + * @return the list of code paths that belong to packages that had their ABIs adjusted. + */ + private static List<String> applyAdjustedAbiToSharedUser(SharedUserSetting sharedUserSetting, + PackageParser.Package scannedPackage, String adjustedAbi) { + if (scannedPackage != null) { + scannedPackage.applicationInfo.primaryCpuAbi = adjustedAbi; + } + List<String> changedAbiCodePath = null; + for (PackageSetting ps : sharedUserSetting.packages) { + 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 + + " (scannedPackage=" + + (scannedPackage != null ? scannedPackage : "null") + + ")"); + } + if (changedAbiCodePath == null) { + changedAbiCodePath = new ArrayList<>(); + } + changedAbiCodePath.add(ps.codePathString); + } + } + } + return changedAbiCodePath; + } + + + /** * Just scans the package without any side effects. * <p>Not entirely true at the moment. There is still one side effect -- this * 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; @@ -10723,7 +10834,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 @@ -10769,7 +10880,10 @@ public class PackageManagerService extends IPackageManager.Stub if (needToDeriveAbi) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "derivePackageAbi"); final boolean extractNativeLibs = !pkg.isLibrary(); - derivePackageAbi(pkg, cpuAbiOverride, extractNativeLibs); + final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths> derivedAbi = + packageAbiHelper.derivePackageAbi(pkg, cpuAbiOverride, extractNativeLibs); + derivedAbi.first.applyTo(pkg); + derivedAbi.second.applyTo(pkg); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); // Some system apps still use directory structure for native libraries @@ -10777,8 +10891,13 @@ 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); + final PackageAbiHelper.Abis abis = packageAbiHelper.getBundledAppAbis( + pkg); + abis.applyTo(pkg); + abis.applyTo(pkgSetting); + final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths = + packageAbiHelper.getNativeLibraryPaths(pkg, sAppLib32InstallDir); + nativeLibraryPaths.applyTo(pkg); } } else { // This is not a first boot or an upgrade, don't bother deriving the @@ -10787,7 +10906,9 @@ public class PackageManagerService extends IPackageManager.Stub pkg.applicationInfo.primaryCpuAbi = primaryCpuAbiFromSettings; pkg.applicationInfo.secondaryCpuAbi = secondaryCpuAbiFromSettings; - setNativeLibraryPaths(pkg, sAppLib32InstallDir); + final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths = + packageAbiHelper.getNativeLibraryPaths(pkg, sAppLib32InstallDir); + nativeLibraryPaths.applyTo(pkg); if (DEBUG_ABI_SELECTION) { Slog.i(TAG, "Using ABIS and native lib paths from settings : " + @@ -10808,7 +10929,9 @@ 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); + final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths = + packageAbiHelper.getNativeLibraryPaths(pkg, sAppLib32InstallDir); + nativeLibraryPaths.applyTo(pkg); } // This is a special case for the "system" package, where the ABI is @@ -10862,8 +10985,9 @@ 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 = applyAdjustedAbiToSharedUser(pkgSetting.sharedUser, pkg, + packageAbiHelper.getAdjustedAbiForSharedUser( + pkgSetting.sharedUser.packages, pkg)); } if (isUnderFactoryTest && pkg.requestedPermissions.contains( @@ -11142,7 +11266,7 @@ public class PackageManagerService extends IPackageManager.Stub final KeySetManagerService ksms = mSettings.mKeySetManagerService; ksms.assertScannedPackageValid(pkg); - synchronized (mPackages) { + synchronized (mLock) { // The special "android" package can only be defined once if (pkg.packageName.equals("android")) { if (mAndroidApplication != null) { @@ -11458,7 +11582,7 @@ public class PackageManagerService extends IPackageManager.Stub } } - @GuardedBy("mPackages") + @GuardedBy("mLock") private boolean addBuiltInSharedLibraryLocked(String path, String name) { if (nonStaticSharedLibExistsLocked(name)) { return false; @@ -11473,7 +11597,7 @@ public class PackageManagerService extends IPackageManager.Stub return true; } - @GuardedBy("mPackages") + @GuardedBy("mLock") private boolean nonStaticSharedLibExistsLocked(String name) { return sharedLibExists(name, SharedLibraryInfo.VERSION_UNDEFINED, mSharedLibraries); } @@ -11487,7 +11611,7 @@ public class PackageManagerService extends IPackageManager.Stub return false; } - @GuardedBy("mPackages") + @GuardedBy("mLock") private void commitSharedLibraryInfoLocked(SharedLibraryInfo libraryInfo) { final String name = libraryInfo.getName(); LongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(name); @@ -11537,7 +11661,7 @@ public class PackageManagerService extends IPackageManager.Stub } if (pkg.packageName.equals("android")) { - synchronized (mPackages) { + synchronized (mLock) { if ((scanFlags & SCAN_CHECK_ONLY) == 0) { // Set up information for our fall-back user intent resolution activity. mPlatformPackage = pkg; @@ -11575,7 +11699,7 @@ public class PackageManagerService extends IPackageManager.Stub ArrayList<PackageParser.Package> clientLibPkgs = null; // writer - synchronized (mPackages) { + synchronized (mLock) { if (!ArrayUtils.isEmpty(reconciledPkg.allowedSharedLibraryInfos)) { for (SharedLibraryInfo info : reconciledPkg.allowedSharedLibraryInfos) { commitSharedLibraryInfoLocked(info); @@ -11621,7 +11745,7 @@ public class PackageManagerService extends IPackageManager.Stub // writer Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "updateSettings"); - synchronized (mPackages) { + synchronized (mLock) { // We don't expect installation to fail beyond this point // Add the new setting to mSettings @@ -11634,6 +11758,7 @@ public class PackageManagerService extends IPackageManager.Stub ksms.addScannedPackageLPw(pkg); mComponentResolver.addAllComponents(pkg, chatty); + mAppsFilter.addPackage(pkg, mPackages); // Don't allow ephemeral applications to define new permissions groups. if ((scanFlags & SCAN_AS_INSTANT_APP) != 0) { @@ -11713,266 +11838,8 @@ 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) { + synchronized (mLock) { mResolverReplaced = true; // Set up information for custom user intent resolution activity. mResolveActivity.applicationInfo = pkg.applicationInfo; @@ -12022,207 +11889,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); } @@ -12271,7 +11937,7 @@ public class PackageManagerService extends IPackageManager.Stub } // writer - synchronized (mPackages) { + synchronized (mLock) { final PackageParser.Package removedPackage = mPackages.remove(packageName); if (removedPackage != null) { cleanPackageDataStructuresLILPw(removedPackage, chatty); @@ -12286,7 +11952,7 @@ public class PackageManagerService extends IPackageManager.Stub } // writer - synchronized (mPackages) { + synchronized (mLock) { // Remove the parent package mPackages.remove(pkg.applicationInfo.packageName); cleanPackageDataStructuresLILPw(pkg, chatty); @@ -12303,7 +11969,7 @@ public class PackageManagerService extends IPackageManager.Stub void cleanPackageDataStructuresLILPw(PackageParser.Package pkg, boolean chatty) { mComponentResolver.removeAllComponents(pkg, chatty); - + mAppsFilter.removePackage(pkg.packageName); mPermissionManager.removeAllPermissions(pkg, chatty); final int instrumentationSize = pkg.instrumentation.size(); @@ -12395,7 +12061,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public void notifyPackageAdded(String packageName, int uid) { final PackageListObserver[] observers; - synchronized (mPackages) { + synchronized (mLock) { if (mPackageListObservers.size() == 0) { return; } @@ -12411,7 +12077,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public void notifyPackageChanged(String packageName, int uid) { final PackageListObserver[] observers; - synchronized (mPackages) { + synchronized (mLock) { if (mPackageListObservers.size() == 0) { return; } @@ -12433,7 +12099,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public void notifyPackageRemoved(String packageName, int uid) { final PackageListObserver[] observers; - synchronized (mPackages) { + synchronized (mLock) { if (mPackageListObservers.size() == 0) { return; } @@ -12682,12 +12348,12 @@ public class PackageManagerService extends IPackageManager.Stub boolean sendAdded = false; boolean sendRemoved = false; // writer - synchronized (mPackages) { + synchronized (mLock) { pkgSetting = mSettings.mPackages.get(packageName); if (pkgSetting == null) { return false; } - if (filterAppAccessLPr(pkgSetting, callingUid, userId)) { + if (shouldFilterApplicationLocked(pkgSetting, callingUid, userId)) { return false; } // Do not allow "android" is being disabled @@ -12743,7 +12409,7 @@ public class PackageManagerService extends IPackageManager.Stub final int callingUid = Binder.getCallingUid(); PackageManagerServiceUtils .enforceSystemOrPhoneCaller("setSystemAppHiddenUntilInstalled", callingUid); - synchronized (mPackages) { + synchronized (mLock) { final PackageSetting pkgSetting = mSettings.mPackages.get(packageName); if (pkgSetting == null || !pkgSetting.isSystem()) { return; @@ -12768,7 +12434,7 @@ public class PackageManagerService extends IPackageManager.Stub final int callingUid = Binder.getCallingUid(); PackageManagerServiceUtils .enforceSystemOrPhoneCaller("setSystemAppInstallState", callingUid); - synchronized (mPackages) { + synchronized (mLock) { final PackageSetting pkgSetting = mSettings.mPackages.get(packageName); // The target app should always be in system if (pkgSetting == null || !pkgSetting.isSystem()) { @@ -12857,12 +12523,12 @@ public class PackageManagerService extends IPackageManager.Stub long callingId = Binder.clearCallingIdentity(); try { // writer - synchronized (mPackages) { + synchronized (mLock) { ps = mSettings.mPackages.get(packageName); if (ps == null) { return true; } - if (filterAppAccessLPr(ps, callingUid, userId)) { + if (shouldFilterApplicationLocked(ps, callingUid, userId)) { return true; } return ps.getHidden(userId); @@ -12918,7 +12584,7 @@ public class PackageManagerService extends IPackageManager.Stub (installFlags & PackageManager.INSTALL_FULL_APP) != 0; // writer - synchronized (mPackages) { + synchronized (mLock) { pkgSetting = mSettings.mPackages.get(packageName); if (pkgSetting == null) { return PackageManager.INSTALL_FAILED_INVALID_URI; @@ -12948,7 +12614,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) { @@ -12966,7 +12633,7 @@ public class PackageManagerService extends IPackageManager.Stub } } sendPackageAddedForUser(packageName, pkgSetting, userId); - synchronized (mPackages) { + synchronized (mLock) { updateSequenceNumberLP(pkgSetting, new int[]{ userId }); } // start async restore with no post-install since we finish install here @@ -12996,8 +12663,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; @@ -13009,7 +12676,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)) { @@ -13051,9 +12718,10 @@ public class PackageManagerService extends IPackageManager.Stub for (int i = 0; i < packageNames.length; i++) { final String packageName = packageNames[i]; final PackageSetting pkgSetting; - synchronized (mPackages) { + synchronized (mLock) { pkgSetting = mSettings.mPackages.get(packageName); - if (pkgSetting == null || filterAppAccessLPr(pkgSetting, callingUid, userId)) { + if (pkgSetting == null + || shouldFilterApplicationLocked(pkgSetting, callingUid, userId)) { Slog.w(TAG, "Could not find package setting for package: " + packageName + ". Skipping..."); unactionedPackages.add(packageName); @@ -13064,7 +12732,7 @@ public class PackageManagerService extends IPackageManager.Stub unactionedPackages.add(packageName); continue; } - synchronized (mPackages) { + synchronized (mLock) { final int oldDistractionFlags = pkgSetting.getDistractionFlags(userId); if (restrictionFlags != oldDistractionFlags) { pkgSetting.setDistractionFlags(restrictionFlags, userId); @@ -13079,7 +12747,7 @@ public class PackageManagerService extends IPackageManager.Stub new String[changedPackagesList.size()]); sendDistractingPackagesChanged(changedPackages, changedUids.toArray(), userId, restrictionFlags); - synchronized (mPackages) { + synchronized (mLock) { scheduleWritePackageRestrictionsLocked(userId); } } @@ -13143,9 +12811,10 @@ public class PackageManagerService extends IPackageManager.Stub continue; } final PackageSetting pkgSetting; - synchronized (mPackages) { + synchronized (mLock) { pkgSetting = mSettings.mPackages.get(packageName); - if (pkgSetting == null || filterAppAccessLPr(pkgSetting, callingUid, userId)) { + if (pkgSetting == null + || shouldFilterApplicationLocked(pkgSetting, callingUid, userId)) { Slog.w(TAG, "Could not find package setting for package: " + packageName + ". Skipping suspending/un-suspending."); unactionedPackages.add(packageName); @@ -13156,7 +12825,7 @@ public class PackageManagerService extends IPackageManager.Stub unactionedPackages.add(packageName); continue; } - synchronized (mPackages) { + synchronized (mLock) { pkgSetting.setSuspended(suspended, callingPackage, dialogInfo, appExtras, launcherExtras, userId); } @@ -13170,7 +12839,7 @@ public class PackageManagerService extends IPackageManager.Stub sendPackagesSuspendedForUser( changedPackages, changedUids.toArray(), userId, suspended, launcherExtras); sendMyPackageSuspendedOrUnsuspended(changedPackages, suspended, appExtras, userId); - synchronized (mPackages) { + synchronized (mLock) { scheduleWritePackageRestrictionsLocked(userId); } } @@ -13184,9 +12853,9 @@ public class PackageManagerService extends IPackageManager.Stub throw new SecurityException("Calling package " + packageName + " does not belong to calling uid " + callingUid); } - synchronized (mPackages) { + synchronized (mLock) { final PackageSetting ps = mSettings.mPackages.get(packageName); - if (ps == null || filterAppAccessLPr(ps, callingUid, userId)) { + if (ps == null || shouldFilterApplicationLocked(ps, callingUid, userId)) { throw new IllegalArgumentException("Unknown target package: " + packageName); } final PackageUserState packageUserState = ps.readUserState(userId); @@ -13236,9 +12905,9 @@ public class PackageManagerService extends IPackageManager.Stub mPermissionManager.enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, false /* checkShell */, "isPackageSuspendedForUser for user " + userId); - synchronized (mPackages) { + synchronized (mLock) { final PackageSetting ps = mSettings.mPackages.get(packageName); - if (ps == null || filterAppAccessLPr(ps, callingUid, userId)) { + if (ps == null || shouldFilterApplicationLocked(ps, callingUid, userId)) { throw new IllegalArgumentException("Unknown target package: " + packageName); } return ps.getSuspended(userId); @@ -13284,7 +12953,7 @@ public class PackageManagerService extends IPackageManager.Stub private void unsuspendForSuspendingPackages(Predicate<String> packagePredicate, int userId) { final List<String> affectedPackages = new ArrayList<>(); final IntArray affectedUids = new IntArray(); - synchronized (mPackages) { + synchronized (mLock) { for (PackageSetting ps : mSettings.mPackages.values()) { final PackageUserState pus = ps.readUserState(userId); if (pus.suspended && packagePredicate.test(pus.suspendingPackage)) { @@ -13379,7 +13048,7 @@ public class PackageManagerService extends IPackageManager.Stub + "\": required for permissions management"); continue; } - synchronized (mPackages) { + synchronized (mLock) { if (mProtectedPackages.isPackageStateProtected(userId, packageName)) { Slog.w(TAG, "Cannot suspend package \"" + packageName + "\": protected package"); @@ -13537,7 +13206,7 @@ public class PackageManagerService extends IPackageManager.Stub } private int getUidForVerifier(VerifierInfo verifierInfo) { - synchronized (mPackages) { + synchronized (mLock) { final PackageParser.Package pkg = mPackages.get(verifierInfo.packageName); if (pkg == null) { return -1; @@ -13703,10 +13372,11 @@ public class PackageManagerService extends IPackageManager.Stub if (getInstantAppPackageName(callingUid) != null) { return INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED; } - synchronized (mPackages) { + synchronized (mLock) { final PackageSetting ps = mSettings.mPackages.get(packageName); if (ps == null - || filterAppAccessLPr(ps, callingUid, UserHandle.getUserId(callingUid))) { + || shouldFilterApplicationLocked( + ps, callingUid, UserHandle.getUserId(callingUid))) { return INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED; } return mSettings.getIntentFilterVerificationStatusLPr(packageName, userId); @@ -13719,9 +13389,10 @@ public class PackageManagerService extends IPackageManager.Stub android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null); boolean result = false; - synchronized (mPackages) { + synchronized (mLock) { final PackageSetting ps = mSettings.mPackages.get(packageName); - if (filterAppAccessLPr(ps, Binder.getCallingUid(), UserHandle.getCallingUserId())) { + if (shouldFilterApplicationLocked( + ps, Binder.getCallingUid(), UserHandle.getCallingUserId())) { return false; } result = mSettings.updateIntentFilterVerificationStatusLPw(packageName, status, userId); @@ -13739,9 +13410,9 @@ public class PackageManagerService extends IPackageManager.Stub if (getInstantAppPackageName(callingUid) != null) { return ParceledListSlice.emptyList(); } - synchronized (mPackages) { + synchronized (mLock) { final PackageSetting ps = mSettings.mPackages.get(packageName); - if (filterAppAccessLPr(ps, callingUid, UserHandle.getUserId(callingUid))) { + if (shouldFilterApplicationLocked(ps, callingUid, UserHandle.getUserId(callingUid))) { return ParceledListSlice.emptyList(); } return new ParceledListSlice<>(mSettings.getIntentFilterVerificationsLPr(packageName)); @@ -13755,7 +13426,7 @@ public class PackageManagerService extends IPackageManager.Stub } final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); - synchronized (mPackages) { + synchronized (mLock) { PackageParser.Package pkg = mPackages.get(packageName); if (pkg == null || pkg.activities == null) { return ParceledListSlice.emptyList(); @@ -13764,7 +13435,7 @@ public class PackageManagerService extends IPackageManager.Stub return ParceledListSlice.emptyList(); } final PackageSetting ps = (PackageSetting) pkg.mExtras; - if (filterAppAccessLPr(ps, callingUid, callingUserId)) { + if (shouldFilterApplicationLocked(ps, callingUid, callingUserId)) { return ParceledListSlice.emptyList(); } final int count = pkg.activities.size(); @@ -13797,10 +13468,10 @@ public class PackageManagerService extends IPackageManager.Stub return; } // writer - synchronized (mPackages) { + synchronized (mLock) { PackageSetting targetPackageSetting = mSettings.mPackages.get(targetPackage); if (targetPackageSetting == null - || filterAppAccessLPr( + || shouldFilterApplicationLocked( targetPackageSetting, callingUid, UserHandle.getUserId(callingUid))) { throw new IllegalArgumentException("Unknown target package: " + targetPackage); } @@ -13879,12 +13550,13 @@ public class PackageManagerService extends IPackageManager.Stub } mContext.getSystemService(AppOpsManager.class).checkPackage(Binder.getCallingUid(), callerPackageName); - synchronized (mPackages) { + synchronized (mLock) { PackageSetting ps = mSettings.mPackages.get(packageName); if (ps == null) { throw new IllegalArgumentException("Unknown target package " + packageName); } - if (filterAppAccessLPr(ps, Binder.getCallingUid(), UserHandle.getCallingUserId())) { + if (shouldFilterApplicationLocked( + ps, Binder.getCallingUid(), UserHandle.getCallingUserId())) { throw new IllegalArgumentException("Unknown target package " + packageName); } if (!Objects.equals(callerPackageName, ps.installerPackageName)) { @@ -14424,7 +14096,7 @@ public class PackageManagerService extends IPackageManager.Stub String packageName = pkgLite.packageName; int installLocation = pkgLite.installLocation; // reader - synchronized (mPackages) { + synchronized (mLock) { // Currently installed package which the new package is attempting to replace or // null if no such package is installed. PackageParser.Package installedPkg = mPackages.get(packageName); @@ -14763,7 +14435,7 @@ public class PackageManagerService extends IPackageManager.Stub mPendingEnableRollback.append(enableRollbackToken, this); final int[] installedUsers; - synchronized (mPackages) { + synchronized (mLock) { PackageSetting ps = mSettings.getPackageLPr(pkgLite.packageName); if (ps != null) { installedUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), @@ -15201,16 +14873,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. */ @@ -15337,26 +14999,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; @@ -15464,7 +15106,7 @@ public class PackageManagerService extends IPackageManager.Stub final int childCount = (ps.childPackageNames != null) ? ps.childPackageNames.size() : 0; for (int i = 0; i < childCount; i++) { PackageSetting childPs = null; - synchronized (mPackages) { + synchronized (mLock) { childPs = mSettings.getPackageLPr(ps.childPackageNames.get(i)); } if (childPs != null) { @@ -15475,7 +15117,7 @@ public class PackageManagerService extends IPackageManager.Stub } } - @GuardedBy("mPackages") + @GuardedBy("mLock") private void enableSystemPackageLPw(PackageParser.Package pkg) { // Enable the parent package mSettings.enableSystemPackageLPw(pkg.packageName); @@ -15487,7 +15129,7 @@ public class PackageManagerService extends IPackageManager.Stub } } - @GuardedBy("mPackages") + @GuardedBy("mLock") private boolean disableSystemPackageLPw(PackageParser.Package oldPkg, PackageParser.Package newPkg) { // Disable the parent package (parent always replaced) @@ -15502,7 +15144,7 @@ public class PackageManagerService extends IPackageManager.Stub return disabled; } - @GuardedBy("mPackages") + @GuardedBy("mLock") private void setInstallerPackageNameLPw(PackageParser.Package pkg, String installerPackageName) { // Enable the parent package @@ -15539,7 +15181,7 @@ public class PackageManagerService extends IPackageManager.Stub final String pkgName = pkg.packageName; if (DEBUG_INSTALL) Slog.d(TAG, "New package installed in " + pkg.codePath); - synchronized (mPackages) { + synchronized (mLock) { // NOTE: This changes slightly to include UPDATE_PERMISSIONS_ALL regardless of the size of pkg.permissions mPermissionManager.updatePermissions(pkg.packageName, pkg); // For system-bundled packages, we assume that installing an upgraded version @@ -15631,7 +15273,7 @@ public class PackageManagerService extends IPackageManager.Stub } } - @GuardedBy({"mInstallLock", "mPackages"}) + @GuardedBy({"mInstallLock", "mLock"}) private void installPackagesTracedLI(List<InstallRequest> requests) { try { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackages"); @@ -15774,7 +15416,7 @@ public class PackageManagerService extends IPackageManager.Stub } } - @GuardedBy("mPackages") + @GuardedBy("mLock") private static Map<String, ReconciledPackage> reconcilePackagesLocked( final ReconcileRequest request, KeySetManagerService ksms) throws ReconcileFailure { @@ -16085,7 +15727,7 @@ public class PackageManagerService extends IPackageManager.Stub return true; } - @GuardedBy("mPackages") + @GuardedBy("mLock") private void commitPackagesLocked(final CommitRequest request) { // TODO: remove any expected failures from this method; this should only be able to fail due // to unavoidable errors (I/O, etc.) @@ -16151,8 +15793,9 @@ public class PackageManagerService extends IPackageManager.Stub } } else { try { + // Settings will be written during the call to updateSettingsLI(). executeDeletePackageLIF(reconciledPkg.deletePackageAction, packageName, - true, request.mAllUsers, true, pkg); + true, request.mAllUsers, false, pkg); } catch (SystemDeleteException e) { if (Build.IS_ENG) { throw new RuntimeException("Unexpected failure", e); @@ -16288,7 +15931,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()); @@ -16342,7 +15986,7 @@ public class PackageManagerService extends IPackageManager.Stub Collections.unmodifiableMap(mPackages), versionInfos, lastStaticSharedLibSettings); CommitRequest commitRequest = null; - synchronized (mPackages) { + synchronized (mLock) { Map<String, ReconciledPackage> reconciledPackages; try { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "reconcilePackages"); @@ -16644,7 +16288,7 @@ public class PackageManagerService extends IPackageManager.Stub // If we are installing a clustered package add results for the children if (pkg.childPackages != null) { - synchronized (mPackages) { + synchronized (mLock) { final int childCount = pkg.childPackages.size(); for (int i = 0; i < childCount; i++) { PackageParser.Package childPkg = pkg.childPackages.get(i); @@ -16706,7 +16350,7 @@ public class PackageManagerService extends IPackageManager.Stub pp = null; boolean systemApp = false; boolean replace = false; - synchronized (mPackages) { + synchronized (mLock) { // Check if installing already existing package if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) { String oldName = mSettings.getRenamedPackageLPr(pkgName); @@ -16807,7 +16451,7 @@ public class PackageManagerService extends IPackageManager.Stub compareRecover); // The new KeySets will be re-added later in the scanning process. if (compatMatch) { - synchronized (mPackages) { + synchronized (mLock) { ksms.removeAppKeySetDataLPw(pkg.packageName); } } @@ -16927,7 +16571,7 @@ public class PackageManagerService extends IPackageManager.Stub scanFlags |= SCAN_NO_DEX; scanFlags |= SCAN_MOVE; - synchronized (mPackages) { + synchronized (mLock) { final PackageSetting ps = mSettings.mPackages.get(pkgName); if (ps == null) { res.setError(INSTALL_FAILED_INTERNAL_ERROR, @@ -16948,7 +16592,11 @@ 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); + final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths> + derivedAbi = mInjector.getAbiHelper().derivePackageAbi( + pkg, abiOverride, extractNativeLibs); + derivedAbi.first.applyTo(pkg); + derivedAbi.second.applyTo(pkg); } catch (PackageManagerException pme) { Slog.e(TAG, "Error deriving application ABI", pme); throw new PrepareFailure(INSTALL_FAILED_INTERNAL_ERROR, @@ -17010,7 +16658,7 @@ public class PackageManagerService extends IPackageManager.Stub final int[] allUsers; final int[] installedUsers; - synchronized (mPackages) { + synchronized (mLock) { oldPackage = mPackages.get(pkgName11); existingPackage = oldPackage; if (DEBUG_INSTALL) { @@ -17145,7 +16793,7 @@ public class PackageManagerService extends IPackageManager.Stub } childRemovedRes.isUpdate = false; childRemovedRes.dataRemoved = true; - synchronized (mPackages) { + synchronized (mLock) { if (childPs != null) { childRemovedRes.origUsers = childPs.queryInstalledUsers( allUsers, @@ -17217,7 +16865,7 @@ public class PackageManagerService extends IPackageManager.Stub if (DEBUG_INSTALL) Slog.d(TAG, "installNewPackageLI: " + pkg); // TODO(patb): MOVE TO RECONCILE - synchronized (mPackages) { + synchronized (mLock) { renamedPackage = mSettings.getRenamedPackageLPr(pkgName1); if (renamedPackage != null) { // A package with the same name is already installed, though @@ -17269,7 +16917,7 @@ public class PackageManagerService extends IPackageManager.Stub // Collect files we care for fs-verity setup. ArrayMap<String, String> fsverityCandidates = new ArrayMap<>(); if (legacyMode) { - synchronized (mPackages) { + synchronized (mLock) { final PackageSetting ps = mSettings.mPackages.get(pkg.packageName); if (ps != null && ps.isPrivileged()) { fsverityCandidates.put(pkg.baseCodePath, null); @@ -17396,7 +17044,7 @@ public class PackageManagerService extends IPackageManager.Stub int count = 0; final String packageName = pkg.packageName; - synchronized (mPackages) { + synchronized (mLock) { // If this is a new install and we see that we've already run verification for this // package, we have nothing to do: it means the state was restored from backup. if (!replacing) { @@ -17454,7 +17102,7 @@ public class PackageManagerService extends IPackageManager.Stub } } - @GuardedBy("mPackages") + @GuardedBy("mLock") private boolean needsNetworkVerificationLPr(ActivityIntentInfo filter) { final ComponentName cn = filter.activity.getComponentName(); final String packageName = cn.getPackageName(); @@ -17477,10 +17125,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; } @@ -17489,7 +17133,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; } @@ -17570,7 +17214,7 @@ public class PackageManagerService extends IPackageManager.Stub final String packageName = versionedPackage.getPackageName(); final long versionCode = versionedPackage.getLongVersionCode(); final String internalPackageName; - synchronized (mPackages) { + synchronized (mLock) { // Normalize package name to handle renamed packages and static libs internalPackageName = resolveInternalPackageNameLPr(packageName, versionCode); } @@ -17683,7 +17327,7 @@ public class PackageManagerService extends IPackageManager.Stub return pkg.packageName; } - @GuardedBy("mPackages") + @GuardedBy("mLock") private String resolveInternalPackageNameLPr(String packageName, long versionCode) { // Handle renamed packages String normalizedPackageName = mSettings.getRenamedPackageLPr(packageName); @@ -17894,7 +17538,7 @@ public class PackageManagerService extends IPackageManager.Stub int[] allUsers; /** enabled state of the uninstalled application */ final int origEnabledState; - synchronized (mPackages) { + synchronized (mLock) { uninstalledPs = mSettings.mPackages.get(packageName); if (uninstalledPs == null) { Slog.w(TAG, "Not removing non-existent package " + packageName); @@ -17960,7 +17604,7 @@ public class PackageManagerService extends IPackageManager.Stub res = deletePackageLIF(packageName, UserHandle.of(removeUser), true, allUsers, deleteFlags | PackageManager.DELETE_CHATTY, info, true, null); } - synchronized (mPackages) { + synchronized (mLock) { if (res) { if (pkg != null) { mInstantAppRegistry.onPackageUninstalledLPw(pkg, info.removedUsers); @@ -17988,7 +17632,7 @@ public class PackageManagerService extends IPackageManager.Stub final PackageParser.Package stubPkg = (disabledSystemPs == null) ? null : disabledSystemPs.pkg; if (stubPkg != null && stubPkg.isStub) { - synchronized (mPackages) { + synchronized (mLock) { // restore the enabled state of the stub; the state is overwritten when // the stub is uninstalled final PackageSetting stubPs = mSettings.mPackages.get(stubPkg.packageName); @@ -18197,7 +17841,7 @@ public class PackageManagerService extends IPackageManager.Stub if (deletedPs != null) { if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) { final SparseBooleanArray changedUsers = new SparseBooleanArray(); - synchronized (mPackages) { + synchronized (mLock) { clearIntentFilterVerificationsLPw(deletedPs.name, UserHandle.USER_ALL); clearDefaultBrowserIfNeeded(packageName); mSettings.mKeySetManagerService.removeAppKeySetDataLPw(packageName); @@ -18253,7 +17897,7 @@ public class PackageManagerService extends IPackageManager.Stub } } } - synchronized (mPackages) { + synchronized (mLock) { // can downgrade to reader if (writeSettings) { // Save settings now @@ -18394,7 +18038,7 @@ public class PackageManagerService extends IPackageManager.Stub outInfo, writeSettings, disabledPs.pkg); // writer - synchronized (mPackages) { + synchronized (mLock) { // NOTE: The system package always needs to be enabled; even if it's for // a compressed stub. If we don't, installing the system package fails // during scan [scanning checks the disabled packages]. We will reverse @@ -18474,7 +18118,7 @@ public class PackageManagerService extends IPackageManager.Stub prepareAppDataAfterInstallLIF(pkg); // writer - synchronized (mPackages) { + synchronized (mLock) { PackageSetting ps = mSettings.mPackages.get(pkg.packageName); // Propagate the permissions state as we do not want to drop on the floor @@ -18523,7 +18167,7 @@ public class PackageManagerService extends IPackageManager.Stub boolean deleteCodeAndResources, int flags, int[] allUserHandles, PackageRemovedInfo outInfo, boolean writeSettings, PackageParser.Package replacingPackage) { - synchronized (mPackages) { + synchronized (mLock) { if (outInfo != null) { outInfo.uid = ps.appId; } @@ -18550,7 +18194,7 @@ public class PackageManagerService extends IPackageManager.Stub final int childCount = (ps.childPackageNames != null) ? ps.childPackageNames.size() : 0; for (int i = 0; i < childCount; i++) { PackageSetting childPs; - synchronized (mPackages) { + synchronized (mLock) { childPs = mSettings.getPackageLPr(ps.childPackageNames.get(i)); } if (childPs != null) { @@ -18581,7 +18225,7 @@ public class PackageManagerService extends IPackageManager.Stub int userId) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.DELETE_PACKAGES, null); - synchronized (mPackages) { + synchronized (mLock) { // Cannot block uninstall of static shared libs as they are // considered a part of the using app (emulating static linking). // Also static libs are installed always on internal storage. @@ -18599,9 +18243,9 @@ public class PackageManagerService extends IPackageManager.Stub @Override public boolean getBlockUninstallForUser(String packageName, int userId) { - synchronized (mPackages) { + synchronized (mLock) { final PackageSetting ps = mSettings.mPackages.get(packageName); - if (ps == null || filterAppAccessLPr(ps, Binder.getCallingUid(), userId)) { + if (ps == null || shouldFilterApplicationLocked(ps, Binder.getCallingUid(), userId)) { return false; } return mSettings.getBlockUninstallLPr(userId, packageName); @@ -18611,7 +18255,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public boolean setRequiredForSystemUser(String packageName, boolean systemUserApp) { enforceSystemOrRoot("setRequiredForSystemUser can only be run by the system or root"); - synchronized (mPackages) { + synchronized (mLock) { PackageSetting ps = mSettings.mPackages.get(packageName); if (ps == null) { Log.w(TAG, "Package doesn't exist: " + packageName); @@ -18649,7 +18293,7 @@ public class PackageManagerService extends IPackageManager.Stub * deleted, {@code null} otherwise. */ @Nullable - @GuardedBy("mPackages") + @GuardedBy("mLock") private static DeletePackageAction mayDeletePackageLocked( PackageRemovedInfo outInfo, PackageSetting ps, @Nullable PackageSetting disabledPs, @Nullable PackageSetting[] children, int flags, UserHandle user) { @@ -18697,7 +18341,7 @@ public class PackageManagerService extends IPackageManager.Stub PackageRemovedInfo outInfo, boolean writeSettings, PackageParser.Package replacingPackage) { final DeletePackageAction action; - synchronized (mPackages) { + synchronized (mLock) { final PackageSetting ps = mSettings.mPackages.get(packageName); final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(ps); PackageSetting[] children = mSettings.getChildSettingsLPr(ps); @@ -18749,7 +18393,7 @@ public class PackageManagerService extends IPackageManager.Stub : UserHandle.USER_ALL; clearPackageStateForUserLIF(ps, removedUserId, outInfo, flags); - synchronized (mPackages) { + synchronized (mLock) { markPackageUninstalledForUserLPw(ps, user); scheduleWritePackageRestrictionsLocked(user); } @@ -18768,7 +18412,7 @@ public class PackageManagerService extends IPackageManager.Stub // they have set the special DELETE_SYSTEM_APP which requests different // semantics than normal for uninstalling system apps. final boolean clearPackageStateAndReturn; - synchronized (mPackages) { + synchronized (mLock) { markPackageUninstalledForUserLPw(ps, user); if (!systemApp) { // Do not uninstall the APK if an app should be cached @@ -18798,7 +18442,7 @@ public class PackageManagerService extends IPackageManager.Stub } if (clearPackageStateAndReturn) { clearPackageStateForUserLIF(ps, userId, outInfo, flags); - synchronized (mPackages) { + synchronized (mLock) { scheduleWritePackageRestrictionsLocked(user); } return; @@ -18808,7 +18452,7 @@ public class PackageManagerService extends IPackageManager.Stub // If we are deleting a composite package for all users, keep track // of result for each child. if (ps.childPackageNames != null && outInfo != null) { - synchronized (mPackages) { + synchronized (mLock) { final int childCount = ps.childPackageNames.size(); outInfo.removedChildPackages = new ArrayMap<>(childCount); for (int i = 0; i < childCount; i++) { @@ -18841,7 +18485,7 @@ public class PackageManagerService extends IPackageManager.Stub if (outInfo != null) { outInfo.removedForAllUsers = mPackages.get(ps.name) == null; if (outInfo.removedChildPackages != null) { - synchronized (mPackages) { + synchronized (mLock) { final int childCount = outInfo.removedChildPackages.size(); for (int i = 0; i < childCount; i++) { PackageRemovedInfo childInfo = outInfo.removedChildPackages.valueAt(i); @@ -18856,7 +18500,7 @@ public class PackageManagerService extends IPackageManager.Stub // child packages that appeared as they are declared in the system // app but were not declared in the update. if (systemApp) { - synchronized (mPackages) { + synchronized (mLock) { PackageSetting updatedPs = mSettings.getPackageLPr(ps.name); final int childCount = (updatedPs.childPackageNames != null) ? updatedPs.childPackageNames.size() : 0; @@ -18884,7 +18528,7 @@ public class PackageManagerService extends IPackageManager.Stub } } - @GuardedBy("mPackages") + @GuardedBy("mLock") private void markPackageUninstalledForUserLPw(PackageSetting ps, UserHandle user) { final int[] userIds = (user == null || user.getIdentifier() == UserHandle.USER_ALL) ? sUserManager.getUserIds() : new int[] {user.getIdentifier()}; @@ -18918,7 +18562,7 @@ public class PackageManagerService extends IPackageManager.Stub private void clearPackageStateForUserLIF(PackageSetting ps, int userId, PackageRemovedInfo outInfo, int flags) { final PackageParser.Package pkg; - synchronized (mPackages) { + synchronized (mLock) { pkg = mPackages.get(ps.name); } @@ -18941,7 +18585,7 @@ public class PackageManagerService extends IPackageManager.Stub if (changedUsers.size() > 0) { updateDefaultHomeNotLocked(changedUsers); postPreferredActivityChangedBroadcast(nextUserId); - synchronized (mPackages) { + synchronized (mLock) { scheduleWritePackageRestrictionsLocked(nextUserId); } } @@ -18971,7 +18615,7 @@ public class PackageManagerService extends IPackageManager.Stub enforceSystemOrRoot("Only the system can clear all profile data"); final PackageParser.Package pkg; - synchronized (mPackages) { + synchronized (mLock) { pkg = mPackages.get(packageName); } @@ -18993,7 +18637,8 @@ public class PackageManagerService extends IPackageManager.Stub true /* requireFullPermission */, false /* checkShell */, "clear application data"); final PackageSetting ps = mSettings.getPackageLPr(packageName); - final boolean filterApp = (ps != null && filterAppAccessLPr(ps, callingUid, userId)); + final boolean filterApp = + (ps != null && shouldFilterApplicationLocked(ps, callingUid, userId)); if (!filterApp && mProtectedPackages.isPackageDataProtected(userId, packageName)) { throw new SecurityException("Cannot clear data for a protected package: " + packageName); @@ -19009,7 +18654,7 @@ public class PackageManagerService extends IPackageManager.Stub synchronized (mInstallLock) { succeeded = clearApplicationUserDataLIF(packageName, userId); } - synchronized (mPackages) { + synchronized (mLock) { mInstantAppRegistry.deleteInstantApplicationMetadataLPw( packageName, userId); } @@ -19048,7 +18693,7 @@ public class PackageManagerService extends IPackageManager.Stub // Try finding details about the requested package PackageParser.Package pkg; - synchronized (mPackages) { + synchronized (mLock) { pkg = mPackages.get(packageName); if (pkg == null) { final PackageSetting ps = mSettings.mPackages.get(packageName); @@ -19143,7 +18788,7 @@ public class PackageManagerService extends IPackageManager.Stub android.Manifest.permission.ACCESS_INSTANT_APPS); final PackageParser.Package pkg; - synchronized (mPackages) { + synchronized (mLock) { pkg = mPackages.get(packageName); } @@ -19186,7 +18831,7 @@ public class PackageManagerService extends IPackageManager.Stub @GuardedBy("mInstallLock") private boolean getPackageSizeInfoLI(String packageName, int userId, PackageStats stats) { final PackageSetting ps; - synchronized (mPackages) { + synchronized (mLock) { ps = mSettings.mPackages.get(packageName); if (ps == null) { Slog.w(TAG, "Failed to find settings for " + packageName); @@ -19218,7 +18863,7 @@ public class PackageManagerService extends IPackageManager.Stub return true; } - @GuardedBy("mPackages") + @GuardedBy("mLock") private int getUidTargetSdkVersionLockedLPr(int uid) { final int appId = UserHandle.getAppId(uid); final Object obj = mSettings.getSettingLPr(appId); @@ -19243,7 +18888,7 @@ public class PackageManagerService extends IPackageManager.Stub return Build.VERSION_CODES.CUR_DEVELOPMENT; } - @GuardedBy("mPackages") + @GuardedBy("mLock") private int getPackageTargetSdkVersionLockedLPr(String packageName) { final PackageParser.Package p = mPackages.get(packageName); if (p != null) { @@ -19287,7 +18932,7 @@ public class PackageManagerService extends IPackageManager.Stub + userId + ":"); filter.dump(new LogPrinter(Log.INFO, TAG), " "); } - synchronized (mPackages) { + synchronized (mLock) { final PreferredIntentResolver pir = mSettings.editPreferredActivitiesLPw(userId); pir.addFilter(new PreferredActivity(filter, match, set, activity, always)); scheduleWritePackageRestrictionsLocked(userId); @@ -19339,7 +18984,7 @@ public class PackageManagerService extends IPackageManager.Stub if (mContext.checkCallingOrSelfPermission( android.Manifest.permission.SET_PREFERRED_APPLICATIONS) != PackageManager.PERMISSION_GRANTED) { - synchronized (mPackages) { + synchronized (mLock) { if (getUidTargetSdkVersionLockedLPr(callingUid) < Build.VERSION_CODES.FROYO) { Slog.w(TAG, "Ignoring replacePreferredActivity() from uid " @@ -19351,7 +18996,7 @@ public class PackageManagerService extends IPackageManager.Stub android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null); } - synchronized (mPackages) { + synchronized (mLock) { final PreferredIntentResolver pir = mSettings.mPreferredActivities.get(userId); if (pir != null) { // Get all of the existing entries that exactly match this filter. @@ -19415,7 +19060,7 @@ public class PackageManagerService extends IPackageManager.Stub return; } // writer - synchronized (mPackages) { + synchronized (mLock) { PackageParser.Package pkg = mPackages.get(packageName); if (pkg == null || !isCallerSameApp(packageName, callingUid)) { if (mContext.checkCallingOrSelfPermission( @@ -19433,7 +19078,8 @@ public class PackageManagerService extends IPackageManager.Stub } final PackageSetting ps = mSettings.getPackageLPr(packageName); if (ps != null - && filterAppAccessLPr(ps, callingUid, UserHandle.getUserId(callingUid))) { + && shouldFilterApplicationLocked( + ps, callingUid, UserHandle.getUserId(callingUid))) { return; } } @@ -19443,14 +19089,14 @@ public class PackageManagerService extends IPackageManager.Stub if (changedUsers.size() > 0) { updateDefaultHomeNotLocked(changedUsers); postPreferredActivityChangedBroadcast(callingUserId); - synchronized (mPackages) { + synchronized (mLock) { scheduleWritePackageRestrictionsLocked(callingUserId); } } } /** This method takes a specific user id as well as UserHandle.USER_ALL. */ - @GuardedBy("mPackages") + @GuardedBy("mLock") private void clearPackagePreferredActivitiesLPw(String packageName, @NonNull SparseBooleanArray outUserChanged, int userId) { ArrayList<PreferredActivity> removed = null; @@ -19485,7 +19131,7 @@ public class PackageManagerService extends IPackageManager.Stub } /** This method takes a specific user id as well as UserHandle.USER_ALL. */ - @GuardedBy("mPackages") + @GuardedBy("mLock") private void clearIntentFilterVerificationsLPw(int userId) { final int packageCount = mPackages.size(); for (int i = 0; i < packageCount; i++) { @@ -19495,7 +19141,7 @@ public class PackageManagerService extends IPackageManager.Stub } /** This method takes a specific user id as well as UserHandle.USER_ALL. */ - @GuardedBy("mPackages") + @GuardedBy("mLock") void clearIntentFilterVerificationsLPw(String packageName, int userId) { if (userId == UserHandle.USER_ALL) { if (mSettings.removeIntentFilterVerificationLPw(packageName, @@ -19539,7 +19185,7 @@ public class PackageManagerService extends IPackageManager.Stub if (changedUsers.size() > 0) { postPreferredActivityChangedBroadcast(userId); } - synchronized (mPackages) { + synchronized (mLock) { mSettings.applyDefaultPreferredAppsLPw(userId); clearIntentFilterVerificationsLPw(userId); primeDomainVerificationsLPw(userId); @@ -19552,7 +19198,7 @@ public class PackageManagerService extends IPackageManager.Stub // callbacks to the package manager to request a default app reset. mPermissionManager.setDefaultBrowser(null, true, true, userId); resetNetworkPolicies(userId); - synchronized (mPackages) { + synchronized (mLock) { scheduleWritePackageRestrictionsLocked(userId); } } finally { @@ -19569,7 +19215,7 @@ public class PackageManagerService extends IPackageManager.Stub int num = 0; final int userId = UserHandle.getCallingUserId(); // reader - synchronized (mPackages) { + synchronized (mLock) { PreferredIntentResolver pir = mSettings.mPreferredActivities.get(userId); if (pir != null) { final Iterator<PreferredActivity> it = pir.filterIterator(); @@ -19609,7 +19255,7 @@ public class PackageManagerService extends IPackageManager.Stub + " for user " + userId + ":"); filter.dump(new LogPrinter(Log.INFO, TAG), " "); } - synchronized (mPackages) { + synchronized (mLock) { mSettings.editPersistentPreferredActivitiesLPw(userId).addFilter( new PersistentPreferredActivity(filter, activity)); scheduleWritePackageRestrictionsLocked(userId); @@ -19627,7 +19273,7 @@ public class PackageManagerService extends IPackageManager.Stub } ArrayList<PersistentPreferredActivity> removed = null; boolean changed = false; - synchronized (mPackages) { + synchronized (mLock) { for (int i=0; i<mSettings.mPersistentPreferredActivities.size(); i++) { final int thisUserId = mSettings.mPersistentPreferredActivities.keyAt(i); PersistentPreferredIntentResolver ppir = mSettings.mPersistentPreferredActivities @@ -19658,7 +19304,7 @@ public class PackageManagerService extends IPackageManager.Stub if (changed) { updateDefaultHomeNotLocked(userId); postPreferredActivityChangedBroadcast(userId); - synchronized (mPackages) { + synchronized (mLock) { scheduleWritePackageRestrictionsLocked(userId); } } @@ -19717,7 +19363,7 @@ public class PackageManagerService extends IPackageManager.Stub serializer.startDocument(null, true); serializer.startTag(null, TAG_PREFERRED_BACKUP); - synchronized (mPackages) { + synchronized (mLock) { mSettings.writePreferredActivitiesLPr(serializer, userId, true); } @@ -19745,7 +19391,7 @@ public class PackageManagerService extends IPackageManager.Stub parser.setInput(new ByteArrayInputStream(backup), StandardCharsets.UTF_8.name()); restoreFromXml(parser, userId, TAG_PREFERRED_BACKUP, (readParser, readUserId) -> { - synchronized (mPackages) { + synchronized (mLock) { mSettings.readPreferredActivitiesLPw(readParser, readUserId); } updateDefaultHomeNotLocked(readUserId); @@ -19775,7 +19421,7 @@ public class PackageManagerService extends IPackageManager.Stub serializer.startDocument(null, true); serializer.startTag(null, TAG_DEFAULT_APPS); - synchronized (mPackages) { + synchronized (mLock) { mSettings.writeDefaultAppsLPr(serializer, userId); } @@ -19804,7 +19450,7 @@ public class PackageManagerService extends IPackageManager.Stub restoreFromXml(parser, userId, TAG_DEFAULT_APPS, (parser1, userId1) -> { final String defaultBrowser; - synchronized (mPackages) { + synchronized (mLock) { mSettings.readDefaultAppsLPw(parser1, userId1); defaultBrowser = mSettings.removeDefaultBrowserPackageNameLPw(userId1); } @@ -19833,7 +19479,7 @@ public class PackageManagerService extends IPackageManager.Stub serializer.startDocument(null, true); serializer.startTag(null, TAG_INTENT_FILTER_VERIFICATION); - synchronized (mPackages) { + synchronized (mLock) { mSettings.writeAllDomainVerificationsLPr(serializer, userId); } @@ -19861,7 +19507,7 @@ public class PackageManagerService extends IPackageManager.Stub parser.setInput(new ByteArrayInputStream(backup), StandardCharsets.UTF_8.name()); restoreFromXml(parser, userId, TAG_INTENT_FILTER_VERIFICATION, (parser1, userId1) -> { - synchronized (mPackages) { + synchronized (mLock) { mSettings.readAllDomainVerificationsLPr(parser1, userId1); mSettings.writeLPr(); } @@ -19886,7 +19532,7 @@ public class PackageManagerService extends IPackageManager.Stub Slog.w(TAG, "Cannot set a crossProfile intent filter with no filter actions"); return; } - synchronized (mPackages) { + synchronized (mLock) { CrossProfileIntentFilter newFilter = new CrossProfileIntentFilter(intentFilter, ownerPackage, targetUserId, flags); CrossProfileIntentResolver resolver = @@ -19914,7 +19560,7 @@ public class PackageManagerService extends IPackageManager.Stub enforceOwnerRights(ownerPackage, callingUid); PackageManagerServiceUtils.enforceShellRestriction( UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, sourceUserId); - synchronized (mPackages) { + synchronized (mLock) { CrossProfileIntentResolver resolver = mSettings.editCrossProfileIntentResolverLPw(sourceUserId); ArraySet<CrossProfileIntentFilter> set = @@ -20066,7 +19712,7 @@ public class PackageManagerService extends IPackageManager.Stub /** <b>must not hold {@link #mPackages}</b> */ private void updateDefaultHomeNotLocked(SparseBooleanArray userIds) { - if (Thread.holdsLock(mPackages)) { + if (Thread.holdsLock(mLock)) { Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName() + " is holding mPackages", new Throwable()); } @@ -20082,7 +19728,7 @@ public class PackageManagerService extends IPackageManager.Stub * @return Whether the ACTION_PREFERRED_ACTIVITY_CHANGED broadcast has been scheduled. */ private boolean updateDefaultHomeNotLocked(int userId) { - if (Thread.holdsLock(mPackages)) { + if (Thread.holdsLock(mLock)) { Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName() + " is holding mPackages", new Throwable()); } @@ -20271,7 +19917,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public void setUpdateAvailable(String packageName, boolean updateAvailable) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null); - synchronized (mPackages) { + synchronized (mLock) { final PackageSetting pkgSetting = mSettings.mPackages.get(packageName); if (pkgSetting != null) { pkgSetting.setUpdateAvailable(updateAvailable); @@ -20316,7 +19962,7 @@ public class PackageManagerService extends IPackageManager.Stub ArrayList<String> components; // reader - synchronized (mPackages) { + synchronized (mLock) { pkgSetting = mSettings.mPackages.get(packageName); if (pkgSetting == null) { if (!isCallerInstantApp) { @@ -20342,7 +19988,7 @@ public class PackageManagerService extends IPackageManager.Stub if (!UserHandle.isSameApp(callingUid, pkgSetting.appId)) { // Don't allow apps that don't have permission to modify other apps if (!allowedByPermission - || filterAppAccessLPr(pkgSetting, callingUid, userId)) { + || shouldFilterApplicationLocked(pkgSetting, callingUid, userId)) { throw new SecurityException( "Attempt to change component state; " + "pid=" + Binder.getCallingPid() @@ -20363,7 +20009,7 @@ public class PackageManagerService extends IPackageManager.Stub throw new SecurityException("Cannot disable a system-generated component"); } - synchronized (mPackages) { + synchronized (mLock) { if (callingUid == Process.SHELL_UID && (pkgSetting.pkgFlags & ApplicationInfo.FLAG_TEST_ONLY) == 0) { // Shell can only change whole packages between ENABLED and DISABLED_USER states @@ -20388,7 +20034,7 @@ public class PackageManagerService extends IPackageManager.Stub } if (className == null) { // We're dealing with an application/package level state change - synchronized (mPackages) { + synchronized (mLock) { if (pkgSetting.getEnabled(userId) == newState) { // Nothing to do return; @@ -20412,11 +20058,11 @@ public class PackageManagerService extends IPackageManager.Stub // Don't care about who enables an app. callingPackage = null; } - synchronized (mPackages) { + synchronized (mLock) { pkgSetting.setEnabled(newState, userId, callingPackage); } } else { - synchronized (mPackages) { + synchronized (mLock) { // We're dealing with a component level state change // First, verify that this is a valid class name. PackageParser.Package pkg = pkgSetting.pkg; @@ -20453,7 +20099,7 @@ public class PackageManagerService extends IPackageManager.Stub } } } - synchronized (mPackages) { + synchronized (mLock) { scheduleWritePackageRestrictionsLocked(userId); updateSequenceNumberLP(pkgSetting, new int[] { userId }); final long callingId = Binder.clearCallingIdentity(); @@ -20514,7 +20160,7 @@ public class PackageManagerService extends IPackageManager.Stub } mPermissionManager.enforceCrossUserPermission(Binder.getCallingUid(), userId, false /* requireFullPermission*/, false /* checkShell */, "flushPackageRestrictions"); - synchronized (mPackages) { + synchronized (mLock) { mSettings.writePackageRestrictionsLPr(userId); mDirtyUsers.remove(userId); if (mDirtyUsers.isEmpty()) { @@ -20561,9 +20207,9 @@ public class PackageManagerService extends IPackageManager.Stub mPermissionManager.enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, true /* checkShell */, "stop package"); // writer - synchronized (mPackages) { + synchronized (mLock) { final PackageSetting ps = mSettings.mPackages.get(packageName); - if (!filterAppAccessLPr(ps, callingUid, userId) + if (!shouldFilterApplicationLocked(ps, callingUid, userId) && mSettings.setPackageStoppedStateLPw(this, packageName, stopped, allowedByPermission, callingUid, userId)) { scheduleWritePackageRestrictionsLocked(userId); @@ -20574,9 +20220,10 @@ public class PackageManagerService extends IPackageManager.Stub @Override public String getInstallerPackageName(String packageName) { final int callingUid = Binder.getCallingUid(); - synchronized (mPackages) { + synchronized (mLock) { final PackageSetting ps = mSettings.mPackages.get(packageName); - if (filterAppAccessLPr(ps, callingUid, UserHandle.getUserId(callingUid))) { + if (shouldFilterApplicationLocked( + ps, callingUid, UserHandle.getUserId(callingUid))) { return null; } // InstallerPackageName for Apex is not stored in PackageManager @@ -20589,7 +20236,7 @@ public class PackageManagerService extends IPackageManager.Stub public boolean isOrphaned(String packageName) { // reader - synchronized (mPackages) { + synchronized (mLock) { if (!mPackages.containsKey(packageName)) { return false; } @@ -20604,8 +20251,9 @@ public class PackageManagerService extends IPackageManager.Stub mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */, false /* checkShell */, "get enabled"); // reader - synchronized (mPackages) { - if (filterAppAccessLPr(mSettings.getPackageLPr(packageName), callingUid, userId)) { + synchronized (mLock) { + if (shouldFilterApplicationLocked( + mSettings.getPackageLPr(packageName), callingUid, userId)) { return COMPONENT_ENABLED_STATE_DISABLED; } return mSettings.getApplicationEnabledSettingLPr(packageName, userId); @@ -20619,8 +20267,9 @@ public class PackageManagerService extends IPackageManager.Stub int callingUid = Binder.getCallingUid(); mPermissionManager.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, false /*checkShell*/, "getComponentEnabled"); - synchronized (mPackages) { - if (filterAppAccessLPr(mSettings.getPackageLPr(component.getPackageName()), callingUid, + synchronized (mLock) { + if (shouldFilterApplicationLocked( + mSettings.getPackageLPr(component.getPackageName()), callingUid, component, TYPE_UNKNOWN, userId)) { return COMPONENT_ENABLED_STATE_DISABLED; } @@ -20681,7 +20330,7 @@ public class PackageManagerService extends IPackageManager.Stub Log.d(TAG, "compatibility mode:" + compatibilityModeEnabled); } - synchronized (mPackages) { + synchronized (mLock) { // Verify that all of the preferred activity components actually // exist. It is possible for applications to be updated and at // that point remove a previously declared activity component that @@ -20717,8 +20366,18 @@ public class PackageManagerService extends IPackageManager.Stub // Now that we've scanned all packages, and granted any default // permissions, ensure permissions are updated. Beware of dragons if you // try optimizing this. - synchronized (mPackages) { + synchronized (mLock) { mPermissionManager.updateAllPermissions(StorageManager.UUID_PRIVATE_INTERNAL, false); + + final PermissionPolicyInternal permissionPolicyInternal = + LocalServices.getService(PermissionPolicyInternal.class); + permissionPolicyInternal.setOnInitializedCallback(userId -> { + // The SDK updated case is already handled when we run during the ctor. + synchronized (mPackages) { + mPermissionManager.updateAllPermissions( + StorageManager.UUID_PRIVATE_INTERNAL, false); + } + }); } // Watch for external volumes that come and go over time @@ -21012,7 +20671,7 @@ public class PackageManagerService extends IPackageManager.Stub } else if ("service-permissions".equals(cmd)) { dumpState.setDump(DumpState.DUMP_SERVICE_PERMISSIONS); } else if ("write".equals(cmd)) { - synchronized (mPackages) { + synchronized (mLock) { mSettings.writeLPr(); pw.println("Settings written."); return; @@ -21025,7 +20684,7 @@ public class PackageManagerService extends IPackageManager.Stub } // reader - synchronized (mPackages) { + synchronized (mLock) { if (dumpState.isDumping(DumpState.DUMP_VERSION) && packageName == null) { if (!checkin) { if (dumpState.onTitlePrinted()) @@ -21404,7 +21063,7 @@ public class PackageManagerService extends IPackageManager.Stub private void dumpProto(FileDescriptor fd) { final ProtoOutputStream proto = new ProtoOutputStream(fd); - synchronized (mPackages) { + synchronized (mLock) { final long requiredVerifierPackageToken = proto.start(PackageServiceDumpProto.REQUIRED_VERIFIER_PACKAGE); proto.write(PackageServiceDumpProto.PackageShortProto.NAME, mRequiredVerifierPackage); @@ -21476,7 +21135,7 @@ public class PackageManagerService extends IPackageManager.Stub } } - @GuardedBy("mPackages") + @GuardedBy("mLock") @SuppressWarnings("resource") private void dumpDexoptStateLPr(PrintWriter pw, String packageName) { final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); @@ -21505,7 +21164,7 @@ public class PackageManagerService extends IPackageManager.Stub } } - @GuardedBy("mPackages") + @GuardedBy("mLock") @SuppressWarnings("resource") private void dumpCompilerStatsLPr(PrintWriter pw, String packageName) { final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); @@ -21655,7 +21314,7 @@ public class PackageManagerService extends IPackageManager.Stub final VersionInfo ver; final List<PackageSetting> packages; - synchronized (mPackages) { + synchronized (mLock) { ver = mSettings.findOrCreateVersion(volumeUuid); packages = mSettings.getVolumePackagesLPr(volumeUuid); } @@ -21705,7 +21364,7 @@ public class PackageManagerService extends IPackageManager.Stub } } - synchronized (mPackages) { + synchronized (mLock) { final boolean sdkUpdated = (ver.sdkVersion != mSdkVersion); if (sdkUpdated) { logCriticalInfo(Log.INFO, "Platform changed from " + ver.sdkVersion + " to " @@ -21741,7 +21400,7 @@ public class PackageManagerService extends IPackageManager.Stub final ArrayList<ApplicationInfo> unloaded = new ArrayList<>(); synchronized (mInstallLock) { - synchronized (mPackages) { + synchronized (mLock) { final List<PackageSetting> packages = mSettings.getVolumePackagesLPr(volumeUuid); for (PackageSetting ps : packages) { if (ps.pkg == null) continue; @@ -21786,7 +21445,7 @@ public class PackageManagerService extends IPackageManager.Stub private void assertPackageKnownAndInstalled(String volumeUuid, String packageName, int userId) throws PackageManagerException { - synchronized (mPackages) { + synchronized (mLock) { // Normalize package name to handle renamed packages packageName = normalizePackageNameLPr(packageName); @@ -21805,7 +21464,7 @@ public class PackageManagerService extends IPackageManager.Stub } private List<String> collectAbsoluteCodePaths() { - synchronized (mPackages) { + synchronized (mLock) { List<String> codePaths = new ArrayList<>(); final int packageCount = mSettings.mPackages.size(); for (int i = 0; i < packageCount; i++) { @@ -21956,7 +21615,7 @@ public class PackageManagerService extends IPackageManager.Stub // Ensure that data directories are ready to roll for all packages // installed for this volume and user final List<PackageSetting> packages; - synchronized (mPackages) { + synchronized (mLock) { packages = mSettings.getVolumePackagesLPr(volumeUuid); } int preparedCount = 0; @@ -21998,7 +21657,7 @@ public class PackageManagerService extends IPackageManager.Stub */ private void prepareAppDataAfterInstallLIF(PackageParser.Package pkg) { final PackageSetting ps; - synchronized (mPackages) { + synchronized (mLock) { ps = mSettings.mPackages.get(pkg.packageName); mSettings.writeKernelMappingLPr(ps); } @@ -22060,7 +21719,7 @@ public class PackageManagerService extends IPackageManager.Stub } final PackageSetting ps; - synchronized (mPackages) { + synchronized (mLock) { ps = mSettings.mPackages.get(pkg.packageName); } final String volumeUuid = pkg.volumeUuid; @@ -22121,7 +21780,7 @@ public class PackageManagerService extends IPackageManager.Stub if ((flags & StorageManager.FLAG_STORAGE_CE) != 0 && ceDataInode != -1) { // TODO: mark this structure as dirty so we persist it! - synchronized (mPackages) { + synchronized (mLock) { if (ps != null) { ps.setCeDataInode(ceDataInode, userId); } @@ -22251,7 +21910,7 @@ public class PackageManagerService extends IPackageManager.Stub } public PackageFreezer(String packageName, int userId, String killReason) { - synchronized (mPackages) { + synchronized (mLock) { mPackageName = packageName; mWeFroze = mFrozenPackages.add(mPackageName); @@ -22289,7 +21948,7 @@ public class PackageManagerService extends IPackageManager.Stub public void close() { mCloseGuard.close(); if (mClosed.compareAndSet(false, true)) { - synchronized (mPackages) { + synchronized (mLock) { if (mWeFroze) { mFrozenPackages.remove(mPackageName); } @@ -22308,7 +21967,7 @@ public class PackageManagerService extends IPackageManager.Stub * Verify that given package is currently frozen. */ private void checkPackageFrozen(String packageName) { - synchronized (mPackages) { + synchronized (mLock) { if (!mFrozenPackages.contains(packageName)) { Slog.wtf(TAG, "Expected " + packageName + " to be frozen!", new Throwable()); } @@ -22352,12 +22011,12 @@ public class PackageManagerService extends IPackageManager.Stub final boolean isCurrentLocationExternal; // reader - synchronized (mPackages) { + synchronized (mLock) { final PackageParser.Package pkg = mPackages.get(packageName); final PackageSetting ps = mSettings.mPackages.get(packageName); if (pkg == null || ps == null - || filterAppAccessLPr(ps, callingUid, user.getIdentifier())) { + || shouldFilterApplicationLocked(ps, callingUid, user.getIdentifier())) { throw new PackageManagerException(MOVE_FAILED_DOESNT_EXIST, "Missing package"); } if (pkg.applicationInfo.isSystemApp()) { @@ -22565,7 +22224,7 @@ public class PackageManagerService extends IPackageManager.Stub */ private void logAppMovedStorage(String packageName, boolean isPreviousLocationExternal) { final PackageParser.Package pkg; - synchronized (mPackages) { + synchronized (mLock) { pkg = mPackages.get(packageName); } if (pkg == null) { @@ -22662,7 +22321,7 @@ public class PackageManagerService extends IPackageManager.Stub /** Called by UserManagerService */ void cleanUpUser(UserManagerService userManager, int userHandle) { - synchronized (mPackages) { + synchronized (mLock) { mDirtyUsers.remove(userHandle); mUserNeedsBadging.delete(userHandle); mSettings.removeUserLPw(userHandle); @@ -22677,7 +22336,7 @@ public class PackageManagerService extends IPackageManager.Stub * that are no longer in use by any other user. * @param userHandle the user being removed */ - @GuardedBy("mPackages") + @GuardedBy("mLock") private void removeUnusedPackagesLPw(UserManagerService userManager, final int userHandle) { final boolean DEBUG_CLEAN_APKS = false; int [] users = userManager.getUserIds(); @@ -22728,7 +22387,7 @@ public class PackageManagerService extends IPackageManager.Stub synchronized (mInstallLock) { mSettings.createNewUserLI(this, mInstaller, userId, disallowedPackages); } - synchronized (mPackages) { + synchronized (mLock) { scheduleWritePackageRestrictionsLocked(userId); scheduleWritePackageListLocked(userId); primeDomainVerificationsLPw(userId); @@ -22745,7 +22404,7 @@ public class PackageManagerService extends IPackageManager.Stub android.Manifest.permission.PACKAGE_VERIFICATION_AGENT, "Only package verification agents can read the verifier device identity"); - synchronized (mPackages) { + synchronized (mLock) { return mSettings.getVerifierDeviceIdentityLPw(); } } @@ -22807,14 +22466,15 @@ public class PackageManagerService extends IPackageManager.Stub if (packageName == null || alias == null) { return null; } - synchronized(mPackages) { + synchronized (mLock) { final PackageParser.Package pkg = mPackages.get(packageName); if (pkg == null) { Slog.w(TAG, "KeySet requested for unknown package: " + packageName); throw new IllegalArgumentException("Unknown package: " + packageName); } final PackageSetting ps = (PackageSetting) pkg.mExtras; - if (filterAppAccessLPr(ps, Binder.getCallingUid(), UserHandle.getCallingUserId())) { + if (shouldFilterApplicationLocked( + ps, Binder.getCallingUid(), UserHandle.getCallingUserId())) { Slog.w(TAG, "KeySet requested for filtered package: " + packageName); throw new IllegalArgumentException("Unknown package: " + packageName); } @@ -22828,7 +22488,7 @@ public class PackageManagerService extends IPackageManager.Stub if (packageName == null) { return null; } - synchronized(mPackages) { + synchronized (mLock) { final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); final PackageParser.Package pkg = mPackages.get(packageName); @@ -22837,7 +22497,7 @@ public class PackageManagerService extends IPackageManager.Stub throw new IllegalArgumentException("Unknown package: " + packageName); } final PackageSetting ps = (PackageSetting) pkg.mExtras; - if (filterAppAccessLPr(ps, callingUid, callingUserId)) { + if (shouldFilterApplicationLocked(ps, callingUid, callingUserId)) { // filter and pretend the package doesn't exist Slog.w(TAG, "KeySet requested for filtered package: " + packageName + ", uid:" + callingUid); @@ -22861,10 +22521,10 @@ public class PackageManagerService extends IPackageManager.Stub if (packageName == null || ks == null) { return false; } - synchronized(mPackages) { + synchronized (mLock) { final PackageParser.Package pkg = mPackages.get(packageName); if (pkg == null - || filterAppAccessLPr((PackageSetting) pkg.mExtras, callingUid, + || shouldFilterApplicationLocked((PackageSetting) pkg.mExtras, callingUid, UserHandle.getUserId(callingUid))) { Slog.w(TAG, "KeySet requested for unknown package: " + packageName); throw new IllegalArgumentException("Unknown package: " + packageName); @@ -22887,10 +22547,10 @@ public class PackageManagerService extends IPackageManager.Stub if (packageName == null || ks == null) { return false; } - synchronized(mPackages) { + synchronized (mLock) { final PackageParser.Package pkg = mPackages.get(packageName); if (pkg == null - || filterAppAccessLPr((PackageSetting) pkg.mExtras, callingUid, + || shouldFilterApplicationLocked((PackageSetting) pkg.mExtras, callingUid, UserHandle.getUserId(callingUid))) { Slog.w(TAG, "KeySet requested for unknown package: " + packageName); throw new IllegalArgumentException("Unknown package: " + packageName); @@ -22904,7 +22564,7 @@ public class PackageManagerService extends IPackageManager.Stub } } - @GuardedBy("mPackages") + @GuardedBy("mLock") private void deletePackageIfUnusedLPr(final String packageName) { PackageSetting ps = mSettings.mPackages.get(packageName); if (ps == null) { @@ -23176,7 +22836,7 @@ public class PackageManagerService extends IPackageManager.Stub } private SigningDetails getSigningDetails(@NonNull String packageName) { - synchronized (mPackages) { + synchronized (mLock) { PackageParser.Package p = mPackages.get(packageName); if (p == null) { return null; @@ -23186,7 +22846,7 @@ public class PackageManagerService extends IPackageManager.Stub } private SigningDetails getSigningDetails(int uid) { - synchronized (mPackages) { + synchronized (mLock) { final int appId = UserHandle.getAppId(uid); final Object obj = mSettings.getSettingLPr(appId); if (obj != null) { @@ -23213,27 +22873,28 @@ 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); + synchronized (mLock) { + return PackageManagerService.this.shouldFilterApplicationLocked( + (PackageSetting) pkg.mExtras, callingUid, userId); } } @Override public boolean filterAppAccess(String packageName, int callingUid, int userId) { - synchronized (mPackages) { + synchronized (mLock) { final PackageParser.Package pkg = mPackages.get(packageName); if (pkg == null) { return false; } return PackageManagerService.this - .filterAppAccessLPr((PackageSetting) pkg.mExtras, callingUid, userId); + .shouldFilterApplicationLocked( + (PackageSetting) pkg.mExtras, callingUid, userId); } } @Override public PackageParser.Package getPackage(String packageName) { - synchronized (mPackages) { + synchronized (mLock) { packageName = resolveInternalPackageNameLPr( packageName, PackageManager.VERSION_CODE_HIGHEST); return mPackages.get(packageName); @@ -23242,7 +22903,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public PackageParser.Package getPackage(int uid) { - synchronized (mPackages) { + synchronized (mLock) { final String[] packageNames = getPackagesForUid(uid); PackageParser.Package pkg = null; final int numPackages = packageNames == null ? 0 : packageNames.length; @@ -23255,7 +22916,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public PackageList getPackageList(PackageListObserver observer) { - synchronized (mPackages) { + synchronized (mLock) { final int N = mPackages.size(); final ArrayList<String> list = new ArrayList<>(N); for (int i = 0; i < N; i++) { @@ -23271,14 +22932,14 @@ public class PackageManagerService extends IPackageManager.Stub @Override public void removePackageListObserver(PackageListObserver observer) { - synchronized (mPackages) { + synchronized (mLock) { mPackageListObservers.remove(observer); } } @Override public PackageParser.Package getDisabledSystemPackage(String packageName) { - synchronized (mPackages) { + synchronized (mLock) { final PackageSetting ps = mSettings.getDisabledSystemPkgLPr(packageName); return (ps != null) ? ps.pkg : null; } @@ -23331,7 +22992,7 @@ public class PackageManagerService extends IPackageManager.Stub public void setKeepUninstalledPackages(final List<String> packageList) { Preconditions.checkNotNull(packageList); List<String> removedFromList = null; - synchronized (mPackages) { + synchronized (mLock) { if (mKeepUninstalledPackages != null) { final int packagesCount = mKeepUninstalledPackages.size(); for (int i = 0; i < packagesCount; i++) { @@ -23357,7 +23018,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public boolean isPermissionsReviewRequired(String packageName, int userId) { - synchronized (mPackages) { + synchronized (mLock) { final PackageParser.Package pkg = mPackages.get(packageName); if (pkg == null) { return false; @@ -23377,7 +23038,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public Bundle getSuspendedPackageLauncherExtras(String packageName, int userId) { - synchronized (mPackages) { + synchronized (mLock) { final PackageSetting ps = mSettings.mPackages.get(packageName); PersistableBundle launcherExtras = null; if (ps != null) { @@ -23389,7 +23050,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public boolean isPackageSuspended(String packageName, int userId) { - synchronized (mPackages) { + synchronized (mLock) { final PackageSetting ps = mSettings.mPackages.get(packageName); return (ps != null) ? ps.getSuspended(userId) : false; } @@ -23397,7 +23058,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public String getSuspendingPackage(String suspendedPackage, int userId) { - synchronized (mPackages) { + synchronized (mLock) { final PackageSetting ps = mSettings.mPackages.get(suspendedPackage); return (ps != null) ? ps.readUserState(userId).suspendingPackage : null; } @@ -23405,7 +23066,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public SuspendDialogInfo getSuspendedDialogInfo(String suspendedPackage, int userId) { - synchronized (mPackages) { + synchronized (mLock) { final PackageSetting ps = mSettings.mPackages.get(suspendedPackage); return (ps != null) ? ps.readUserState(userId).dialogInfo : null; } @@ -23413,7 +23074,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public int getDistractingPackageRestrictions(String packageName, int userId) { - synchronized (mPackages) { + synchronized (mLock) { final PackageSetting ps = mSettings.mPackages.get(packageName); return (ps != null) ? ps.getDistractionFlags(userId) : RESTRICTION_NONE; } @@ -23500,7 +23161,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public boolean isPackageEphemeral(int userId, String packageName) { - synchronized (mPackages) { + synchronized (mLock) { final PackageSetting ps = mSettings.mPackages.get(packageName); return ps != null ? ps.getInstantApp(userId) : false; } @@ -23508,21 +23169,21 @@ public class PackageManagerService extends IPackageManager.Stub @Override public boolean wasPackageEverLaunched(String packageName, int userId) { - synchronized (mPackages) { + synchronized (mLock) { return mSettings.wasPackageEverLaunchedLPr(packageName, userId); } } @Override public boolean isEnabledAndMatches(ComponentInfo info, int flags, int userId) { - synchronized (mPackages) { + synchronized (mLock) { return mSettings.isEnabledAndMatchLPr(info, flags, userId); } } @Override public boolean userNeedsBadging(int userId) { - synchronized (mPackages) { + synchronized (mLock) { return PackageManagerService.this.userNeedsBadging(userId); } } @@ -23544,7 +23205,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public void grantEphemeralAccess(int userId, Intent intent, int targetAppId, int ephemeralAppId) { - synchronized (mPackages) { + synchronized (mLock) { mInstantAppRegistry.grantInstantAccessLPw(userId, intent, targetAppId, ephemeralAppId); } @@ -23552,7 +23213,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public boolean isInstantAppInstallerComponent(ComponentName component) { - synchronized (mPackages) { + synchronized (mLock) { return mInstantAppInstallerActivity != null && mInstantAppInstallerActivity.getComponentName().equals(component); } @@ -23576,7 +23237,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public boolean isPackagePersistent(String packageName) { - synchronized (mPackages) { + synchronized (mLock) { PackageParser.Package pkg = mPackages.get(packageName); return pkg != null ? ((pkg.applicationInfo.flags&(ApplicationInfo.FLAG_SYSTEM @@ -23588,7 +23249,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public boolean isLegacySystemApp(PackageParser.Package pkg) { - synchronized (mPackages) { + synchronized (mLock) { final PackageSetting ps = (PackageSetting) pkg.mExtras; return mPromoteSystemApps && ps.isSystem() @@ -23599,7 +23260,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public List<PackageInfo> getOverlayPackages(int userId) { final ArrayList<PackageInfo> overlayPackages = new ArrayList<PackageInfo>(); - synchronized (mPackages) { + synchronized (mLock) { for (PackageParser.Package p : mPackages.values()) { if (p.mOverlayTarget != null) { PackageInfo pkg = generatePackageInfo((PackageSetting)p.mExtras, 0, userId); @@ -23615,7 +23276,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public List<String> getTargetPackageNames(int userId) { List<String> targetPackages = new ArrayList<>(); - synchronized (mPackages) { + synchronized (mLock) { for (PackageParser.Package p : mPackages.values()) { if (p.mOverlayTarget == null) { targetPackages.add(p.packageName); @@ -23628,7 +23289,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public boolean setEnabledOverlayPackages(int userId, @NonNull String targetPackageName, @Nullable List<String> overlayPackageNames) { - synchronized (mPackages) { + synchronized (mLock) { if (targetPackageName == null || mPackages.get(targetPackageName) == null) { Slog.e(TAG, "failed to find package " + targetPackageName); return false; @@ -23675,28 +23336,28 @@ public class PackageManagerService extends IPackageManager.Stub @Override public void addIsolatedUid(int isolatedUid, int ownerUid) { - synchronized (mPackages) { + synchronized (mLock) { mIsolatedOwners.put(isolatedUid, ownerUid); } } @Override public void removeIsolatedUid(int isolatedUid) { - synchronized (mPackages) { + synchronized (mLock) { mIsolatedOwners.delete(isolatedUid); } } @Override public int getUidTargetSdkVersion(int uid) { - synchronized (mPackages) { + synchronized (mLock) { return getUidTargetSdkVersionLockedLPr(uid); } } @Override public int getPackageTargetSdkVersion(String packageName) { - synchronized (mPackages) { + synchronized (mLock) { return getPackageTargetSdkVersionLockedLPr(packageName); } } @@ -23708,44 +23369,44 @@ public class PackageManagerService extends IPackageManager.Stub @Override public boolean canAccessComponent(int callingUid, ComponentName component, int userId) { - synchronized (mPackages) { + synchronized (mLock) { final PackageSetting ps = mSettings.mPackages.get(component.getPackageName()); - return ps != null && !PackageManagerService.this.filterAppAccessLPr( + return ps != null && !PackageManagerService.this.shouldFilterApplicationLocked( ps, callingUid, component, TYPE_UNKNOWN, userId); } } @Override public boolean hasInstantApplicationMetadata(String packageName, int userId) { - synchronized (mPackages) { + synchronized (mLock) { return mInstantAppRegistry.hasInstantApplicationMetadataLPr(packageName, userId); } } @Override public void notifyPackageUse(String packageName, int reason) { - synchronized (mPackages) { + synchronized (mLock) { PackageManagerService.this.notifyPackageUseLocked(packageName, reason); } } @Override public SparseArray<String> getAppsWithSharedUserIds() { - synchronized (mPackages) { + synchronized (mLock) { return getAppsWithSharedUserIdsLocked(); } } @Override public String getSharedUserIdForPackage(String packageName) { - synchronized (mPackages) { + synchronized (mLock) { return getSharedUserIdForPackageLocked(packageName); } } @Override public String[] getPackagesForSharedUserId(String sharedUserId, int userId) { - synchronized (mPackages) { + synchronized (mLock) { return getPackagesForSharedUserIdLocked(sharedUserId, userId); } } @@ -23774,7 +23435,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public ArraySet<String> getEnabledComponents(String packageName, int userId) { - synchronized (mPackages) { + synchronized (mLock) { PackageSetting setting = mSettings.getPackageLPr(packageName); if (setting == null) { return new ArraySet<>(); @@ -23785,7 +23446,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public ArraySet<String> getDisabledComponents(String packageName, int userId) { - synchronized (mPackages) { + synchronized (mLock) { PackageSetting setting = mSettings.getPackageLPr(packageName); if (setting == null) { return new ArraySet<>(); @@ -23797,7 +23458,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public @PackageManager.EnabledState int getApplicationEnabledState( String packageName, int userId) { - synchronized (mPackages) { + synchronized (mLock) { PackageSetting setting = mSettings.getPackageLPr(packageName); if (setting == null) { return COMPONENT_ENABLED_STATE_DEFAULT; @@ -23817,7 +23478,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public boolean compileLayouts(String packageName) { PackageParser.Package pkg; - synchronized (mPackages) { + synchronized (mLock) { pkg = mPackages.get(packageName); if (pkg == null) { return false; @@ -23834,7 +23495,7 @@ public class PackageManagerService extends IPackageManager.Stub @Nullable @Override public String removeLegacyDefaultBrowserPackageName(int userId) { - synchronized (mPackages) { + synchronized (mLock) { return mSettings.removeDefaultBrowserPackageNameLPw(userId); } } @@ -23887,7 +23548,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public void setRuntimePermissionsFingerPrint(@NonNull String fingerPrint, @UserIdInt int userId) { - synchronized (mPackages) { + synchronized (mLock) { mSettings.setRuntimePermissionsFingerPrintLPr(fingerPrint, userId); } } @@ -23903,7 +23564,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public void writeSettings(boolean async) { - synchronized (mPackages) { + synchronized (mLock) { if (async) { scheduleWriteSettingsLocked(); } else { @@ -23914,7 +23575,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public void writePermissionSettings(int[] userIds, boolean async) { - synchronized (mPackages) { + synchronized (mLock) { for (int userId : userIds) { mSettings.writeRuntimePermissionsForUserLPr(userId, !async); } @@ -23925,7 +23586,7 @@ public class PackageManagerService extends IPackageManager.Stub public int getTargetSdk(int uid) { int userId = UserHandle.getUserId(uid); - synchronized (mPackages) { + synchronized (mLock) { final Object obj = mSettings.getSettingLPr(UserHandle.getAppId(uid)); if (obj instanceof PackageSetting) { final PackageSetting ps = (PackageSetting) obj; @@ -23956,7 +23617,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public boolean isCallerInstallerOfRecord( @NonNull PackageParser.Package pkg, int callingUid) { - synchronized (mPackages) { + synchronized (mLock) { if (pkg == null) { return false; } @@ -23973,14 +23634,14 @@ public class PackageManagerService extends IPackageManager.Stub @Override public boolean areDefaultRuntimePermissionsGranted(int userId) { - synchronized (mPackages) { + synchronized (mLock) { return mSettings.areDefaultRuntimePermissionsGrantedLPr(userId); } } @Override public void setReadExternalStorageEnforced(boolean enforced) { - synchronized (mPackages) { + synchronized (mLock) { if (mSettings.mReadExternalStorageEnforced != null && mSettings.mReadExternalStorageEnforced == enforced) { return; @@ -23991,10 +23652,10 @@ public class PackageManagerService extends IPackageManager.Stub } } - @GuardedBy("mPackages") + @GuardedBy("mLock") private SparseArray<String> getAppsWithSharedUserIdsLocked() { final SparseArray<String> sharedUserIds = new SparseArray<>(); - synchronized (mPackages) { + synchronized (mLock) { for (SharedUserSetting setting : mSettings.getAllSharedUsersLPw()) { sharedUserIds.put(UserHandle.getAppId(setting.userId), setting.name); } @@ -24002,13 +23663,13 @@ public class PackageManagerService extends IPackageManager.Stub return sharedUserIds; } - @GuardedBy("mPackages") + @GuardedBy("mLock") private String getSharedUserIdForPackageLocked(String packageName) { final PackageSetting ps = mSettings.mPackages.get(packageName); return (ps != null && ps.isSharedUser()) ? ps.sharedUser.name : null; } - @GuardedBy("mPackages") + @GuardedBy("mLock") private String[] getPackagesForSharedUserIdLocked(String sharedUserId, int userId) { try { final SharedUserSetting sus = mSettings.getSharedUserLPw( @@ -24040,7 +23701,7 @@ public class PackageManagerService extends IPackageManager.Stub mContext.enforceCallingOrSelfPermission( Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, "setRuntimePermissionVersion"); - synchronized (mPackages) { + synchronized (mLock) { return mSettings.getDefaultRuntimePermissionsVersionLPr(userId); } } @@ -24052,13 +23713,13 @@ public class PackageManagerService extends IPackageManager.Stub mContext.enforceCallingOrSelfPermission( Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, "setRuntimePermissionVersion"); - synchronized (mPackages) { + synchronized (mLock) { mSettings.setDefaultRuntimePermissionsVersionLPr(version, userId); } } void forEachPackage(Consumer<PackageParser.Package> actionLocked) { - synchronized (mPackages) { + synchronized (mLock) { int numPackages = mPackages.size(); for (int i = 0; i < numPackages; i++) { actionLocked.accept(mPackages.valueAt(i)); @@ -24068,7 +23729,7 @@ public class PackageManagerService extends IPackageManager.Stub void forEachInstalledPackage(@NonNull Consumer<PackageParser.Package> actionLocked, @UserIdInt int userId) { - synchronized (mPackages) { + synchronized (mLock) { int numPackages = mPackages.size(); for (int i = 0; i < numPackages; i++) { PackageParser.Package pkg = mPackages.valueAt(i); @@ -24090,7 +23751,7 @@ public class PackageManagerService extends IPackageManager.Stub * @return A copy of the values of mPackages. */ Collection<PackageParser.Package> getPackages() { - synchronized (mPackages) { + synchronized (mLock) { return new ArrayList<>(mPackages.values()); } } @@ -24143,9 +23804,9 @@ public class PackageManagerService extends IPackageManager.Stub mPermissionManager.enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, false /* checkShell */, "get install reason"); - synchronized (mPackages) { + synchronized (mLock) { final PackageSetting ps = mSettings.mPackages.get(packageName); - if (filterAppAccessLPr(ps, callingUid, userId)) { + if (shouldFilterApplicationLocked(ps, callingUid, userId)) { return PackageManager.INSTALL_REASON_UNKNOWN; } if (ps != null) { @@ -24229,13 +23890,13 @@ public class PackageManagerService extends IPackageManager.Stub if (!isInstantApp(packageName, userId)) { return null; } - synchronized (mPackages) { + synchronized (mLock) { return mInstantAppRegistry.getInstantAppAndroidIdLPw(packageName, userId); } } boolean canHaveOatDir(String packageName) { - synchronized (mPackages) { + synchronized (mLock) { PackageParser.Package p = mPackages.get(packageName); if (p == null) { return false; @@ -24260,7 +23921,7 @@ public class PackageManagerService extends IPackageManager.Stub final List<String> codePaths; final String oatDir; final PackageParser.Package pkg; - synchronized (mPackages) { + synchronized (mLock) { pkg = mPackages.get(packageName); } instructionSets = getAppDexInstructionSets(pkg.applicationInfo); @@ -24281,7 +23942,7 @@ public class PackageManagerService extends IPackageManager.Stub Set<String> getUnusedPackages(long downgradeTimeThresholdMillis) { Set<String> unusedPackages = new HashSet<>(); long currentTimeInMillis = System.currentTimeMillis(); - synchronized (mPackages) { + synchronized (mLock) { for (PackageParser.Package pkg : mPackages.values()) { PackageSetting ps = mSettings.mPackages.get(pkg.packageName); if (ps == null) { @@ -24316,7 +23977,7 @@ public class PackageManagerService extends IPackageManager.Stub + SET_HARMFUL_APP_WARNINGS + " permission."); } - synchronized(mPackages) { + synchronized (mLock) { mSettings.setHarmfulAppWarningLPw(packageName, warning, userId); scheduleWritePackageRestrictionsLocked(userId); } @@ -24337,7 +23998,7 @@ public class PackageManagerService extends IPackageManager.Stub + SET_HARMFUL_APP_WARNINGS + " permission."); } - synchronized(mPackages) { + synchronized (mLock) { return mSettings.getHarmfulAppWarningLPr(packageName, userId); } } 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/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 3bc2236a8221..c1cb53d4ff0f 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -4020,7 +4020,7 @@ public final class Settings { String[] seinfos; int[] targetSdkVersions; int packagesCount; - synchronized (mPackages) { + synchronized (mLock) { Collection<PackageSetting> packages = mPackages.values(); packagesCount = packages.size(); volumeUuids = new String[packagesCount]; @@ -4064,7 +4064,7 @@ public final class Settings { Slog.w(TAG, "Failed to prepare app data", e); } } - synchronized (mPackages) { + synchronized (mLock) { applyDefaultPreferredAppsLPw(userHandle); } } diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java index bf141a0f139b..bdeaf028bac8 100644 --- a/services/core/java/com/android/server/pm/StagingManager.java +++ b/services/core/java/com/android/server/pm/StagingManager.java @@ -180,6 +180,8 @@ public class StagingManager { checkDowngrade(session, activePackage, pkg); result.add(pkg); } + Slog.d(TAG, "Session " + session.sessionId + " has following APEX packages: [" + + result.stream().map(p -> p.packageName).collect(Collectors.joining(",")) + "]"); return result; } @@ -206,7 +208,7 @@ public class StagingManager { throws PackageManagerException { final long activeVersion = activePackage.applicationInfo.longVersionCode; final long newVersionCode = newPackage.applicationInfo.longVersionCode; - boolean allowsDowngrade = PackageManagerServiceUtils.isDowngradePermitted( + final boolean allowsDowngrade = PackageManagerServiceUtils.isDowngradePermitted( session.params.installFlags, activePackage.applicationInfo.flags); if (activeVersion > newVersionCode && !allowsDowngrade) { if (!mApexManager.abortActiveSession()) { @@ -225,6 +227,7 @@ public class StagingManager { } private void preRebootVerification(@NonNull PackageInstallerSession session) { + Slog.d(TAG, "Starting preRebootVerification for session " + session.sessionId); final boolean hasApex = sessionContainsApex(session); // APEX checks. For single-package sessions, check if they contain an APEX. For // multi-package sessions, find all the child sessions that contain an APEX. @@ -243,6 +246,8 @@ public class StagingManager { if (sessionContainsApk(session)) { try { + Slog.d(TAG, "Running a pre-reboot verification for APKs in session " + + session.sessionId + " by performing a dry-run install"); installApksInSession(session, /* preReboot */ true); // TODO(b/118865310): abort the session on apexd. } catch (PackageManagerException e) { @@ -277,6 +282,7 @@ public class StagingManager { // On the other hand, if the order of the calls was inverted (first call apexd, then mark // session as ready), then if a device gets rebooted right after the call to apexd, only // apex part of the train will be applied, leaving device in an inconsistent state. + Slog.d(TAG, "Marking session " + session.sessionId + " as ready"); session.setStagedSessionReady(); if (!hasApex) { // Session doesn't contain apex, nothing to do. @@ -315,6 +321,7 @@ public class StagingManager { } private void resumeSession(@NonNull PackageInstallerSession session) { + Slog.d(TAG, "Resuming session " + session.sessionId); final boolean hasApex = sessionContainsApex(session); if (hasApex) { // Check with apexservice whether the apex packages have been activated. @@ -348,9 +355,12 @@ public class StagingManager { + "retry at next reboot."); return; } + Slog.i(TAG, "APEX packages in session " + session.sessionId + + " were successfully activated. Proceeding with APK packages, if any"); } // The APEX part of the session is activated, proceed with the installation of APKs. try { + Slog.d(TAG, "Installing APK packages in session " + session.sessionId); installApksInSession(session, /* preReboot */ false); } catch (PackageManagerException e) { session.setStagedSessionFailed(e.error, e.getMessage()); @@ -370,6 +380,7 @@ public class StagingManager { return; } + Slog.d(TAG, "Marking session " + session.sessionId + " as applied"); session.setStagedSessionApplied(); if (hasApex) { mApexManager.markStagedSessionSuccessful(session.sessionId); diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING index b829f0b53086..f7b60c25ce4a 100644 --- a/services/core/java/com/android/server/pm/TEST_MAPPING +++ b/services/core/java/com/android/server/pm/TEST_MAPPING @@ -8,6 +8,20 @@ }, { "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": [ 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 fe0b3a3966f9..3e655edf5db0 100644 --- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java @@ -1170,6 +1170,11 @@ public final class DefaultPermissionGrantPolicy { final int flags = mContext.getPackageManager().getPermissionFlags( permission, pkg.packageName, user); + // If we are trying to grant as system fixed and already system fixed + // then the system can change the system fixed grant state. + final boolean changingGrantForSystemFixed = systemFixed + && (flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0; + // Certain flags imply that the permission's current state by the system or // device/profile owner or the user. In these cases we do not want to clobber the // current state. @@ -1177,7 +1182,8 @@ public final class DefaultPermissionGrantPolicy { // Unless the caller wants to override user choices. The override is // to make sure we can grant the needed permission to the default // sms and phone apps after the user chooses this in the UI. - if (!isFixedOrUserSet(flags) || ignoreSystemPackage) { + if (!isFixedOrUserSet(flags) || ignoreSystemPackage + || changingGrantForSystemFixed) { // Never clobber policy fixed permissions. // We must allow the grant of a system-fixed permission because // system-fixed is sticky, but the permission itself may be revoked. @@ -1196,6 +1202,14 @@ public final class DefaultPermissionGrantPolicy { PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT, user); } + // If the system tries to change a system fixed permission from one fixed + // state to another we need to drop the fixed flag to allow the grant. + if (changingGrantForSystemFixed) { + mContext.getPackageManager().updatePermissionFlags(permission, + pkg.packageName, flags, + flags & ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED, user); + } + if (pm.checkPermission(permission, pkg.packageName) != PackageManager.PERMISSION_GRANTED) { mContext.getPackageManager() 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 03be8e119e03..210a7afb11a2 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -125,6 +125,7 @@ import com.android.server.pm.permission.PermissionManagerServiceInternal.Default 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.PermissionPolicyInternal; import com.android.server.policy.SoftRestrictedPermissionPolicy; import libcore.util.EmptyArray; @@ -227,6 +228,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { @GuardedBy("mLock") private boolean mSystemReady; + @GuardedBy("mLock") + private PermissionPolicyInternal mPermissionPolicyInternal; + /** * For each foreground/background permission the mapping: * Background permission -> foreground permissions @@ -1451,7 +1455,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { * @param pkg The package for which to reset. * @param userId The device user for which to do a reset. */ - @GuardedBy("mPackages") + @GuardedBy("mLock") private void resetRuntimePermissionsInternal(final PackageParser.Package pkg, final int userId) { final String packageName = pkg.packageName; @@ -2443,6 +2447,13 @@ public class PermissionManagerService extends IPermissionManager.Stub { boolean softRestricted = bp.isSoftRestricted(); for (int userId : currentUserIds) { + // If permission policy is not ready we don't deal with restricted + // permissions as the policy may whitelist some permissions. Once + // the policy is initialized we would re-evaluate permissions. + final boolean permissionPolicyInitialized = + mPermissionPolicyInternal != null + && mPermissionPolicyInternal.isInitialized(userId); + PermissionState permState = origPermissions .getRuntimePermissionState(perm, userId); int flags = permState != null ? permState.getFlags() : 0; @@ -2457,7 +2468,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { if (appSupportsRuntimePermissions) { // If hard restricted we don't allow holding it - if (hardRestricted) { + if (permissionPolicyInitialized && hardRestricted) { if (!restrictionExempt) { if (permState != null && permState.isGranted() && permissionsState.revokeRuntimePermission( @@ -2470,7 +2481,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } // If soft restricted we allow holding in a restricted form - } else if (softRestricted) { + } else if (permissionPolicyInitialized && softRestricted) { // Regardless if granted set the restriction flag as it // may affect app treatment based on this permission. if (!restrictionExempt && !restrictionApplied) { @@ -2489,7 +2500,8 @@ public class PermissionManagerService extends IPermissionManager.Stub { flags &= ~FLAG_PERMISSION_REVOKE_ON_UPGRADE; wasChanged = true; // Hard restricted permissions cannot be held. - } else if (!hardRestricted || restrictionExempt) { + } else if (!permissionPolicyInitialized + || (!hardRestricted || restrictionExempt)) { if (permState != null && permState.isGranted()) { if (permissionsState.grantRuntimePermission(bp, userId) == PERMISSION_OPERATION_FAILURE) { @@ -2518,33 +2530,28 @@ public class PermissionManagerService extends IPermissionManager.Stub { // If legacy app always grant the permission but if restricted // and not exempt take a note a restriction should be applied. - if ((hardRestricted || softRestricted) - && !restrictionExempt && !restrictionApplied) { + if (permissionPolicyInitialized + && (hardRestricted || softRestricted) + && !restrictionExempt && !restrictionApplied) { flags |= FLAG_PERMISSION_APPLY_RESTRICTION; wasChanged = true; } } // If unrestricted or restriction exempt, don't apply restriction. - if (!(hardRestricted || softRestricted) || restrictionExempt) { - if (restrictionApplied) { - flags &= ~FLAG_PERMISSION_APPLY_RESTRICTION; - // Dropping restriction on a legacy app requires a review. - if (!appSupportsRuntimePermissions) { - flags |= FLAG_PERMISSION_REVIEW_REQUIRED; + if (permissionPolicyInitialized) { + if (!(hardRestricted || softRestricted) || restrictionExempt) { + if (restrictionApplied) { + flags &= ~FLAG_PERMISSION_APPLY_RESTRICTION; + // Dropping restriction on a legacy app implies a review + if (!appSupportsRuntimePermissions) { + flags |= FLAG_PERMISSION_REVIEW_REQUIRED; + } + wasChanged = true; } - wasChanged = true; } } - if (hardRestricted && !restrictionExempt - && (flags & FLAG_PERMISSION_SYSTEM_FIXED) != 0) { - // Applying a hard restriction implies revoking it. This might - // lead to a system-fixed, revoked permission. - flags &= ~FLAG_PERMISSION_SYSTEM_FIXED; - wasChanged = true; - } - if (wasChanged) { updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId); } @@ -2579,6 +2586,13 @@ public class PermissionManagerService extends IPermissionManager.Stub { boolean softRestricted = bp.isSoftRestricted(); for (int userId : currentUserIds) { + // If permission policy is not ready we don't deal with restricted + // permissions as the policy may whitelist some permissions. Once + // the policy is initialized we would re-evaluate permissions. + final boolean permissionPolicyInitialized = + mPermissionPolicyInternal != null + && mPermissionPolicyInternal.isInitialized(userId); + boolean wasChanged = false; boolean restrictionExempt = @@ -2589,7 +2603,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { if (appSupportsRuntimePermissions) { // If hard restricted we don't allow holding it - if (hardRestricted) { + if (permissionPolicyInitialized && hardRestricted) { if (!restrictionExempt) { if (permState != null && permState.isGranted() && permissionsState.revokeRuntimePermission( @@ -2602,7 +2616,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } // If soft restricted we allow holding in a restricted form - } else if (softRestricted) { + } else if (permissionPolicyInitialized && softRestricted) { // Regardless if granted set the restriction flag as it // may affect app treatment based on this permission. if (!restrictionExempt && !restrictionApplied) { @@ -2621,7 +2635,8 @@ public class PermissionManagerService extends IPermissionManager.Stub { flags &= ~FLAG_PERMISSION_REVOKE_ON_UPGRADE; wasChanged = true; // Hard restricted permissions cannot be held. - } else if (!hardRestricted || restrictionExempt) { + } else if (!permissionPolicyInitialized || + (!hardRestricted || restrictionExempt)) { if (permissionsState.grantRuntimePermission(bp, userId) != PERMISSION_OPERATION_FAILURE) { wasChanged = true; @@ -2637,22 +2652,25 @@ public class PermissionManagerService extends IPermissionManager.Stub { // If legacy app always grant the permission but if restricted // and not exempt take a note a restriction should be applied. - if ((hardRestricted || softRestricted) - && !restrictionExempt && !restrictionApplied) { + if (permissionPolicyInitialized + && (hardRestricted || softRestricted) + && !restrictionExempt && !restrictionApplied) { flags |= FLAG_PERMISSION_APPLY_RESTRICTION; wasChanged = true; } } // If unrestricted or restriction exempt, don't apply restriction. - if (!(hardRestricted || softRestricted) || restrictionExempt) { - if (restrictionApplied) { - flags &= ~FLAG_PERMISSION_APPLY_RESTRICTION; - // Dropping restriction on a legacy app requires a review. - if (!appSupportsRuntimePermissions) { - flags |= FLAG_PERMISSION_REVIEW_REQUIRED; + if (permissionPolicyInitialized) { + if (!(hardRestricted || softRestricted) || restrictionExempt) { + if (restrictionApplied) { + flags &= ~FLAG_PERMISSION_APPLY_RESTRICTION; + // Dropping restriction on a legacy app implies a review + if (!appSupportsRuntimePermissions) { + flags |= FLAG_PERMISSION_REVIEW_REQUIRED; + } + wasChanged = true; } - wasChanged = true; } } @@ -3959,6 +3977,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { } mPermissionControllerManager = mContext.getSystemService(PermissionControllerManager.class); + mPermissionPolicyInternal = LocalServices.getService(PermissionPolicyInternal.class); int[] grantPermissionsUserIds = EMPTY_INT_ARRAY; for (int userId : UserManagerService.getInstance().getUserIds()) { diff --git a/services/core/java/com/android/server/policy/PermissionPolicyInternal.java b/services/core/java/com/android/server/policy/PermissionPolicyInternal.java index 7760c1edd9e6..6084c6718577 100644 --- a/services/core/java/com/android/server/policy/PermissionPolicyInternal.java +++ b/services/core/java/com/android/server/policy/PermissionPolicyInternal.java @@ -18,6 +18,7 @@ package com.android.server.policy; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.content.Intent; /** @@ -26,6 +27,19 @@ import android.content.Intent; public abstract class PermissionPolicyInternal { /** + * Callback for initializing the permission policy service. + */ + public interface OnInitializedCallback { + + /** + * Called when initialized for the given user. + * + * @param userId The initialized user. + */ + void onInitialized(@UserIdInt int userId); + } + + /** * Check whether an activity should be started. * * @param intent the {@link Intent} for the activity start @@ -36,4 +50,17 @@ public abstract class PermissionPolicyInternal { */ public abstract boolean checkStartActivity(@NonNull Intent intent, int callingUid, @Nullable String callingPackage); + + /** + * @return Whether the policy is initialized for a user. + */ + public abstract boolean isInitialized(@UserIdInt int userId); + + /** + * Set a callback for users being initialized. If the user is already + * initialized the callback will not be invoked. + * + * @param callback The callback to register. + */ + public abstract void setOnInitializedCallback(@NonNull OnInitializedCallback callback); } diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java index 8da7f7bb6898..f68a06bb191a 100644 --- a/services/core/java/com/android/server/policy/PermissionPolicyService.java +++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java @@ -66,6 +66,7 @@ import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.pm.permission.PermissionManagerServiceInternal; +import com.android.server.policy.PermissionPolicyInternal.OnInitializedCallback; import java.util.ArrayList; import java.util.concurrent.CountDownLatch; @@ -86,6 +87,10 @@ public final class PermissionPolicyService extends SystemService { @GuardedBy("mLock") private final SparseBooleanArray mIsStarted = new SparseBooleanArray(); + /** Callbacks for when a user is initialized */ + @GuardedBy("mLock") + private OnInitializedCallback mOnInitializedCallback; + /** * Whether an async {@link #synchronizePackagePermissionsAndAppOpsForUser} is currently * scheduled for a package/user. @@ -240,12 +245,20 @@ public final class PermissionPolicyService extends SystemService { grantOrUpgradeDefaultRuntimePermissionsIfNeeded(userId); + final OnInitializedCallback callback; + synchronized (mLock) { mIsStarted.put(userId, true); + callback = mOnInitializedCallback; } // Force synchronization as permissions might have changed synchronizePermissionsAndAppOpsForUser(userId); + + // Tell observers we are initialized for this user. + if (callback != null) { + callback.onInitialized(userId); + } } @Override @@ -809,6 +822,18 @@ public final class PermissionPolicyService extends SystemService { return true; } + @Override + public boolean isInitialized(int userId) { + return isStarted(userId); + } + + @Override + public void setOnInitializedCallback(@NonNull OnInitializedCallback callback) { + synchronized (mLock) { + mOnInitializedCallback = callback; + } + } + /** * Check if the intent action is removed for the calling package (often based on target SDK * version). If the action is removed, we'll silently cancel the activity launch. 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/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 015464e94ffc..31e8bbdab71c 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -30,6 +30,7 @@ import static android.app.ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_DOWN; import static android.app.ActivityOptions.ANIM_THUMBNAIL_ASPECT_SCALE_UP; import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN; import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP; +import static android.app.ActivityOptions.ANIM_UNDEFINED; import static android.app.ActivityTaskManager.INVALID_STACK_ID; import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.AppOpsManager.MODE_ALLOWED; @@ -2096,6 +2097,7 @@ final class ActivityRecord extends ConfigurationContainer { pendingOptions.getRemoteAnimationAdapter()); break; case ANIM_NONE: + case ANIM_UNDEFINED: break; default: Slog.e(TAG_WM, "applyOptionsLocked: Unknown animationType=" + animationType); diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java index beff5fb7a8f9..1c56a107ab9e 100644 --- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java @@ -1191,8 +1191,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { final PackageInfo packageInfo; try { packageInfo = mService.mContext.getPackageManager() - .getPackageInfoAsUser(callingPackage, PackageManager.GET_PERMISSIONS, - UserHandle.getUserId(callingUid)); + .getPackageInfo(callingPackage, PackageManager.GET_PERMISSIONS); } catch (PackageManager.NameNotFoundException e) { Slog.i(TAG, "Cannot find package info for " + callingPackage); return ACTIVITY_RESTRICTION_NONE; diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index a334ed8ad281..9ed93c46733a 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -187,6 +187,7 @@ class ActivityStarter { private boolean mNoAnimation; private boolean mKeepCurTransition; private boolean mAvoidMoveToFront; + private boolean mFrozeTaskList; // We must track when we deliver the new intent since multiple code paths invoke // {@link #deliverNewIntent}. This is due to early returns in the code path. This flag is used @@ -483,6 +484,7 @@ class ActivityStarter { mNoAnimation = starter.mNoAnimation; mKeepCurTransition = starter.mKeepCurTransition; mAvoidMoveToFront = starter.mAvoidMoveToFront; + mFrozeTaskList = starter.mFrozeTaskList; mVoiceSession = starter.mVoiceSession; mVoiceInteractor = starter.mVoiceInteractor; @@ -1093,6 +1095,14 @@ class ActivityStarter { void postStartActivityProcessing(ActivityRecord r, int result, ActivityStack startedActivityStack) { + if (!ActivityManager.isStartResultSuccessful(result)) { + if (mFrozeTaskList) { + // If we specifically froze the task list as part of starting an activity, then + // reset the frozen list state if it failed to start. This is normally otherwise + // called when the freeze-timeout has elapsed. + mSupervisor.mRecentTasks.resetFreezeTaskListReorderingOnTimeout(); + } + } if (ActivityManager.isStartResultFatalError(result)) { return; } @@ -1485,6 +1495,14 @@ class ActivityStarter { mLaunchParams.hasPreferredDisplay() ? mLaunchParams.mPreferredDisplayId : DEFAULT_DISPLAY; + // If requested, freeze the task list + if (mOptions != null && mOptions.freezeRecentTasksReordering() + && mSupervisor.mRecentTasks.isCallerRecents(r.launchedFromUid) + && !mSupervisor.mRecentTasks.isFreezeTaskListReorderingSet()) { + mFrozeTaskList = true; + mSupervisor.mRecentTasks.setFreezeTaskListReordering(); + } + // Do not start home activity if it cannot be launched on preferred display. We are not // doing this in ActivityStackSupervisor#canPlaceEntityOnDisplay because it might // fallback to launch on other displays. @@ -1776,6 +1794,7 @@ class ActivityStarter { mNoAnimation = false; mKeepCurTransition = false; mAvoidMoveToFront = false; + mFrozeTaskList = false; mVoiceSession = null; mVoiceInteractor = null; diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 7e741a01868b..b351faf7e9f1 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -1012,7 +1012,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { public final int startActivities(IApplicationThread caller, String callingPackage, Intent[] intents, String[] resolvedTypes, IBinder resultTo, Bundle bOptions, int userId) { - assertPackageMatchesCallingUid(callingPackage); final String reason = "startActivities"; enforceNotIsolatedCaller(reason); userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId, reason); @@ -1032,11 +1031,10 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { true /*validateIncomingUser*/); } - private int startActivityAsUser(IApplicationThread caller, String callingPackage, + int startActivityAsUser(IApplicationThread caller, String callingPackage, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId, boolean validateIncomingUser) { - assertPackageMatchesCallingUid(callingPackage); enforceNotIsolatedCaller("startActivityAsUser"); userId = getActivityStartController().checkTargetUser(userId, validateIncomingUser, @@ -1209,7 +1207,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { public final WaitResult startActivityAndWait(IApplicationThread caller, String callingPackage, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) { - assertPackageMatchesCallingUid(callingPackage); final WaitResult res = new WaitResult(); synchronized (mGlobalLock) { enforceNotIsolatedCaller("startActivityAndWait"); @@ -1237,7 +1234,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { public final int startActivityWithConfig(IApplicationThread caller, String callingPackage, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, Configuration config, Bundle bOptions, int userId) { - assertPackageMatchesCallingUid(callingPackage); synchronized (mGlobalLock) { enforceNotIsolatedCaller("startActivityWithConfig"); userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId, @@ -1287,7 +1283,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, IBinder permissionToken, boolean ignoreTargetSecurity, int userId) { - assertPackageMatchesCallingUid(callingPackage); // This is very dangerous -- it allows you to perform a start activity (including // permission grants) as any app that may launch one of your own activities. So we only // allow this in two cases: @@ -1417,7 +1412,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { Intent intent, String resolvedType, IVoiceInteractionSession session, IVoiceInteractor interactor, int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) { - assertPackageMatchesCallingUid(callingPackage); mAmInternal.enforceCallingPermission(BIND_VOICE_INTERACTION, "startVoiceActivity()"); if (session == null || interactor == null) { throw new NullPointerException("null session or interactor"); @@ -1441,7 +1435,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public int startAssistantActivity(String callingPackage, int callingPid, int callingUid, Intent intent, String resolvedType, Bundle bOptions, int userId) { - assertPackageMatchesCallingUid(callingPackage); mAmInternal.enforceCallingPermission(BIND_VOICE_INTERACTION, "startAssistantActivity()"); userId = handleIncomingUser(callingPid, callingUid, userId, "startAssistantActivity"); @@ -2367,9 +2360,15 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { void moveTaskToFrontLocked(@Nullable IApplicationThread appThread, @Nullable String callingPackage, int taskId, int flags, SafeActivityOptions options, boolean fromRecents) { + final int callingPid = Binder.getCallingPid(); final int callingUid = Binder.getCallingUid(); - assertPackageMatchesCallingUid(callingPackage); + if (!isSameApp(callingUid, callingPackage)) { + String msg = "Permission Denial: moveTaskToFrontLocked() from pid=" + + Binder.getCallingPid() + " as package " + callingPackage; + Slog.w(TAG, msg); + throw new SecurityException(msg); + } if (!checkAppSwitchAllowedLocked(callingPid, callingUid, -1, -1, "Task to front")) { SafeActivityOptions.abort(options); return; @@ -2421,7 +2420,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { /** * Return true if callingUid is system, or packageName belongs to that callingUid. */ - private boolean isSameApp(int callingUid, @Nullable String packageName) { + boolean isSameApp(int callingUid, @Nullable String packageName) { try { if (callingUid != 0 && callingUid != SYSTEM_UID) { if (packageName == null) { @@ -2438,21 +2437,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { return true; } - /** - * Checks that the provided package name matches the current calling UID, throws a security - * exception if it doesn't. - */ - void assertPackageMatchesCallingUid(@Nullable String packageName) { - final int callingUid = Binder.getCallingUid(); - if (isSameApp(callingUid, packageName)) { - return; - } - final String msg = "Permission Denial: package=" + packageName - + " does not belong to uid=" + callingUid; - Slog.w(TAG, msg); - throw new SecurityException(msg); - } - boolean checkAppSwitchAllowedLocked(int sourcePid, int sourceUid, int callingPid, int callingUid, String name) { if (mAppSwitchesAllowedTime < SystemClock.uptimeMillis()) { @@ -2986,7 +2970,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public List<IBinder> getAppTasks(String callingPackage) { int callingUid = Binder.getCallingUid(); - assertPackageMatchesCallingUid(callingPackage); long ident = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { @@ -6093,7 +6076,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { SafeActivityOptions options, int userId, boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart) { - assertPackageMatchesCallingUid(callingPackage); synchronized (mGlobalLock) { return getActivityStartController().startActivitiesInPackage(uid, realCallingPid, realCallingUid, callingPackage, intents, resolvedTypes, resultTo, options, @@ -6109,7 +6091,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { int userId, TaskRecord inTask, String reason, boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart) { - assertPackageMatchesCallingUid(callingPackage); synchronized (mGlobalLock) { return getActivityStartController().startActivityInPackage(uid, realCallingPid, realCallingUid, callingPackage, intent, resolvedType, resultTo, resultWho, diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java index a8f7768efc87..1eb7455135c7 100644 --- a/services/core/java/com/android/server/wm/AppTaskImpl.java +++ b/services/core/java/com/android/server/wm/AppTaskImpl.java @@ -27,6 +27,7 @@ import android.os.Binder; import android.os.Bundle; import android.os.IBinder; import android.os.UserHandle; +import android.util.Slog; /** * An implementation of IAppTask, that allows an app to manage its own tasks via @@ -96,7 +97,12 @@ class AppTaskImpl extends IAppTask.Stub { // Will bring task to front if it already has a root activity. final int callingPid = Binder.getCallingPid(); final int callingUid = Binder.getCallingUid(); - mService.assertPackageMatchesCallingUid(callingPackage); + if (!mService.isSameApp(callingUid, callingPackage)) { + String msg = "Permission Denial: moveToFront() from pid=" + + Binder.getCallingPid() + " as package " + callingPackage; + Slog.w(TAG, msg); + throw new SecurityException(msg); + } final long origId = Binder.clearCallingIdentity(); try { synchronized (mService.mGlobalLock) { @@ -128,7 +134,6 @@ class AppTaskImpl extends IAppTask.Stub { public int startActivity(IBinder whoThread, String callingPackage, Intent intent, String resolvedType, Bundle bOptions) { checkCaller(); - mService.assertPackageMatchesCallingUid(callingPackage); int callingUser = UserHandle.getCallingUserId(); TaskRecord tr; diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index 592fa5800425..aefc152cb286 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -2487,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; } 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/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/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java index 0d18b30c1b0b..f51d4c4aa3f7 100644 --- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java +++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java @@ -294,6 +294,10 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { private int getPreferredLaunchDisplay(@Nullable TaskRecord task, @Nullable ActivityOptions options, ActivityRecord source, LaunchParams currentParams) { + if (!mSupervisor.mService.mSupportsMultiDisplay) { + return DEFAULT_DISPLAY; + } + int displayId = INVALID_DISPLAY; final int optionLaunchId = options != null ? options.getLaunchDisplayId() : INVALID_DISPLAY; if (optionLaunchId != INVALID_DISPLAY) { @@ -321,6 +325,12 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { displayId = sourceDisplayId; } + if (displayId == INVALID_DISPLAY && options != null) { + final int callerDisplayId = options.getCallerDisplayId(); + if (DEBUG) appendLog("display-from-caller=" + callerDisplayId); + displayId = callerDisplayId; + } + if (displayId != INVALID_DISPLAY && mSupervisor.mRootActivityContainer.getActivityDisplay(displayId) == null) { displayId = currentParams.mPreferredDisplayId; 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/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java index 636f4e6d09d0..05cfbd4e39d9 100644 --- a/services/core/java/com/android/server/wm/WindowManagerInternal.java +++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java @@ -511,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 70e446c51c43..fbdc54a2435b 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -7455,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 f74e2c01699d..4a91dac27d7e 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -566,7 +566,8 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio * activities are allowed to be resumed per process. * @return {@code true} if the activity is allowed to be resumed by compatibility * restrictions, which the activity was the topmost visible activity in process or the app is - * targeting after Q. + * targeting after Q. Note that non-focusable activity, in picture-in-picture mode for instance, + * does not count as a topmost activity. */ boolean updateTopResumingActivityInProcessIfNeeded(@NonNull ActivityRecord activity) { if (mInfo.targetSdkVersion >= Q || mPreQTopResumedActivity == activity) { @@ -582,9 +583,13 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio boolean canUpdate = false; final ActivityDisplay topDisplay = mPreQTopResumedActivity != null ? mPreQTopResumedActivity.getDisplay() : null; - // Update the topmost activity if current top activity was not on any display or no - // longer visible. - if (topDisplay == null || !mPreQTopResumedActivity.visible) { + // Update the topmost activity if current top activity is + // - not on any display OR + // - no longer visible OR + // - not focusable (in PiP mode for instance) + if (topDisplay == null + || !mPreQTopResumedActivity.visible + || !mPreQTopResumedActivity.isFocusable()) { canUpdate = true; } diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 41840a4c531c..d90e66e10e73 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -370,6 +370,13 @@ class WindowStateAnimator { // of the proper size. The preserved surface will still be removed when client // finishes drawing to the new surface. mSurfaceDestroyDeferred = false; + + // Make sure to reparent any children of the new surface back to the preserved + // surface before destroying it. + if (mSurfaceController != null && mPendingDestroySurface != null) { + mPostDrawTransaction.reparentChildren(mSurfaceController.mSurfaceControl, + mPendingDestroySurface.mSurfaceControl).apply(); + } destroySurfaceLocked(); mSurfaceDestroyDeferred = true; return; 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..16007d775283 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; @@ -101,7 +102,6 @@ import com.android.server.incident.IncidentCompanionService; import com.android.server.input.InputManagerService; import com.android.server.inputmethod.InputMethodManagerService; import com.android.server.inputmethod.MultiClientInputMethodManagerService; -import com.android.server.job.JobSchedulerService; import com.android.server.lights.LightsService; import com.android.server.media.MediaResourceMonitorService; import com.android.server.media.MediaRouterService; @@ -1098,6 +1098,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); @@ -1569,8 +1574,9 @@ public final class SystemServer { mSystemServiceManager.startService(ColorDisplayService.class); t.traceEnd(); + // TODO(aml-jobscheduler): Think about how to do it properly. t.traceBegin("StartJobScheduler"); - mSystemServiceManager.startService(JobSchedulerService.class); + mSystemServiceManager.startService(JOB_SCHEDULER_SERVICE_CLASS); t.traceEnd(); t.traceBegin("StartSoundTrigger"); @@ -1974,6 +1980,11 @@ public final class SystemServer { } t.traceEnd(); + // Permission policy service + t.traceBegin("StartPermissionPolicyService"); + mSystemServiceManager.startService(PermissionPolicyService.class); + t.traceEnd(); + t.traceBegin("MakePackageManagerServiceReady"); mPackageManagerService.systemReady(); t.traceEnd(); @@ -2008,11 +2019,6 @@ public final class SystemServer { mSystemServiceManager.startBootPhase(t, SystemService.PHASE_DEVICE_SPECIFIC_SERVICES_READY); t.traceEnd(); - // Permission policy service - t.traceBegin("StartPermissionPolicyService"); - mSystemServiceManager.startService(PermissionPolicyService.class); - t.traceEnd(); - // These are needed to propagate to the runnable below. final NetworkManagementService networkManagementF = networkManagement; final NetworkStatsService networkStatsF = networkStats; 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/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp index 2baa4d81515a..ad94e6159b87 100644 --- a/services/tests/mockingservicestests/Android.bp +++ b/services/tests/mockingservicestests/Android.bp @@ -20,6 +20,7 @@ android_test { static_libs: [ "services.core", "services.net", + "jobscheduler-service", "androidx.test.runner", "mockito-target-extended-minus-junit4", "platform-test-annotations", diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java index 06c83a26cca6..6feac520e538 100644 --- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java @@ -53,6 +53,7 @@ import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.longThat; +import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; @@ -129,9 +130,12 @@ public class DeviceIdleControllerTest { ConnectivityService connectivityService; LocationManager locationManager; ConstraintController constraintController; + // Freeze time for testing. + long nowElapsed; InjectorForTest(Context ctx) { super(ctx); + nowElapsed = SystemClock.elapsedRealtime(); } @Override @@ -156,6 +160,11 @@ public class DeviceIdleControllerTest { } @Override + long getElapsedRealtime() { + return nowElapsed; + } + + @Override LocationManager getLocationManager() { return locationManager; } @@ -494,11 +503,44 @@ public class DeviceIdleControllerTest { mDeviceIdleController.becomeActiveLocked("testing", 0); verifyStateConditions(STATE_ACTIVE); + setAlarmSoon(false); + setChargingOn(false); + setScreenOn(false); + + mDeviceIdleController.becomeInactiveIfAppropriateLocked(); + verifyStateConditions(STATE_INACTIVE); + verify(mDeviceIdleController) + .scheduleAlarmLocked(eq(mConstants.INACTIVE_TIMEOUT), eq(false)); + } + + @Test + public void testStateActiveToStateInactive_UpcomingAlarm() { + final long timeUntilAlarm = mConstants.MIN_TIME_TO_ALARM / 2; + // Set an upcoming alarm that will prevent full idle. + doReturn(mInjector.getElapsedRealtime() + timeUntilAlarm) + .when(mAlarmManager).getNextWakeFromIdleTime(); + + InOrder inOrder = inOrder(mDeviceIdleController); + + enterDeepState(STATE_ACTIVE); + setQuickDozeEnabled(false); setChargingOn(false); setScreenOn(false); mDeviceIdleController.becomeInactiveIfAppropriateLocked(); verifyStateConditions(STATE_INACTIVE); + inOrder.verify(mDeviceIdleController) + .scheduleAlarmLocked(eq(timeUntilAlarm + mConstants.INACTIVE_TIMEOUT), eq(false)); + + enterDeepState(STATE_ACTIVE); + setQuickDozeEnabled(true); + setChargingOn(false); + setScreenOn(false); + + mDeviceIdleController.becomeInactiveIfAppropriateLocked(); + verifyStateConditions(STATE_QUICK_DOZE_DELAY); + inOrder.verify(mDeviceIdleController).scheduleAlarmLocked( + eq(timeUntilAlarm + mConstants.QUICK_DOZE_DELAY_TIMEOUT), eq(false)); } @Test @@ -515,42 +557,68 @@ public class DeviceIdleControllerTest { @Test public void testTransitionFromAnyStateToStateQuickDozeDelay() { + setAlarmSoon(false); + InOrder inOrder = inOrder(mDeviceIdleController); + enterDeepState(STATE_ACTIVE); setQuickDozeEnabled(true); setChargingOn(false); setScreenOn(false); verifyStateConditions(STATE_QUICK_DOZE_DELAY); + inOrder.verify(mDeviceIdleController) + .scheduleAlarmLocked(eq(mConstants.QUICK_DOZE_DELAY_TIMEOUT), eq(false)); enterDeepState(STATE_INACTIVE); setQuickDozeEnabled(true); verifyStateConditions(STATE_QUICK_DOZE_DELAY); + inOrder.verify(mDeviceIdleController) + .scheduleAlarmLocked(eq(mConstants.QUICK_DOZE_DELAY_TIMEOUT), eq(false)); enterDeepState(STATE_IDLE_PENDING); setQuickDozeEnabled(true); verifyStateConditions(STATE_QUICK_DOZE_DELAY); + inOrder.verify(mDeviceIdleController) + .scheduleAlarmLocked(eq(mConstants.QUICK_DOZE_DELAY_TIMEOUT), eq(false)); enterDeepState(STATE_SENSING); setQuickDozeEnabled(true); verifyStateConditions(STATE_QUICK_DOZE_DELAY); + inOrder.verify(mDeviceIdleController) + .scheduleAlarmLocked(eq(mConstants.QUICK_DOZE_DELAY_TIMEOUT), eq(false)); enterDeepState(STATE_LOCATING); setQuickDozeEnabled(true); verifyStateConditions(STATE_QUICK_DOZE_DELAY); + inOrder.verify(mDeviceIdleController) + .scheduleAlarmLocked(eq(mConstants.QUICK_DOZE_DELAY_TIMEOUT), eq(false)); // IDLE should stay as IDLE. enterDeepState(STATE_IDLE); + // Clear out any alarm setting from the order before checking for this section. + inOrder.verify(mDeviceIdleController, atLeastOnce()) + .scheduleAlarmLocked(anyLong(), anyBoolean()); setQuickDozeEnabled(true); verifyStateConditions(STATE_IDLE); + inOrder.verify(mDeviceIdleController, never()).scheduleAlarmLocked(anyLong(), anyBoolean()); // IDLE_MAINTENANCE should stay as IDLE_MAINTENANCE. enterDeepState(STATE_IDLE_MAINTENANCE); + // Clear out any alarm setting from the order before checking for this section. + inOrder.verify(mDeviceIdleController, atLeastOnce()) + .scheduleAlarmLocked(anyLong(), anyBoolean()); setQuickDozeEnabled(true); verifyStateConditions(STATE_IDLE_MAINTENANCE); + inOrder.verify(mDeviceIdleController, never()).scheduleAlarmLocked(anyLong(), anyBoolean()); + // State is already QUICK_DOZE_DELAY. No work should be done. enterDeepState(STATE_QUICK_DOZE_DELAY); + // Clear out any alarm setting from the order before checking for this section. + inOrder.verify(mDeviceIdleController, atLeastOnce()) + .scheduleAlarmLocked(anyLong(), anyBoolean()); setQuickDozeEnabled(true); mDeviceIdleController.becomeInactiveIfAppropriateLocked(); verifyStateConditions(STATE_QUICK_DOZE_DELAY); + inOrder.verify(mDeviceIdleController, never()).scheduleAlarmLocked(anyLong(), anyBoolean()); } @Test diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp index 869913dec646..b37e4602ed97 100644 --- a/services/tests/servicestests/Android.bp +++ b/services/tests/servicestests/Android.bp @@ -39,6 +39,8 @@ android_test { "platformprotosnano", "hamcrest-library", "servicestests-utils", + "xml-writer-device-lib", + "jobscheduler-service", ], aidl: { diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityGestureDetectorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityGestureDetectorTest.java index aad7230bbc89..cdcc338b3928 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityGestureDetectorTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityGestureDetectorTest.java @@ -16,6 +16,7 @@ package com.android.server.accessibility; +import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; @@ -174,6 +175,7 @@ public class AccessibilityGestureDetectorTest { } // Check that correct gesture was recognized. - verify(mResultListener).onGestureCompleted(gestureId); + verify(mResultListener).onGestureCompleted( + argThat(gestureInfo -> gestureInfo.getGestureId() == gestureId)); } } 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/compat/CompatConfigTest.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java index d008ca66b762..f3c5e99f5f90 100644 --- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java +++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java @@ -22,9 +22,17 @@ import android.content.pm.ApplicationInfo; import androidx.test.runner.AndroidJUnit4; +import com.android.compat.annotation.Change; +import com.android.compat.annotation.XmlWriter; + import org.junit.Test; import org.junit.runner.RunWith; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.UUID; + @RunWith(AndroidJUnit4.class) public class CompatConfigTest { @@ -35,6 +43,27 @@ public class CompatConfigTest { return ai; } + private File createTempDir() { + String base = System.getProperty("java.io.tmpdir"); + File dir = new File(base, UUID.randomUUID().toString()); + assertThat(dir.mkdirs()).isTrue(); + return dir; + } + + private void writeChangesToFile(Change[] changes, File f) { + XmlWriter writer = new XmlWriter(); + for (Change change: changes) { + writer.addChange(change); + } + try { + f.createNewFile(); + writer.write(new FileOutputStream(f)); + } catch (IOException e) { + throw new RuntimeException( + "Encountered an error while writing compat config file", e); + } + } + @Test public void testUnknownChangeEnabled() { CompatConfig pc = new CompatConfig(); @@ -170,4 +199,45 @@ public class CompatConfigTest { sysApp.flags |= ApplicationInfo.FLAG_SYSTEM; assertThat(pc.isChangeEnabled(1234L, sysApp)).isTrue(); } + + @Test + public void testReadConfig() { + Change[] changes = {new Change(1234L, "MY_CHANGE1", false, 2), new Change(1235L, + "MY_CHANGE2", true, null), new Change(1236L, "MY_CHANGE3", false, null)}; + + File dir = createTempDir(); + writeChangesToFile(changes, new File(dir.getPath() + "/platform_compat_config.xml")); + + CompatConfig pc = new CompatConfig(); + pc.initConfigFromLib(dir); + + assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 1))).isFalse(); + assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 3))).isTrue(); + assertThat(pc.isChangeEnabled(1235L, makeAppInfo("com.some.package", 5))).isFalse(); + assertThat(pc.isChangeEnabled(1236L, makeAppInfo("com.some.package", 1))).isTrue(); + } + + @Test + public void testReadConfigMultipleFiles() { + Change[] changes1 = {new Change(1234L, "MY_CHANGE1", false, 2)}; + Change[] changes2 = {new Change(1235L, "MY_CHANGE2", true, null), new Change(1236L, + "MY_CHANGE3", false, null)}; + + File dir = createTempDir(); + writeChangesToFile(changes1, + new File(dir.getPath() + "/libcore_platform_compat_config.xml")); + writeChangesToFile(changes2, + new File(dir.getPath() + "/frameworks_platform_compat_config.xml")); + + + CompatConfig pc = new CompatConfig(); + pc.initConfigFromLib(dir); + + assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 1))).isFalse(); + assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 3))).isTrue(); + assertThat(pc.isChangeEnabled(1235L, makeAppInfo("com.some.package", 5))).isFalse(); + assertThat(pc.isChangeEnabled(1236L, makeAppInfo("com.some.package", 1))).isTrue(); + } } + + diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java new file mode 100644 index 000000000000..65f9e321e084 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java @@ -0,0 +1,340 @@ +/* + * 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 org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; + +import android.app.AppOpsManager; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageParser; +import android.os.Build; +import android.os.Process; +import android.permission.IPermissionManager; +import android.util.ArrayMap; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.Map; + +@RunWith(JUnit4.class) +public class AppsFilterTest { + + private static final int DUMMY_CALLING_UID = 10345; + + @Mock + IPermissionManager mPermissionManagerMock; + + @Mock + AppsFilter.ConfigProvider mConfigProviderMock; + + @Mock + AppOpsManager mAppOpsManager; + + private Map<String, PackageParser.Package> mExisting = new ArrayMap<>(); + + private static PackageBuilder pkg(String packageName) { + return new PackageBuilder(packageName) + .setApplicationInfoTargetSdkVersion(Build.VERSION_CODES.R); + } + + private static PackageBuilder pkg(String packageName, Intent... queries) { + return pkg(packageName).setQueriesIntents(queries); + } + + private static PackageBuilder pkg(String packageName, String... queriesPackages) { + return pkg(packageName).setQueriesPackages(queriesPackages); + } + + private static PackageBuilder pkg(String packageName, IntentFilter... filters) { + final PackageBuilder packageBuilder = pkg(packageName).addActivity( + pkg -> new PackageParser.ParseComponentArgs(pkg, new String[1], 0, 0, 0, 0, 0, 0, + new String[]{packageName}, 0, 0, 0), new ActivityInfo()); + for (IntentFilter filter : filters) { + packageBuilder.addActivityIntentInfo(0 /* index */, activity -> { + final PackageParser.ActivityIntentInfo info = + new PackageParser.ActivityIntentInfo(activity); + if (filter.countActions() > 0) { + filter.actionsIterator().forEachRemaining(info::addAction); + } + if (filter.countCategories() > 0) { + filter.actionsIterator().forEachRemaining(info::addAction); + } + if (filter.countDataAuthorities() > 0) { + filter.authoritiesIterator().forEachRemaining(info::addDataAuthority); + } + if (filter.countDataSchemes() > 0) { + filter.schemesIterator().forEachRemaining(info::addDataScheme); + } + return info; + }); + } + return packageBuilder; + } + + @Before + public void setup() throws Exception { + mExisting = new ArrayMap<>(); + + MockitoAnnotations.initMocks(this); + when(mPermissionManagerMock + .checkPermission(anyString(), anyString(), anyInt())) + .thenReturn(PackageManager.PERMISSION_DENIED); + when(mConfigProviderMock.isEnabled()).thenReturn(true); + when(mAppOpsManager.checkOpNoThrow(eq(AppOpsManager.OP_QUERY_ALL_PACKAGES), eq( + DUMMY_CALLING_UID), anyString())).thenReturn(AppOpsManager.MODE_DEFAULT); + } + + @Test + public void testQueriesAction_FilterMatches() { + final AppsFilter appsFilter = + new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager, + new String[]{}, false); + + PackageSetting target = simulateAddPackage(appsFilter, + pkg("com.some.package", new IntentFilter("TEST_ACTION"))).build(); + PackageSetting calling = simulateAddPackage(appsFilter, + pkg("com.some.other.package", new Intent("TEST_ACTION"))).build(); + + assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0)); + } + + @Test + public void testQueriesAction_NoMatchingAction_Filters() { + final AppsFilter appsFilter = + new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager, + new String[]{}, false); + + PackageSetting target = simulateAddPackage(appsFilter, + pkg("com.some.package")).build(); + PackageSetting calling = simulateAddPackage(appsFilter, + pkg("com.some.other.package", new Intent("TEST_ACTION"))).build(); + + assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0)); + } + + @Test + public void testQueriesAction_NoMatchingActionFilterLowSdk_DoesntFilter() { + final AppsFilter appsFilter = + new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager, + new String[]{}, false); + + PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package")).build(); + PackageSetting calling = simulateAddPackage(appsFilter, pkg("com.some.other.package", + new Intent("TEST_ACTION")).setApplicationInfoTargetSdkVersion( + Build.VERSION_CODES.P)).build(); + + assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0)); + } + + @Test + public void testNoQueries_Filters() { + final AppsFilter appsFilter = + new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager, + new String[]{}, false); + + PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package")).build(); + PackageSetting calling = simulateAddPackage(appsFilter, + pkg("com.some.other.package")).build(); + + assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0)); + } + + @Test + public void testForceQueryable_DoesntFilter() { + final AppsFilter appsFilter = + new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager, + new String[]{}, false); + + PackageSetting target = + simulateAddPackage(appsFilter, pkg("com.some.package").setForceQueryable(true)) + .build(); + PackageSetting calling = simulateAddPackage(appsFilter, + pkg("com.some.other.package")).build(); + + assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0)); + } + + @Test + public void testForceQueryableByDevice_SystemCaller_DoesntFilter() { + final AppsFilter appsFilter = + new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager, + new String[]{"com.some.package"}, false); + + PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package")) + .setPkgFlags(ApplicationInfo.FLAG_SYSTEM) + .build(); + PackageSetting calling = simulateAddPackage(appsFilter, + pkg("com.some.other.package")).build(); + + assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0)); + } + + @Test + public void testForceQueryableByDevice_NonSystemCaller_Filters() { + final AppsFilter appsFilter = + new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager, + new String[]{"com.some.package"}, false); + + PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package")).build(); + PackageSetting calling = simulateAddPackage(appsFilter, + pkg("com.some.other.package")).build(); + + assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0)); + } + + + @Test + public void testSystemQueryable_DoesntFilter() { + final AppsFilter appsFilter = + new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager, + new String[]{}, true /* system force queryable */); + + PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package")) + .setPkgFlags(ApplicationInfo.FLAG_SYSTEM) + .build(); + PackageSetting calling = simulateAddPackage(appsFilter, + pkg("com.some.other.package")).build(); + + assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0)); + } + + @Test + public void testQueriesPackage_DoesntFilter() { + final AppsFilter appsFilter = + new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager, + new String[]{}, false); + + PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package")).build(); + PackageSetting calling = simulateAddPackage(appsFilter, + pkg("com.some.other.package", "com.some.package")).build(); + + assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0)); + } + + @Test + public void testNoQueries_AppOpModeDeny_Filters() { + when(mAppOpsManager.checkOpNoThrow(eq(AppOpsManager.OP_QUERY_ALL_PACKAGES), eq( + DUMMY_CALLING_UID), anyString())).thenReturn(AppOpsManager.MODE_ERRORED); + final AppsFilter appsFilter = + new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager, + new String[]{}, false); + + PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package")).build(); + PackageSetting calling = simulateAddPackage(appsFilter, + pkg("com.some.other.package")).build(); + + assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0)); + } + + @Test + public void testNoQueries_AppOpModeAllow_DoesntFilter() { + when(mAppOpsManager.checkOpNoThrow(eq(AppOpsManager.OP_QUERY_ALL_PACKAGES), eq( + DUMMY_CALLING_UID), anyString())).thenReturn(AppOpsManager.MODE_ALLOWED); + final AppsFilter appsFilter = + new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager, + new String[]{}, false); + + PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package")).build(); + PackageSetting calling = simulateAddPackage(appsFilter, + pkg("com.some.other.package")).build(); + + assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0)); + } + + @Test + public void testNoQueries_AppOpModeIgnore_Filters() { + when(mAppOpsManager.checkOpNoThrow(eq(AppOpsManager.OP_QUERY_ALL_PACKAGES), eq( + DUMMY_CALLING_UID), anyString())).thenReturn(AppOpsManager.MODE_IGNORED); + final AppsFilter appsFilter = + new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager, + new String[]{}, false); + + PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package")).build(); + PackageSetting calling = simulateAddPackage(appsFilter, + pkg("com.some.other.package")).build(); + + assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0)); + } + + @Test + public void testNoQueries_FeatureOff_DoesntFilter() { + when(mConfigProviderMock.isEnabled()).thenReturn(false); + final AppsFilter appsFilter = + new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager, + new String[]{}, false); + + PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package")).build(); + PackageSetting calling = simulateAddPackage(appsFilter, + pkg("com.some.other.package")).build(); + + assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0)); + } + + @Test + public void testSystemUid_DoesntFilter() { + final AppsFilter appsFilter = + new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager, + new String[]{}, false); + + PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package")).build(); + + assertFalse(appsFilter.shouldFilterApplication(0, null, target, 0)); + assertFalse(appsFilter.shouldFilterApplication( + Process.FIRST_APPLICATION_UID - 1, null, target, 0)); + } + + @Test + public void testNonSystemUid_NoCallingSetting_Filters() { + final AppsFilter appsFilter = + new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager, + new String[]{}, false); + + PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package")).build(); + + assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, null, target, 0)); + } + + private PackageSettingBuilder simulateAddPackage(AppsFilter filter, + PackageBuilder newPkgBuilder) { + PackageParser.Package newPkg = newPkgBuilder.build(); + filter.addPackage(newPkg, mExisting); + mExisting.put(newPkg.packageName, newPkg); + return new PackageSettingBuilder() + .setPackage(newPkg) + .setName(newPkg.packageName) + .setCodePath("/") + .setResourcePath("/") + .setPVersionCode(1L); + } + +} + 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..c38672cfc93c --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/pm/PackageBuilder.java @@ -0,0 +1,176 @@ +/* + * 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.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageParser; + +import com.android.internal.util.ArrayUtils; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; + +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; + } + + public PackageBuilder setApplicationInfoTargetSdkVersion(int versionCode) { + mPkg.applicationInfo.targetSdkVersion = versionCode; + return this; + } + + public PackageBuilder setQueriesIntents(Collection<Intent> queriesIntents) { + mPkg.mQueriesIntents = new ArrayList<>(queriesIntents); + return this; + } + + public PackageBuilder setQueriesIntents(Intent... intents) { + return setQueriesIntents(Arrays.asList(intents)); + } + + public PackageBuilder setQueriesPackages(Collection<String> queriesPackages) { + mPkg.mQueriesPackages = new ArrayList<>(queriesPackages); + return this; + } + + public PackageBuilder setQueriesPackages(String... queriesPackages) { + return setQueriesPackages(Arrays.asList(queriesPackages)); + } + + public PackageBuilder setForceQueryable(boolean forceQueryable) { + mPkg.mForceQueryable = forceQueryable; + return this; + } + + public interface ParseComponentArgsCreator { + PackageParser.ParseComponentArgs create(PackageParser.Package pkg); + } + + public PackageBuilder addActivity(ParseComponentArgsCreator argsCreator, ActivityInfo info) { + mPkg.activities.add(new PackageParser.Activity(argsCreator.create(mPkg), info)); + return this; + } + + public interface ActivityIntentInfoCreator { + PackageParser.ActivityIntentInfo create(PackageParser.Activity activity); + } + + public PackageBuilder addActivityIntentInfo( + int activityIndex, ActivityIntentInfoCreator creator) { + final PackageParser.Activity activity = mPkg.activities.get(activityIndex); + activity.intents.add(creator.create(activity)); + 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..06c6314ab907 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java @@ -0,0 +1,162 @@ +/* + * 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.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<>(); + private PackageParser.Package mPkg; + + public PackageSettingBuilder setPackage(PackageParser.Package pkg) { + this.mPkg = pkg; + return this; + } + + 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.pkg = mPkg; + 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..dd3d8b929793 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java @@ -0,0 +1,551 @@ +/* + * 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.nullable; +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 android.util.Pair; + +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 +// TODO: shared user tests +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}); + } + + @Before + public void setupDefaultAbiBehavior() throws Exception { + when(mMockPackageAbiHelper.derivePackageAbi( + any(PackageParser.Package.class), nullable(String.class), anyBoolean())) + .thenReturn(new Pair<>( + new PackageAbiHelper.Abis("derivedPrimary", "derivedSecondary"), + new PackageAbiHelper.NativeLibraryPaths( + "derivedRootDir", true, "derivedNativeDir", "derivedNativeDir2"))); + when(mMockPackageAbiHelper.getNativeLibraryPaths( + any(PackageParser.Package.class), any(File.class))) + .thenReturn(new PackageAbiHelper.NativeLibraryPaths( + "getRootDir", true, "getNativeDir", "getNativeDir2" + )); + when(mMockPackageAbiHelper.getBundledAppAbis( + any(PackageParser.Package.class))) + .thenReturn(new PackageAbiHelper.Abis("bundledPrimary", "bundledSecondary")); + } + + @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)); + assertPathsNotDerived(scanResult); + } + + @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()); + + assertPathsNotDerived(scanResult); + } + + @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(); + + + final PackageManagerService.ScanResult scanResult = executeScan(new ScanRequestBuilder( + basicPackage) + .setPkgSetting(pkgSetting) + .addScanFlag(SCAN_FIRST_BOOT_OR_UPGRADE) + .build()); + + assertAbiAndPathssDerived(scanResult); + } + + @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; + assertBasicApplicationInfo(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.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 assertBasicApplicationInfo(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)); + } + + private static void assertAbiAndPathssDerived(PackageManagerService.ScanResult scanResult) { + final ApplicationInfo applicationInfo = scanResult.pkgSetting.pkg.applicationInfo; + assertThat(applicationInfo.primaryCpuAbi, is("derivedPrimary")); + assertThat(applicationInfo.secondaryCpuAbi, is("derivedSecondary")); + + assertThat(applicationInfo.nativeLibraryRootDir, is("derivedRootDir")); + assertThat(scanResult.pkgSetting.legacyNativeLibraryPathString, is("derivedRootDir")); + assertThat(applicationInfo.nativeLibraryRootRequiresIsa, is(true)); + assertThat(applicationInfo.nativeLibraryDir, is("derivedNativeDir")); + assertThat(applicationInfo.secondaryNativeLibraryDir, is("derivedNativeDir2")); + } + + private static void assertPathsNotDerived(PackageManagerService.ScanResult scanResult) { + final ApplicationInfo applicationInfo = scanResult.pkgSetting.pkg.applicationInfo; + assertThat(applicationInfo.nativeLibraryRootDir, is("getRootDir")); + assertThat(scanResult.pkgSetting.legacyNativeLibraryPathString, is("getRootDir")); + assertThat(applicationInfo.nativeLibraryRootRequiresIsa, is(true)); + assertThat(applicationInfo.nativeLibraryDir, is("getNativeDir")); + assertThat(applicationInfo.secondaryNativeLibraryDir, is("getNativeDir2")); + } +} 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/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java index a08923bafe28..3d944671ef25 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -842,4 +842,51 @@ public class ActivityStarterTests extends ActivityTestsBase { // Ensure the activity is moved to secondary display. assertEquals(secondaryDisplay, topActivity.getDisplay()); } + + /** + * This test ensures that starting an activity with the freeze-task-list activity option will + * actually freeze the task list + */ + @Test + public void testFreezeTaskListActivityOption() { + RecentTasks recentTasks = mock(RecentTasks.class); + mService.mStackSupervisor.setRecentTasks(recentTasks); + doReturn(true).when(recentTasks).isCallerRecents(anyInt()); + + final ActivityStarter starter = prepareStarter(0 /* flags */); + final ActivityOptions options = ActivityOptions.makeBasic(); + options.setFreezeRecentTasksReordering(); + + starter.setReason("testFreezeTaskListActivityOption") + .setActivityOptions(new SafeActivityOptions(options)) + .execute(); + + verify(recentTasks, times(1)).setFreezeTaskListReordering(); + verify(recentTasks, times(0)).resetFreezeTaskListReorderingOnTimeout(); + } + + /** + * This test ensures that if we froze the task list as a part of starting an activity that fails + * to start, that we also reset the task list. + */ + @Test + public void testFreezeTaskListActivityOptionFailedStart_expectResetFreezeTaskList() { + RecentTasks recentTasks = mock(RecentTasks.class); + mService.mStackSupervisor.setRecentTasks(recentTasks); + doReturn(true).when(recentTasks).isCallerRecents(anyInt()); + + final ActivityStarter starter = prepareStarter(0 /* flags */); + final ActivityOptions options = ActivityOptions.makeBasic(); + options.setFreezeRecentTasksReordering(); + + starter.setReason("testFreezeTaskListActivityOptionFailedStart") + .setActivityOptions(new SafeActivityOptions(options)) + .execute(); + + // Simulate a failed start + starter.postStartActivityProcessing(null, START_ABORTED, null); + + verify(recentTasks, times(1)).setFreezeTaskListReordering(); + verify(recentTasks, times(1)).resetFreezeTaskListReorderingOnTimeout(); + } } 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/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java index 29398b6b6e75..2cebebfc292e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java @@ -1251,6 +1251,22 @@ public class TaskLaunchParamsModifierTests extends ActivityTestsBase { assertEquals(startingBounds, adjustedBounds); } + @Test + public void testNoMultiDisplaySupports() { + final boolean orgValue = mService.mSupportsMultiDisplay; + final TestActivityDisplay display = createNewActivityDisplay(WINDOWING_MODE_FULLSCREEN); + mCurrent.mPreferredDisplayId = display.mDisplayId; + + try { + mService.mSupportsMultiDisplay = false; + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null, + mActivity, /* source */ null, /* options */ null, mCurrent, mResult)); + assertEquals(DEFAULT_DISPLAY, mResult.mPreferredDisplayId); + } finally { + mService.mSupportsMultiDisplay = orgValue; + } + } + private TestActivityDisplay createNewActivityDisplay(int windowingMode) { final TestActivityDisplay display = addNewActivityDisplayAt(ActivityDisplay.POSITION_TOP); display.setWindowingMode(windowingMode); 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/iorap/compiler.py b/startop/scripts/iorap/compiler.py index a335b30da8a5..9527e28c94fe 100755 --- a/startop/scripts/iorap/compiler.py +++ b/startop/scripts/iorap/compiler.py @@ -305,7 +305,7 @@ def main(argv): transform_perfetto_trace_to_systrace(options.perfetto_trace_file, trace_file.name) return run(sql_db_path, - options.trace_file, + trace_file.name, options.trace_duration, options.output_file, inode_table, diff --git a/startop/view_compiler/dex_builder.cc b/startop/view_compiler/dex_builder.cc index 6047e8c74e38..09f9c04d9e2c 100644 --- a/startop/view_compiler/dex_builder.cc +++ b/startop/view_compiler/dex_builder.cc @@ -102,6 +102,12 @@ std::ostream& operator<<(std::ostream& out, const Instruction::Op& opcode) { case Instruction::Op::kCheckCast: out << "kCheckCast"; return out; + case Instruction::Op::kGetStaticField: + out << "kGetStaticField"; + return out; + case Instruction::Op::kSetStaticField: + out << "kSetStaticField"; + return out; } } @@ -229,6 +235,22 @@ ir::Type* DexBuilder::GetOrAddType(const std::string& descriptor) { return type; } +ir::FieldDecl* DexBuilder::GetOrAddField(TypeDescriptor parent, const std::string& name, + TypeDescriptor type) { + const auto key = std::make_tuple(parent, name); + if (field_decls_by_key_.find(key) != field_decls_by_key_.end()) { + return field_decls_by_key_[key]; + } + + ir::FieldDecl* field = Alloc<ir::FieldDecl>(); + field->parent = GetOrAddType(parent); + field->name = GetOrAddString(name); + field->type = GetOrAddType(type); + dex_file_->fields_map[field->orig_index] = field; + field_decls_by_key_[key] = field; + return field; +} + ir::Proto* Prototype::Encode(DexBuilder* dex) const { auto* proto = dex->Alloc<ir::Proto>(); proto->shorty = dex->GetOrAddString(Shorty()); @@ -360,6 +382,9 @@ void MethodBuilder::EncodeInstruction(const Instruction& instruction) { return EncodeNew(instruction); case Instruction::Op::kCheckCast: return EncodeCast(instruction); + case Instruction::Op::kGetStaticField: + case Instruction::Op::kSetStaticField: + return EncodeStaticFieldOp(instruction); } } @@ -428,7 +453,7 @@ void MethodBuilder::EncodeInvoke(const Instruction& instruction, ::art::Instruct // first move all the arguments into contiguous temporary registers. std::array<Value, kMaxArgs> scratch = GetScratchRegisters<kMaxArgs>(); - const auto& prototype = dex_->GetPrototypeByMethodId(instruction.method_id()); + const auto& prototype = dex_->GetPrototypeByMethodId(instruction.index_argument()); CHECK(prototype.has_value()); for (size_t i = 0; i < instruction.args().size(); ++i) { @@ -452,12 +477,12 @@ void MethodBuilder::EncodeInvoke(const Instruction& instruction, ::art::Instruct Encode3rc(InvokeToInvokeRange(opcode), instruction.args().size(), - instruction.method_id(), + instruction.index_argument(), RegisterValue(scratch[0])); } else { Encode35c(opcode, instruction.args().size(), - instruction.method_id(), + instruction.index_argument(), arguments[0], arguments[1], arguments[2], @@ -514,6 +539,35 @@ void MethodBuilder::EncodeCast(const Instruction& instruction) { Encode21c(::art::Instruction::CHECK_CAST, RegisterValue(*instruction.dest()), type.value()); } +void MethodBuilder::EncodeStaticFieldOp(const Instruction& instruction) { + switch (instruction.opcode()) { + case Instruction::Op::kGetStaticField: { + CHECK(instruction.dest().has_value()); + CHECK(instruction.dest()->is_variable()); + CHECK_EQ(0, instruction.args().size()); + + Encode21c(::art::Instruction::SGET, + RegisterValue(*instruction.dest()), + instruction.index_argument()); + break; + } + case Instruction::Op::kSetStaticField: { + CHECK(!instruction.dest().has_value()); + const auto& args = instruction.args(); + CHECK_EQ(1, args.size()); + CHECK(args[0].is_variable()); + + Encode21c(::art::Instruction::SPUT, + RegisterValue(args[0]), + instruction.index_argument()); + break; + } + default: { + LOG(FATAL) << "Unsupported static field operation"; + } + } +} + size_t MethodBuilder::RegisterValue(const Value& value) const { if (value.is_register()) { return value.value(); diff --git a/startop/view_compiler/dex_builder.h b/startop/view_compiler/dex_builder.h index 541d80077bd3..3f9ac43ae532 100644 --- a/startop/view_compiler/dex_builder.h +++ b/startop/view_compiler/dex_builder.h @@ -153,6 +153,7 @@ class Instruction { kBranchEqz, kBranchNEqz, kCheckCast, + kGetStaticField, kInvokeDirect, kInvokeInterface, kInvokeStatic, @@ -162,6 +163,7 @@ class Instruction { kNew, kReturn, kReturnObject, + kSetStaticField }; //////////////////////// @@ -170,12 +172,12 @@ class Instruction { // For instructions with no return value and no arguments. static inline Instruction OpNoArgs(Op opcode) { - return Instruction{opcode, /*method_id*/ 0, /*dest*/ {}}; + return Instruction{opcode, /*index_argument*/ 0, /*dest*/ {}}; } // For most instructions, which take some number of arguments and have an optional return value. template <typename... T> static inline Instruction OpWithArgs(Op opcode, std::optional<const Value> dest, T... args) { - return Instruction{opcode, /*method_id=*/0, /*result_is_object=*/false, dest, args...}; + return Instruction{opcode, /*index_argument=*/0, /*result_is_object=*/false, dest, args...}; } // A cast instruction. Basically, `(type)val` @@ -186,77 +188,87 @@ class Instruction { // For method calls. template <typename... T> - static inline Instruction InvokeVirtual(size_t method_id, std::optional<const Value> dest, + static inline Instruction InvokeVirtual(size_t index_argument, std::optional<const Value> dest, Value this_arg, T... args) { return Instruction{ - Op::kInvokeVirtual, method_id, /*result_is_object=*/false, dest, this_arg, args...}; + Op::kInvokeVirtual, index_argument, /*result_is_object=*/false, dest, this_arg, args...}; } // Returns an object template <typename... T> - static inline Instruction InvokeVirtualObject(size_t method_id, std::optional<const Value> dest, + static inline Instruction InvokeVirtualObject(size_t index_argument, std::optional<const Value> dest, Value this_arg, T... args) { return Instruction{ - Op::kInvokeVirtual, method_id, /*result_is_object=*/true, dest, this_arg, args...}; + Op::kInvokeVirtual, index_argument, /*result_is_object=*/true, dest, this_arg, args...}; } // For direct calls (basically, constructors). template <typename... T> - static inline Instruction InvokeDirect(size_t method_id, std::optional<const Value> dest, + static inline Instruction InvokeDirect(size_t index_argument, std::optional<const Value> dest, Value this_arg, T... args) { return Instruction{ - Op::kInvokeDirect, method_id, /*result_is_object=*/false, dest, this_arg, args...}; + Op::kInvokeDirect, index_argument, /*result_is_object=*/false, dest, this_arg, args...}; } // Returns an object template <typename... T> - static inline Instruction InvokeDirectObject(size_t method_id, std::optional<const Value> dest, + static inline Instruction InvokeDirectObject(size_t index_argument, std::optional<const Value> dest, Value this_arg, T... args) { return Instruction{ - Op::kInvokeDirect, method_id, /*result_is_object=*/true, dest, this_arg, args...}; + Op::kInvokeDirect, index_argument, /*result_is_object=*/true, dest, this_arg, args...}; } // For static calls. template <typename... T> - static inline Instruction InvokeStatic(size_t method_id, std::optional<const Value> dest, + static inline Instruction InvokeStatic(size_t index_argument, std::optional<const Value> dest, T... args) { - return Instruction{Op::kInvokeStatic, method_id, /*result_is_object=*/false, dest, args...}; + return Instruction{Op::kInvokeStatic, index_argument, /*result_is_object=*/false, dest, args...}; } // Returns an object template <typename... T> - static inline Instruction InvokeStaticObject(size_t method_id, std::optional<const Value> dest, + static inline Instruction InvokeStaticObject(size_t index_argument, std::optional<const Value> dest, T... args) { - return Instruction{Op::kInvokeStatic, method_id, /*result_is_object=*/true, dest, args...}; + return Instruction{Op::kInvokeStatic, index_argument, /*result_is_object=*/true, dest, args...}; } // For static calls. template <typename... T> - static inline Instruction InvokeInterface(size_t method_id, std::optional<const Value> dest, + static inline Instruction InvokeInterface(size_t index_argument, std::optional<const Value> dest, T... args) { - return Instruction{Op::kInvokeInterface, method_id, /*result_is_object=*/false, dest, args...}; + return Instruction{Op::kInvokeInterface, index_argument, /*result_is_object=*/false, dest, args...}; + + } + + static inline Instruction GetStaticField(size_t field_id, Value dest) { + return Instruction{Op::kGetStaticField, field_id, dest}; + } + + static inline Instruction SetStaticField(size_t field_id, Value value) { + return Instruction{Op::kSetStaticField, field_id, /*result_is_object=*/false, /*dest=*/{}, value}; } + /////////////// // Accessors // /////////////// Op opcode() const { return opcode_; } - size_t method_id() const { return method_id_; } + size_t index_argument() const { return index_argument_; } bool result_is_object() const { return result_is_object_; } const std::optional<const Value>& dest() const { return dest_; } const std::vector<const Value>& args() const { return args_; } private: - inline Instruction(Op opcode, size_t method_id, std::optional<const Value> dest) - : opcode_{opcode}, method_id_{method_id}, result_is_object_{false}, dest_{dest}, args_{} {} + inline Instruction(Op opcode, size_t index_argument, std::optional<const Value> dest) + : opcode_{opcode}, index_argument_{index_argument}, result_is_object_{false}, dest_{dest}, args_{} {} template <typename... T> - inline constexpr Instruction(Op opcode, size_t method_id, bool result_is_object, + inline constexpr Instruction(Op opcode, size_t index_argument, bool result_is_object, std::optional<const Value> dest, T... args) : opcode_{opcode}, - method_id_{method_id}, + index_argument_{index_argument}, result_is_object_{result_is_object}, dest_{dest}, args_{args...} {} const Op opcode_; // The index of the method to invoke, for kInvokeVirtual and similar opcodes. - const size_t method_id_{0}; + const size_t index_argument_{0}; const bool result_is_object_; const std::optional<const Value> dest_; const std::vector<const Value> args_; @@ -319,6 +331,7 @@ class MethodBuilder { void EncodeBranch(art::Instruction::Code op, const Instruction& instruction); void EncodeNew(const Instruction& instruction); void EncodeCast(const Instruction& instruction); + void EncodeStaticFieldOp(const Instruction& instruction); // Low-level instruction format encoding. See // https://source.android.com/devices/tech/dalvik/instruction-formats for documentation of @@ -481,6 +494,11 @@ class DexBuilder { // See the TypeDescriptor class for help generating these. GetOrAddType can be used to declare // imported classes. ir::Type* GetOrAddType(const std::string& descriptor); + inline ir::Type* GetOrAddType(TypeDescriptor descriptor) { + return GetOrAddType(descriptor.descriptor()); + } + + ir::FieldDecl* GetOrAddField(TypeDescriptor parent, const std::string& name, TypeDescriptor type); // Returns the method id for the method, creating it if it has not been created yet. const MethodDeclData& GetOrDeclareMethod(TypeDescriptor type, const std::string& name, @@ -526,6 +544,9 @@ class DexBuilder { // Keep track of already-encoded protos. std::map<Prototype, ir::Proto*> proto_map_; + + // Keep track of fields that have been declared + std::map<std::tuple<TypeDescriptor, std::string>, ir::FieldDecl*> field_decls_by_key_; }; template <typename... T> diff --git a/startop/view_compiler/dex_builder_test/Android.bp b/startop/view_compiler/dex_builder_test/Android.bp index 22a3cfafbc44..1214538e8f0d 100644 --- a/startop/view_compiler/dex_builder_test/Android.bp +++ b/startop/view_compiler/dex_builder_test/Android.bp @@ -39,6 +39,7 @@ android_test { srcs: [ "src/android/startop/test/DexBuilderTest.java", "src/android/startop/test/LayoutCompilerTest.java", + "src/android/startop/test/TestClass.java", ], sdk_version: "current", data: [":generate_dex_testcases", ":generate_compiled_layout1", ":generate_compiled_layout2"], diff --git a/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java b/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java index f7b1674894f5..6c0b8bbe8b83 100644 --- a/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java +++ b/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java @@ -24,10 +24,10 @@ import java.lang.reflect.Method; // Adding tests here requires changes in several other places. See README.md in // the view_compiler directory for more information. -public class DexBuilderTest { +public final class DexBuilderTest { static ClassLoader loadDexFile(String filename) throws Exception { return new PathClassLoader("/data/local/tmp/dex-builder-test/" + filename, - ClassLoader.getSystemClassLoader()); + DexBuilderTest.class.getClassLoader()); } public void hello() {} @@ -167,4 +167,23 @@ public class DexBuilderTest { } Assert.assertTrue(castFailed); } + + @Test + public void readStaticField() throws Exception { + ClassLoader loader = loadDexFile("simple.dex"); + Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests"); + Method method = clazz.getMethod("readStaticField"); + TestClass.staticInteger = 5; + Assert.assertEquals(5, method.invoke(null)); + } + + @Test + public void setStaticField() throws Exception { + ClassLoader loader = loadDexFile("simple.dex"); + Class clazz = loader.loadClass("android.startop.test.testcases.SimpleTests"); + Method method = clazz.getMethod("setStaticField"); + TestClass.staticInteger = 5; + method.invoke(null); + Assert.assertEquals(7, TestClass.staticInteger); + } } diff --git a/startop/view_compiler/dex_builder_test/src/android/startop/test/TestClass.java b/startop/view_compiler/dex_builder_test/src/android/startop/test/TestClass.java new file mode 100644 index 000000000000..dd7792306030 --- /dev/null +++ b/startop/view_compiler/dex_builder_test/src/android/startop/test/TestClass.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package android.startop.test; + + /** + * A simple class to help test DexBuilder. + */ +public final class TestClass { + public static int staticInteger; + + public int instanceField; +} diff --git a/startop/view_compiler/dex_testcase_generator.cc b/startop/view_compiler/dex_testcase_generator.cc index f62ec5dde85e..fee5e722bc55 100644 --- a/startop/view_compiler/dex_testcase_generator.cc +++ b/startop/view_compiler/dex_testcase_generator.cc @@ -282,6 +282,37 @@ void GenerateSimpleTestCases(const string& outdir) { method.Encode(); }(castObjectToString); + // Read a static field + // integer readStaticField() { return TestClass.staticInteger; } + MethodBuilder readStaticField{ + cbuilder.CreateMethod("readStaticField", Prototype{TypeDescriptor::Int()})}; + [&](MethodBuilder& method) { + const ir::FieldDecl* field = + dex_file.GetOrAddField(TypeDescriptor::FromClassname("android.startop.test.TestClass"), + "staticInteger", + TypeDescriptor::Int()); + Value result{method.MakeRegister()}; + method.AddInstruction(Instruction::GetStaticField(field->orig_index, result)); + method.BuildReturn(result, /*is_object=*/false); + method.Encode(); + }(readStaticField); + + // Set a static field + // void setStaticField() { TestClass.staticInteger = 7; } + MethodBuilder setStaticField{ + cbuilder.CreateMethod("setStaticField", Prototype{TypeDescriptor::Void()})}; + [&](MethodBuilder& method) { + const ir::FieldDecl* field = + dex_file.GetOrAddField(TypeDescriptor::FromClassname("android.startop.test.TestClass"), + "staticInteger", + TypeDescriptor::Int()); + Value number{method.MakeRegister()}; + method.BuildConst4(number, 7); + method.AddInstruction(Instruction::SetStaticField(field->orig_index, number)); + method.BuildReturn(); + method.Encode(); + }(setStaticField); + slicer::MemView image{dex_file.CreateImage()}; std::ofstream out_file(outdir + "/simple.dex"); out_file.write(image.ptr<const char>(), image.size()); diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 91bea5ff479b..cd79f37bb2ce 100755 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -2151,6 +2151,15 @@ public class CarrierConfigManager { "data_warning_notification_bool"; /** + * Controls if the device should automatically warn the user that sim voice & data function + * might be limited due to dual sim scenario. When set to {@true} display the notification, + * {@code false} otherwise. + * @hide + */ + public static final String KEY_LIMITED_SIM_FUNCTION_NOTIFICATION_FOR_DSDS_BOOL = + "limited_sim_function_notification_for_dsds_bool"; + + /** * Controls the cellular data limit. * <p> * If the user uses more than this amount of data in their billing cycle, as defined by @@ -3029,6 +3038,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. * @@ -3326,6 +3352,7 @@ public class CarrierConfigManager { sDefaults.putInt(KEY_MONTHLY_DATA_CYCLE_DAY_INT, DATA_CYCLE_USE_PLATFORM_DEFAULT); sDefaults.putLong(KEY_DATA_WARNING_THRESHOLD_BYTES_LONG, DATA_CYCLE_USE_PLATFORM_DEFAULT); sDefaults.putBoolean(KEY_DATA_WARNING_NOTIFICATION_BOOL, true); + sDefaults.putBoolean(KEY_LIMITED_SIM_FUNCTION_NOTIFICATION_FOR_DSDS_BOOL, false); sDefaults.putLong(KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG, DATA_CYCLE_USE_PLATFORM_DEFAULT); sDefaults.putBoolean(KEY_DATA_LIMIT_NOTIFICATION_BOOL, true); sDefaults.putBoolean(KEY_DATA_RAPID_NOTIFICATION_BOOL, true); @@ -3463,6 +3490,7 @@ public class CarrierConfigManager { -89, /* SIGNAL_STRENGTH_GREAT */ }); sDefaults.putBoolean(KEY_SUPPORT_WPS_OVER_IMS_BOOL, true); + sDefaults.putAll(Ims.getDefaults()); } /** @@ -3659,4 +3687,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/CellSignalStrengthGsm.java b/telephony/java/android/telephony/CellSignalStrengthGsm.java index 9d732baf7514..fb16d540fbb4 100644 --- a/telephony/java/android/telephony/CellSignalStrengthGsm.java +++ b/telephony/java/android/telephony/CellSignalStrengthGsm.java @@ -143,7 +143,7 @@ public final class CellSignalStrengthGsm extends CellSignalStrength implements P } /** - * Get the signal strength as dBm + * Get the signal strength as dBm. */ @Override public int getDbm() { @@ -163,18 +163,17 @@ public final class CellSignalStrengthGsm extends CellSignalStrength implements P } /** - * Return the Received Signal Strength Indicator + * Return the Received Signal Strength Indicator. * * @return the RSSI in dBm (-113, -51) or * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE}. - * @hide */ public int getRssi() { return mRssi; } /** - * Return the Bit Error Rate + * Return the Bit Error Rate. * * @return the bit error rate (0-7, 99) as defined in TS 27.007 8.5 or * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE}. 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/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java new file mode 100644 index 000000000000..3c343dd19a86 --- /dev/null +++ b/telephony/java/android/telephony/ims/ImsRcsManager.java @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims; + +import android.Manifest; +import android.annotation.CallbackExecutor; +import android.annotation.NonNull; +import android.annotation.RequiresPermission; +import android.content.Context; +import android.os.Binder; +import android.telephony.SubscriptionManager; +import android.telephony.ims.aidl.IImsCapabilityCallback; +import android.telephony.ims.feature.ImsFeature; +import android.telephony.ims.feature.RcsFeature; + +import java.util.concurrent.Executor; + +/** + * Manager for interfacing with the framework RCS services, including the User Capability Exchange + * (UCE) service, as well as managing user settings. + * + * Use {@link #createForSubscriptionId(Context, int)} to create an instance of this manager. + * @hide + */ +public class ImsRcsManager { + + /** + * Receives RCS availability status updates from the ImsService. + * + * @see #isAvailable(int) + * @see #registerRcsAvailabilityCallback(Executor, AvailabilityCallback) + * @see #unregisterRcsAvailabilityCallback(AvailabilityCallback) + */ + public static class AvailabilityCallback { + + private static class CapabilityBinder extends IImsCapabilityCallback.Stub { + + private final AvailabilityCallback mLocalCallback; + private Executor mExecutor; + + CapabilityBinder(AvailabilityCallback c) { + mLocalCallback = c; + } + + @Override + public void onCapabilitiesStatusChanged(int config) { + if (mLocalCallback == null) return; + + Binder.withCleanCallingIdentity(() -> + mExecutor.execute(() -> mLocalCallback.onAvailabilityChanged( + new RcsFeature.RcsImsCapabilities(config)))); + } + + @Override + public void onQueryCapabilityConfiguration(int capability, int radioTech, + boolean isEnabled) { + // This is not used for public interfaces. + } + + @Override + public void onChangeCapabilityConfigurationError(int capability, int radioTech, + @ImsFeature.ImsCapabilityError int reason) { + // This is not used for public interfaces + } + + private void setExecutor(Executor executor) { + mExecutor = executor; + } + } + + private final CapabilityBinder mBinder = new CapabilityBinder(this); + + /** + * The availability of the feature's capabilities has changed to either available or + * unavailable. + * <p> + * If unavailable, the feature does not support the capability at the current time. This may + * be due to network or subscription provisioning changes, such as the IMS registration + * being lost, network type changing, or OMA-DM provisioning updates. + * + * @param capabilities The new availability of the capabilities. + */ + public void onAvailabilityChanged(RcsFeature.RcsImsCapabilities capabilities) { + } + + /**@hide*/ + public final IImsCapabilityCallback getBinder() { + return mBinder; + } + + private void setExecutor(Executor executor) { + mBinder.setExecutor(executor); + } + } + + private final int mSubId; + private final Context mContext; + + + /** + * Create an instance of ImsRcsManager for the subscription id specified. + * + * @param context The context to create this ImsRcsManager instance within. + * @param subscriptionId The ID of the subscription that this ImsRcsManager will use. + * @see android.telephony.SubscriptionManager#getActiveSubscriptionInfoList() + * @throws IllegalArgumentException if the subscription is invalid. + * @hide + */ + public static ImsRcsManager createForSubscriptionId(Context context, int subscriptionId) { + if (!SubscriptionManager.isValidSubscriptionId(subscriptionId)) { + throw new IllegalArgumentException("Invalid subscription ID"); + } + + return new ImsRcsManager(context, subscriptionId); + } + + /** + * Use {@link #createForSubscriptionId(Context, int)} to create an instance of this class. + */ + private ImsRcsManager(Context context, int subId) { + mContext = context; + mSubId = subId; + } + + /** + * Registers an {@link AvailabilityCallback} with the system, which will provide RCS + * availability updates for the subscription specified. + * + * Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to + * subscription changed events and call + * {@link #unregisterRcsAvailabilityCallback(AvailabilityCallback)} to clean up after a + * subscription is removed. + * <p> + * When the callback is registered, it will initiate the callback c to be called with the + * current capabilities. + * + * @param executor The executor the callback events should be run on. + * @param c The RCS {@link AvailabilityCallback} to be registered. + * @see #unregisterRcsAvailabilityCallback(AvailabilityCallback) + * @throws ImsException if the subscription associated with this instance of + * {@link ImsRcsManager} is valid, but the ImsService associated with the subscription is not + * available. This can happen if the ImsService has crashed, for example, or if the subscription + * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes. + */ + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public void registerRcsAvailabilityCallback(@CallbackExecutor Executor executor, + @NonNull AvailabilityCallback c) throws ImsException { + if (c == null) { + throw new IllegalArgumentException("Must include a non-null AvailabilityCallback."); + } + if (executor == null) { + throw new IllegalArgumentException("Must include a non-null Executor."); + } + c.setExecutor(executor); + throw new UnsupportedOperationException("registerRcsAvailabilityCallback is not" + + "supported."); + } + + /** + * Removes an existing RCS {@link AvailabilityCallback}. + * <p> + * When the subscription associated with this callback is removed (SIM removed, ESIM swap, + * etc...), this callback will automatically be unregistered. If this method is called for an + * inactive subscription, it will result in a no-op. + * @param c The RCS {@link AvailabilityCallback} to be removed. + * @see #registerRcsAvailabilityCallback(Executor, AvailabilityCallback) + */ + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public void unregisterRcsAvailabilityCallback(@NonNull AvailabilityCallback c) { + if (c == null) { + throw new IllegalArgumentException("Must include a non-null AvailabilityCallback."); + } + throw new UnsupportedOperationException("unregisterRcsAvailabilityCallback is not" + + "supported."); + } + + /** + * Query for the capability of an IMS RCS service provided by the framework. + * <p> + * This only reports the status of RCS capabilities provided by the framework, not necessarily + * RCS capabilities provided over-the-top by applications. + * + * @param capability The RCS capability to query. + * @return true if the RCS capability is capable for this subscription, false otherwise. This + * does not necessarily mean that we are registered for IMS and the capability is available, but + * rather the subscription is capable of this service over IMS. + * @see #isAvailable(int) + * @see android.telephony.CarrierConfigManager#KEY_USE_RCS_PRESENCE_BOOL + */ + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public boolean isCapable(@RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability) { + throw new UnsupportedOperationException("isCapable is not supported."); + } + + /** + * Query the availability of an IMS RCS capability. + * <p> + * This only reports the status of RCS capabilities provided by the framework, not necessarily + * RCS capabilities provided by over-the-top by applications. + * + * @param capability the RCS capability to query. + * @return true if the RCS capability is currently available for the associated subscription, + * false otherwise. If the capability is available, IMS is registered and the service is + * currently available over IMS. + * @see #isCapable(int) + */ + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public boolean isAvailable(@RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability) { + throw new UnsupportedOperationException("isAvailable is not supported."); + } + + /** + * @return A new {@link RcsUceAdapter} used for User Capability Exchange (UCE) operations for + * this subscription. + */ + @NonNull + public RcsUceAdapter getUceAdapter() { + return new RcsUceAdapter(mSubId); + } +} diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java index cc037e3ea814..effdf48067c3 100644 --- a/telephony/java/android/telephony/ims/ProvisioningManager.java +++ b/telephony/java/android/telephony/ims/ProvisioningManager.java @@ -387,6 +387,24 @@ public class ProvisioningManager { } } + /** + * Notify the framework that an RCS autoconfiguration XML file has been received for + * provisioning. + * @param config The XML file to be read. ASCII/UTF8 encoded text if not compressed. + * @param isCompressed The XML file is compressed in gzip format and must be decompressed + * before being read. + * @hide + */ + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + public void notifyRcsAutoConfigurationReceived(@NonNull byte[] config, boolean isCompressed) { + if (config == null) { + throw new IllegalArgumentException("Must include a non-null config XML file."); + } + // TODO: Connect to ImsConfigImplBase. + throw new UnsupportedOperationException("notifyRcsAutoConfigurationReceived is not" + + "supported"); + } + private static boolean isImsAvailableOnDevice() { IPackageManager pm = IPackageManager.Stub.asInterface(ServiceManager.getService("package")); if (pm == null) { diff --git a/tools/preload2/src/com/android/preload/classdataretrieval/ClassDataRetriever.java b/telephony/java/android/telephony/ims/RcsContactUceCapability.aidl index f04360fc1942..bef6a4037fea 100644 --- a/tools/preload2/src/com/android/preload/classdataretrieval/ClassDataRetriever.java +++ b/telephony/java/android/telephony/ims/RcsContactUceCapability.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 The Android Open Source Project + * 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. @@ -14,16 +14,7 @@ * limitations under the License. */ -package com.android.preload.classdataretrieval; -import com.android.ddmlib.Client; +package android.telephony.ims; -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 RcsContactUceCapability; diff --git a/telephony/java/android/telephony/ims/RcsContactUceCapability.java b/telephony/java/android/telephony/ims/RcsContactUceCapability.java new file mode 100644 index 000000000000..492170b1069a --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsContactUceCapability.java @@ -0,0 +1,291 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.net.Uri; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Contains the User Capability Exchange capabilities corresponding to a contact's URI. + * @hide + */ +public final class RcsContactUceCapability implements Parcelable { + + /** Supports 1-to-1 chat */ + public static final int CAPABILITY_CHAT_STANDALONE = (1 << 0); + /** Supports group chat */ + public static final int CAPABILITY_CHAT_SESSION = (1 << 1); + /** Supports full store and forward group chat information. */ + public static final int CAPABILITY_CHAT_SESSION_STORE_FORWARD = (1 << 2); + /** + * Supports file transfer via Message Session Relay Protocol (MSRP) without Store and Forward. + */ + public static final int CAPABILITY_FILE_TRANSFER = (1 << 3); + /** Supports File Transfer Thumbnail */ + public static final int CAPABILITY_FILE_TRANSFER_THUMBNAIL = (1 << 4); + /** Supports File Transfer with Store and Forward */ + public static final int CAPABILITY_FILE_TRANSFER_STORE_FORWARD = (1 << 5); + /** Supports File Transfer via HTTP */ + public static final int CAPABILITY_FILE_TRANSFER_HTTP = (1 << 6); + /** Supports file transfer via SMS */ + public static final int CAPABILITY_FILE_TRANSFER_SMS = (1 << 7); + /** Supports image sharing */ + public static final int CAPABILITY_IMAGE_SHARE = (1 << 8); + /** Supports video sharing during a circuit-switch call (IR.74)*/ + public static final int CAPABILITY_VIDEO_SHARE_DURING_CS_CALL = (1 << 9); + /** Supports video share outside of voice call (IR.84) */ + public static final int CAPABILITY_VIDEO_SHARE = (1 << 10); + /** Supports social presence information */ + public static final int CAPABILITY_SOCIAL_PRESENCE = (1 << 11); + /** Supports capability discovery via presence */ + public static final int CAPABILITY_DISCOVERY_VIA_PRESENCE = (1 << 12); + /** Supports IP Voice calling over LTE or IWLAN (IR.92/IR.51) */ + public static final int CAPABILITY_IP_VOICE_CALL = (1 << 13); + /** Supports IP video calling (IR.94) */ + public static final int CAPABILITY_IP_VIDEO_CALL = (1 << 14); + /** Supports Geolocation PUSH during 1-to-1 or multiparty chat */ + public static final int CAPABILITY_GEOLOCATION_PUSH = (1 << 15); + /** Supports Geolocation PUSH via SMS for fallback. */ + public static final int CAPABILITY_GEOLOCATION_PUSH_SMS = (1 << 16); + /** Supports Geolocation pull. */ + public static final int CAPABILITY_GEOLOCATION_PULL = (1 << 17); + /** Supports Geolocation pull using file transfer support. */ + public static final int CAPABILITY_GEOLOCATION_PULL_FILE_TRANSFER = (1 << 18); + /** Supports RCS voice calling */ + public static final int CAPABILITY_RCS_VOICE_CALL = (1 << 19); + /** Supports RCS video calling */ + public static final int CAPABILITY_RCS_VIDEO_CALL = (1 << 20); + /** Supports RCS video calling, where video media can not be dropped */ + public static final int CAPABILITY_RCS_VIDEO_ONLY_CALL = (1 << 21); + + /** @hide*/ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "CAPABILITY_", flag = true, value = { + CAPABILITY_CHAT_STANDALONE, + CAPABILITY_CHAT_SESSION, + CAPABILITY_CHAT_SESSION_STORE_FORWARD, + CAPABILITY_FILE_TRANSFER, + CAPABILITY_FILE_TRANSFER_THUMBNAIL, + CAPABILITY_FILE_TRANSFER_STORE_FORWARD, + CAPABILITY_FILE_TRANSFER_HTTP, + CAPABILITY_FILE_TRANSFER_SMS, + CAPABILITY_IMAGE_SHARE, + CAPABILITY_VIDEO_SHARE_DURING_CS_CALL, + CAPABILITY_VIDEO_SHARE, + CAPABILITY_SOCIAL_PRESENCE, + CAPABILITY_DISCOVERY_VIA_PRESENCE, + CAPABILITY_IP_VOICE_CALL, + CAPABILITY_IP_VIDEO_CALL, + CAPABILITY_GEOLOCATION_PUSH, + CAPABILITY_GEOLOCATION_PUSH_SMS, + CAPABILITY_GEOLOCATION_PULL, + CAPABILITY_GEOLOCATION_PULL_FILE_TRANSFER, + CAPABILITY_RCS_VOICE_CALL, + CAPABILITY_RCS_VIDEO_CALL, + CAPABILITY_RCS_VIDEO_ONLY_CALL + }) + public @interface CapabilityFlag {} + + /** + * Builder to help construct {@link RcsContactUceCapability} instances. + */ + public static class Builder { + + private final RcsContactUceCapability mCapabilities; + + /** + * Create the Builder, which can be used to set UCE capabilities as well as custom + * capability extensions. + * @param contact The contact URI that the capabilities are attached to. + */ + public Builder(@NonNull Uri contact) { + mCapabilities = new RcsContactUceCapability(contact); + } + + /** + * Add a UCE capability bit-field as well as the associated URI that the framework should + * use for those services. This is mainly used for capabilities that may use a URI separate + * from the contact's URI, for example the URI to use for VT calls. + * @param type The capability to map to a service URI that is different from the contact's + * URI. + */ + public Builder add(@CapabilityFlag int type, @NonNull Uri serviceUri) { + mCapabilities.mCapabilities |= type; + // Put each of these capabilities into the map separately. + for (int shift = 0; shift < Integer.SIZE; shift++) { + int cap = type & (1 << shift); + if (cap != 0) { + mCapabilities.mServiceMap.put(cap, serviceUri); + // remove that capability from the field. + type &= ~cap; + } + if (type == 0) { + // no need to keep going, end early. + break; + } + } + return this; + } + + /** + * Add a UCE capability flag that this contact supports. + * @param type the capability that the contact supports. + */ + public Builder add(@CapabilityFlag int type) { + mCapabilities.mCapabilities |= type; + return this; + } + + /** + * Add a carrier specific service tag. + * @param extension A string containing a carrier specific service tag that is an extension + * of the {@link CapabilityFlag}s that are defined here. + */ + public Builder add(@NonNull String extension) { + mCapabilities.mExtensionTags.add(extension); + return this; + } + + /** + * @return the constructed instance. + */ + public RcsContactUceCapability build() { + return mCapabilities; + } + } + + private final Uri mContactUri; + private int mCapabilities; + private List<String> mExtensionTags = new ArrayList<>(); + private Map<Integer, Uri> mServiceMap = new HashMap<>(); + + /** + * Use {@link Builder} to build an instance of this interface. + * @param contact The URI associated with this capability information. + * @hide + */ + RcsContactUceCapability(@NonNull Uri contact) { + mContactUri = contact; + } + + private RcsContactUceCapability(Parcel in) { + mContactUri = in.readParcelable(Uri.class.getClassLoader()); + mCapabilities = in.readInt(); + in.readStringList(mExtensionTags); + // read mServiceMap as key,value pair + int mapSize = in.readInt(); + for (int i = 0; i < mapSize; i++) { + mServiceMap.put(in.readInt(), in.readParcelable(Uri.class.getClassLoader())); + } + } + + public static final Creator<RcsContactUceCapability> CREATOR = + new Creator<RcsContactUceCapability>() { + @Override + public RcsContactUceCapability createFromParcel(Parcel in) { + return new RcsContactUceCapability(in); + } + + @Override + public RcsContactUceCapability[] newArray(int size) { + return new RcsContactUceCapability[size]; + } + }; + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeParcelable(mContactUri, 0); + out.writeInt(mCapabilities); + out.writeStringList(mExtensionTags); + // write mServiceMap as key,value pairs + int mapSize = mServiceMap.keySet().size(); + out.writeInt(mapSize); + for (int key : mServiceMap.keySet()) { + out.writeInt(key); + out.writeParcelable(mServiceMap.get(key), 0); + } + } + + @Override + public int describeContents() { + return 0; + } + + /** + * Query for a capability + * @param type The capability flag to query. + * @return true if the capability flag specified is set, false otherwise. + */ + public boolean isCapable(@CapabilityFlag int type) { + return (mCapabilities & type) > 0; + } + + /** + * @return true if the extension service tag is set, false otherwise. + */ + public boolean isCapable(@NonNull String extensionTag) { + return mExtensionTags.contains(extensionTag); + } + + /** + * @return An immutable list containing all of the extension tags that have been set as capable. + * @throws UnsupportedOperationException if this list is modified. + */ + public @NonNull List<String> getCapableExtensionTags() { + return Collections.unmodifiableList(mExtensionTags); + } + + /** + * Retrieves the {@link Uri} associated with the capability being queried. + * <p> + * This will typically be the contact {@link Uri} available via {@link #getContactUri()} unless + * a different service {@link Uri} was associated with this capability using + * {@link Builder#add(int, Uri)}. + * + * @return a String containing the {@link Uri} associated with the service tag or + * {@code null} if this capability is not set as capable. + * @see #isCapable(int) + */ + public @Nullable Uri getServiceUri(@CapabilityFlag int type) { + Uri result = mServiceMap.getOrDefault(type, null); + // If the capability is capable, but does not have a service URI associated, use the default + // contact URI. + if (result == null) { + return isCapable(type) ? getContactUri() : null; + } + return result; + } + + /** + * @return the URI representing the contact associated with the capabilities. + */ + public @NonNull Uri getContactUri() { + return mContactUri; + } +} diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java new file mode 100644 index 000000000000..a6a7a84c2c65 --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java @@ -0,0 +1,276 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims; + +import android.Manifest; +import android.annotation.CallbackExecutor; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.RequiresPermission; +import android.content.Context; +import android.net.Uri; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.List; +import java.util.concurrent.Executor; + +/** + * Manages RCS User Capability Exchange for the subscription specified. + * + * @see ImsRcsManager#getUceAdapter() for information on creating an instance of this class. + * @hide + */ +public class RcsUceAdapter { + + /** + * An unknown error has caused the request to fail. + */ + public static final int ERROR_GENERIC_FAILURE = 1; + /** + * The carrier network does not have UCE support enabled for this subscriber. + */ + public static final int ERROR_NOT_ENABLED = 2; + /** + * The data network that the device is connected to does not support UCE currently (e.g. it is + * 1x only currently). + */ + public static final int ERROR_NOT_AVAILABLE = 3; + /** + * The network has responded with SIP 403 error and a reason "User not registered." + */ + public static final int ERROR_NOT_REGISTERED = 4; + /** + * The network has responded to this request with a SIP 403 error and reason "not authorized for + * presence" for this subscriber. + */ + public static final int ERROR_NOT_AUTHORIZED = 5; + /** + * The network has responded to this request with a SIP 403 error and no reason. + */ + public static final int ERROR_FORBIDDEN = 6; + /** + * The contact URI requested is not provisioned for VoLTE or it is not known as an IMS + * subscriber to the carrier network. + */ + public static final int ERROR_NOT_FOUND = 7; + /** + * The capabilities request contained too many URIs for the carrier network to handle. Retry + * with a lower number of contact numbers. The number varies per carrier. + */ + // TODO: Try to integrate this into the API so that the service will split based on carrier. + public static final int ERROR_REQUEST_TOO_LARGE = 8; + /** + * The network did not respond to the capabilities request before the request timed out. + */ + public static final int ERROR_REQUEST_TIMEOUT = 10; + /** + * The request failed due to the service having insufficient memory. + */ + public static final int ERROR_INSUFFICIENT_MEMORY = 11; + /** + * The network was lost while trying to complete the request. + */ + public static final int ERROR_LOST_NETWORK = 12; + /** + * The request has failed because the same request has already been added to the queue. + */ + public static final int ERROR_ALREADY_IN_QUEUE = 13; + + /**@hide*/ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "ERROR_", value = { + ERROR_GENERIC_FAILURE, + ERROR_NOT_ENABLED, + ERROR_NOT_AVAILABLE, + ERROR_NOT_REGISTERED, + ERROR_NOT_AUTHORIZED, + ERROR_FORBIDDEN, + ERROR_NOT_FOUND, + ERROR_REQUEST_TOO_LARGE, + ERROR_REQUEST_TIMEOUT, + ERROR_INSUFFICIENT_MEMORY, + ERROR_LOST_NETWORK, + ERROR_ALREADY_IN_QUEUE + }) + public @interface ErrorCode {} + + /** + * The last publish has resulted in a "200 OK" response or the device is using SIP OPTIONS for + * UCE. + */ + public static final int PUBLISH_STATE_200_OK = 1; + + /** + * The hasn't published its capabilities since boot or hasn't gotten any publish response yet. + */ + public static final int PUBLISH_STATE_NOT_PUBLISHED = 2; + + /** + * The device has tried to publish its capabilities, which has resulted in an error. This error + * is related to the fact that the device is not VoLTE provisioned. + */ + public static final int PUBLISH_STATE_VOLTE_PROVISION_ERROR = 3; + + /** + * The device has tried to publish its capabilities, which has resulted in an error. This error + * is related to the fact that the device is not RCS or UCE provisioned. + */ + public static final int PUBLISH_STATE_RCS_PROVISION_ERROR = 4; + + /** + * The last publish resulted in a "408 Request Timeout" response. + */ + public static final int PUBLISH_STATE_REQUEST_TIMEOUT = 5; + + /** + * The last publish resulted in another unknown error, such as SIP 503 - "Service Unavailable" + * or SIP 423 - "Interval too short". + * <p> + * Device shall retry with exponential back-off. + */ + public static final int PUBLISH_STATE_OTHER_ERROR = 6; + + /**@hide*/ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "PUBLISH_STATE_", value = { + PUBLISH_STATE_200_OK, + PUBLISH_STATE_NOT_PUBLISHED, + PUBLISH_STATE_VOLTE_PROVISION_ERROR, + PUBLISH_STATE_RCS_PROVISION_ERROR, + PUBLISH_STATE_REQUEST_TIMEOUT, + PUBLISH_STATE_OTHER_ERROR + }) + public @interface PublishState {} + + + /** + * Provides a one-time callback for the response to a UCE request. After this callback is called + * by the framework, the reference to this callback will be discarded on the service side. + * @see #requestCapabilities(Executor, List, CapabilitiesCallback) + */ + public static class CapabilitiesCallback { + + /** + * Notify this application that the pending capability request has returned successfully. + * @param contactCapabilities List of capabilities associated with each contact requested. + */ + public void onCapabilitiesReceived( + @NonNull List<RcsContactUceCapability> contactCapabilities) { + + } + + /** + * The pending request has resulted in an error and may need to be retried, depending on the + * error code. + * @param errorCode The reason for the framework being unable to process the request. + */ + public void onError(@ErrorCode int errorCode) { + + } + } + + private final int mSubId; + + /** + * Not to be instantiated directly, use + * {@link ImsRcsManager#createForSubscriptionId(Context, int)} and + * {@link ImsRcsManager#getUceAdapter()} to instantiate this manager class. + */ + RcsUceAdapter(int subId) { + mSubId = subId; + } + + /** + * Request the User Capability Exchange capabilities for one or more contacts. + * <p> + * Be sure to check the availability of this feature using + * {@link ImsRcsManager#isAvailable(int)} and ensuring + * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_OPTIONS_UCE} or + * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE} is enabled or else + * this operation will fail with {@link #ERROR_NOT_AVAILABLE} or {@link #ERROR_NOT_ENABLED}. + * + * @param executor The executor that will be used when the request is completed and the + * {@link CapabilitiesCallback} is called. + * @param contactNumbers A list of numbers that the capabilities are being requested for. + * @param c A one-time callback for when the request for capabilities completes or there is an + * error processing the request. + * @throws ImsException if the subscription associated with this instance of + * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not + * available. This can happen if the ImsService has crashed, for example, or if the subscription + * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes. + */ + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public void requestCapabilities(@CallbackExecutor Executor executor, + @NonNull List<Uri> contactNumbers, + @NonNull CapabilitiesCallback c) throws ImsException { + throw new UnsupportedOperationException("isUceSettingEnabled is not supported."); + } + + /** + * Gets the last publish result from the UCE service if the device is using an RCS presence + * server. + * @return The last publish result from the UCE service. If the device is using SIP OPTIONS, + * this method will return {@link #PUBLISH_STATE_200_OK} as well. + * @throws ImsException if the subscription associated with this instance of + * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not + * available. This can happen if the ImsService has crashed, for example, or if the subscription + * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes. + */ + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public @PublishState int getUcePublishState() throws ImsException { + throw new UnsupportedOperationException("getPublishState is not supported."); + } + + /** + * The user’s setting for whether or not Presence and User Capability Exchange (UCE) is enabled + * for the associated subscription. + * + * @return true if the user’s setting for UCE is enabled, false otherwise. If false, + * {@link ImsRcsManager#isCapable(int)} will return false for + * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_OPTIONS_UCE} and + * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE} + * @see #setUceSettingEnabled(boolean) + * @throws ImsException if the subscription associated with this instance of + * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not + * available. This can happen if the ImsService has crashed, for example, or if the subscription + * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes. + */ + @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public boolean isUceSettingEnabled() throws ImsException { + // TODO: add SubscriptionController column for this property. + throw new UnsupportedOperationException("isUceSettingEnabled is not supported."); + } + /** + * Change the user’s setting for whether or not UCE is enabled for the associated subscription. + * @param isEnabled the user's setting for whether or not they wish for Presence and User + * Capability Exchange to be enabled. If false, + * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_OPTIONS_UCE} and + * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE} capability will be + * disabled, depending on which type of UCE the carrier supports. + * @see #isUceSettingEnabled() + * @throws ImsException if the subscription associated with this instance of + * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not + * available. This can happen if the ImsService has crashed, for example, or if the subscription + * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes. + */ + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + public void setUceSettingEnabled(boolean isEnabled) throws ImsException { + // TODO: add SubscriptionController column for this property. + throw new UnsupportedOperationException("setUceSettingEnabled is not supported."); + } +} 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/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java index a637e16d0a48..5fae3eebbfc6 100644 --- a/telephony/java/android/telephony/ims/feature/RcsFeature.java +++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java @@ -16,8 +16,14 @@ package android.telephony.ims.feature; +import android.annotation.IntDef; import android.annotation.SystemApi; import android.telephony.ims.aidl.IImsRcsFeature; +import android.telephony.ims.stub.RcsPresenceExchangeImplBase; +import android.telephony.ims.stub.RcsSipOptionsImplBase; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; /** * Base implementation of the RcsFeature APIs. Any ImsService wishing to support RCS should extend @@ -32,18 +38,165 @@ public class RcsFeature extends ImsFeature { // Empty Default Implementation. }; + /** + * Contains the capabilities defined and supported by a {@link RcsFeature} in the + * form of a bitmask. The capabilities that are used in the RcsFeature are + * defined as: + * {@link RcsImsCapabilityFlag#CAPABILITY_TYPE_OPTIONS_UCE} + * {@link RcsImsCapabilityFlag#CAPABILITY_TYPE_PRESENCE_UCE} + * + * The enabled capabilities of this RcsFeature will be set by the framework + * using {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)}. + * After the capabilities have been set, the RcsFeature may then perform the necessary bring up + * of the capability and notify the capability status as true using + * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}. This will signal to the + * framework that the capability is available for usage. + * @hide + */ + public static class RcsImsCapabilities extends Capabilities { + /** @hide*/ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "CAPABILITY_TYPE_", flag = true, value = { + CAPABILITY_TYPE_OPTIONS_UCE, + CAPABILITY_TYPE_PRESENCE_UCE + }) + public @interface RcsImsCapabilityFlag {} - public RcsFeature() { - super(); + /** + * This carrier supports User Capability Exchange using SIP OPTIONS as defined by the + * framework. If set, the RcsFeature should support capability exchange using SIP OPTIONS. + * If not set, this RcsFeature should not service capability requests. + * @hide + */ + public static final int CAPABILITY_TYPE_OPTIONS_UCE = 1 << 0; + + /** + * This carrier supports User Capability Exchange using a presence server as defined by the + * framework. If set, the RcsFeature should support capability exchange using a presence + * server. If not set, this RcsFeature should not publish capabilities or service capability + * requests using presence. + * @hide + */ + public static final int CAPABILITY_TYPE_PRESENCE_UCE = 1 << 1; + + /**@hide*/ + public RcsImsCapabilities(@RcsImsCapabilityFlag int capabilities) { + + } + + /**@hide*/ + @Override + public void addCapabilities(@RcsImsCapabilityFlag int capabilities) { + + } + + /**@hide*/ + @Override + public void removeCapabilities(@RcsImsCapabilityFlag int capabilities) { + + } + + /**@hide*/ + @Override + public boolean isCapable(@RcsImsCapabilityFlag int capabilities) { + return false; + } + } + /** + * Query the current {@link RcsImsCapabilities} status set by the RcsFeature. If a capability is + * set, the {@link RcsFeature} has brought up the capability and is ready for framework + * requests. To change the status of the capabilities + * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)} should be called. + * @hide + */ + @Override + public final RcsImsCapabilities queryCapabilityStatus() { + throw new UnsupportedOperationException(); } /** - * {@inheritDoc} + * Notify the framework that the capabilities status has changed. If a capability is enabled, + * this signals to the framework that the capability has been initialized and is ready. + * Call {@link #queryCapabilityStatus()} to return the current capability status. + * @hide + */ + public final void notifyCapabilitiesStatusChanged(RcsImsCapabilities c) { + throw new UnsupportedOperationException(); + } + + /** + * Provides the RcsFeature with the ability to return the framework capability configuration set + * by the framework. When the framework calls + * {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)} to + * enable or disable capability A, this method should return the correct configuration for + * capability A afterwards (until it has changed). + * @hide + */ + public boolean queryCapabilityConfiguration( + @RcsImsCapabilities.RcsImsCapabilityFlag int capability) { + throw new UnsupportedOperationException(); + } + /** + * Called from the framework when the {@link RcsImsCapabilities} that have been configured for + * this {@link RcsFeature} has changed. + * <p> + * For each newly enabled capability flag, the corresponding capability should be brought up in + * the {@link RcsFeature} and registered on the network. For each newly disabled capability + * flag, the corresponding capability should be brought down, and deregistered. Once a new + * capability has been initialized and is ready for usage, the status of that capability should + * also be set to true using {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}. This + * will notify the framework that the capability is ready. + * <p> + * If for some reason one or more of these capabilities can not be enabled/disabled, + * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError(int, int, int)} should + * be called for each capability change that resulted in an error. + * @hide */ @Override public void changeEnabledCapabilities(CapabilityChangeRequest request, CapabilityCallbackProxy c) { - // Do nothing for base implementation. + throw new UnsupportedOperationException(); + } + + /** + * Retrieve the implementation of SIP OPTIONS for this {@link RcsFeature}. + * <p> + * Will only be requested by the framework if capability exchange via SIP OPTIONS is + * configured as capable during a + * {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)} + * operation and the RcsFeature sets the status of the capability to true using + * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}. + * + * @return An instance of {@link RcsSipOptionsImplBase} that implements SIP options exchange if + * it is supported by the device. + * @hide + */ + public RcsSipOptionsImplBase getOptionsExchangeImpl() { + // Base Implementation, override to implement functionality + return new RcsSipOptionsImplBase(); + } + + /** + * Retrieve the implementation of UCE presence for this {@link RcsFeature}. + * Will only be requested by the framework if presence exchang is configured as capable during + * a {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)} + * operation and the RcsFeature sets the status of the capability to true using + * {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}. + * + * @return An instance of {@link RcsPresenceExchangeImplBase} that implements presence + * exchange if it is supported by the device. + * @hide + */ + public RcsPresenceExchangeImplBase getPresenceExchangeImpl() { + // Base Implementation, override to implement functionality. + return new RcsPresenceExchangeImplBase(); + } + + /** + * Construct a new {@link RcsFeature} instance. + */ + public RcsFeature() { + super(); } /**{@inheritDoc}*/ diff --git a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java index 321bfff40652..3e135cc9f048 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) { @@ -342,6 +348,17 @@ public class ImsConfigImplBase { } /** + * The framework has received an RCS autoconfiguration XML file for provisioning. + * + * @param config The XML file to be read, if not compressed, it should be in ASCII/UTF8 format. + * @param isCompressed The XML file is compressed in gzip format and must be decompressed + * before being read. + * @hide + */ + public void notifyRcsAutoConfigurationReceived(byte[] config, boolean isCompressed) { + } + + /** * Sets the configuration value for this ImsService. * * @param item an integer key. @@ -387,4 +404,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/android/telephony/ims/stub/RcsCapabilityExchange.java b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchange.java new file mode 100644 index 000000000000..289fd4c8a134 --- /dev/null +++ b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchange.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims.stub; + +import android.annotation.IntDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Base class for different types of Capability exchange, presence using + * {@link RcsPresenceExchangeImplBase} and SIP OPTIONS exchange using {@link RcsSipOptionsImplBase}. + * + * @hide + */ +public class RcsCapabilityExchange { + + /** Service is unknown. */ + public static final int COMMAND_CODE_SERVICE_UNKNOWN = 0; + /** The command completed successfully. */ + public static final int COMMAND_CODE_SUCCESS = 1; + /** The command failed with an unknown error. */ + public static final int COMMAND_CODE_GENERIC_FAILURE = 2; + /** Invalid parameter(s). */ + public static final int COMMAND_CODE_INVALID_PARAM = 3; + /** Fetch error. */ + public static final int COMMAND_CODE_FETCH_ERROR = 4; + /** Request timed out. */ + public static final int COMMAND_CODE_REQUEST_TIMEOUT = 5; + /** Failure due to insufficient memory available. */ + public static final int COMMAND_CODE_INSUFFICIENT_MEMORY = 6; + /** Network connection is lost. */ + public static final int COMMAND_CODE_LOST_NETWORK_CONNECTION = 7; + /** Requested feature/resource is not supported. */ + public static final int COMMAND_CODE_NOT_SUPPORTED = 8; + /** Contact or resource is not found. */ + public static final int COMMAND_CODE_NOT_FOUND = 9; + /** Service is not available. */ + public static final int COMMAND_CODE_SERVICE_UNAVAILABLE = 10; + /** No Change in Capabilities */ + public static final int COMMAND_CODE_NO_CHANGE_IN_CAP = 11; + + /** @hide*/ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "COMMAND_CODE_", value = { + COMMAND_CODE_SERVICE_UNKNOWN, + COMMAND_CODE_SUCCESS, + COMMAND_CODE_GENERIC_FAILURE, + COMMAND_CODE_INVALID_PARAM, + COMMAND_CODE_FETCH_ERROR, + COMMAND_CODE_REQUEST_TIMEOUT, + COMMAND_CODE_INSUFFICIENT_MEMORY, + COMMAND_CODE_LOST_NETWORK_CONNECTION, + COMMAND_CODE_NOT_SUPPORTED, + COMMAND_CODE_NOT_FOUND, + COMMAND_CODE_SERVICE_UNAVAILABLE, + COMMAND_CODE_NO_CHANGE_IN_CAP + }) + public @interface CommandCode {} + + /** + * Provides the framework with an update as to whether or not a command completed successfully + * locally. This includes capabilities requests and updates from the network. If it does not + * complete successfully, then the framework may retry the command again later, depending on the + * error. If the command does complete successfully, the framework will then wait for network + * updates. + * + * @param code The result of the pending command. If {@link #COMMAND_CODE_SUCCESS}, further + * updates will be sent for this command using the associated operationToken. + * @param operationToken the token associated with the pending command. + */ + public final void onCommandUpdate(@CommandCode int code, int operationToken) { + throw new UnsupportedOperationException(); + } +} diff --git a/telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java b/telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java new file mode 100644 index 000000000000..44024703042d --- /dev/null +++ b/telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims.stub; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.net.Uri; +import android.telephony.ims.RcsContactUceCapability; +import android.util.Log; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.List; + +/** + * Base implementation for RCS User Capability Exchange using Presence. Any ImsService implementing + * this service must implement the stub methods {@link #requestCapabilities(List, int)} and + * {@link #updateCapabilities(RcsContactUceCapability, int)}. + * + * @hide + */ +public class RcsPresenceExchangeImplBase extends RcsCapabilityExchange { + + private static final String LOG_TAG = "RcsPresenceExchangeIB"; + + /** + * The request has resulted in any other 4xx/5xx/6xx that is not covered below. No retry will be + * attempted. + */ + public static final int RESPONSE_SUBSCRIBE_GENERIC_FAILURE = -1; + + /** + * The request has succeeded with a “200” message from the network. + */ + public static final int RESPONSE_SUCCESS = 0; + + /** + * The request has resulted in a “403” (User Not Registered) error from the network. Will retry + * capability polling with an exponential backoff. + */ + public static final int RESPONSE_NOT_REGISTERED = 1; + + /** + * The request has resulted in a “403” (not authorized (Requestor)) error from the network. No + * retry will be attempted. + */ + public static final int RESPONSE_NOT_AUTHORIZED_FOR_PRESENCE = 2; + + /** + * The request has resulted in a "403” (Forbidden) or other “403” error from the network and + * will be handled the same as “404” Not found. No retry will be attempted. + */ + public static final int RESPONSE_FORBIDDEN = 3; + + /** + * The request has resulted in a “404” (Not found) result from the network. No retry will be + * attempted. + */ + public static final int RESPONSE_NOT_FOUND = 4; + + /** + * The request has resulted in a “408” response. Retry after exponential backoff. + */ + public static final int RESPONSE_SIP_REQUEST_TIMEOUT = 5; + + /** + * The network has responded with a “413” (Too Large) response from the network. Capability + * request contains too many items and must be shrunk before the request will be accepted. + */ + public static final int RESPONSE_SUBSCRIBE_TOO_LARGE = 6; + + /** + * The request has resulted in a “423” response. Retry after exponential backoff. + */ + public static final int RESPONSE_SIP_INTERVAL_TOO_SHORT = 7; + + /** + * The request has resulted in a “503” response. Retry after exponential backoff. + */ + public static final int RESPONSE_SIP_SERVICE_UNAVAILABLE = 8; + + /** @hide*/ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "RESPONSE_", value = { + RESPONSE_SUBSCRIBE_GENERIC_FAILURE, + RESPONSE_SUCCESS, + RESPONSE_NOT_REGISTERED, + RESPONSE_NOT_AUTHORIZED_FOR_PRESENCE, + RESPONSE_FORBIDDEN, + RESPONSE_NOT_FOUND, + RESPONSE_SIP_REQUEST_TIMEOUT, + RESPONSE_SUBSCRIBE_TOO_LARGE, + RESPONSE_SIP_INTERVAL_TOO_SHORT, + RESPONSE_SIP_SERVICE_UNAVAILABLE + }) + public @interface PresenceResponseCode {} + + /** + * Provide the framework with a subsequent network response update to + * {@link #updateCapabilities(RcsContactUceCapability, int)} and + * {@link #requestCapabilities(List, int)} operations. + * @param code The SIP response code sent from the network for the operation token specified. + * @param reason The optional reason response from the network. If the network provided no + * reason with the code, the string should be empty. + * @param operationToken The token associated with the operation this service is providing a + * response for. + */ + public final void onNetworkResponse(@PresenceResponseCode int code, @NonNull String reason, + int operationToken) { + throw new UnsupportedOperationException(); + } + + /** + * Provides the framework with the requested contacts’ capabilities requested by the framework + * using {@link #requestCapabilities(List, int)} . + */ + public final void onCapabilityRequestResponse(@NonNull List<RcsContactUceCapability> infos, + int operationToken) { + throw new UnsupportedOperationException(); + } + + /** + * Trigger the framework to provide a capability update using + * {@link #updateCapabilities(RcsContactUceCapability, int)}. This is typically used when trying + * to generate an initial PUBLISH for a new subscription to the network. + * <p> + * The device will cache all presence publications after boot until this method is called once. + */ + public final void onNotifyUpdateCapabilites() { + throw new UnsupportedOperationException(); + } + + /** + * Notify the framework that the device’s capabilities have been unpublished from the network. + */ + public final void onUnpublish() { + throw new UnsupportedOperationException(); + } + + /** + * The user capabilities of one or multiple contacts have been requested. + * <p> + * This must be followed up with one call to {@link #onCommandUpdate(int, int)} with an update + * as to whether or not the command completed as well as subsequent network + * updates using {@link #onNetworkResponse(int, String, int)}. When the operation is completed, + * {@link #onCapabilityRequestResponse(List, int)} should be called with + * the presence information for the contacts specified. + * @param uris A {@link List} of the URIs that the framework is requesting the UCE capabilities + * for. + * @param operationToken The token associated with this operation. Updates to this request using + * {@link #onCommandUpdate(int, int)}, {@link #onNetworkResponse(int, String, int)}, and + * {@link #onCapabilityRequestResponse(List, int)} must use the same operation token + * in response. + */ + public void requestCapabilities(@NonNull List<Uri> uris, int operationToken) { + // Stub - to be implemented by service + Log.w(LOG_TAG, "requestCapabilities called with no implementation."); + onCommandUpdate(COMMAND_CODE_GENERIC_FAILURE, operationToken); + } + + /** + * The capabilities of this device have been updated and should be published + * to the network. The framework will expect one {@link #onCommandUpdate(int, int)} call to + * indicate whether or not this operation failed first as well as network response + * updates to this update using {@link #onNetworkResponse(int, String, int)}. + * @param capabilities The capabilities for this device. + * @param operationToken The token associated with this operation. Any subsequent + * {@link #onCommandUpdate(int, int)} or {@link #onNetworkResponse(int, String, int)} + * calls regarding this update must use the same token. + */ + public void updateCapabilities(@NonNull RcsContactUceCapability capabilities, + int operationToken) { + // Stub - to be implemented by service + Log.w(LOG_TAG, "updateCapabilities called with no implementation."); + onCommandUpdate(COMMAND_CODE_GENERIC_FAILURE, operationToken); + } +} diff --git a/telephony/java/android/telephony/ims/stub/RcsSipOptionsImplBase.java b/telephony/java/android/telephony/ims/stub/RcsSipOptionsImplBase.java new file mode 100644 index 000000000000..3343074ea52c --- /dev/null +++ b/telephony/java/android/telephony/ims/stub/RcsSipOptionsImplBase.java @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims.stub; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.net.Uri; +import android.telephony.ims.RcsContactUceCapability; +import android.util.Log; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Base implementation for RCS User Capability Exchange using SIP OPTIONS. + * + * @hide + */ +public class RcsSipOptionsImplBase extends RcsCapabilityExchange { + + private static final String LOG_TAG = "RcsSipOptionsImplBase"; + + /** + * Indicates a SIP response from the remote user other than 200, 480, 408, 404, or 604. + */ + public static final int RESPONSE_GENERIC_FAILURE = -1; + + /** + * Indicates that the remote user responded with a 200 OK response. + */ + public static final int RESPONSE_SUCCESS = 0; + + /** + * Indicates that the remote user responded with a 480 TEMPORARY UNAVAILABLE response. + */ + public static final int RESPONSE_TEMPORARILY_UNAVAILABLE = 1; + + /** + * Indicates that the remote user responded with a 408 REQUEST TIMEOUT response. + */ + public static final int RESPONSE_REQUEST_TIMEOUT = 2; + + /** + * Indicates that the remote user responded with a 404 NOT FOUND response. + */ + public static final int RESPONSE_NOT_FOUND = 3; + + /** + * Indicates that the remote user responded with a 604 DOES NOT EXIST ANYWHERE response. + */ + public static final int RESPONSE_DOES_NOT_EXIST_ANYWHERE = 4; + + /** @hide*/ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "RESPONSE_", value = { + RESPONSE_GENERIC_FAILURE, + RESPONSE_SUCCESS, + RESPONSE_TEMPORARILY_UNAVAILABLE, + RESPONSE_REQUEST_TIMEOUT, + RESPONSE_NOT_FOUND, + RESPONSE_DOES_NOT_EXIST_ANYWHERE + }) + public @interface SipResponseCode {} + + /** + * Send the response of a SIP OPTIONS capability exchange to the framework. If {@code code} is + * {@link #RESPONSE_SUCCESS}, info must be non-null. + * @param code The SIP response code that was sent by the network in response to the request + * sent by {@link #sendCapabilityRequest(Uri, RcsContactUceCapability, int)}. + * @param reason The optional SIP response reason sent by the network. If none was sent, this + * should be an empty string. + * @param info the contact's UCE capabilities associated with the capability request. + * @param operationToken The token associated with the original capability request, set by + * {@link #sendCapabilityRequest(Uri, RcsContactUceCapability, int)}. + */ + public final void onCapabilityRequestResponse(@SipResponseCode int code, @NonNull String reason, + @Nullable RcsContactUceCapability info, int operationToken) { + throw new UnsupportedOperationException(); + } + + /** + * Inform the framework of a query for this device's UCE capabilities. + * <p> + * The framework will respond via the + * {@link #respondToCapabilityRequest(String, RcsContactUceCapability, int)} or + * {@link #respondToCapabilityRequestWithError(Uri, int, String, int)} method. + * @param contactUri The URI associated with the remote contact that is requesting capabilities. + * @param remoteInfo The remote contact's capability information. + * @param operationToken An unique operation token that you have generated that will be returned + * by the framework in + * {@link #respondToCapabilityRequest(String, RcsContactUceCapability, int)}. + */ + public final void onRemoteCapabilityRequest(@NonNull Uri contactUri, + @NonNull RcsContactUceCapability remoteInfo, int operationToken) { + throw new UnsupportedOperationException(); + } + + /** + * Push one's own capabilities to a remote user via the SIP OPTIONS presence exchange mechanism + * in order to receive the capabilities of the remote user in response. + * <p> + * The implementer must call + * {@link #onCapabilityRequestResponse(int, String, RcsContactUceCapability, int)} to send the + * response of this query back to the framework. + * @param contactUri The URI of the remote user that we wish to get the capabilities of. + * @param capabilities The capabilities of this device to send to the remote user. + * @param operationToken A token generated by the framework that will be passed through + * {@link #onCapabilityRequestResponse(int, String, RcsContactUceCapability, int)} when this + * operation has succeeded. + */ + public void sendCapabilityRequest(@NonNull Uri contactUri, + @NonNull RcsContactUceCapability capabilities, int operationToken) { + // Stub - to be implemented by service + Log.w(LOG_TAG, "sendCapabilityRequest called with no implementation."); + onCommandUpdate(COMMAND_CODE_GENERIC_FAILURE, operationToken); + } + + /** + * Respond to a remote capability request from the contact specified with the capabilities of + * this device. + * <p> + * The framework will use the same token and uri as what was passed in to + * {@link #onRemoteCapabilityRequest(Uri, RcsContactUceCapability, int)}. + * @param contactUri The URI of the remote contact. + * @param ownCapabilities The capabilities of this device. + * @param operationToken The token generated by the framework that this service obtained when + * {@link #onRemoteCapabilityRequest(Uri, RcsContactUceCapability, int)} was called. + */ + public void respondToCapabilityRequest(@NonNull String contactUri, + @NonNull RcsContactUceCapability ownCapabilities, int operationToken) { + // Stub - to be implemented by service + Log.w(LOG_TAG, "respondToCapabilityRequest called with no implementation."); + onCommandUpdate(COMMAND_CODE_GENERIC_FAILURE, operationToken); + } + + /** + * Respond to a remote capability request from the contact specified with the specified error. + * <p> + * The framework will use the same token and uri as what was passed in to + * {@link #onRemoteCapabilityRequest(Uri, RcsContactUceCapability, int)}. + * @param contactUri A URI containing the remote contact. + * @param code The SIP response code to respond with. + * @param reason A non-null String containing the reason associated with the SIP code. + * @param operationToken The token provided by the framework when + * {@link #onRemoteCapabilityRequest(Uri, RcsContactUceCapability, int)} was called. + * + */ + public void respondToCapabilityRequestWithError(@NonNull Uri contactUri, + @SipResponseCode int code, @NonNull String reason, int operationToken) { + // Stub - to be implemented by service + Log.w(LOG_TAG, "respondToCapabiltyRequestWithError called with no implementation."); + onCommandUpdate(COMMAND_CODE_GENERIC_FAILURE, operationToken); + } +} diff --git a/test-base/Android.mk b/test-base/Android.mk deleted file mode 100644 index a9d30cf3131a..000000000000 --- a/test-base/Android.mk +++ /dev/null @@ -1,29 +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. -# - -LOCAL_PATH:= $(call my-dir) - -ifeq ($(HOST_OS),linux) -# Build the legacy-performance-test-hostdex library -# ================================================= -# This contains the android.test.PerformanceTestCase class only -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := src/android/test/PerformanceTestCase.java -LOCAL_MODULE := legacy-performance-test-hostdex - -include $(BUILD_HOST_DALVIK_STATIC_JAVA_LIBRARY) -endif # HOST_OS == linux diff --git a/tests/Codegen/runTest.sh b/tests/Codegen/runTest.sh index fe3adf9b2a70..bc1aae03e9a6 100755 --- a/tests/Codegen/runTest.sh +++ b/tests/Codegen/runTest.sh @@ -10,7 +10,7 @@ else return $? } - header_and_eval m -j16 codegen && \ + header_and_eval m -j16 codegen_cli && \ 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 && \ diff --git a/tests/Codegen/src/com/android/codegentest/SampleDataClass.java b/tests/Codegen/src/com/android/codegentest/SampleDataClass.java index 03127ec2814b..f69a092ed3f7 100644 --- a/tests/Codegen/src/com/android/codegentest/SampleDataClass.java +++ b/tests/Codegen/src/com/android/codegentest/SampleDataClass.java @@ -339,8 +339,8 @@ public final class SampleDataClass implements Parcelable { - // Code below generated by codegen v0.0.1. - // on Jul 17, 2019, 5:10:26 PM PDT + // Code below generated by codegen v1.0.0. + // on Jul 29, 2019, 2:50:21 PM PDT // // DO NOT MODIFY! // @@ -409,8 +409,8 @@ public final class SampleDataClass implements Parcelable { public @interface StateName {} @DataClass.Generated( - time = 1563408627046L, - codegenVersion = "0.0.1", + time = 1564437021513L, + codegenVersion = "1.0.0", 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)") diff --git a/tests/JobSchedulerPerfTests/Android.bp b/tests/JobSchedulerPerfTests/Android.bp index 22308076d4ff..c51b811f0735 100644 --- a/tests/JobSchedulerPerfTests/Android.bp +++ b/tests/JobSchedulerPerfTests/Android.bp @@ -19,6 +19,7 @@ android_test { "androidx.test.rules", "apct-perftests-utils", "services", + "jobscheduler-service", ], platform_apis: true, certificate: "platform", 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/codegen/Android.bp b/tools/codegen/Android.bp index 805b2968bca0..677bee2cce81 100644 --- a/tools/codegen/Android.bp +++ b/tools/codegen/Android.bp @@ -1,5 +1,5 @@ java_binary_host { - name: "codegen", + name: "codegen_cli", manifest: "manifest.txt", srcs: [ "src/**/*.kt", diff --git a/tools/codegen/src/com/android/codegen/SharedConstants.kt b/tools/codegen/src/com/android/codegen/SharedConstants.kt index 41641f6dab47..175eea6ef0d0 100644 --- a/tools/codegen/src/com/android/codegen/SharedConstants.kt +++ b/tools/codegen/src/com/android/codegen/SharedConstants.kt @@ -1,4 +1,4 @@ package com.android.codegen const val CODEGEN_NAME = "codegen" -const val CODEGEN_VERSION = "0.0.1"
\ No newline at end of file +const val CODEGEN_VERSION = "1.0.0"
\ No newline at end of file diff --git a/tools/dump-coverage/README.md b/tools/dump-coverage/README.md index 2bab4bc8c984..d1c10bc2e520 100644 --- a/tools/dump-coverage/README.md +++ b/tools/dump-coverage/README.md @@ -16,7 +16,7 @@ adb shell 'mkdir /data/data/com.android.deskclock/folder-to-use' Then we can run the command to dump the data: ``` -adb shell 'am attach-agent com.android.deskclock /system/lib/libdumpcoverage.so=dump:/data/data/com.android.deskclock/folder-to-use' +adb shell 'am attach-agent com.android.deskclock /system/lib/libdumpcoverage.so=dump:/data/data/com.android.deskclock/folder-to-use/coverage-file.ec' ``` We can also reset the coverage information with @@ -28,10 +28,10 @@ adb shell 'am attach-agent com.android.deskclock /system/lib/libdumpcoverage.so= then perform more actions, then dump the data again. To get the files, we can get ``` -adb pull /data/data/com.android.deskclock/folder-to-use ~/path-on-your-computer +adb pull /data/data/com.android.deskclock/folder-to-use/coverage-file.ec ~/path-on-your-computer ``` -And you should have timestamped `.exec` files on your machine under the folder `~/path-on-your-computer` +And you should have `coverage-file.ec` on your machine under the folder `~/path-on-your-computer` # Details diff --git a/tools/dump-coverage/dump_coverage.cc b/tools/dump-coverage/dump_coverage.cc index 3de1865b8018..0808e776f190 100644 --- a/tools/dump-coverage/dump_coverage.cc +++ b/tools/dump-coverage/dump_coverage.cc @@ -18,20 +18,10 @@ #include <jvmti.h> #include <string.h> -#include <atomic> -#include <ctime> #include <fstream> -#include <iomanip> -#include <iostream> -#include <istream> -#include <memory> -#include <sstream> -#include <string> -#include <vector> using std::get; using std::tuple; -using std::chrono::system_clock; namespace dump_coverage { @@ -87,35 +77,11 @@ static jbyteArray GetExecutionData(JNIEnv* env) { return java_result_array; } -// Gets the filename to write execution data to -// dirname: the directory in which to place the file -// outputs <dirname>/YYYY-MM-DD-HH-MM-SS.SSS.exec -static std::string GetFilename(const std::string& dirname) { - system_clock::time_point time_point = system_clock::now(); - auto seconds = std::chrono::time_point_cast<std::chrono::seconds>(time_point); - auto fractional_time = time_point - seconds; - auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(fractional_time); - - std::time_t time = system_clock::to_time_t(time_point); - auto tm = *std::gmtime(&time); - - std::ostringstream oss; - oss - << dirname - << "/" - << std::put_time(&tm, "%Y-%m-%d-%H-%M-%S.") - << std::setfill('0') << std::setw(3) << millis.count() - << ".ec"; - return oss.str(); -} - -// Writes the execution data to a file -// data, length: represent the data, as a sequence of bytes -// dirname: directory name to contain the file +// Writes the execution data to a file. +// data, length: represent the data, as a sequence of bytes. +// filename: file to write coverage data to. // returns JNI_ERR if there is an error in writing the file, otherwise JNI_OK. -static jint WriteFile(const char* data, int length, const std::string& dirname) { - auto filename = GetFilename(dirname); - +static jint WriteFile(const char* data, int length, const std::string& filename) { LOG(INFO) << "Writing file of length " << length << " to '" << filename << "'"; std::ofstream file(filename, std::ios::binary); @@ -136,11 +102,11 @@ static jint WriteFile(const char* data, int length, const std::string& dirname) return JNI_OK; } -// Grabs execution data and writes it to a file -// dirname: directory name to contain the file +// Grabs execution data and writes it to a file. +// filename: file to write coverage data to. // returns JNI_ERR if there is an error writing the file. // Will crash if the Agent isn't found or if any Java Exception occurs. -static jint Dump(const std::string& dirname) { +static jint Dump(const std::string& filename) { LOG(INFO) << "Dumping file"; JNIEnv* env = GetJNIEnv(); @@ -152,12 +118,12 @@ static jint Dump(const std::string& dirname) { int result_len = env->GetArrayLength(java_result_array); - return WriteFile((const char*) result_ptr, result_len, dirname); + return WriteFile((const char*) result_ptr, result_len, filename); } // Resets execution data, performing the equivalent of // Agent.getInstance().reset(); -// args: should be empty +// args: should be empty. // returns JNI_ERR if the arguments are invalid. // Will crash if the Agent isn't found or if any Java Exception occurs. static jint Reset(const std::string& args) { 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/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/src/android/processor/staledataclass/StaleDataclassProcessor.kt b/tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt index 9e51180509a8..e5ec17a1d18d 100644 --- a/tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt +++ b/tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt @@ -125,6 +125,7 @@ class StaleDataclassProcessor: AbstractProcessor() { val lastGenerated = annotationParams["time"] as Long val codegenVersion = annotationParams["codegenVersion"] as String + val codegenMajorVersion = codegenVersion.substringBefore(".") val sourceRelative = File(annotationParams["sourceFile"] as String) val lastGenInputSignatures = (annotationParams["inputSignatures"] as String).lines().toSet() @@ -151,7 +152,7 @@ class StaleDataclassProcessor: AbstractProcessor() { stale += Stale(clazz, source, lastGenerated) } - if (codegenVersion != CODEGEN_VERSION) { + if (codegenMajorVersion != CODEGEN_VERSION.substringBefore(".")) { stale += Stale(clazz, source, lastGenerated) } } diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index 89ed0800e31c..2f7400d001aa 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -671,6 +671,14 @@ public class WifiManager { public static final int IFACE_IP_MODE_LOCAL_ONLY = 2; /** + * Broadcast intent action indicating that the wifi network settings + * had been reset. + * @hide + */ + public static final String WIFI_NETWORK_SETTINGS_RESET_ACTION = + "android.net.wifi.action.NETWORK_SETTINGS_RESET"; + + /** * Broadcast intent action indicating that a connection to the supplicant has * been established (and it is now possible * to perform Wi-Fi operations) or the connection to the supplicant has been |