diff options
409 files changed, 10243 insertions, 6170 deletions
diff --git a/Android.bp b/Android.bp index 5796fb17046c..12bc90680dd5 100644 --- a/Android.bp +++ b/Android.bp @@ -653,6 +653,33 @@ gensrcs { output_extension: "srcjar", } +gensrcs { + name: "framework-cppstream-protos", + depfile: true, + + tools: [ + "aprotoc", + "protoc-gen-cppstream", + ], + + cmd: "mkdir -p $(genDir) " + + "&& $(location aprotoc) " + + " --plugin=$(location protoc-gen-cppstream) " + + " --dependency_out=$(depfile) " + + " --cppstream_out=$(genDir) " + + " -Iexternal/protobuf/src " + + " -I . " + + " $(in)", + + srcs: [ + ":ipconnectivity-proto-src", + "core/proto/**/*.proto", + "libs/incident/**/*.proto", + ], + + output_extension: "proto.h", +} + filegroup { name: "framework-annotations", srcs: [ @@ -1010,43 +1037,6 @@ aidl_interface { }, } -gensrcs { - name: "gen-platform-proto-constants", - depfile: true, - - tools: [ - "aprotoc", - "protoc-gen-cppstream", - ], - - srcs: [ - "core/proto/android/os/backtrace.proto", - "core/proto/android/os/batterytype.proto", - "core/proto/android/os/cpufreq.proto", - "core/proto/android/os/cpuinfo.proto", - "core/proto/android/os/data.proto", - "core/proto/android/os/kernelwake.proto", - "core/proto/android/os/pagetypeinfo.proto", - "core/proto/android/os/procrank.proto", - "core/proto/android/os/ps.proto", - "core/proto/android/os/system_properties.proto", - "core/proto/android/util/event_log_tags.proto", - "core/proto/android/util/log.proto", - ], - - // Append protoc-gen-cppstream tool's PATH otherwise aprotoc can't find the plugin tool - cmd: "mkdir -p $(genDir) " + - "&& $(location aprotoc) " + - " --plugin=$(location protoc-gen-cppstream) " + - " --dependency_out=$(depfile) " + - " --cppstream_out=$(genDir) " + - " -Iexternal/protobuf/src " + - " -I . " + - " $(in)", - - output_extension: "proto.h", -} - subdirs = [ "cmds/*", diff --git a/apct-tests/perftests/blobstore/Android.bp b/apct-tests/perftests/blobstore/Android.bp new file mode 100644 index 000000000000..be5072ce3d9d --- /dev/null +++ b/apct-tests/perftests/blobstore/Android.bp @@ -0,0 +1,28 @@ +// Copyright (C) 2020 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +android_test { + name: "BlobStorePerfTests", + srcs: ["src/**/*.java"], + static_libs: [ + "BlobStoreTestUtils", + "androidx.test.rules", + "androidx.annotation_annotation", + "apct-perftests-utils", + "ub-uiautomator", + ], + platform_apis: true, + test_suites: ["device-tests"], + certificate: "platform", +}
\ No newline at end of file diff --git a/apct-tests/perftests/blobstore/AndroidManifest.xml b/apct-tests/perftests/blobstore/AndroidManifest.xml new file mode 100644 index 000000000000..21d0726927af --- /dev/null +++ b/apct-tests/perftests/blobstore/AndroidManifest.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.perftests.blob"> + + <application> + <uses-library android:name="android.test.runner" /> + </application> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.perftests.blob"/> + +</manifest>
\ No newline at end of file diff --git a/apct-tests/perftests/blobstore/AndroidTest.xml b/apct-tests/perftests/blobstore/AndroidTest.xml new file mode 100644 index 000000000000..19456c6d81d7 --- /dev/null +++ b/apct-tests/perftests/blobstore/AndroidTest.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<configuration description="Runs BlobStorePerfTests metric instrumentation."> + <option name="test-suite-tag" value="apct" /> + <option name="test-suite-tag" value="apct-metric-instrumentation" /> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true" /> + <option name="test-file-name" value="BlobStorePerfTests.apk" /> + </target_preparer> + + <test class="com.android.tradefed.testtype.AndroidJUnitTest" > + <option name="package" value="com.android.perftests.blob" /> + <option name="hidden-api-checks" value="false"/> + </test> +</configuration>
\ No newline at end of file diff --git a/apct-tests/perftests/blobstore/src/com/android/perftests/blob/AtraceUtils.java b/apct-tests/perftests/blobstore/src/com/android/perftests/blob/AtraceUtils.java new file mode 100644 index 000000000000..0208dab33746 --- /dev/null +++ b/apct-tests/perftests/blobstore/src/com/android/perftests/blob/AtraceUtils.java @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2020 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.perftests.blob; + +import android.app.Instrumentation; +import android.app.UiAutomation; +import android.os.ParcelFileDescriptor; +import android.perftests.utils.TraceMarkParser; +import android.perftests.utils.TraceMarkParser.TraceMarkSlice; +import android.support.test.uiautomator.UiDevice; +import android.util.Log; + +import androidx.test.platform.app.InstrumentationRegistry; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.List; +import java.util.function.BiConsumer; + +// Copy of com.android.frameworks.perftests.am.util.AtraceUtils. TODO: avoid this duplication. +public class AtraceUtils { + private static final String TAG = "AtraceUtils"; + private static final boolean VERBOSE = true; + + private static final String ATRACE_START = "atrace --async_start -b %d -c %s"; + private static final String ATRACE_DUMP = "atrace --async_dump"; + private static final String ATRACE_STOP = "atrace --async_stop"; + private static final int DEFAULT_ATRACE_BUF_SIZE = 1024; + + private UiAutomation mAutomation; + private static AtraceUtils sUtils = null; + private boolean mStarted = false; + + private AtraceUtils(Instrumentation instrumentation) { + mAutomation = instrumentation.getUiAutomation(); + } + + public static AtraceUtils getInstance(Instrumentation instrumentation) { + if (sUtils == null) { + sUtils = new AtraceUtils(instrumentation); + } + return sUtils; + } + + /** + * @param categories The list of the categories to trace, separated with space. + */ + public void startTrace(String categories) { + synchronized (this) { + if (mStarted) { + throw new IllegalStateException("atrace already started"); + } + runShellCommand(String.format( + ATRACE_START, DEFAULT_ATRACE_BUF_SIZE, categories)); + mStarted = true; + } + } + + public void stopTrace() { + synchronized (this) { + mStarted = false; + runShellCommand(ATRACE_STOP); + } + } + + private String runShellCommand(String cmd) { + try { + return UiDevice.getInstance( + InstrumentationRegistry.getInstrumentation()).executeShellCommand(cmd); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * @param parser The function that can accept the buffer of atrace dump and parse it. + * @param handler The parse result handler + */ + public void performDump(TraceMarkParser parser, + BiConsumer<String, List<TraceMarkSlice>> handler) { + parser.reset(); + try { + if (VERBOSE) { + Log.i(TAG, "Collecting atrace dump..."); + } + writeDataToBuf(mAutomation.executeShellCommand(ATRACE_DUMP), parser); + } catch (IOException e) { + Log.e(TAG, "Error in reading dump", e); + } + parser.forAllSlices(handler); + } + + // The given file descriptor here will be closed by this function + private void writeDataToBuf(ParcelFileDescriptor pfDescriptor, + TraceMarkParser parser) throws IOException { + InputStream inputStream = new ParcelFileDescriptor.AutoCloseInputStream(pfDescriptor); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) { + String line; + while ((line = reader.readLine()) != null) { + parser.visit(line); + } + } + } +} diff --git a/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java b/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java new file mode 100644 index 000000000000..8e0ea9888e4c --- /dev/null +++ b/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java @@ -0,0 +1,146 @@ +/* + * Copyright 2020 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.perftests.blob; + +import android.app.blob.BlobStoreManager; +import android.content.Context; +import android.perftests.utils.ManualBenchmarkState; +import android.perftests.utils.PerfManualStatusReporter; +import android.perftests.utils.TraceMarkParser; +import android.perftests.utils.TraceMarkParser.TraceMarkSlice; +import android.support.test.uiautomator.UiDevice; + +import androidx.test.filters.LargeTest; +import androidx.test.platform.app.InstrumentationRegistry; + +import com.android.utils.blob.DummyBlobData; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +@LargeTest +@RunWith(Parameterized.class) +public class BlobStorePerfTests { + // From frameworks/native/cmds/atrace/atrace.cpp + private static final String ATRACE_CATEGORY_SYSTEM_SERVER = "ss"; + // From f/b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java + private static final String ATRACE_COMPUTE_DIGEST_PREFIX = "computeBlobDigest-"; + + private Context mContext; + private BlobStoreManager mBlobStoreManager; + private AtraceUtils mAtraceUtils; + private ManualBenchmarkState mState; + + @Rule + public PerfManualStatusReporter mPerfManualStatusReporter = new PerfManualStatusReporter(); + + @Parameterized.Parameter(0) + public int fileSizeInMb; + + @Parameterized.Parameters(name = "{0}MB") + public static Collection<Object[]> getParameters() { + return Arrays.asList(new Object[][] { + { 25 }, + { 50 }, + { 100 }, + { 200 }, + }); + } + + @Before + public void setUp() { + mContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + mBlobStoreManager = (BlobStoreManager) mContext.getSystemService( + Context.BLOB_STORE_SERVICE); + mAtraceUtils = AtraceUtils.getInstance(InstrumentationRegistry.getInstrumentation()); + mState = mPerfManualStatusReporter.getBenchmarkState(); + } + + @After + public void tearDown() { + // TODO: Add a blob_store shell command to trigger idle maintenance to avoid hardcoding + // job id like this. + // From BlobStoreConfig.IDLE_JOB_ID = 191934935. + runShellCommand("cmd jobscheduler run -f android 191934935"); + } + + @Test + public void testComputeDigest() throws Exception { + mAtraceUtils.startTrace(ATRACE_CATEGORY_SYSTEM_SERVER); + try { + final List<Long> durations = new ArrayList<>(); + final DummyBlobData blobData = prepareDataBlob(fileSizeInMb); + final TraceMarkParser parser = new TraceMarkParser( + line -> line.name.startsWith(ATRACE_COMPUTE_DIGEST_PREFIX)); + while (mState.keepRunning(durations)) { + commitBlob(blobData); + + durations.clear(); + collectDigestDurationsFromTrace(parser, durations); + // TODO: get and delete blobId before next iteration. + } + } finally { + mAtraceUtils.stopTrace(); + } + } + + private void collectDigestDurationsFromTrace(TraceMarkParser parser, List<Long> durations) { + mAtraceUtils.performDump(parser, (key, slices) -> { + for (TraceMarkSlice slice : slices) { + durations.add(TimeUnit.MICROSECONDS.toNanos(slice.getDurationInMicroseconds())); + } + }); + } + + private DummyBlobData prepareDataBlob(int fileSizeInMb) throws Exception { + final DummyBlobData blobData = new DummyBlobData(mContext, + fileSizeInMb * 1024 * 1024 /* bytes */); + blobData.prepare(); + return blobData; + } + + private void commitBlob(DummyBlobData blobData) throws Exception { + final long sessionId = mBlobStoreManager.createSession(blobData.getBlobHandle()); + try (BlobStoreManager.Session session = mBlobStoreManager.openSession(sessionId)) { + blobData.writeToSession(session); + final CompletableFuture<Integer> callback = new CompletableFuture<>(); + session.commit(mContext.getMainExecutor(), callback::complete); + // Ignore commit callback result. + callback.get(); + } + } + + private String runShellCommand(String cmd) { + try { + return UiDevice.getInstance( + InstrumentationRegistry.getInstrumentation()).executeShellCommand(cmd); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/apex/blobstore/TEST_MAPPING b/apex/blobstore/TEST_MAPPING index cfe19a530b27..25a15371ae47 100644 --- a/apex/blobstore/TEST_MAPPING +++ b/apex/blobstore/TEST_MAPPING @@ -4,7 +4,7 @@ "name": "CtsBlobStoreTestCases" }, { - "name": "FrameworksServicesTests", + "name": "FrameworksMockingServicesTests", "options": [ { "include-filter": "com.android.server.blob" diff --git a/apex/blobstore/framework/java/android/app/blob/BlobHandle.java b/apex/blobstore/framework/java/android/app/blob/BlobHandle.java index f110b36c7e90..d339afac5c77 100644 --- a/apex/blobstore/framework/java/android/app/blob/BlobHandle.java +++ b/apex/blobstore/framework/java/android/app/blob/BlobHandle.java @@ -257,6 +257,11 @@ public final class BlobHandle implements Parcelable { return Base64.encodeToString(digest, Base64.NO_WRAP); } + /** @hide */ + public boolean isExpired() { + return expiryTimeMillis != 0 && expiryTimeMillis < System.currentTimeMillis(); + } + public static final @NonNull Creator<BlobHandle> CREATOR = new Creator<BlobHandle>() { @Override public @NonNull BlobHandle createFromParcel(@NonNull Parcel source) { diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java index aba3e8cadfa3..c12e0ec8aec9 100644 --- a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java +++ b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java @@ -64,9 +64,9 @@ class BlobMetadata { private final Context mContext; - public final long blobId; - public final BlobHandle blobHandle; - public final int userId; + private final long mBlobId; + private final BlobHandle mBlobHandle; + private final int mUserId; @GuardedBy("mMetadataLock") private final ArraySet<Committer> mCommitters = new ArraySet<>(); @@ -90,9 +90,21 @@ class BlobMetadata { BlobMetadata(Context context, long blobId, BlobHandle blobHandle, int userId) { mContext = context; - this.blobId = blobId; - this.blobHandle = blobHandle; - this.userId = userId; + this.mBlobId = blobId; + this.mBlobHandle = blobHandle; + this.mUserId = userId; + } + + long getBlobId() { + return mBlobId; + } + + BlobHandle getBlobHandle() { + return mBlobHandle; + } + + int getUserId() { + return mUserId; } void addCommitter(@NonNull Committer committer) { @@ -159,7 +171,7 @@ class BlobMetadata { boolean hasLeases() { synchronized (mMetadataLock) { - return mLeasees.isEmpty(); + return !mLeasees.isEmpty(); } } @@ -196,7 +208,7 @@ class BlobMetadata { File getBlobFile() { if (mBlobFile == null) { - mBlobFile = BlobStoreConfig.getBlobFile(blobId); + mBlobFile = BlobStoreConfig.getBlobFile(mBlobId); } return mBlobFile; } @@ -244,7 +256,7 @@ class BlobMetadata { void dump(IndentingPrintWriter fout, DumpArgs dumpArgs) { fout.println("blobHandle:"); fout.increaseIndent(); - blobHandle.dump(fout, dumpArgs.shouldDumpFull()); + mBlobHandle.dump(fout, dumpArgs.shouldDumpFull()); fout.decreaseIndent(); fout.println("Committers:"); @@ -274,11 +286,11 @@ class BlobMetadata { void writeToXml(XmlSerializer out) throws IOException { synchronized (mMetadataLock) { - XmlUtils.writeLongAttribute(out, ATTR_ID, blobId); - XmlUtils.writeIntAttribute(out, ATTR_USER_ID, userId); + XmlUtils.writeLongAttribute(out, ATTR_ID, mBlobId); + XmlUtils.writeIntAttribute(out, ATTR_USER_ID, mUserId); out.startTag(null, TAG_BLOB_HANDLE); - blobHandle.writeToXml(out); + mBlobHandle.writeToXml(out); out.endTag(null, TAG_BLOB_HANDLE); for (int i = 0, count = mCommitters.size(); i < count; ++i) { diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java index eb414b0f11a6..ba2e559afdab 100644 --- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java +++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java @@ -18,12 +18,15 @@ package com.android.server.blob; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Environment; +import android.util.Log; import android.util.Slog; import java.io.File; +import java.util.concurrent.TimeUnit; class BlobStoreConfig { public static final String TAG = "BlobStore"; + public static final boolean LOGV = Log.isLoggable(TAG, Log.VERBOSE); public static final int CURRENT_XML_VERSION = 1; @@ -32,6 +35,20 @@ class BlobStoreConfig { private static final String SESSIONS_INDEX_FILE_NAME = "sessions_index.xml"; private static final String BLOBS_INDEX_FILE_NAME = "blobs_index.xml"; + /** + * Job Id for idle maintenance job ({@link BlobStoreIdleJobService}). + */ + public static final int IDLE_JOB_ID = 0xB70B1D7; // 191934935L + /** + * Max time period (in millis) between each idle maintenance job run. + */ + public static final long IDLE_JOB_PERIOD_MILLIS = TimeUnit.DAYS.toMillis(1); + + /** + * Timeout in millis after which sessions with no updates will be deleted. + */ + public static final long SESSION_EXPIRY_TIMEOUT_MILLIS = TimeUnit.DAYS.toMillis(7); + @Nullable public static File prepareBlobFile(long sessionId) { final File blobsDir = prepareBlobsDir(); diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreIdleJobService.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreIdleJobService.java new file mode 100644 index 000000000000..460e776b9ff6 --- /dev/null +++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreIdleJobService.java @@ -0,0 +1,70 @@ +/* + * Copyright 2020 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.blob; + +import static com.android.server.blob.BlobStoreConfig.IDLE_JOB_ID; +import static com.android.server.blob.BlobStoreConfig.IDLE_JOB_PERIOD_MILLIS; +import static com.android.server.blob.BlobStoreConfig.LOGV; +import static com.android.server.blob.BlobStoreConfig.TAG; + +import android.app.job.JobInfo; +import android.app.job.JobParameters; +import android.app.job.JobScheduler; +import android.app.job.JobService; +import android.content.ComponentName; +import android.content.Context; +import android.os.AsyncTask; +import android.util.Slog; + +import com.android.server.LocalServices; + +/** + * Maintenance job to clean up stale sessions and blobs. + */ +public class BlobStoreIdleJobService extends JobService { + @Override + public boolean onStartJob(final JobParameters params) { + AsyncTask.execute(() -> { + final BlobStoreManagerInternal blobStoreManagerInternal = LocalServices.getService( + BlobStoreManagerInternal.class); + blobStoreManagerInternal.onIdleMaintenance(); + jobFinished(params, false); + }); + return false; + } + + @Override + public boolean onStopJob(final JobParameters params) { + Slog.d(TAG, "Idle maintenance job is stopped; id=" + params.getJobId() + + ", reason=" + JobParameters.getReasonCodeDescription(params.getStopReason())); + return false; + } + + static void schedule(Context context) { + final JobScheduler jobScheduler = (JobScheduler) context.getSystemService( + Context.JOB_SCHEDULER_SERVICE); + final JobInfo job = new JobInfo.Builder(IDLE_JOB_ID, + new ComponentName(context, BlobStoreIdleJobService.class)) + .setRequiresDeviceIdle(true) + .setRequiresCharging(true) + .setPeriodic(IDLE_JOB_PERIOD_MILLIS) + .build(); + jobScheduler.schedule(job); + if (LOGV) { + Slog.v(TAG, "Scheduling the idle maintenance job"); + } + } +} diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerInternal.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerInternal.java new file mode 100644 index 000000000000..5358245f517f --- /dev/null +++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerInternal.java @@ -0,0 +1,28 @@ +/* + * Copyright 2020 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.blob; + +/** + * BlobStoreManager local system service interface. + * + * Only for use within the system server. + */ +public abstract class BlobStoreManagerInternal { + /** + * Triggered from idle maintenance job to cleanup stale blobs and sessions. + */ + public abstract void onIdleMaintenance(); +} diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java index 13f095e5a503..0ba34cab6560 100644 --- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java +++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java @@ -28,6 +28,8 @@ import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES; import static android.os.UserHandle.USER_NULL; import static com.android.server.blob.BlobStoreConfig.CURRENT_XML_VERSION; +import static com.android.server.blob.BlobStoreConfig.LOGV; +import static com.android.server.blob.BlobStoreConfig.SESSION_EXPIRY_TIMEOUT_MILLIS; import static com.android.server.blob.BlobStoreConfig.TAG; import static com.android.server.blob.BlobStoreSession.STATE_ABANDONED; import static com.android.server.blob.BlobStoreSession.STATE_COMMITTED; @@ -61,6 +63,7 @@ import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManagerInternal; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.AtomicFile; import android.util.ExceptionUtils; import android.util.LongSparseArray; @@ -94,8 +97,10 @@ import java.io.IOException; import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Objects; +import java.util.Set; /** * Service responsible for maintaining and facilitating access to data blobs published by apps. @@ -115,6 +120,10 @@ public class BlobStoreManagerService extends SystemService { @GuardedBy("mBlobsLock") private final SparseArray<ArrayMap<BlobHandle, BlobMetadata>> mBlobsMap = new SparseArray<>(); + // Contains all ids that are currently in use. + @GuardedBy("mBlobsLock") + private final ArraySet<Long> mKnownBlobIds = new ArraySet<>(); + private final Context mContext; private final Handler mHandler; private final Injector mInjector; @@ -151,6 +160,7 @@ public class BlobStoreManagerService extends SystemService { @Override public void onStart() { publishBinderService(Context.BLOB_STORE_SERVICE, new Stub()); + LocalServices.addService(BlobStoreManagerInternal.class, new LocalService()); mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class); registerReceivers(); @@ -164,6 +174,8 @@ public class BlobStoreManagerService extends SystemService { readBlobSessionsLocked(allPackages); readBlobsInfoLocked(allPackages); } + } else if (phase == PHASE_BOOT_COMPLETED) { + BlobStoreIdleJobService.schedule(mContext); } } @@ -215,6 +227,40 @@ public class BlobStoreManagerService extends SystemService { } } + @VisibleForTesting + void addKnownIdsForTest(long... knownIds) { + synchronized (mBlobsLock) { + for (long id : knownIds) { + mKnownBlobIds.add(id); + } + } + } + + @VisibleForTesting + Set<Long> getKnownIdsForTest() { + synchronized (mBlobsLock) { + return mKnownBlobIds; + } + } + + @GuardedBy("mBlobsLock") + private void addSessionForUserLocked(BlobStoreSession session, int userId) { + getUserSessionsLocked(userId).put(session.getSessionId(), session); + mKnownBlobIds.add(session.getSessionId()); + } + + @GuardedBy("mBlobsLock") + private void addBlobForUserLocked(BlobMetadata blobMetadata, int userId) { + addBlobForUserLocked(blobMetadata, getUserBlobsLocked(userId)); + } + + @GuardedBy("mBlobsLock") + private void addBlobForUserLocked(BlobMetadata blobMetadata, + ArrayMap<BlobHandle, BlobMetadata> userBlobs) { + userBlobs.put(blobMetadata.getBlobHandle(), blobMetadata); + mKnownBlobIds.add(blobMetadata.getBlobId()); + } + private long createSessionInternal(BlobHandle blobHandle, int callingUid, String callingPackage) { synchronized (mBlobsLock) { @@ -223,7 +269,11 @@ public class BlobStoreManagerService extends SystemService { final BlobStoreSession session = new BlobStoreSession(mContext, sessionId, blobHandle, callingUid, callingPackage, mSessionStateChangeListener); - getUserSessionsLocked(UserHandle.getUserId(callingUid)).put(sessionId, session); + addSessionForUserLocked(session, UserHandle.getUserId(callingUid)); + if (LOGV) { + Slog.v(TAG, "Created session for " + blobHandle + + "; callingUid=" + callingUid + ", callingPackage=" + callingPackage); + } writeBlobSessionsAsync(); return sessionId; } @@ -251,7 +301,10 @@ public class BlobStoreManagerService extends SystemService { callingUid, callingPackage); session.open(); session.abandon(); - + if (LOGV) { + Slog.v(TAG, "Deleted session with id " + sessionId + + "; callingUid=" + callingUid + ", callingPackage=" + callingPackage); + } writeBlobSessionsAsync(); } } @@ -286,6 +339,10 @@ public class BlobStoreManagerService extends SystemService { } blobMetadata.addLeasee(callingPackage, callingUid, descriptionResId, leaseExpiryTimeMillis); + if (LOGV) { + Slog.v(TAG, "Acquired lease on " + blobHandle + + "; callingUid=" + callingUid + ", callingPackage=" + callingPackage); + } writeBlobsInfoAsync(); } } @@ -301,6 +358,10 @@ public class BlobStoreManagerService extends SystemService { + "; callingUid=" + callingUid + ", callingPackage=" + callingPackage); } blobMetadata.removeLeasee(callingPackage, callingUid); + if (LOGV) { + Slog.v(TAG, "Released lease on " + blobHandle + + "; callingUid=" + callingUid + ", callingPackage=" + callingPackage); + } writeBlobsInfoAsync(); } } @@ -329,6 +390,10 @@ public class BlobStoreManagerService extends SystemService { session.getSessionFile().delete(); getUserSessionsLocked(UserHandle.getUserId(session.getOwnerUid())) .remove(session.getSessionId()); + mKnownBlobIds.remove(session.getSessionId()); + if (LOGV) { + Slog.v(TAG, "Session is invalid; deleted " + session); + } break; case STATE_COMMITTED: session.verifyBlobData(); @@ -340,7 +405,7 @@ public class BlobStoreManagerService extends SystemService { if (blob == null) { blob = new BlobMetadata(mContext, session.getSessionId(), session.getBlobHandle(), userId); - userBlobs.put(session.getBlobHandle(), blob); + addBlobForUserLocked(blob, userBlobs); } final Committer newCommitter = new Committer(session.getOwnerPackageName(), session.getOwnerUid(), session.getBlobAccessMode()); @@ -355,6 +420,9 @@ public class BlobStoreManagerService extends SystemService { } getUserSessionsLocked(UserHandle.getUserId(session.getOwnerUid())) .remove(session.getSessionId()); + if (LOGV) { + Slog.v(TAG, "Successfully committed session " + session); + } break; default: Slog.wtf(TAG, "Invalid session state: " @@ -397,6 +465,9 @@ public class BlobStoreManagerService extends SystemService { out.endTag(null, TAG_SESSIONS); out.endDocument(); sessionsIndexFile.finishWrite(fos); + if (LOGV) { + Slog.v(TAG, "Finished persisting sessions data"); + } } catch (Exception e) { sessionsIndexFile.failWrite(fos); Slog.wtf(TAG, "Error writing sessions data", e); @@ -437,8 +508,8 @@ public class BlobStoreManagerService extends SystemService { if (userPackages != null && session.getOwnerPackageName().equals( userPackages.get(session.getOwnerUid()))) { - getUserSessionsLocked(UserHandle.getUserId(session.getOwnerUid())).put( - session.getSessionId(), session); + addSessionForUserLocked(session, + UserHandle.getUserId(session.getOwnerUid())); } else { // Unknown package or the session data does not belong to this package. session.getSessionFile().delete(); @@ -446,6 +517,9 @@ public class BlobStoreManagerService extends SystemService { mCurrentMaxSessionId = Math.max(mCurrentMaxSessionId, session.getSessionId()); } } + if (LOGV) { + Slog.v(TAG, "Finished reading sessions data"); + } } catch (Exception e) { Slog.wtf(TAG, "Error reading sessions data", e); } @@ -479,6 +553,9 @@ public class BlobStoreManagerService extends SystemService { out.endTag(null, TAG_BLOBS); out.endDocument(); blobsIndexFile.finishWrite(fos); + if (LOGV) { + Slog.v(TAG, "Finished persisting blobs data"); + } } catch (Exception e) { blobsIndexFile.failWrite(fos); Slog.wtf(TAG, "Error writing blobs data", e); @@ -510,18 +587,21 @@ public class BlobStoreManagerService extends SystemService { if (TAG_BLOB.equals(in.getName())) { final BlobMetadata blobMetadata = BlobMetadata.createFromXml(mContext, in); - final SparseArray<String> userPackages = allPackages.get(blobMetadata.userId); + final SparseArray<String> userPackages = allPackages.get( + blobMetadata.getUserId()); if (userPackages == null) { blobMetadata.getBlobFile().delete(); } else { - getUserBlobsLocked(blobMetadata.userId).put( - blobMetadata.blobHandle, blobMetadata); + addBlobForUserLocked(blobMetadata, blobMetadata.getUserId()); blobMetadata.removeInvalidCommitters(userPackages); blobMetadata.removeInvalidLeasees(userPackages); } - mCurrentMaxSessionId = Math.max(mCurrentMaxSessionId, blobMetadata.blobId); + mCurrentMaxSessionId = Math.max(mCurrentMaxSessionId, blobMetadata.getBlobId()); } } + if (LOGV) { + Slog.v(TAG, "Finished reading blobs data"); + } } catch (Exception e) { Slog.wtf(TAG, "Error reading blobs data", e); } @@ -614,6 +694,7 @@ public class BlobStoreManagerService extends SystemService { if (session.getOwnerUid() == uid && session.getOwnerPackageName().equals(packageName)) { session.getSessionFile().delete(); + mKnownBlobIds.remove(session.getSessionId()); indicesToRemove.add(i); } } @@ -633,6 +714,7 @@ public class BlobStoreManagerService extends SystemService { // Delete the blob if it doesn't have any active leases. if (!blobMetadata.hasLeases()) { blobMetadata.getBlobFile().delete(); + mKnownBlobIds.remove(blobMetadata.getBlobId()); indicesToRemove.add(i); } } @@ -640,6 +722,10 @@ public class BlobStoreManagerService extends SystemService { userBlobs.removeAt(indicesToRemove.get(i)); } writeBlobsInfoAsync(); + if (LOGV) { + Slog.v(TAG, "Removed blobs data associated with pkg=" + + packageName + ", uid=" + uid); + } } } @@ -651,6 +737,7 @@ public class BlobStoreManagerService extends SystemService { for (int i = 0, count = userSessions.size(); i < count; ++i) { final BlobStoreSession session = userSessions.valueAt(i); session.getSessionFile().delete(); + mKnownBlobIds.remove(session.getSessionId()); } } @@ -660,9 +747,105 @@ public class BlobStoreManagerService extends SystemService { for (int i = 0, count = userBlobs.size(); i < count; ++i) { final BlobMetadata blobMetadata = userBlobs.valueAt(i); blobMetadata.getBlobFile().delete(); + mKnownBlobIds.remove(blobMetadata.getBlobId()); + } + } + if (LOGV) { + Slog.v(TAG, "Removed blobs data in user " + userId); + } + } + } + + @GuardedBy("mBlobsLock") + @VisibleForTesting + void handleIdleMaintenanceLocked() { + // Cleanup any left over data on disk that is not part of index. + final ArrayList<Long> deletedBlobIds = new ArrayList<>(); + final ArrayList<File> filesToDelete = new ArrayList<>(); + final File blobsDir = BlobStoreConfig.getBlobsDir(); + if (blobsDir.exists()) { + for (File file : blobsDir.listFiles()) { + try { + final long id = Long.parseLong(file.getName()); + if (mKnownBlobIds.indexOf(id) < 0) { + filesToDelete.add(file); + deletedBlobIds.add(id); + } + } catch (NumberFormatException e) { + Slog.wtf(TAG, "Error parsing the file name: " + file, e); + filesToDelete.add(file); + } + } + for (int i = 0, count = filesToDelete.size(); i < count; ++i) { + filesToDelete.get(i).delete(); + } + } + + // Cleanup any stale blobs. + for (int i = 0, userCount = mBlobsMap.size(); i < userCount; ++i) { + final ArrayMap<BlobHandle, BlobMetadata> userBlobs = mBlobsMap.valueAt(i); + userBlobs.entrySet().removeIf(entry -> { + final BlobHandle blobHandle = entry.getKey(); + final BlobMetadata blobMetadata = entry.getValue(); + boolean shouldRemove = false; + + // Cleanup expired data blobs. + if (blobHandle.isExpired()) { + shouldRemove = true; + } + + // Cleanup blobs with no active leases. + // TODO: Exclude blobs which were just committed. + if (!blobMetadata.hasLeases()) { + shouldRemove = true; + } + + if (shouldRemove) { + blobMetadata.getBlobFile().delete(); + mKnownBlobIds.remove(blobMetadata.getBlobId()); + deletedBlobIds.add(blobMetadata.getBlobId()); + } + return shouldRemove; + }); + } + writeBlobsInfoAsync(); + + // Cleanup any stale sessions. + final ArrayList<Integer> indicesToRemove = new ArrayList<>(); + for (int i = 0, userCount = mSessions.size(); i < userCount; ++i) { + final LongSparseArray<BlobStoreSession> userSessions = mSessions.valueAt(i); + indicesToRemove.clear(); + for (int j = 0, sessionsCount = userSessions.size(); j < sessionsCount; ++j) { + final BlobStoreSession blobStoreSession = userSessions.valueAt(j); + boolean shouldRemove = false; + + // Cleanup sessions which haven't been modified in a while. + if (blobStoreSession.getSessionFile().lastModified() + < System.currentTimeMillis() - SESSION_EXPIRY_TIMEOUT_MILLIS) { + shouldRemove = true; } + + // Cleanup sessions with already expired data. + if (blobStoreSession.getBlobHandle().isExpired()) { + shouldRemove = true; + } + + if (shouldRemove) { + blobStoreSession.getSessionFile().delete(); + mKnownBlobIds.remove(blobStoreSession.getSessionId()); + indicesToRemove.add(j); + deletedBlobIds.add(blobStoreSession.getSessionId()); + } + } + for (int j = 0; j < indicesToRemove.size(); ++j) { + userSessions.removeAt(indicesToRemove.get(j)); } } + if (LOGV) { + Slog.v(TAG, "Completed idle maintenance; deleted " + + Arrays.toString(deletedBlobIds.toArray())); + } + writeBlobSessionsAsync(); } void runClearAllSessions(@UserIdInt int userId) { @@ -727,10 +910,10 @@ public class BlobStoreManagerService extends SystemService { fout.increaseIndent(); for (int j = 0, blobsCount = userBlobs.size(); j < blobsCount; ++j) { final BlobMetadata blobMetadata = userBlobs.valueAt(j); - if (!dumpArgs.shouldDumpBlob(blobMetadata.blobId)) { + if (!dumpArgs.shouldDumpBlob(blobMetadata.getBlobId())) { continue; } - fout.println("Blob #" + blobMetadata.blobId); + fout.println("Blob #" + blobMetadata.getBlobId()); fout.increaseIndent(); blobMetadata.dump(fout, dumpArgs); fout.decreaseIndent(); @@ -742,6 +925,9 @@ public class BlobStoreManagerService extends SystemService { private class PackageChangedReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { + if (LOGV) { + Slog.v(TAG, "Received " + intent); + } switch (intent.getAction()) { case Intent.ACTION_PACKAGE_FULLY_REMOVED: case Intent.ACTION_PACKAGE_DATA_CLEARED: @@ -893,6 +1079,14 @@ public class BlobStoreManagerService extends SystemService { final DumpArgs dumpArgs = DumpArgs.parse(args); final IndentingPrintWriter fout = new IndentingPrintWriter(writer, " "); + if (dumpArgs.shouldDumpHelp()) { + writer.println("dumpsys blob_store [options]:"); + fout.increaseIndent(); + dumpArgs.dumpArgsUsage(fout); + fout.decreaseIndent(); + return; + } + synchronized (mBlobsLock) { fout.println("mCurrentMaxSessionId: " + mCurrentMaxSessionId); fout.println(); @@ -926,6 +1120,7 @@ public class BlobStoreManagerService extends SystemService { private boolean mDumpOnlySelectedSections; private boolean mDumpSessions; private boolean mDumpBlobs; + private boolean mDumpHelp; public boolean shouldDumpSession(String packageName, int uid, long blobId) { if (!CollectionUtils.isEmpty(mDumpPackages) @@ -971,6 +1166,10 @@ public class BlobStoreManagerService extends SystemService { || mDumpUserIds.indexOf(userId) >= 0; } + public boolean shouldDumpHelp() { + return mDumpHelp; + } + private DumpArgs() {} public static DumpArgs parse(String[] args) { @@ -1000,6 +1199,8 @@ public class BlobStoreManagerService extends SystemService { dumpArgs.mDumpUserIds.add(getIntArgRequired(args, ++i, "userId")); } else if ("--blob".equals(opt) || "-b".equals(opt)) { dumpArgs.mDumpBlobIds.add(getLongArgRequired(args, ++i, "blobId")); + } else if ("--help".equals(opt) || "-h".equals(opt)) { + dumpArgs.mDumpHelp = true; } else { // Everything else is assumed to be blob ids. dumpArgs.mDumpBlobIds.add(getLongArgRequired(args, i, "blobId")); @@ -1040,6 +1241,40 @@ public class BlobStoreManagerService extends SystemService { } return value; } + + private void dumpArgsUsage(IndentingPrintWriter pw) { + pw.println("--help | -h"); + printWithIndent(pw, "Dump this help text"); + pw.println("--sessions"); + printWithIndent(pw, "Dump only the sessions info"); + pw.println("--blobs"); + printWithIndent(pw, "Dump only the committed blobs info"); + pw.println("--package | -p [package-name]"); + printWithIndent(pw, "Dump blobs info associated with the given package"); + pw.println("--uid | -u [uid]"); + printWithIndent(pw, "Dump blobs info associated with the given uid"); + pw.println("--user [user-id]"); + printWithIndent(pw, "Dump blobs info in the given user"); + pw.println("--blob | -b [session-id | blob-id]"); + printWithIndent(pw, "Dump blob info corresponding to the given ID"); + pw.println("--full | -f"); + printWithIndent(pw, "Dump full unredacted blobs data"); + } + + private void printWithIndent(IndentingPrintWriter pw, String str) { + pw.increaseIndent(); + pw.println(str); + pw.decreaseIndent(); + } + } + + private class LocalService extends BlobStoreManagerInternal { + @Override + public void onIdleMaintenance() { + synchronized (mBlobsLock) { + handleIdleMaintenanceLocked(); + } + } } @VisibleForTesting diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java index 54a299722754..bd35b86babd8 100644 --- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java +++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java @@ -21,11 +21,13 @@ import static android.app.blob.XmlTags.ATTR_PACKAGE; import static android.app.blob.XmlTags.ATTR_UID; import static android.app.blob.XmlTags.TAG_ACCESS_MODE; import static android.app.blob.XmlTags.TAG_BLOB_HANDLE; +import static android.os.Trace.TRACE_TAG_SYSTEM_SERVER; import static android.system.OsConstants.O_CREAT; import static android.system.OsConstants.O_RDONLY; import static android.system.OsConstants.O_RDWR; import static android.system.OsConstants.SEEK_SET; +import static com.android.server.blob.BlobStoreConfig.LOGV; import static com.android.server.blob.BlobStoreConfig.TAG; import android.annotation.BytesLong; @@ -40,6 +42,7 @@ import android.os.FileUtils; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.RevocableFileDescriptor; +import android.os.Trace; import android.os.storage.StorageManager; import android.system.ErrnoException; import android.system.Os; @@ -381,15 +384,22 @@ class BlobStoreSession extends IBlobStoreSession.Stub { void verifyBlobData() { byte[] actualDigest = null; try { + Trace.traceBegin(TRACE_TAG_SYSTEM_SERVER, + "computeBlobDigest-i" + mSessionId + "-l" + getSessionFile().length()); actualDigest = FileUtils.digest(getSessionFile(), mBlobHandle.algorithm); } catch (IOException | NoSuchAlgorithmException e) { Slog.e(TAG, "Error computing the digest", e); + } finally { + Trace.traceEnd(TRACE_TAG_SYSTEM_SERVER); } synchronized (mSessionLock) { if (actualDigest != null && Arrays.equals(actualDigest, mBlobHandle.digest)) { mState = STATE_VERIFIED_VALID; // Commit callback will be sent once the data is persisted. } else { + if (LOGV) { + Slog.v(TAG, "Digest of the data didn't match the given BlobHandle.digest"); + } mState = STATE_VERIFIED_INVALID; sendCommitCallbackResult(COMMIT_RESULT_ERROR); } @@ -447,6 +457,16 @@ class BlobStoreSession extends IBlobStoreSession.Stub { } } + @Override + public String toString() { + return "BlobStoreSession {" + + "id:" + mSessionId + + ",handle:" + mBlobHandle + + ",uid:" + mOwnerUid + + ",pkg:" + mOwnerPackageName + + "}"; + } + private void assertCallerIsOwner() { final int callingUid = Binder.getCallingUid(); if (callingUid != mOwnerUid) { diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java index 0bb07caf0b00..088cadba89ab 100644 --- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java +++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java @@ -1287,7 +1287,7 @@ public class JobInfo implements Parcelable { * {@link #setPeriodic(long)} or {@link #setPersisted(boolean)}. To continually monitor * for content changes, you need to schedule a new JobInfo observing the same URIs * before you finish execution of the JobService handling the most recent changes. - * Following this pattern will ensure you do not lost any content changes: while your + * Following this pattern will ensure you do not lose any content changes: while your * job is running, the system will continue monitoring for content changes, and propagate * any it sees over to the next job you schedule.</p> * diff --git a/apex/media/framework/Android.bp b/apex/media/framework/Android.bp index 91df09865037..23ae8afc1694 100644 --- a/apex/media/framework/Android.bp +++ b/apex/media/framework/Android.bp @@ -38,6 +38,12 @@ java_library { "android.media", ], + optimize: { + enabled: true, + shrink: true, + proguard_flags_files: ["updatable-media-proguard.flags"], + }, + installable: true, // TODO: build against stable API surface. Use core_platform for now to avoid diff --git a/apex/media/framework/java/android/media/MediaParser.java b/apex/media/framework/java/android/media/MediaParser.java index d59270c6a51b..96110e1541f8 100644 --- a/apex/media/framework/java/android/media/MediaParser.java +++ b/apex/media/framework/java/android/media/MediaParser.java @@ -15,6 +15,7 @@ */ package android.media; +import android.annotation.CheckResult; import android.annotation.NonNull; import android.annotation.Nullable; import android.net.Uri; @@ -32,6 +33,7 @@ import com.google.android.exoplayer2.extractor.PositionHolder; import com.google.android.exoplayer2.extractor.SeekMap.SeekPoints; import com.google.android.exoplayer2.extractor.TrackOutput; import com.google.android.exoplayer2.extractor.amr.AmrExtractor; +import com.google.android.exoplayer2.extractor.flac.FlacExtractor; import com.google.android.exoplayer2.extractor.flv.FlvExtractor; import com.google.android.exoplayer2.extractor.mkv.MatroskaExtractor; import com.google.android.exoplayer2.extractor.mp3.Mp3Extractor; @@ -382,6 +384,7 @@ public final class MediaParser { * parse the input. */ @NonNull + @CheckResult private static UnrecognizedInputFormatException createForExtractors( @NonNull String... extractorNames) { StringBuilder builder = new StringBuilder(); @@ -536,7 +539,7 @@ public final class MediaParser { } } if (mExtractor == null) { - UnrecognizedInputFormatException.createForExtractors(mExtractorNamesPool); + throw UnrecognizedInputFormatException.createForExtractors(mExtractorNamesPool); } return true; } @@ -912,6 +915,7 @@ public final class MediaParser { extractorFactoriesByName.put("exo.Ac4Extractor", Ac4Extractor::new); extractorFactoriesByName.put("exo.AdtsExtractor", AdtsExtractor::new); extractorFactoriesByName.put("exo.AmrExtractor", AmrExtractor::new); + extractorFactoriesByName.put("exo.FlacExtractor", FlacExtractor::new); extractorFactoriesByName.put("exo.FlvExtractor", FlvExtractor::new); extractorFactoriesByName.put("exo.FragmentedMp4Extractor", FragmentedMp4Extractor::new); extractorFactoriesByName.put("exo.MatroskaExtractor", MatroskaExtractor::new); diff --git a/apex/media/framework/updatable-media-proguard.flags b/apex/media/framework/updatable-media-proguard.flags new file mode 100644 index 000000000000..4e7d8422bf44 --- /dev/null +++ b/apex/media/framework/updatable-media-proguard.flags @@ -0,0 +1,2 @@ +# Keep all symbols in android.media. +-keep class android.media.* {*;} diff --git a/apex/sdkextensions/testing/Android.bp b/apex/sdkextensions/testing/Android.bp index e6451cc29bc2..f2f5b321fafe 100644 --- a/apex/sdkextensions/testing/Android.bp +++ b/apex/sdkextensions/testing/Android.bp @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -apex { +apex_test { name: "test_com.android.sdkext", visibility: [ "//system/apex/tests" ], defaults: ["com.android.sdkext-defaults"], diff --git a/apex/statsd/framework/java/android/app/StatsManager.java b/apex/statsd/framework/java/android/app/StatsManager.java index a1de330c300a..411482b88326 100644 --- a/apex/statsd/framework/java/android/app/StatsManager.java +++ b/apex/statsd/framework/java/android/app/StatsManager.java @@ -476,7 +476,7 @@ public final class StatsManager { /** * Registers a callback for an atom when that atom is to be pulled. The stats service will * invoke pullData in the callback when the stats service determines that this atom needs to be - * pulled. + * pulled. This method should not be called by third-party apps. * * @param atomTag The tag of the atom for this puller callback. * @param metadata Optional metadata specifying the timeout, cool down time, and @@ -485,6 +485,7 @@ public final class StatsManager { * @param executor The executor in which to run the callback. * */ + @RequiresPermission(android.Manifest.permission.REGISTER_STATS_PULL_ATOM) public void registerPullAtomCallback(int atomTag, @Nullable PullAtomMetadata metadata, @NonNull @CallbackExecutor Executor executor, @NonNull StatsPullAtomCallback callback) { @@ -510,11 +511,12 @@ public final class StatsManager { /** * Unregisters a callback for an atom when that atom is to be pulled. Note that any ongoing - * pulls will still occur. + * pulls will still occur. This method should not be called by third-party apps. * * @param atomTag The tag of the atom of which to unregister * */ + @RequiresPermission(android.Manifest.permission.REGISTER_STATS_PULL_ATOM) public void unregisterPullAtomCallback(int atomTag) { synchronized (sLock) { try { diff --git a/core/java/android/util/StatsLog.java b/apex/statsd/framework/java/android/util/StatsLog.java index 791073794b0e..791073794b0e 100644 --- a/core/java/android/util/StatsLog.java +++ b/apex/statsd/framework/java/android/util/StatsLog.java diff --git a/apex/statsd/service/java/com/android/server/stats/StatsCompanion.java b/apex/statsd/service/java/com/android/server/stats/StatsCompanion.java index 4383b503bfe7..4495dc9de71e 100644 --- a/apex/statsd/service/java/com/android/server/stats/StatsCompanion.java +++ b/apex/statsd/service/java/com/android/server/stats/StatsCompanion.java @@ -38,11 +38,15 @@ public class StatsCompanion { private static final String TAG = "StatsCompanion"; private static final boolean DEBUG = false; - static void enforceStatsCompanionPermission(Context context) { + private static final int AID_STATSD = 1066; + + static void enforceStatsdCallingUid() { if (Binder.getCallingPid() == Process.myPid()) { return; } - context.enforceCallingPermission(android.Manifest.permission.STATSCOMPANION, null); + if (Binder.getCallingUid() != AID_STATSD) { + throw new SecurityException("Not allowed to access StatsCompanion"); + } } /** @@ -114,7 +118,7 @@ public class StatsCompanion { @Override public void sendDataBroadcast(long lastReportTimeNs) { - enforceStatsCompanionPermission(mContext); + enforceStatsdCallingUid(); Intent intent = new Intent(); intent.putExtra(EXTRA_LAST_REPORT_TIME, lastReportTimeNs); try { @@ -126,7 +130,7 @@ public class StatsCompanion { @Override public void sendActiveConfigsChangedBroadcast(long[] configIds) { - enforceStatsCompanionPermission(mContext); + enforceStatsdCallingUid(); Intent intent = new Intent(); intent.putExtra(StatsManager.EXTRA_STATS_ACTIVE_CONFIG_KEYS, configIds); try { @@ -142,7 +146,7 @@ public class StatsCompanion { @Override public void sendSubscriberBroadcast(long configUid, long configId, long subscriptionId, long subscriptionRuleId, String[] cookies, StatsDimensionsValue dimensionsValue) { - enforceStatsCompanionPermission(mContext); + enforceStatsdCallingUid(); Intent intent = new Intent() .putExtra(StatsManager.EXTRA_STATS_CONFIG_UID, configUid) diff --git a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java index 3e9a488fb5b8..a735cb8f14af 100644 --- a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java +++ b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java @@ -398,7 +398,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { @Override // Binder call public void setAnomalyAlarm(long timestampMs) { - StatsCompanion.enforceStatsCompanionPermission(mContext); + StatsCompanion.enforceStatsdCallingUid(); if (DEBUG) Slog.d(TAG, "Setting anomaly alarm for " + timestampMs); final long callingToken = Binder.clearCallingIdentity(); try { @@ -414,7 +414,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { @Override // Binder call public void cancelAnomalyAlarm() { - StatsCompanion.enforceStatsCompanionPermission(mContext); + StatsCompanion.enforceStatsdCallingUid(); if (DEBUG) Slog.d(TAG, "Cancelling anomaly alarm"); final long callingToken = Binder.clearCallingIdentity(); try { @@ -426,7 +426,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { @Override // Binder call public void setAlarmForSubscriberTriggering(long timestampMs) { - StatsCompanion.enforceStatsCompanionPermission(mContext); + StatsCompanion.enforceStatsdCallingUid(); if (DEBUG) { Slog.d(TAG, "Setting periodic alarm in about " + (timestampMs @@ -445,7 +445,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { @Override // Binder call public void cancelAlarmForSubscriberTriggering() { - StatsCompanion.enforceStatsCompanionPermission(mContext); + StatsCompanion.enforceStatsdCallingUid(); if (DEBUG) { Slog.d(TAG, "Cancelling periodic alarm"); } @@ -459,7 +459,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { @Override // Binder call public void setPullingAlarm(long nextPullTimeMs) { - StatsCompanion.enforceStatsCompanionPermission(mContext); + StatsCompanion.enforceStatsdCallingUid(); if (DEBUG) { Slog.d(TAG, "Setting pulling alarm in about " + (nextPullTimeMs - SystemClock.elapsedRealtime())); @@ -477,7 +477,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { @Override // Binder call public void cancelPullingAlarm() { - StatsCompanion.enforceStatsCompanionPermission(mContext); + StatsCompanion.enforceStatsdCallingUid(); if (DEBUG) { Slog.d(TAG, "Cancelling pulling alarm"); } @@ -491,7 +491,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { @Override // Binder call public void statsdReady() { - StatsCompanion.enforceStatsCompanionPermission(mContext); + StatsCompanion.enforceStatsdCallingUid(); if (DEBUG) { Slog.d(TAG, "learned that statsdReady"); } @@ -503,7 +503,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { @Override public void triggerUidSnapshot() { - StatsCompanion.enforceStatsCompanionPermission(mContext); + StatsCompanion.enforceStatsdCallingUid(); synchronized (sStatsdLock) { final long token = Binder.clearCallingIdentity(); try { @@ -518,7 +518,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { @Override // Binder call public boolean checkPermission(String permission, int pid, int uid) { - StatsCompanion.enforceStatsCompanionPermission(mContext); + StatsCompanion.enforceStatsdCallingUid(); return mContext.checkPermission(permission, pid, uid) == PackageManager.PERMISSION_GRANTED; } diff --git a/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java b/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java index 04d8b006f51d..c1dc584b89b4 100644 --- a/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java +++ b/apex/statsd/service/java/com/android/server/stats/StatsManagerService.java @@ -171,8 +171,8 @@ public class StatsManagerService extends IStatsManagerService.Stub { @Override public void registerPullAtomCallback(int atomTag, long coolDownNs, long timeoutNs, int[] additiveFields, IPullAtomCallback pullerCallback) { + enforceRegisterStatsPullAtomPermission(); int callingUid = Binder.getCallingUid(); - final long token = Binder.clearCallingIdentity(); PullerKey key = new PullerKey(callingUid, atomTag); PullerValue val = new PullerValue(coolDownNs, timeoutNs, additiveFields, pullerCallback); @@ -187,6 +187,7 @@ public class StatsManagerService extends IStatsManagerService.Stub { return; } + final long token = Binder.clearCallingIdentity(); try { statsd.registerPullAtomCallback( callingUid, atomTag, coolDownNs, timeoutNs, additiveFields, pullerCallback); @@ -199,8 +200,8 @@ public class StatsManagerService extends IStatsManagerService.Stub { @Override public void unregisterPullAtomCallback(int atomTag) { + enforceRegisterStatsPullAtomPermission(); int callingUid = Binder.getCallingUid(); - final long token = Binder.clearCallingIdentity(); PullerKey key = new PullerKey(callingUid, atomTag); // Always remove the puller from StatsManagerService even if statsd is down. When statsd @@ -214,6 +215,7 @@ public class StatsManagerService extends IStatsManagerService.Stub { return; } + final long token = Binder.clearCallingIdentity(); try { statsd.unregisterPullAtomCallback(callingUid, atomTag); } catch (RemoteException e) { @@ -502,6 +504,13 @@ public class StatsManagerService extends IStatsManagerService.Stub { } } + private void enforceRegisterStatsPullAtomPermission() { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.REGISTER_STATS_PULL_ATOM, + "Need REGISTER_STATS_PULL_ATOM permission."); + } + + /** * Clients should call this if blocking until statsd to be ready is desired * diff --git a/api/current.txt b/api/current.txt index 006ac0b27306..a5a51d66375d 100644 --- a/api/current.txt +++ b/api/current.txt @@ -2873,7 +2873,7 @@ package android.accessibilityservice { method public void onSystemActionsChanged(); method public final boolean performGlobalAction(int); method public final void setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo); - method public boolean takeScreenshot(int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.graphics.Bitmap>); + method public boolean takeScreenshot(int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.accessibilityservice.AccessibilityService.ScreenshotResult>); field public static final int GESTURE_2_FINGER_DOUBLE_TAP = 20; // 0x14 field public static final int GESTURE_2_FINGER_SINGLE_TAP = 19; // 0x13 field public static final int GESTURE_2_FINGER_SWIPE_DOWN = 26; // 0x1a @@ -2888,6 +2888,13 @@ package android.accessibilityservice { field public static final int GESTURE_3_FINGER_SWIPE_RIGHT = 32; // 0x20 field public static final int GESTURE_3_FINGER_SWIPE_UP = 29; // 0x1d field public static final int GESTURE_3_FINGER_TRIPLE_TAP = 24; // 0x18 + field public static final int GESTURE_4_FINGER_DOUBLE_TAP = 38; // 0x26 + field public static final int GESTURE_4_FINGER_SINGLE_TAP = 37; // 0x25 + field public static final int GESTURE_4_FINGER_SWIPE_DOWN = 34; // 0x22 + field public static final int GESTURE_4_FINGER_SWIPE_LEFT = 35; // 0x23 + field public static final int GESTURE_4_FINGER_SWIPE_RIGHT = 36; // 0x24 + field public static final int GESTURE_4_FINGER_SWIPE_UP = 33; // 0x21 + field public static final int GESTURE_4_FINGER_TRIPLE_TAP = 39; // 0x27 field public static final int GESTURE_DOUBLE_TAP = 17; // 0x11 field public static final int GESTURE_DOUBLE_TAP_AND_HOLD = 18; // 0x12 field public static final int GESTURE_SWIPE_DOWN = 2; // 0x2 @@ -2945,6 +2952,12 @@ package android.accessibilityservice { method public void onMagnificationChanged(@NonNull android.accessibilityservice.AccessibilityService.MagnificationController, @NonNull android.graphics.Region, float, float, float); } + public static final class AccessibilityService.ScreenshotResult { + method @Nullable public android.graphics.ColorSpace getColorSpace(); + method @NonNull public android.hardware.HardwareBuffer getHardwareBuffer(); + method public long getTimestamp(); + } + public static final class AccessibilityService.SoftKeyboardController { method public void addOnShowModeChangedListener(@NonNull android.accessibilityservice.AccessibilityService.SoftKeyboardController.OnShowModeChangedListener); method public void addOnShowModeChangedListener(@NonNull android.accessibilityservice.AccessibilityService.SoftKeyboardController.OnShowModeChangedListener, @Nullable android.os.Handler); @@ -3850,7 +3863,7 @@ package android.app { method public void onPerformDirectAction(@NonNull String, @NonNull android.os.Bundle, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<android.os.Bundle>); method public void onPictureInPictureModeChanged(boolean, android.content.res.Configuration); method @Deprecated public void onPictureInPictureModeChanged(boolean); - method public void onPictureInPictureRequested(); + method public boolean onPictureInPictureRequested(); method @CallSuper protected void onPostCreate(@Nullable android.os.Bundle); method public void onPostCreate(@Nullable android.os.Bundle, @Nullable android.os.PersistableBundle); method @CallSuper protected void onPostResume(); @@ -6803,7 +6816,7 @@ package android.app.admin { ctor public DevicePolicyKeyguardService(); method @Nullable public void dismiss(); method @Nullable public final android.os.IBinder onBind(@Nullable android.content.Intent); - method @Nullable public android.view.SurfaceControl onSurfaceReady(@Nullable android.os.IBinder); + method @Nullable public android.view.SurfaceControlViewHost.SurfacePackage onSurfaceReady(@Nullable android.os.IBinder); } public class DevicePolicyManager { @@ -26799,7 +26812,6 @@ package android.media { method public abstract void onSelectRoute(@NonNull String, @NonNull String); method public abstract void onSetVolume(@NonNull String, int); method public abstract void onTransferToRoute(@NonNull String, @NonNull String); - method public abstract void onUpdateVolume(@NonNull String, int); field public static final long REQUEST_ID_UNKNOWN = 0L; // 0x0L field public static final String SERVICE_INTERFACE = "android.media.MediaRoute2ProviderService"; } @@ -30127,7 +30139,7 @@ package android.net { method @NonNull public android.net.NetworkCapabilities setLinkDownstreamBandwidthKbps(int); method @NonNull public android.net.NetworkCapabilities setLinkUpstreamBandwidthKbps(int); method @NonNull public android.net.NetworkCapabilities setNetworkSpecifier(@NonNull android.net.NetworkSpecifier); - method public void setOwnerUid(int); + method @NonNull public android.net.NetworkCapabilities setOwnerUid(int); method @NonNull public android.net.NetworkCapabilities setSignalStrength(int); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkCapabilities> CREATOR; @@ -35688,6 +35700,7 @@ package android.os { field public static final String INCREMENTAL; field public static final int PREVIEW_SDK_INT; field public static final String RELEASE; + field @NonNull public static final String RELEASE_OR_CODENAME; field @Deprecated public static final String SDK; field public static final int SDK_INT; field public static final String SECURITY_PATCH; @@ -44062,8 +44075,8 @@ package android.service.voice { } public static final class AlwaysOnHotwordDetector.ModelParamRange { - method public int end(); - method public int start(); + method public int getEnd(); + method public int getStart(); } public class VoiceInteractionService extends android.app.Service { diff --git a/api/system-current.txt b/api/system-current.txt index 253d6b63e663..a2de8fd11f9e 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -188,6 +188,7 @@ package android { field public static final String REGISTER_CALL_PROVIDER = "android.permission.REGISTER_CALL_PROVIDER"; field public static final String REGISTER_CONNECTION_MANAGER = "android.permission.REGISTER_CONNECTION_MANAGER"; field public static final String REGISTER_SIM_SUBSCRIPTION = "android.permission.REGISTER_SIM_SUBSCRIPTION"; + field public static final String REGISTER_STATS_PULL_ATOM = "android.permission.REGISTER_STATS_PULL_ATOM"; field public static final String REMOTE_DISPLAY_PROVIDER = "android.permission.REMOTE_DISPLAY_PROVIDER"; field public static final String REMOVE_DRM_CERTIFICATES = "android.permission.REMOVE_DRM_CERTIFICATES"; field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS"; @@ -692,7 +693,7 @@ package android.app { method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public long[] getRegisteredExperimentIds() throws android.app.StatsManager.StatsUnavailableException; method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public byte[] getReports(long) throws android.app.StatsManager.StatsUnavailableException; method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public byte[] getStatsMetadata() throws android.app.StatsManager.StatsUnavailableException; - method public void registerPullAtomCallback(int, @Nullable android.app.StatsManager.PullAtomMetadata, @NonNull java.util.concurrent.Executor, @NonNull android.app.StatsManager.StatsPullAtomCallback); + method @RequiresPermission(android.Manifest.permission.REGISTER_STATS_PULL_ATOM) public void registerPullAtomCallback(int, @Nullable android.app.StatsManager.PullAtomMetadata, @NonNull java.util.concurrent.Executor, @NonNull android.app.StatsManager.StatsPullAtomCallback); method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public void removeConfig(long) throws android.app.StatsManager.StatsUnavailableException; method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public boolean removeConfiguration(long); method @NonNull @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public long[] setActiveConfigsChangedOperation(@Nullable android.app.PendingIntent) throws android.app.StatsManager.StatsUnavailableException; @@ -700,7 +701,7 @@ package android.app { method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public boolean setBroadcastSubscriber(long, long, android.app.PendingIntent); method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public boolean setDataFetchOperation(long, android.app.PendingIntent); method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public void setFetchReportsOperation(android.app.PendingIntent, long) throws android.app.StatsManager.StatsUnavailableException; - method public void unregisterPullAtomCallback(int); + method @RequiresPermission(android.Manifest.permission.REGISTER_STATS_PULL_ATOM) public void unregisterPullAtomCallback(int); field public static final String ACTION_STATSD_STARTED = "android.app.action.STATSD_STARTED"; field public static final String EXTRA_STATS_ACTIVE_CONFIG_KEYS = "android.app.extra.STATS_ACTIVE_CONFIG_KEYS"; field public static final String EXTRA_STATS_BROADCAST_SUBSCRIBER_COOKIES = "android.app.extra.STATS_BROADCAST_SUBSCRIBER_COOKIES"; @@ -1151,6 +1152,16 @@ package android.app.backup { } +package android.app.compat { + + public final class CompatChanges { + method public static boolean isChangeEnabled(long); + method public static boolean isChangeEnabled(long, @NonNull String, @NonNull android.os.UserHandle); + method public static boolean isChangeEnabled(long, int); + } + +} + package android.app.contentsuggestions { public final class ClassificationsRequest implements android.os.Parcelable { @@ -1472,9 +1483,9 @@ package android.bluetooth { public final class BluetoothAdapter { method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean addOnMetadataChangedListener(@NonNull android.bluetooth.BluetoothDevice, @NonNull java.util.concurrent.Executor, @NonNull android.bluetooth.BluetoothAdapter.OnMetadataChangedListener); - method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean connectAllEnabledProfiles(@NonNull android.bluetooth.BluetoothDevice); + method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean connectAllEnabledProfiles(@NonNull android.bluetooth.BluetoothDevice); method public boolean disableBLE(); - method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean disconnectAllEnabledProfiles(@NonNull android.bluetooth.BluetoothDevice); + method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean disconnectAllEnabledProfiles(@NonNull android.bluetooth.BluetoothDevice); method public boolean enableBLE(); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean enableNoAutoConnect(); method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean factoryReset(); @@ -2098,6 +2109,10 @@ package android.content.pm { field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.LauncherApps.AppUsageLimit> CREATOR; } + public static class LauncherApps.ShortcutQuery { + method @NonNull public android.content.pm.LauncherApps.ShortcutQuery setLocusIds(@Nullable java.util.List<android.content.LocusId>); + } + public class PackageInstaller { method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setPermissionsResult(int, boolean); field public static final int DATA_LOADER_TYPE_INCREMENTAL = 2; // 0x2 @@ -3680,17 +3695,17 @@ package android.hardware.soundtrigger { } public static final class SoundTrigger.ModelParamRange implements android.os.Parcelable { + method public int getEnd(); + method public int getStart(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.hardware.soundtrigger.SoundTrigger.ModelParamRange> CREATOR; - field public final int end; - field public final int start; } public static final class SoundTrigger.ModuleProperties implements android.os.Parcelable { method public int describeContents(); method public void writeToParcel(android.os.Parcel, int); - field public static final int CAPABILITY_ECHO_CANCELLATION = 1; // 0x1 - field public static final int CAPABILITY_NOISE_SUPPRESSION = 2; // 0x2 + field public static final int AUDIO_CAPABILITY_ECHO_CANCELLATION = 1; // 0x1 + field public static final int AUDIO_CAPABILITY_NOISE_SUPPRESSION = 2; // 0x2 field @NonNull public static final android.os.Parcelable.Creator<android.hardware.soundtrigger.SoundTrigger.ModuleProperties> CREATOR; field public final int audioCapabilities; field @NonNull public final String description; @@ -6292,7 +6307,9 @@ package android.net { method @Nullable public String getSSID(); method @NonNull public int[] getTransportTypes(); method public boolean satisfiedByNetworkCapabilities(@Nullable android.net.NetworkCapabilities); - method public void setAdministratorUids(@NonNull java.util.List<java.lang.Integer>); + method @NonNull public android.net.NetworkCapabilities setAdministratorUids(@NonNull java.util.List<java.lang.Integer>); + method @NonNull public android.net.NetworkCapabilities setRequestorPackageName(@NonNull String); + method @NonNull public android.net.NetworkCapabilities setRequestorUid(int); method @NonNull public android.net.NetworkCapabilities setSSID(@Nullable String); method @NonNull public android.net.NetworkCapabilities setTransportInfo(@NonNull android.net.TransportInfo); field public static final int NET_CAPABILITY_OEM_PAID = 22; // 0x16 @@ -6344,6 +6361,8 @@ package android.net { } public class NetworkRequest implements android.os.Parcelable { + method @Nullable public String getRequestorPackageName(); + method public int getRequestorUid(); method public boolean satisfiedBy(@Nullable android.net.NetworkCapabilities); } @@ -6428,7 +6447,6 @@ package android.net { } public abstract class NetworkSpecifier { - method public void assertValidFromUid(int); method @Nullable public android.net.NetworkSpecifier redact(); method public abstract boolean satisfiedBy(@Nullable android.net.NetworkSpecifier); } @@ -7556,13 +7574,12 @@ package android.net.wifi { @Deprecated public static class WifiConfiguration.NetworkSelectionStatus { method @Deprecated public int getDisableReasonCounter(int); method @Deprecated public long getDisableTime(); + method @Deprecated public static int getMaxNetworkSelectionDisableReason(); method @Deprecated @Nullable public static String getNetworkDisableReasonString(int); method @Deprecated public int getNetworkSelectionDisableReason(); method @Deprecated public int getNetworkSelectionStatus(); method @Deprecated @NonNull public String getNetworkStatusString(); method @Deprecated public boolean hasEverConnected(); - method @Deprecated public boolean isNetworkEnabled(); - method @Deprecated public boolean isNetworkPermanentlyDisabled(); field @Deprecated public static final int DISABLED_ASSOCIATION_REJECTION = 1; // 0x1 field @Deprecated public static final int DISABLED_AUTHENTICATION_FAILURE = 2; // 0x2 field @Deprecated public static final int DISABLED_AUTHENTICATION_NO_CREDENTIALS = 5; // 0x5 @@ -7573,7 +7590,6 @@ package android.net.wifi { field @Deprecated public static final int DISABLED_NONE = 0; // 0x0 field @Deprecated public static final int DISABLED_NO_INTERNET_PERMANENT = 6; // 0x6 field @Deprecated public static final int DISABLED_NO_INTERNET_TEMPORARY = 4; // 0x4 - field @Deprecated public static final int NETWORK_SELECTION_DISABLED_MAX = 10; // 0xa field @Deprecated public static final int NETWORK_SELECTION_ENABLED = 0; // 0x0 field @Deprecated public static final int NETWORK_SELECTION_PERMANENTLY_DISABLED = 2; // 0x2 field @Deprecated public static final int NETWORK_SELECTION_TEMPORARY_DISABLED = 1; // 0x1 @@ -8197,7 +8213,7 @@ package android.net.wifi.wificond { ctor public NativeScanResult(); method public int describeContents(); method @NonNull public byte[] getBssid(); - method @NonNull public java.util.BitSet getCapabilities(); + method @NonNull public int getCapabilities(); method public int getFrequencyMhz(); method @NonNull public byte[] getInformationElements(); method @NonNull public java.util.List<android.net.wifi.wificond.RadioChainInfo> getRadioChainInfos(); @@ -8233,12 +8249,12 @@ package android.net.wifi.wificond { public final class PnoSettings implements android.os.Parcelable { ctor public PnoSettings(); method public int describeContents(); - method public int getIntervalMillis(); + method public long getIntervalMillis(); method public int getMin2gRssiDbm(); method public int getMin5gRssiDbm(); method public int getMin6gRssiDbm(); method @NonNull public java.util.List<android.net.wifi.wificond.PnoNetwork> getPnoNetworks(); - method public void setIntervalMillis(int); + method public void setIntervalMillis(long); method public void setMin2gRssiDbm(int); method public void setMin5gRssiDbm(int); method public void setMin6gRssiDbm(int); @@ -8263,10 +8279,10 @@ package android.net.wifi.wificond { method @Nullable public android.net.wifi.wificond.DeviceWiphyCapabilities getDeviceWiphyCapabilities(@NonNull String); method @NonNull public java.util.List<android.net.wifi.wificond.NativeScanResult> getScanResults(@NonNull String, int); method @Nullable public android.net.wifi.wificond.WifiCondManager.TxPacketCounters getTxPacketCounters(@NonNull String); - method public boolean initialize(@NonNull Runnable); method @Nullable public static android.net.wifi.wificond.WifiCondManager.OemSecurityType parseOemSecurityTypeElement(int, int, @NonNull byte[]); method public boolean registerApCallback(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.wificond.WifiCondManager.SoftApCallback); method public void sendMgmtFrame(@NonNull String, @NonNull byte[], int, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.wificond.WifiCondManager.SendMgmtFrameCallback); + method public void setOnServiceDeadCallback(@NonNull Runnable); method public boolean setupInterfaceForClientMode(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.wificond.WifiCondManager.ScanEventCallback, @NonNull android.net.wifi.wificond.WifiCondManager.ScanEventCallback); method public boolean setupInterfaceForSoftApMode(@NonNull String); method @Nullable public android.net.wifi.wificond.WifiCondManager.SignalPollResult signalPoll(@NonNull String); @@ -12407,7 +12423,6 @@ package android.telephony { field public static final int CDMA_SUBSCRIPTION_RUIM_SIM = 0; // 0x0 field public static final int CDMA_SUBSCRIPTION_UNKNOWN = -1; // 0xffffffff field public static final int CHANGE_ICC_LOCK_SUCCESS = 2147483647; // 0x7fffffff - field public static final int DEFAULT_PREFERRED_NETWORK_MODE = 0; // 0x0 field public static final String EXTRA_ANOMALY_DESCRIPTION = "android.telephony.extra.ANOMALY_DESCRIPTION"; field public static final String EXTRA_ANOMALY_ID = "android.telephony.extra.ANOMALY_ID"; field @Deprecated public static final String EXTRA_APN_PROTOCOL = "apnProto"; diff --git a/api/system-lint-baseline.txt b/api/system-lint-baseline.txt index a57e178879fa..0caee6bebbda 100644 --- a/api/system-lint-baseline.txt +++ b/api/system-lint-baseline.txt @@ -64,10 +64,6 @@ GenericException: android.service.autofill.augmented.FillWindow#finalize(): -HeavyBitSet: android.net.wifi.wificond.NativeScanResult#getCapabilities(): - - - IntentBuilderName: android.content.Context#registerReceiverForAllUsers(android.content.BroadcastReceiver, android.content.IntentFilter, String, android.os.Handler): Methods creating an Intent should be named `create<Foo>Intent()`, was `registerReceiverForAllUsers` diff --git a/api/test-current.txt b/api/test-current.txt index d3e7ea161fc6..e1f83822609d 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -183,6 +183,9 @@ package android.app { field public static final int HISTORICAL_MODE_DISABLED = 0; // 0x0 field public static final int HISTORICAL_MODE_ENABLED_ACTIVE = 1; // 0x1 field public static final int HISTORICAL_MODE_ENABLED_PASSIVE = 2; // 0x2 + field public static final String KEY_BG_STATE_SETTLE_TIME = "bg_state_settle_time"; + field public static final String KEY_FG_SERVICE_STATE_SETTLE_TIME = "fg_service_state_settle_time"; + field public static final String KEY_TOP_STATE_SETTLE_TIME = "top_state_settle_time"; field public static final String OPSTR_ACCEPT_HANDOVER = "android:accept_handover"; field public static final String OPSTR_ACCESS_NOTIFICATIONS = "android:access_notifications"; field public static final String OPSTR_ACTIVATE_VPN = "android:activate_vpn"; diff --git a/cmds/incident/Android.bp b/cmds/incident/Android.bp index 9e9dac14c802..94855aa0311f 100644 --- a/cmds/incident/Android.bp +++ b/cmds/incident/Android.bp @@ -26,7 +26,7 @@ cc_binary { "libcutils", "liblog", "libutils", - "libincident", + "libincidentpriv", ], static_libs: [ diff --git a/cmds/incident_helper/Android.bp b/cmds/incident_helper/Android.bp index 64f4c667820d..f07743ec2ee6 100644 --- a/cmds/incident_helper/Android.bp +++ b/cmds/incident_helper/Android.bp @@ -44,7 +44,7 @@ cc_defaults { "src/ih_util.cpp", ], - generated_headers: ["gen-platform-proto-constants"], + generated_headers: ["framework-cppstream-protos"], shared_libs: [ "libbase", diff --git a/cmds/incidentd/Android.bp b/cmds/incidentd/Android.bp index 25e0328b4f38..c47526abad53 100644 --- a/cmds/incidentd/Android.bp +++ b/cmds/incidentd/Android.bp @@ -43,7 +43,7 @@ cc_binary { ], local_include_dirs: ["src"], - generated_headers: ["gen-platform-proto-constants"], + generated_headers: ["framework-cppstream-protos"], proto: { type: "lite", @@ -54,7 +54,7 @@ cc_binary { "libbinder", "libdebuggerd_client", "libdumputils", - "libincident", + "libincidentpriv", "liblog", "libprotoutil", "libservices", @@ -98,7 +98,7 @@ cc_test { ], local_include_dirs: ["src"], - generated_headers: ["gen-platform-proto-constants"], + generated_headers: ["framework-cppstream-protos"], srcs: [ "tests/**/*.cpp", @@ -128,7 +128,7 @@ cc_test { "libbinder", "libdebuggerd_client", "libdumputils", - "libincident", + "libincidentpriv", "liblog", "libprotobuf-cpp-full", "libprotoutil", diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp index e6cc1dafc179..7e069a6b4372 100644 --- a/cmds/statsd/Android.bp +++ b/cmds/statsd/Android.bp @@ -65,7 +65,6 @@ cc_defaults { "src/config/ConfigListener.cpp", "src/config/ConfigManager.cpp", "src/experiment_ids.proto", - "src/external/GpuStatsPuller.cpp", "src/external/Perfetto.cpp", "src/external/PullResultReceiver.cpp", "src/external/puller_util.cpp", @@ -111,7 +110,7 @@ cc_defaults { ], cflags: [ - // "-DNEW_ENCODING_SCHEME", + "-DNEW_ENCODING_SCHEME", ], local_include_dirs: [ @@ -119,22 +118,19 @@ cc_defaults { ], static_libs: [ - "android.frameworks.stats@1.0", "libbase", "libcutils", - "liblog", "libprotoutil", "libstatslog", - "libstatssocket", + "libstatsmetadata", "libsysutils", ], shared_libs: [ "libbinder", - "libgraphicsenv", - "libhidlbase", "libincident", + "liblog", "libservices", - "libstatsmetadata", + "libstatssocket", "libutils", ], } @@ -161,7 +157,7 @@ genrule { ], } -cc_library_shared { +cc_library_static { name: "libstatsmetadata", host_supported: true, generated_sources: [ @@ -224,8 +220,6 @@ cc_binary { shared_libs: ["libgtest_prod"], - vintf_fragments: ["android.frameworks.stats@1.0-service.xml"], - init_rc: ["statsd.rc"], } @@ -278,8 +272,6 @@ cc_test { "tests/e2e/PartialBucket_e2e_test.cpp", "tests/e2e/ValueMetric_pull_e2e_test.cpp", "tests/e2e/WakelockDuration_e2e_test.cpp", - "tests/external/GpuStatsPuller_test.cpp", - "tests/external/IncidentReportArgs_test.cpp", "tests/external/puller_util_test.cpp", "tests/external/StatsCallbackPuller_test.cpp", "tests/external/StatsPuller_test.cpp", diff --git a/cmds/statsd/android.frameworks.stats@1.0-service.xml b/cmds/statsd/android.frameworks.stats@1.0-service.xml deleted file mode 100644 index bb02f66a28b1..000000000000 --- a/cmds/statsd/android.frameworks.stats@1.0-service.xml +++ /dev/null @@ -1,11 +0,0 @@ -<manifest version="1.0" type="framework"> - <hal> - <name>android.frameworks.stats</name> - <transport>hwbinder</transport> - <version>1.0</version> - <interface> - <name>IStats</name> - <instance>default</instance> - </interface> - </hal> -</manifest> diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index 0256e3617dd7..a06e59c8e409 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -1354,7 +1354,6 @@ Status StatsService::sendWatchdogRollbackOccurredAtom(const int32_t rollbackType return Status::ok(); } - Status StatsService::getRegisteredExperimentIds(std::vector<int64_t>* experimentIdsOut) { ENFORCE_UID(AID_SYSTEM); // TODO: add verifier permission @@ -1372,103 +1371,6 @@ Status StatsService::getRegisteredExperimentIds(std::vector<int64_t>* experiment return Status::ok(); } -hardware::Return<void> StatsService::reportSpeakerImpedance( - const SpeakerImpedance& speakerImpedance) { - android::util::stats_write(android::util::SPEAKER_IMPEDANCE_REPORTED, - speakerImpedance.speakerLocation, speakerImpedance.milliOhms); - - return hardware::Void(); -} - -hardware::Return<void> StatsService::reportHardwareFailed(const HardwareFailed& hardwareFailed) { - android::util::stats_write(android::util::HARDWARE_FAILED, int32_t(hardwareFailed.hardwareType), - hardwareFailed.hardwareLocation, int32_t(hardwareFailed.errorCode)); - - return hardware::Void(); -} - -hardware::Return<void> StatsService::reportPhysicalDropDetected( - const PhysicalDropDetected& physicalDropDetected) { - android::util::stats_write(android::util::PHYSICAL_DROP_DETECTED, - int32_t(physicalDropDetected.confidencePctg), physicalDropDetected.accelPeak, - physicalDropDetected.freefallDuration); - - return hardware::Void(); -} - -hardware::Return<void> StatsService::reportChargeCycles(const ChargeCycles& chargeCycles) { - std::vector<int32_t> buckets = chargeCycles.cycleBucket; - int initialSize = buckets.size(); - for (int i = 0; i < 10 - initialSize; i++) { - buckets.push_back(-1); // Push -1 for buckets that do not exist. - } - android::util::stats_write(android::util::CHARGE_CYCLES_REPORTED, buckets[0], buckets[1], - buckets[2], buckets[3], buckets[4], buckets[5], buckets[6], buckets[7], buckets[8], - buckets[9]); - - return hardware::Void(); -} - -hardware::Return<void> StatsService::reportBatteryHealthSnapshot( - const BatteryHealthSnapshotArgs& batteryHealthSnapshotArgs) { - android::util::stats_write(android::util::BATTERY_HEALTH_SNAPSHOT, - int32_t(batteryHealthSnapshotArgs.type), batteryHealthSnapshotArgs.temperatureDeciC, - batteryHealthSnapshotArgs.voltageMicroV, batteryHealthSnapshotArgs.currentMicroA, - batteryHealthSnapshotArgs.openCircuitVoltageMicroV, - batteryHealthSnapshotArgs.resistanceMicroOhm, batteryHealthSnapshotArgs.levelPercent); - - return hardware::Void(); -} - -hardware::Return<void> StatsService::reportSlowIo(const SlowIo& slowIo) { - android::util::stats_write(android::util::SLOW_IO, int32_t(slowIo.operation), slowIo.count); - - return hardware::Void(); -} - -hardware::Return<void> StatsService::reportBatteryCausedShutdown( - const BatteryCausedShutdown& batteryCausedShutdown) { - android::util::stats_write(android::util::BATTERY_CAUSED_SHUTDOWN, - batteryCausedShutdown.voltageMicroV); - - return hardware::Void(); -} - -hardware::Return<void> StatsService::reportUsbPortOverheatEvent( - const UsbPortOverheatEvent& usbPortOverheatEvent) { - android::util::stats_write(android::util::USB_PORT_OVERHEAT_EVENT_REPORTED, - usbPortOverheatEvent.plugTemperatureDeciC, usbPortOverheatEvent.maxTemperatureDeciC, - usbPortOverheatEvent.timeToOverheat, usbPortOverheatEvent.timeToHysteresis, - usbPortOverheatEvent.timeToInactive); - - return hardware::Void(); -} - -hardware::Return<void> StatsService::reportSpeechDspStat( - const SpeechDspStat& speechDspStat) { - android::util::stats_write(android::util::SPEECH_DSP_STAT_REPORTED, - speechDspStat.totalUptimeMillis, speechDspStat.totalDowntimeMillis, - speechDspStat.totalCrashCount, speechDspStat.totalRecoverCount); - - return hardware::Void(); -} - -hardware::Return<void> StatsService::reportVendorAtom(const VendorAtom& vendorAtom) { - std::string reverseDomainName = (std::string) vendorAtom.reverseDomainName; - if (vendorAtom.atomId < 100000 || vendorAtom.atomId >= 200000) { - ALOGE("Atom ID %ld is not a valid vendor atom ID", (long) vendorAtom.atomId); - return hardware::Void(); - } - if (reverseDomainName.length() > 50) { - ALOGE("Vendor atom reverse domain name %s is too long.", reverseDomainName.c_str()); - return hardware::Void(); - } - LogEvent event(getWallClockSec() * NS_PER_SEC, getElapsedRealtimeNs(), vendorAtom); - mProcessor->OnLogEvent(&event); - - return hardware::Void(); -} - void StatsService::binderDied(const wp <IBinder>& who) { ALOGW("statscompanion service died"); StatsdStats::getInstance().noteSystemServerRestart(getWallClockSec()); diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h index af3016f86773..82a5a5305df4 100644 --- a/cmds/statsd/src/StatsService.h +++ b/cmds/statsd/src/StatsService.h @@ -27,8 +27,6 @@ #include "shell/ShellSubscriber.h" #include "statscompanion_util.h" -#include <android/frameworks/stats/1.0/IStats.h> -#include <android/frameworks/stats/1.0/types.h> #include <android/os/BnStatsd.h> #include <android/os/IPendingIntentRef.h> #include <android/os/IStatsCompanionService.h> @@ -41,7 +39,6 @@ using namespace android; using namespace android::binder; -using namespace android::frameworks::stats::V1_0; using namespace android::os; using namespace std; @@ -49,10 +46,7 @@ namespace android { namespace os { namespace statsd { -using android::hardware::Return; - class StatsService : public BnStatsd, - public IStats, public IBinder::DeathRecipient { public: StatsService(const sp<Looper>& handlerLooper, std::shared_ptr<LogEventQueue> queue); @@ -207,61 +201,6 @@ public: */ virtual Status getRegisteredExperimentIds(std::vector<int64_t>* expIdsOut); - /** - * Binder call to get SpeakerImpedance atom. - */ - virtual Return<void> reportSpeakerImpedance(const SpeakerImpedance& speakerImpedance) override; - - /** - * Binder call to get HardwareFailed atom. - */ - virtual Return<void> reportHardwareFailed(const HardwareFailed& hardwareFailed) override; - - /** - * Binder call to get PhysicalDropDetected atom. - */ - virtual Return<void> reportPhysicalDropDetected( - const PhysicalDropDetected& physicalDropDetected) override; - - /** - * Binder call to get ChargeCyclesReported atom. - */ - virtual Return<void> reportChargeCycles(const ChargeCycles& chargeCycles) override; - - /** - * Binder call to get BatteryHealthSnapshot atom. - */ - virtual Return<void> reportBatteryHealthSnapshot( - const BatteryHealthSnapshotArgs& batteryHealthSnapshotArgs) override; - - /** - * Binder call to get SlowIo atom. - */ - virtual Return<void> reportSlowIo(const SlowIo& slowIo) override; - - /** - * Binder call to get BatteryCausedShutdown atom. - */ - virtual Return<void> reportBatteryCausedShutdown( - const BatteryCausedShutdown& batteryCausedShutdown) override; - - /** - * Binder call to get UsbPortOverheatEvent atom. - */ - virtual Return<void> reportUsbPortOverheatEvent( - const UsbPortOverheatEvent& usbPortOverheatEvent) override; - - /** - * Binder call to get Speech DSP state atom. - */ - virtual Return<void> reportSpeechDspStat( - const SpeechDspStat& speechDspStat) override; - - /** - * Binder call to get vendor atom. - */ - virtual Return<void> reportVendorAtom(const VendorAtom& vendorAtom) override; - /** IBinder::DeathRecipient */ virtual void binderDied(const wp<IBinder>& who) override; diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 183d7411489c..71afc32686f1 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -391,6 +391,7 @@ message Atom { WifiHealthStatReported wifi_health_stat_reported = 251 [(module) = "wifi"]; WifiFailureStatReported wifi_failure_stat_reported = 252 [(module) = "wifi"]; WifiConnectionResultReported wifi_connection_result_reported = 253 [(module) = "wifi"]; + SdkExtensionStatus sdk_extension_status = 354; } // Pulled events will start at field 10000. @@ -8337,3 +8338,27 @@ message CellBroadcastMessageError { // Exception message (or log message) associated with the error (max 1000 chars) optional string exception_message = 2; } + +/** + * Logs when the SDK Extensions test app has polled the current version. + * This is atom ID 354. + * + * Logged from: + * vendor/google_testing/integration/packages/apps/SdkExtensionsTestApp/ + */ +message SdkExtensionStatus { + enum ApiCallStatus { + CALL_NOT_ATTEMPTED = 0; + CALL_SUCCESSFUL = 1; + CALL_FAILED = 2; + } + + optional ApiCallStatus result = 1; + + // The R extension version, i.e. android.os.ext.SdkExtension.getExtensionVersion(R). + optional int32 r_extension_version = 2; + + // A number identifying which particular symbol's call failed, if any. 0 means no missing symbol. + // "Failed" here can mean a symbol that wasn't meant to be visible was, or the other way around. + optional int32 failed_call_symbol = 3; +} diff --git a/cmds/statsd/src/external/GpuStatsPuller.cpp b/cmds/statsd/src/external/GpuStatsPuller.cpp deleted file mode 100644 index 3229ba82fe3c..000000000000 --- a/cmds/statsd/src/external/GpuStatsPuller.cpp +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "GpuStatsPuller.h" - -#include <binder/IServiceManager.h> -#include <graphicsenv/GpuStatsInfo.h> -#include <graphicsenv/IGpuService.h> - -#include "logd/LogEvent.h" - -#include "stats_log_util.h" -#include "statslog.h" - -namespace android { -namespace os { -namespace statsd { - -using android::util::ProtoReader; - -GpuStatsPuller::GpuStatsPuller(const int tagId) : StatsPuller(tagId) { -} - -static sp<IGpuService> getGpuService() { - const sp<IBinder> binder = defaultServiceManager()->checkService(String16("gpu")); - if (!binder) { - ALOGE("Failed to get gpu service"); - return nullptr; - } - - return interface_cast<IGpuService>(binder); -} - -static bool pullGpuStatsGlobalInfo(const sp<IGpuService>& gpuService, - std::vector<std::shared_ptr<LogEvent>>* data) { - std::vector<GpuStatsGlobalInfo> stats; - status_t status = gpuService->getGpuStatsGlobalInfo(&stats); - if (status != OK) { - return false; - } - - data->clear(); - data->reserve(stats.size()); - for (const auto& info : stats) { - std::shared_ptr<LogEvent> event = make_shared<LogEvent>( - android::util::GPU_STATS_GLOBAL_INFO, getWallClockNs(), getElapsedRealtimeNs()); - if (!event->write(info.driverPackageName)) return false; - if (!event->write(info.driverVersionName)) return false; - if (!event->write((int64_t)info.driverVersionCode)) return false; - if (!event->write(info.driverBuildTime)) return false; - if (!event->write((int64_t)info.glLoadingCount)) return false; - if (!event->write((int64_t)info.glLoadingFailureCount)) return false; - if (!event->write((int64_t)info.vkLoadingCount)) return false; - if (!event->write((int64_t)info.vkLoadingFailureCount)) return false; - if (!event->write(info.vulkanVersion)) return false; - if (!event->write(info.cpuVulkanVersion)) return false; - if (!event->write(info.glesVersion)) return false; - if (!event->write((int64_t)info.angleLoadingCount)) return false; - if (!event->write((int64_t)info.angleLoadingFailureCount)) return false; - event->init(); - data->emplace_back(event); - } - - return true; -} - -static bool pullGpuStatsAppInfo(const sp<IGpuService>& gpuService, - std::vector<std::shared_ptr<LogEvent>>* data) { - std::vector<GpuStatsAppInfo> stats; - status_t status = gpuService->getGpuStatsAppInfo(&stats); - if (status != OK) { - return false; - } - - data->clear(); - data->reserve(stats.size()); - for (const auto& info : stats) { - std::shared_ptr<LogEvent> event = make_shared<LogEvent>( - android::util::GPU_STATS_APP_INFO, getWallClockNs(), getElapsedRealtimeNs()); - if (!event->write(info.appPackageName)) return false; - if (!event->write((int64_t)info.driverVersionCode)) return false; - if (!event->writeBytes(int64VectorToProtoByteString(info.glDriverLoadingTime))) { - return false; - } - if (!event->writeBytes(int64VectorToProtoByteString(info.vkDriverLoadingTime))) { - return false; - } - if (!event->writeBytes(int64VectorToProtoByteString(info.angleDriverLoadingTime))) { - return false; - } - if (!event->write(info.cpuVulkanInUse)) return false; - if (!event->write(info.falsePrerotation)) return false; - if (!event->write(info.gles1InUse)) return false; - event->init(); - data->emplace_back(event); - } - - return true; -} - -bool GpuStatsPuller::PullInternal(std::vector<std::shared_ptr<LogEvent>>* data) { - const sp<IGpuService> gpuService = getGpuService(); - if (!gpuService) { - return false; - } - - switch (mTagId) { - case android::util::GPU_STATS_GLOBAL_INFO: - return pullGpuStatsGlobalInfo(gpuService, data); - case android::util::GPU_STATS_APP_INFO: - return pullGpuStatsAppInfo(gpuService, data); - default: - break; - } - - return false; -} - -static std::string protoOutputStreamToByteString(ProtoOutputStream& proto) { - if (!proto.size()) return ""; - - std::string byteString; - sp<ProtoReader> reader = proto.data(); - while (reader->readBuffer() != nullptr) { - const size_t toRead = reader->currentToRead(); - byteString.append((char*)reader->readBuffer(), toRead); - reader->move(toRead); - } - - if (byteString.size() != proto.size()) return ""; - - return byteString; -} - -std::string int64VectorToProtoByteString(const std::vector<int64_t>& value) { - if (value.empty()) return ""; - - ProtoOutputStream proto; - for (const auto& ele : value) { - proto.write(android::util::FIELD_TYPE_INT64 | android::util::FIELD_COUNT_REPEATED | - 1 /* field id */, - (long long)ele); - } - - return protoOutputStreamToByteString(proto); -} - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/external/GpuStatsPuller.h b/cmds/statsd/src/external/GpuStatsPuller.h deleted file mode 100644 index 2da199c51e0f..000000000000 --- a/cmds/statsd/src/external/GpuStatsPuller.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "StatsPuller.h" - -namespace android { -namespace os { -namespace statsd { - -/** - * Pull GpuStats from GpuService. - */ -class GpuStatsPuller : public StatsPuller { -public: - explicit GpuStatsPuller(const int tagId); - bool PullInternal(std::vector<std::shared_ptr<LogEvent>>* data) override; -}; - -// convert a int64_t vector into a byte string for proto message like: -// message RepeatedInt64Wrapper { -// repeated int64 value = 1; -// } -std::string int64VectorToProtoByteString(const std::vector<int64_t>& value); - -} // namespace statsd -} // namespace os -} // namespace android diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp index 15d7e33d63fd..3ceff7529440 100644 --- a/cmds/statsd/src/external/StatsPullerManager.cpp +++ b/cmds/statsd/src/external/StatsPullerManager.cpp @@ -32,7 +32,6 @@ #include "../logd/LogEvent.h" #include "../stats_log_util.h" #include "../statscompanion_util.h" -#include "GpuStatsPuller.h" #include "StatsCallbackPuller.h" #include "TrainInfoPuller.h" #include "statslog.h" @@ -51,15 +50,6 @@ StatsPullerManager::StatsPullerManager() : kAllPullAtomInfo({ // TrainInfo. {{.atomTag = android::util::TRAIN_INFO}, new TrainInfoPuller()}, - - // GpuStatsGlobalInfo - {{.atomTag = android::util::GPU_STATS_GLOBAL_INFO}, - new GpuStatsPuller(android::util::GPU_STATS_GLOBAL_INFO)}, - - // GpuStatsAppInfo - {{.atomTag = android::util::GPU_STATS_APP_INFO}, - new GpuStatsPuller(android::util::GPU_STATS_APP_INFO)}, - }), mNextPullTimeNs(NO_ALARM_UPDATE) { } diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp index e8fc603088a1..103eb0c78bd5 100644 --- a/cmds/statsd/src/logd/LogEvent.cpp +++ b/cmds/statsd/src/logd/LogEvent.cpp @@ -195,37 +195,6 @@ LogEvent::LogEvent(const string& trainName, int64_t trainVersionCode, bool requi } LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs, - const VendorAtom& vendorAtom) { - mLogdTimestampNs = wallClockTimestampNs; - mElapsedTimestampNs = elapsedTimestampNs; - mTagId = vendorAtom.atomId; - mLogUid = AID_STATSD; - - mValues.push_back( - FieldValue(Field(mTagId, getSimpleField(1)), Value(vendorAtom.reverseDomainName))); - for (int i = 0; i < (int)vendorAtom.values.size(); i++) { - switch (vendorAtom.values[i].getDiscriminator()) { - case VendorAtom::Value::hidl_discriminator::intValue: - mValues.push_back(FieldValue(Field(mTagId, getSimpleField(i + 2)), - Value(vendorAtom.values[i].intValue()))); - break; - case VendorAtom::Value::hidl_discriminator::longValue: - mValues.push_back(FieldValue(Field(mTagId, getSimpleField(i + 2)), - Value(vendorAtom.values[i].longValue()))); - break; - case VendorAtom::Value::hidl_discriminator::floatValue: - mValues.push_back(FieldValue(Field(mTagId, getSimpleField(i + 2)), - Value(vendorAtom.values[i].floatValue()))); - break; - case VendorAtom::Value::hidl_discriminator::stringValue: - mValues.push_back(FieldValue(Field(mTagId, getSimpleField(i + 2)), - Value(vendorAtom.values[i].stringValue()))); - break; - } - } -} - -LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs, const InstallTrainInfo& trainInfo) { mLogdTimestampNs = wallClockTimestampNs; mElapsedTimestampNs = elapsedTimestampNs; diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h index e4b784e069ca..5509c093a6f6 100644 --- a/cmds/statsd/src/logd/LogEvent.h +++ b/cmds/statsd/src/logd/LogEvent.h @@ -18,7 +18,6 @@ #include "FieldValue.h" -#include <android/frameworks/stats/1.0/types.h> #include <android/util/ProtoOutputStream.h> #include <private/android_logger.h> #include <stats_event_list.h> @@ -27,8 +26,6 @@ #include <string> #include <vector> -using namespace android::frameworks::stats::V1_0; - namespace android { namespace os { namespace statsd { @@ -103,9 +100,6 @@ public: const std::vector<uint8_t>& experimentIds, int32_t userId); explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs, - const VendorAtom& vendorAtom); - - explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs, const InstallTrainInfo& installTrainInfo); ~LogEvent(); diff --git a/cmds/statsd/src/main.cpp b/cmds/statsd/src/main.cpp index 58bfeb337da4..140ef4e9cea9 100644 --- a/cmds/statsd/src/main.cpp +++ b/cmds/statsd/src/main.cpp @@ -23,7 +23,6 @@ #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> #include <binder/ProcessState.h> -#include <hidl/HidlTransportSupport.h> #include <utils/Looper.h> #include <stdio.h> @@ -75,8 +74,6 @@ int main(int /*argc*/, char** /*argv*/) { ps->giveThreadPoolName(); IPCThreadState::self()->disableBackgroundScheduling(true); - ::android::hardware::configureRpcThreadpool(4 /*threads*/, false /*willJoin*/); - std::shared_ptr<LogEventQueue> eventQueue = std::make_shared<LogEventQueue>(2000 /*buffer limit. Buffer is NOT pre-allocated*/); @@ -89,12 +86,6 @@ int main(int /*argc*/, char** /*argv*/) { return -1; } - auto ret = gStatsService->registerAsService(); - if (ret != ::android::OK) { - ALOGE("Failed to add service as HIDL service"); - return 1; // or handle error - } - registerSigHandler(); gStatsService->sayHiToStatsCompanion(); diff --git a/cmds/statsd/src/subscriber/IncidentdReporter.cpp b/cmds/statsd/src/subscriber/IncidentdReporter.cpp index d86e29131661..30c90b1e1f71 100644 --- a/cmds/statsd/src/subscriber/IncidentdReporter.cpp +++ b/cmds/statsd/src/subscriber/IncidentdReporter.cpp @@ -21,10 +21,8 @@ #include "packages/UidMap.h" #include "stats_log_util.h" -#include <android/os/IIncidentManager.h> -#include <android/os/IncidentReportArgs.h> #include <android/util/ProtoOutputStream.h> -#include <binder/IServiceManager.h> +#include <incident/incident_report.h> #include <vector> @@ -132,7 +130,7 @@ bool GenerateIncidentReport(const IncidentdDetails& config, int64_t rule_id, int return false; } - IncidentReportArgs incidentReport; + android::os::IncidentReportRequest incidentReport; vector<uint8_t> protoData; getProtoData(rule_id, metricId, dimensionKey, metricValue, configKey, @@ -146,30 +144,21 @@ bool GenerateIncidentReport(const IncidentdDetails& config, int64_t rule_id, int uint8_t dest; switch (config.dest()) { case IncidentdDetails_Destination_AUTOMATIC: - dest = android::os::PRIVACY_POLICY_AUTOMATIC; + dest = INCIDENT_REPORT_PRIVACY_POLICY_AUTOMATIC; break; case IncidentdDetails_Destination_EXPLICIT: - dest = android::os::PRIVACY_POLICY_EXPLICIT; + dest = INCIDENT_REPORT_PRIVACY_POLICY_EXPLICIT; break; default: - dest = android::os::PRIVACY_POLICY_AUTOMATIC; + dest = INCIDENT_REPORT_PRIVACY_POLICY_AUTOMATIC; } incidentReport.setPrivacyPolicy(dest); - incidentReport.setReceiverPkg(config.receiver_pkg()); + incidentReport.setReceiverPackage(config.receiver_pkg()); - incidentReport.setReceiverCls(config.receiver_cls()); + incidentReport.setReceiverClass(config.receiver_cls()); - sp<IIncidentManager> service = interface_cast<IIncidentManager>( - defaultServiceManager()->getService(android::String16("incident"))); - if (service == nullptr) { - ALOGW("Failed to fetch incident service."); - return false; - } - VLOG("Calling incidentd %p", service.get()); - binder::Status s = service->reportIncident(incidentReport); - VLOG("Report incident status: %s", s.toString8().string()); - return s.isOk(); + return incidentReport.takeReport() == NO_ERROR; } } // namespace statsd diff --git a/cmds/statsd/tests/external/GpuStatsPuller_test.cpp b/cmds/statsd/tests/external/GpuStatsPuller_test.cpp deleted file mode 100644 index ae92705aff4c..000000000000 --- a/cmds/statsd/tests/external/GpuStatsPuller_test.cpp +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#undef LOG_TAG -#define LOG_TAG "GpuStatsPuller_test" - -#include <gmock/gmock.h> -#include <gtest/gtest.h> - -#include <graphicsenv/GpuStatsInfo.h> -#include <log/log.h> - -#include "src/external/GpuStatsPuller.h" -#include "statslog.h" - -#ifdef __ANDROID__ - -namespace android { -namespace os { -namespace statsd { - -// clang-format off -static const std::string DRIVER_PACKAGE_NAME = "TEST_DRIVER"; -static const std::string DRIVER_VERSION_NAME = "TEST_DRIVER_VERSION"; -static const std::string APP_PACKAGE_NAME = "TEST_APP"; -static const int64_t TIMESTAMP_WALLCLOCK = 111; -static const int64_t TIMESTAMP_ELAPSED = 222; -static const int64_t DRIVER_VERSION_CODE = 333; -static const int64_t DRIVER_BUILD_TIME = 444; -static const int64_t GL_LOADING_COUNT = 3; -static const int64_t GL_LOADING_FAILURE_COUNT = 1; -static const int64_t VK_LOADING_COUNT = 4; -static const int64_t VK_LOADING_FAILURE_COUNT = 0; -static const int64_t ANGLE_LOADING_COUNT = 2; -static const int64_t ANGLE_LOADING_FAILURE_COUNT = 1; -static const int64_t GL_DRIVER_LOADING_TIME_0 = 555; -static const int64_t GL_DRIVER_LOADING_TIME_1 = 666; -static const int64_t VK_DRIVER_LOADING_TIME_0 = 777; -static const int64_t VK_DRIVER_LOADING_TIME_1 = 888; -static const int64_t VK_DRIVER_LOADING_TIME_2 = 999; -static const int64_t ANGLE_DRIVER_LOADING_TIME_0 = 1010; -static const int64_t ANGLE_DRIVER_LOADING_TIME_1 = 1111; -static const int32_t VULKAN_VERSION = 1; -static const int32_t CPU_VULKAN_VERSION = 2; -static const int32_t GLES_VERSION = 3; -static const bool CPU_VULKAN_IN_USE = true; -static const bool FALSE_PREROTATION = true; -static const bool GLES_1_IN_USE = true; -static const size_t NUMBER_OF_VALUES_GLOBAL = 13; -static const size_t NUMBER_OF_VALUES_APP = 8; -// clang-format on - -class MockGpuStatsPuller : public GpuStatsPuller { -public: - MockGpuStatsPuller(const int tagId, vector<std::shared_ptr<LogEvent>>* data) - : GpuStatsPuller(tagId), mData(data){}; - -private: - bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override { - *data = *mData; - return true; - } - - vector<std::shared_ptr<LogEvent>>* mData; -}; - -class GpuStatsPuller_test : public ::testing::Test { -public: - GpuStatsPuller_test() { - const ::testing::TestInfo* const test_info = - ::testing::UnitTest::GetInstance()->current_test_info(); - ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); - } - - ~GpuStatsPuller_test() { - const ::testing::TestInfo* const test_info = - ::testing::UnitTest::GetInstance()->current_test_info(); - ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name()); - } -}; - -TEST_F(GpuStatsPuller_test, PullGpuStatsGlobalInfo) { - vector<std::shared_ptr<LogEvent>> inData, outData; - std::shared_ptr<LogEvent> event = make_shared<LogEvent>(android::util::GPU_STATS_GLOBAL_INFO, - TIMESTAMP_WALLCLOCK, TIMESTAMP_ELAPSED); - EXPECT_TRUE(event->write(DRIVER_PACKAGE_NAME)); - EXPECT_TRUE(event->write(DRIVER_VERSION_NAME)); - EXPECT_TRUE(event->write(DRIVER_VERSION_CODE)); - EXPECT_TRUE(event->write(DRIVER_BUILD_TIME)); - EXPECT_TRUE(event->write(GL_LOADING_COUNT)); - EXPECT_TRUE(event->write(GL_LOADING_FAILURE_COUNT)); - EXPECT_TRUE(event->write(VK_LOADING_COUNT)); - EXPECT_TRUE(event->write(VK_LOADING_FAILURE_COUNT)); - EXPECT_TRUE(event->write(VULKAN_VERSION)); - EXPECT_TRUE(event->write(CPU_VULKAN_VERSION)); - EXPECT_TRUE(event->write(GLES_VERSION)); - EXPECT_TRUE(event->write(ANGLE_LOADING_COUNT)); - EXPECT_TRUE(event->write(ANGLE_LOADING_FAILURE_COUNT)); - event->init(); - inData.emplace_back(event); - MockGpuStatsPuller mockPuller(android::util::GPU_STATS_GLOBAL_INFO, &inData); - mockPuller.ForceClearCache(); - mockPuller.Pull(&outData); - - ASSERT_EQ(1, outData.size()); - EXPECT_EQ(android::util::GPU_STATS_GLOBAL_INFO, outData[0]->GetTagId()); - ASSERT_EQ(NUMBER_OF_VALUES_GLOBAL, outData[0]->size()); - EXPECT_EQ(DRIVER_PACKAGE_NAME, outData[0]->getValues()[0].mValue.str_value); - EXPECT_EQ(DRIVER_VERSION_NAME, outData[0]->getValues()[1].mValue.str_value); - EXPECT_EQ(DRIVER_VERSION_CODE, outData[0]->getValues()[2].mValue.long_value); - EXPECT_EQ(DRIVER_BUILD_TIME, outData[0]->getValues()[3].mValue.long_value); - EXPECT_EQ(GL_LOADING_COUNT, outData[0]->getValues()[4].mValue.long_value); - EXPECT_EQ(GL_LOADING_FAILURE_COUNT, outData[0]->getValues()[5].mValue.long_value); - EXPECT_EQ(VK_LOADING_COUNT, outData[0]->getValues()[6].mValue.long_value); - EXPECT_EQ(VK_LOADING_FAILURE_COUNT, outData[0]->getValues()[7].mValue.long_value); - EXPECT_EQ(VULKAN_VERSION, outData[0]->getValues()[8].mValue.int_value); - EXPECT_EQ(CPU_VULKAN_VERSION, outData[0]->getValues()[9].mValue.int_value); - EXPECT_EQ(GLES_VERSION, outData[0]->getValues()[10].mValue.int_value); - EXPECT_EQ(ANGLE_LOADING_COUNT, outData[0]->getValues()[11].mValue.long_value); - EXPECT_EQ(ANGLE_LOADING_FAILURE_COUNT, outData[0]->getValues()[12].mValue.long_value); -} - -TEST_F(GpuStatsPuller_test, PullGpuStatsAppInfo) { - vector<std::shared_ptr<LogEvent>> inData, outData; - std::shared_ptr<LogEvent> event = make_shared<LogEvent>(android::util::GPU_STATS_APP_INFO, - TIMESTAMP_WALLCLOCK, TIMESTAMP_ELAPSED); - EXPECT_TRUE(event->write(APP_PACKAGE_NAME)); - EXPECT_TRUE(event->write(DRIVER_VERSION_CODE)); - std::vector<int64_t> glDriverLoadingTime; - glDriverLoadingTime.emplace_back(GL_DRIVER_LOADING_TIME_0); - glDriverLoadingTime.emplace_back(GL_DRIVER_LOADING_TIME_1); - std::vector<int64_t> vkDriverLoadingTime; - vkDriverLoadingTime.emplace_back(VK_DRIVER_LOADING_TIME_0); - vkDriverLoadingTime.emplace_back(VK_DRIVER_LOADING_TIME_1); - vkDriverLoadingTime.emplace_back(VK_DRIVER_LOADING_TIME_2); - std::vector<int64_t> angleDriverLoadingTime; - angleDriverLoadingTime.emplace_back(ANGLE_DRIVER_LOADING_TIME_0); - angleDriverLoadingTime.emplace_back(ANGLE_DRIVER_LOADING_TIME_1); - EXPECT_TRUE(event->write(int64VectorToProtoByteString(glDriverLoadingTime))); - EXPECT_TRUE(event->write(int64VectorToProtoByteString(vkDriverLoadingTime))); - EXPECT_TRUE(event->write(int64VectorToProtoByteString(angleDriverLoadingTime))); - EXPECT_TRUE(event->write(CPU_VULKAN_IN_USE)); - EXPECT_TRUE(event->write(FALSE_PREROTATION)); - EXPECT_TRUE(event->write(GLES_1_IN_USE)); - event->init(); - inData.emplace_back(event); - MockGpuStatsPuller mockPuller(android::util::GPU_STATS_APP_INFO, &inData); - mockPuller.ForceClearCache(); - mockPuller.Pull(&outData); - - ASSERT_EQ(1, outData.size()); - EXPECT_EQ(android::util::GPU_STATS_APP_INFO, outData[0]->GetTagId()); - ASSERT_EQ(NUMBER_OF_VALUES_APP, outData[0]->size()); - EXPECT_EQ(APP_PACKAGE_NAME, outData[0]->getValues()[0].mValue.str_value); - EXPECT_EQ(DRIVER_VERSION_CODE, outData[0]->getValues()[1].mValue.long_value); - EXPECT_EQ(int64VectorToProtoByteString(glDriverLoadingTime), - outData[0]->getValues()[2].mValue.str_value); - EXPECT_EQ(int64VectorToProtoByteString(vkDriverLoadingTime), - outData[0]->getValues()[3].mValue.str_value); - EXPECT_EQ(int64VectorToProtoByteString(angleDriverLoadingTime), - outData[0]->getValues()[4].mValue.str_value); - EXPECT_EQ(CPU_VULKAN_IN_USE, outData[0]->getValues()[5].mValue.int_value); - EXPECT_EQ(FALSE_PREROTATION, outData[0]->getValues()[6].mValue.int_value); - EXPECT_EQ(GLES_1_IN_USE, outData[0]->getValues()[7].mValue.int_value); -} - -} // namespace statsd -} // namespace os -} // namespace android -#else -GTEST_LOG_(INFO) << "This test does nothing.\n"; -#endif diff --git a/core/java/android/accessibilityservice/AccessibilityGestureEvent.java b/core/java/android/accessibilityservice/AccessibilityGestureEvent.java index 9cf1de93e344..ace13513e39d 100644 --- a/core/java/android/accessibilityservice/AccessibilityGestureEvent.java +++ b/core/java/android/accessibilityservice/AccessibilityGestureEvent.java @@ -31,6 +31,13 @@ import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_RIGHT; import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_UP; import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_TRIPLE_TAP; +import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_DOUBLE_TAP; +import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_SINGLE_TAP; +import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_SWIPE_DOWN; +import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_SWIPE_LEFT; +import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_SWIPE_RIGHT; +import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_SWIPE_UP; +import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_TRIPLE_TAP; import static android.accessibilityservice.AccessibilityService.GESTURE_DOUBLE_TAP; import static android.accessibilityservice.AccessibilityService.GESTURE_DOUBLE_TAP_AND_HOLD; import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_DOWN; @@ -105,7 +112,14 @@ public final class AccessibilityGestureEvent implements Parcelable { GESTURE_3_FINGER_SWIPE_DOWN, GESTURE_3_FINGER_SWIPE_LEFT, GESTURE_3_FINGER_SWIPE_RIGHT, - GESTURE_3_FINGER_SWIPE_UP + GESTURE_3_FINGER_SWIPE_UP, + GESTURE_4_FINGER_DOUBLE_TAP, + GESTURE_4_FINGER_SINGLE_TAP, + GESTURE_4_FINGER_SWIPE_DOWN, + GESTURE_4_FINGER_SWIPE_LEFT, + GESTURE_4_FINGER_SWIPE_RIGHT, + GESTURE_4_FINGER_SWIPE_UP, + GESTURE_4_FINGER_TRIPLE_TAP }) @Retention(RetentionPolicy.SOURCE) public @interface GestureId {} @@ -165,6 +179,9 @@ public final class AccessibilityGestureEvent implements Parcelable { case GESTURE_3_FINGER_SINGLE_TAP: return "GESTURE_3_FINGER_SINGLE_TAP"; case GESTURE_3_FINGER_DOUBLE_TAP: return "GESTURE_3_FINGER_DOUBLE_TAP"; case GESTURE_3_FINGER_TRIPLE_TAP: return "GESTURE_3_FINGER_TRIPLE_TAP"; + case GESTURE_4_FINGER_SINGLE_TAP: return "GESTURE_4_FINGER_SINGLE_TAP"; + case GESTURE_4_FINGER_DOUBLE_TAP: return "GESTURE_4_FINGER_DOUBLE_TAP"; + case GESTURE_4_FINGER_TRIPLE_TAP: return "GESTURE_4_FINGER_TRIPLE_TAP"; case GESTURE_DOUBLE_TAP: return "GESTURE_DOUBLE_TAP"; case GESTURE_DOUBLE_TAP_AND_HOLD: return "GESTURE_DOUBLE_TAP_AND_HOLD"; case GESTURE_SWIPE_DOWN: return "GESTURE_SWIPE_DOWN"; @@ -191,6 +208,10 @@ public final class AccessibilityGestureEvent implements Parcelable { case GESTURE_3_FINGER_SWIPE_LEFT: return "GESTURE_3_FINGER_SWIPE_LEFT"; case GESTURE_3_FINGER_SWIPE_RIGHT: return "GESTURE_3_FINGER_SWIPE_RIGHT"; case GESTURE_3_FINGER_SWIPE_UP: return "GESTURE_3_FINGER_SWIPE_UP"; + case GESTURE_4_FINGER_SWIPE_DOWN: return "GESTURE_4_FINGER_SWIPE_DOWN"; + case GESTURE_4_FINGER_SWIPE_LEFT: return "GESTURE_4_FINGER_SWIPE_LEFT"; + case GESTURE_4_FINGER_SWIPE_RIGHT: return "GESTURE_4_FINGER_SWIPE_RIGHT"; + case GESTURE_4_FINGER_SWIPE_UP: return "GESTURE_4_FINGER_SWIPE_UP"; default: return Integer.toHexString(eventType); } } diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index 2165fb35a0e5..b65f68e177ca 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -28,7 +28,9 @@ import android.content.Context; import android.content.Intent; import android.content.pm.ParceledListSlice; import android.graphics.Bitmap; +import android.graphics.ColorSpace; import android.graphics.Region; +import android.hardware.HardwareBuffer; import android.os.Binder; import android.os.Build; import android.os.Handler; @@ -388,6 +390,27 @@ public abstract class AccessibilityService extends Service { */ public static final int GESTURE_3_FINGER_SWIPE_RIGHT = 32; + /** The user has performed a four-finger swipe up gesture on the touch screen. */ + public static final int GESTURE_4_FINGER_SWIPE_UP = 33; + + /** The user has performed a four-finger swipe down gesture on the touch screen. */ + public static final int GESTURE_4_FINGER_SWIPE_DOWN = 34; + + /** The user has performed a four-finger swipe left gesture on the touch screen. */ + public static final int GESTURE_4_FINGER_SWIPE_LEFT = 35; + + /** The user has performed a four-finger swipe right gesture on the touch screen. */ + public static final int GESTURE_4_FINGER_SWIPE_RIGHT = 36; + + /** The user has performed a four-finger single tap gesture on the touch screen. */ + public static final int GESTURE_4_FINGER_SINGLE_TAP = 37; + + /** The user has performed a four-finger double tap gesture on the touch screen. */ + public static final int GESTURE_4_FINGER_DOUBLE_TAP = 38; + + /** The user has performed a four-finger triple tap gesture on the touch screen. */ + public static final int GESTURE_4_FINGER_TRIPLE_TAP = 39; + /** * The {@link Intent} that must be declared as handled by the service. */ @@ -564,7 +587,12 @@ public abstract class AccessibilityService extends Service { private FingerprintGestureController mFingerprintGestureController; /** @hide */ - public static final String KEY_ACCESSIBILITY_SCREENSHOT = "screenshot"; + public static final String KEY_ACCESSIBILITY_SCREENSHOT_HARDWAREBUFFER = + "screenshot_hardwareBuffer"; + + /** @hide */ + public static final String KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE_ID = + "screenshot_colorSpaceId"; /** * Callback for {@link android.view.accessibility.AccessibilityEvent}s. @@ -1867,8 +1895,9 @@ public abstract class AccessibilityService extends Service { } /** - * Takes a screenshot of the specified display and returns it by {@link Bitmap.Config#HARDWARE} - * format. + * Takes a screenshot of the specified display and returns it via an + * {@link AccessibilityService.ScreenshotResult}. You can use {@link Bitmap#wrapHardwareBuffer} + * to construct the bitmap from the ScreenshotResult's payload. * <p> * <strong>Note:</strong> In order to take screenshot your service has * to declare the capability to take screenshot by setting the @@ -1886,7 +1915,7 @@ public abstract class AccessibilityService extends Service { * @return {@code true} if the taking screenshot accepted, {@code false} if not. */ public boolean takeScreenshot(int displayId, @NonNull @CallbackExecutor Executor executor, - @NonNull Consumer<Bitmap> callback) { + @NonNull Consumer<ScreenshotResult> callback) { Preconditions.checkNotNull(executor, "executor cannot be null"); Preconditions.checkNotNull(callback, "callback cannot be null"); final IAccessibilityServiceConnection connection = @@ -1896,14 +1925,22 @@ public abstract class AccessibilityService extends Service { return false; } try { - connection.takeScreenshotWithCallback(displayId, new RemoteCallback((result) -> { - final Bitmap screenshot = result.getParcelable(KEY_ACCESSIBILITY_SCREENSHOT); - final long identity = Binder.clearCallingIdentity(); - try { - executor.execute(() -> callback.accept(screenshot)); - } finally { - Binder.restoreCallingIdentity(identity); + connection.takeScreenshot(displayId, new RemoteCallback((result) -> { + if (result == null) { + sendScreenshotResult(executor, callback, null); + return; + } + final HardwareBuffer hardwareBuffer = + result.getParcelable(KEY_ACCESSIBILITY_SCREENSHOT_HARDWAREBUFFER); + final int colorSpaceId = + result.getInt(KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE_ID); + ColorSpace colorSpace = null; + if (colorSpaceId >= 0 && colorSpaceId < ColorSpace.Named.values().length) { + colorSpace = ColorSpace.get(ColorSpace.Named.values()[colorSpaceId]); } + ScreenshotResult screenshot = new ScreenshotResult(hardwareBuffer, + colorSpace, System.currentTimeMillis()); + sendScreenshotResult(executor, callback, screenshot); })); } catch (RemoteException re) { throw new RuntimeException(re); @@ -2302,4 +2339,67 @@ public abstract class AccessibilityService extends Service { this.handler = handler; } } + + private void sendScreenshotResult(Executor executor, Consumer<ScreenshotResult> callback, + ScreenshotResult screenshot) { + final ScreenshotResult result = screenshot; + final long identity = Binder.clearCallingIdentity(); + try { + executor.execute(() -> callback.accept(result)); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + /** + * Class including hardwareBuffer, colorSpace, and timestamp to be the result for + * {@link AccessibilityService#takeScreenshot} API. + * <p> + * <strong>Note:</strong> colorSpace would be null if the name of this colorSpace isn't at + * {@link ColorSpace.Named}. + * </p> + */ + public static final class ScreenshotResult { + private final @NonNull HardwareBuffer mHardwareBuffer; + private final @Nullable ColorSpace mColorSpace; + private final long mTimestamp; + + private ScreenshotResult(@NonNull HardwareBuffer hardwareBuffer, + @Nullable ColorSpace colorSpace, long timestamp) { + Preconditions.checkNotNull(hardwareBuffer, "hardwareBuffer cannot be null"); + mHardwareBuffer = hardwareBuffer; + mColorSpace = colorSpace; + mTimestamp = timestamp; + } + + /** + * Gets the colorSpace identifying a specific organization of colors of the screenshot. + * + * @return the colorSpace or {@code null} if the name of colorSpace isn't at + * {@link ColorSpace.Named} + */ + @Nullable + public ColorSpace getColorSpace() { + return mColorSpace; + } + + /** + * Gets the hardwareBuffer representing a memory buffer of the screenshot. + * + * @return the hardwareBuffer + */ + @NonNull + public HardwareBuffer getHardwareBuffer() { + return mHardwareBuffer; + } + + /** + * Gets the timestamp of taking the screenshot. + * + * @return the timestamp from {@link System#currentTimeMillis()} + */ + public long getTimestamp() { + return mTimestamp; + }; + } } diff --git a/core/java/android/accessibilityservice/GestureDescription.java b/core/java/android/accessibilityservice/GestureDescription.java index 3b79d217c513..a821dadf4948 100644 --- a/core/java/android/accessibilityservice/GestureDescription.java +++ b/core/java/android/accessibilityservice/GestureDescription.java @@ -40,7 +40,7 @@ import java.util.List; */ public final class GestureDescription { /** Gestures may contain no more than this many strokes */ - private static final int MAX_STROKE_COUNT = 10; + private static final int MAX_STROKE_COUNT = 20; /** * Upper bound on total gesture duration. Nearly all gestures will be much shorter. @@ -194,7 +194,10 @@ public final class GestureDescription { public Builder addStroke(@NonNull StrokeDescription strokeDescription) { if (mStrokes.size() >= MAX_STROKE_COUNT) { throw new IllegalStateException( - "Attempting to add too many strokes to a gesture"); + "Attempting to add too many strokes to a gesture. Maximum is " + + MAX_STROKE_COUNT + + ", got " + + mStrokes.size()); } mStrokes.add(strokeDescription); diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl index 5db4dd7470d8..9177d4d27491 100644 --- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl +++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl @@ -110,7 +110,5 @@ interface IAccessibilityServiceConnection { int getWindowIdForLeashToken(IBinder token); - Bitmap takeScreenshot(int displayId); - - void takeScreenshotWithCallback(int displayId, in RemoteCallback callback); + void takeScreenshot(int displayId, in RemoteCallback callback); } diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index f31c6148f89f..642f51b6bb63 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -2880,13 +2880,14 @@ public class Activity extends ContextThemeWrapper * {@link #enterPictureInPictureMode(PictureInPictureParams)} at this time. For example, the * system will call this method when the activity is being put into the background, so the app * developer might want to switch an activity into PIP mode instead.</p> + * + * @return {@code true} if the activity received this callback regardless of if it acts on it + * or not. If {@code false}, the framework will assume the app hasn't been updated to leverage + * this callback and will in turn send a legacy callback of {@link #onUserLeaveHint()} for the + * app to enter picture-in-picture mode. */ - public void onPictureInPictureRequested() { - // Previous recommendation was for apps to enter picture-in-picture in onUserLeaveHint() - // which is sent after onPause(). This new method allows the system to request the app to - // go into picture-in-picture decoupling it from life cycle events. For backwards - // compatibility we schedule the life cycle events if the app didn't override this method. - mMainThread.schedulePauseAndReturnToCurrentState(mToken); + public boolean onPictureInPictureRequested() { + return false; } void dispatchMovedToDisplay(int displayId, Configuration config) { diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 206c7710c12f..db9aa18dbd5a 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -1472,7 +1472,7 @@ public class ActivityManager { dest.writeInt(1); dest.writeString(mLabel); } - if (mIcon == null) { + if (mIcon == null || mIcon.isRecycled()) { dest.writeInt(0); } else { dest.writeInt(1); diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index c901d2a29821..192156726984 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -3772,7 +3772,15 @@ public final class ActivityThread extends ClientTransactionHandler { return; } - r.activity.onPictureInPictureRequested(); + final boolean receivedByApp = r.activity.onPictureInPictureRequested(); + if (!receivedByApp) { + // Previous recommendation was for apps to enter picture-in-picture in + // onUserLeavingHint() for cases such as the app being put into the background. For + // backwards compatibility with apps that are not using the newer + // onPictureInPictureRequested() callback, we schedule the life cycle events needed to + // trigger onUserLeavingHint(), then we return the activity to its previous state. + schedulePauseWithUserLeaveHintAndReturnToCurrentState(r); + } } /** @@ -3780,18 +3788,7 @@ public final class ActivityThread extends ClientTransactionHandler { * return to its previous state. This allows activities that rely on onUserLeaveHint instead of * onPictureInPictureRequested to enter picture-in-picture. */ - public void schedulePauseAndReturnToCurrentState(IBinder token) { - final ActivityClientRecord r = mActivities.get(token); - if (r == null) { - Log.w(TAG, "Activity to request pause with user leaving hint to no longer exists"); - return; - } - - if (r.mIsUserLeaving) { - // The activity is about to perform user leaving, so there's no need to cycle ourselves. - return; - } - + private void schedulePauseWithUserLeaveHintAndReturnToCurrentState(ActivityClientRecord r) { final int prevState = r.getLifecycleState(); if (prevState != ON_RESUME && prevState != ON_PAUSE) { return; @@ -4544,7 +4541,6 @@ public final class ActivityThread extends ClientTransactionHandler { if (r != null) { if (userLeaving) { performUserLeavingActivity(r); - r.mIsUserLeaving = false; } r.activity.mConfigChangeFlags |= configChanges; @@ -4559,7 +4555,6 @@ public final class ActivityThread extends ClientTransactionHandler { } final void performUserLeavingActivity(ActivityClientRecord r) { - r.mIsUserLeaving = true; mInstrumentation.callActivityOnPictureInPictureRequested(r.activity); mInstrumentation.callActivityOnUserLeaving(r.activity); } diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 861d41d4ce88..6ef99a3def92 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -27,6 +27,8 @@ import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; import android.app.usage.UsageStatsManager; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledAfter; import android.compat.annotation.UnsupportedAppUsage; import android.content.ContentResolver; import android.content.Context; @@ -101,6 +103,18 @@ import java.util.function.Supplier; @SystemService(Context.APP_OPS_SERVICE) public class AppOpsManager { /** + * This is a subtle behavior change to {@link #startWatchingMode}. + * + * Before this change the system called back for the switched op. After the change the system + * will call back for the actually requested op or all switched ops if no op is specified. + * + * @hide + */ + @ChangeId + @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q) + public static final long CALL_BACK_ON_CHANGED_LISTENER_WITH_SWITCHED_OP_CHANGE = 148180766L; + + /** * <p>App ops allows callers to:</p> * * <ul> @@ -147,6 +161,38 @@ public class AppOpsManager { static IBinder sClientId; + /** + * How many seconds we want for a drop in uid state from top to settle before applying it. + * + * <>Set a parameter to {@link android.provider.Settings.Global#APP_OPS_CONSTANTS} + * + * @hide + */ + @TestApi + public static final String KEY_TOP_STATE_SETTLE_TIME = "top_state_settle_time"; + + /** + * How many second we want for a drop in uid state from foreground to settle before applying it. + * + * <>Set a parameter to {@link android.provider.Settings.Global#APP_OPS_CONSTANTS} + * + * @hide + */ + @TestApi + public static final String KEY_FG_SERVICE_STATE_SETTLE_TIME = + "fg_service_state_settle_time"; + + /** + * How many seconds we want for a drop in uid state from background to settle before applying + * it. + * + * <>Set a parameter to {@link android.provider.Settings.Global#APP_OPS_CONSTANTS} + * + * @hide + */ + @TestApi + public static final String KEY_BG_STATE_SETTLE_TIME = "bg_state_settle_time"; + /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(flag = true, prefix = { "HISTORICAL_MODE_" }, value = { @@ -2808,7 +2854,7 @@ public class AppOpsManager { * @param flags The op flags * * @return the last access time (in milliseconds since epoch start (January 1, 1970 - * 00:00:00.000 GMT - Gregorian)) or {@code -1} + * 00:00:00.000 GMT - Gregorian)) or {@code -1} if there was no access * * @see #getLastAccessForegroundTime(int) * @see #getLastAccessBackgroundTime(int) @@ -2825,7 +2871,7 @@ public class AppOpsManager { * @param flags The op flags * * @return the last access time (in milliseconds since epoch start (January 1, 1970 - * 00:00:00.000 GMT - Gregorian)) or {@code -1} + * 00:00:00.000 GMT - Gregorian)) or {@code -1} if there was no foreground access * * @see #getLastAccessTime(int) * @see #getLastAccessBackgroundTime(int) @@ -2843,7 +2889,7 @@ public class AppOpsManager { * @param flags The op flags * * @return the last access time (in milliseconds since epoch start (January 1, 1970 - * 00:00:00.000 GMT - Gregorian)) or {@code -1} + * 00:00:00.000 GMT - Gregorian)) or {@code -1} if there was no background access * * @see #getLastAccessTime(int) * @see #getLastAccessForegroundTime(int) @@ -2860,7 +2906,7 @@ public class AppOpsManager { * * @param flags The op flags * - * @return the last access event of {@code null} + * @return the last access event of {@code null} if there was no access */ private @Nullable NoteOpEvent getLastAccessEvent(@UidState int fromUidState, @UidState int toUidState, @OpFlags int flags) { @@ -2875,7 +2921,7 @@ public class AppOpsManager { * @param flags The op flags * * @return the last access time (in milliseconds since epoch start (January 1, 1970 - * 00:00:00.000 GMT - Gregorian)) or {@code -1} + * 00:00:00.000 GMT - Gregorian)) or {@code -1} if there was no access * * @see #getLastAccessTime(int) * @see #getLastAccessForegroundTime(int) @@ -2898,7 +2944,7 @@ public class AppOpsManager { * @param flags The op flags * * @return the last rejection time (in milliseconds since epoch start (January 1, 1970 - * 00:00:00.000 GMT - Gregorian)) or {@code -1} + * 00:00:00.000 GMT - Gregorian)) or {@code -1} if there was no rejection * * @see #getLastRejectForegroundTime(int) * @see #getLastRejectBackgroundTime(int) @@ -2915,7 +2961,7 @@ public class AppOpsManager { * @param flags The op flags * * @return the last rejection time (in milliseconds since epoch start (January 1, 1970 - * 00:00:00.000 GMT - Gregorian)) or {@code -1} + * 00:00:00.000 GMT - Gregorian)) or {@code -1} if there was no foreground rejection * * @see #getLastRejectTime(int) * @see #getLastRejectBackgroundTime(int) @@ -2933,7 +2979,7 @@ public class AppOpsManager { * @param flags The op flags * * @return the last rejection time (in milliseconds since epoch start (January 1, 1970 - * 00:00:00.000 GMT - Gregorian)) or {@code -1} + * 00:00:00.000 GMT - Gregorian)) or {@code -1} if there was no background rejection * * @see #getLastRejectTime(int) * @see #getLastRejectForegroundTime(int) @@ -2950,8 +2996,7 @@ public class AppOpsManager { * * @param flags The op flags * - * @return the last rejection time (in milliseconds since epoch start (January 1, 1970 - * 00:00:00.000 GMT - Gregorian)) or {@code -1} + * @return the last rejection event of {@code null} if there was no rejection * * @see #getLastRejectTime(int) * @see #getLastRejectForegroundTime(int) @@ -2970,7 +3015,8 @@ public class AppOpsManager { * @param toUidState The highest UID state for which to query (inclusive) * @param flags The op flags * - * @return the last access time (in milliseconds since epoch) or {@code -1} + * @return the last access time (in milliseconds since epoch) or {@code -1} if there was no + * rejection * * @see #getLastRejectTime(int) * @see #getLastRejectForegroundTime(int) @@ -2993,7 +3039,7 @@ public class AppOpsManager { * * @param flags The op flags * - * @return the duration in milliseconds or {@code -1} + * @return the duration in milliseconds or {@code -1} if there was no rejection * * @see #getLastForegroundDuration(int) * @see #getLastBackgroundDuration(int) @@ -3009,7 +3055,7 @@ public class AppOpsManager { * * @param flags The op flags * - * @return the duration in milliseconds or {@code -1} + * @return the duration in milliseconds or {@code -1} if there was no foreground rejection * * @see #getLastDuration(int) * @see #getLastBackgroundDuration(int) @@ -3026,7 +3072,7 @@ public class AppOpsManager { * * @param flags The op flags * - * @return the duration in milliseconds or {@code -1} + * @return the duration in milliseconds or {@code -1} if there was no background rejection * * @see #getLastDuration(int) * @see #getLastForegroundDuration(int) @@ -3045,7 +3091,7 @@ public class AppOpsManager { * @param toUidState The highest UID state for which to query (inclusive) * @param flags The op flags * - * @return the duration in milliseconds or {@code -1} + * @return the duration in milliseconds or {@code -1} if there was no rejection * * @see #getLastDuration(int) * @see #getLastForegroundDuration(int) @@ -3069,7 +3115,7 @@ public class AppOpsManager { * * @param flags The op flags * - * @return The proxy name or {@code null} + * @return The proxy info or {@code null} if there was no proxy access * * @see #getLastForegroundProxyInfo(int) * @see #getLastBackgroundProxyInfo(int) @@ -3086,7 +3132,7 @@ public class AppOpsManager { * * @param flags The op flags * - * @return The proxy name or {@code null} + * @return The proxy info or {@code null} if there was no proxy access * * @see #getLastProxyInfo(int) * @see #getLastBackgroundProxyInfo(int) @@ -3104,7 +3150,7 @@ public class AppOpsManager { * * @param flags The op flags * - * @return The proxy name or {@code null} + * @return The proxy info or {@code null} if there was no proxy background access * * @see #getLastProxyInfo(int) * @see #getLastForegroundProxyInfo(int) @@ -3124,7 +3170,7 @@ public class AppOpsManager { * @param toUidState The highest UID state for which to query (inclusive) * @param flags The op flags * - * @return The proxy name or {@code null} + * @return The proxy info or {@code null} if there was no proxy foreground access * * @see #getLastProxyInfo(int) * @see #getLastForegroundProxyInfo(int) @@ -3380,7 +3426,7 @@ public class AppOpsManager { * @param flags The op flags * * @return the last access time (in milliseconds since epoch start (January 1, 1970 - * 00:00:00.000 GMT - Gregorian)) or {@code -1} + * 00:00:00.000 GMT - Gregorian)) or {@code -1} if there was no access * * @see #getLastAccessForegroundTime(int) * @see #getLastAccessBackgroundTime(int) @@ -3397,7 +3443,7 @@ public class AppOpsManager { * @param flags The op flags * * @return the last access time (in milliseconds since epoch start (January 1, 1970 - * 00:00:00.000 GMT - Gregorian)) or {@code -1} + * 00:00:00.000 GMT - Gregorian)) or {@code -1} if there was no foreground access * * @see #getLastAccessTime(int) * @see #getLastAccessBackgroundTime(int) @@ -3415,7 +3461,7 @@ public class AppOpsManager { * @param flags The op flags * * @return the last access time (in milliseconds since epoch start (January 1, 1970 - * 00:00:00.000 GMT - Gregorian)) or {@code -1} + * 00:00:00.000 GMT - Gregorian)) or {@code -1} if there was no background access * * @see #getLastAccessTime(int) * @see #getLastAccessForegroundTime(int) @@ -3432,7 +3478,7 @@ public class AppOpsManager { * * @param flags The op flags * - * @return the last access event of {@code null} + * @return the last access event of {@code null} if there was no access */ private @Nullable NoteOpEvent getLastAccessEvent(@UidState int fromUidState, @UidState int toUidState, @OpFlags int flags) { @@ -3458,7 +3504,7 @@ public class AppOpsManager { * @param flags The op flags * * @return the last access time (in milliseconds since epoch start (January 1, 1970 - * 00:00:00.000 GMT - Gregorian)) or {@code -1} + * 00:00:00.000 GMT - Gregorian)) or {@code -1} if there was no access * * @see #getLastAccessTime(int) * @see #getLastAccessForegroundTime(int) @@ -3494,7 +3540,7 @@ public class AppOpsManager { * @param flags The op flags * * @return the last rejection time (in milliseconds since epoch start (January 1, 1970 - * 00:00:00.000 GMT - Gregorian)) or {@code -1} + * 00:00:00.000 GMT - Gregorian)) or {@code -1} if there was no rejection * * @see #getLastRejectForegroundTime(int) * @see #getLastRejectBackgroundTime(int) @@ -3511,7 +3557,7 @@ public class AppOpsManager { * @param flags The op flags * * @return the last rejection time (in milliseconds since epoch start (January 1, 1970 - * 00:00:00.000 GMT - Gregorian)) or {@code -1} + * 00:00:00.000 GMT - Gregorian)) or {@code -1} if there was no foreground rejection * * @see #getLastRejectTime(int) * @see #getLastRejectBackgroundTime(int) @@ -3529,7 +3575,7 @@ public class AppOpsManager { * @param flags The op flags * * @return the last rejection time (in milliseconds since epoch start (January 1, 1970 - * 00:00:00.000 GMT - Gregorian)) or {@code -1} + * 00:00:00.000 GMT - Gregorian)) or {@code -1} if there was no background rejection * * @see #getLastRejectTime(int) * @see #getLastRejectForegroundTime(int) @@ -3546,7 +3592,7 @@ public class AppOpsManager { * * @param flags The op flags * - * @return the last reject event of {@code null} + * @return the last reject event of {@code null} if there was no rejection */ private @Nullable NoteOpEvent getLastRejectEvent(@UidState int fromUidState, @UidState int toUidState, @OpFlags int flags) { @@ -3572,7 +3618,7 @@ public class AppOpsManager { * @param flags The op flags * * @return the last rejection time (in milliseconds since epoch start (January 1, 1970 - * 00:00:00.000 GMT - Gregorian)) or {@code -1} + * 00:00:00.000 GMT - Gregorian)) or {@code -1} if there was no rejection * * @see #getLastRejectTime(int) * @see #getLastRejectForegroundTime(int) @@ -3616,7 +3662,7 @@ public class AppOpsManager { * * @param flags The op flags * - * @return the duration in milliseconds or {@code -1} + * @return the duration in milliseconds or {@code -1} if there was no access * * @see #getLastForegroundDuration(int) * @see #getLastBackgroundDuration(int) @@ -3632,7 +3678,7 @@ public class AppOpsManager { * * @param flags The op flags * - * @return the duration in milliseconds or {@code -1} + * @return the duration in milliseconds or {@code -1} if there was no foreground access * * @see #getLastDuration(int) * @see #getLastBackgroundDuration(int) @@ -3649,7 +3695,7 @@ public class AppOpsManager { * * @param flags The op flags * - * @return the duration in milliseconds or {@code -1} + * @return the duration in milliseconds or {@code -1} if there was no background access * * @see #getLastDuration(int) * @see #getLastForegroundDuration(int) @@ -3668,7 +3714,7 @@ public class AppOpsManager { * @param toUidState The highest UID state for which to query (inclusive) * @param flags The op flags * - * @return the duration in milliseconds or {@code -1} + * @return the duration in milliseconds or {@code -1} if there was no access * * @see #getLastDuration(int) * @see #getLastForegroundDuration(int) @@ -3743,7 +3789,7 @@ public class AppOpsManager { * * @param flags The op flags * - * @return The proxy name or {@code null} + * @return The proxy info or {@code null} if there was no proxy access * * @see #getLastForegroundProxyInfo(int) * @see #getLastBackgroundProxyInfo(int) @@ -3760,7 +3806,7 @@ public class AppOpsManager { * * @param flags The op flags * - * @return The proxy name or {@code null} + * @return The proxy info or {@code null} if there was no foreground proxy access * * @see #getLastProxyInfo(int) * @see #getLastBackgroundProxyInfo(int) @@ -3778,7 +3824,7 @@ public class AppOpsManager { * * @param flags The op flags * - * @return The proxy name or {@code null} + * @return The proxy info or {@code null} if there was no background proxy access * * @see #getLastProxyInfo(int) * @see #getLastForegroundProxyInfo(int) @@ -3798,7 +3844,7 @@ public class AppOpsManager { * @param toUidState The highest UID state for which to query (inclusive) * @param flags The op flags * - * @return The proxy name or {@code null} + * @return The proxy info or {@code null} if there was no proxy access * * @see #getLastProxyInfo(int) * @see #getLastForegroundProxyInfo(int) diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index 4b1ba0278682..16c0910f1273 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -48,6 +48,8 @@ interface INotificationManager void clearData(String pkg, int uid, boolean fromApp); void enqueueTextToast(String pkg, IBinder token, CharSequence text, int duration, int displayId, @nullable ITransientNotificationCallback callback); void enqueueToast(String pkg, IBinder token, ITransientNotification callback, int duration, int displayId); + // TODO(b/144152069): Remove this after assessing impact on dogfood. + void enqueueTextOrCustomToast(String pkg, IBinder token, ITransientNotification callback, int duration, int displayId, boolean isCustom); void cancelToast(String pkg, IBinder token); void finishToken(String pkg, IBinder token); diff --git a/core/java/android/app/ITaskOrganizerController.aidl b/core/java/android/app/ITaskOrganizerController.aidl index 168f782d02a6..bfc42ef8848d 100644 --- a/core/java/android/app/ITaskOrganizerController.aidl +++ b/core/java/android/app/ITaskOrganizerController.aidl @@ -31,8 +31,19 @@ interface ITaskOrganizerController { */ void registerTaskOrganizer(ITaskOrganizer organizer, int windowingMode); - /** Apply multiple WindowContainer operations at once. */ - void applyContainerTransaction(in WindowContainerTransaction t); + /** + * Apply multiple WindowContainer operations at once. + * @param organizer If non-null this transaction will use the synchronization + * scheme described in BLASTSyncEngine.java. The SurfaceControl transaction + * containing the effects of this WindowContainer transaction will be passed + * to the organizers Transaction ready callback. If null the transaction + * will apply with non particular synchronization constraints (other than + * it will all apply at once). + * @return If organizer was non-null returns an ID for the sync operation which will + * later be passed to transactionReady. This lets TaskOrganizer implementations + * differentiate overlapping sync operations. + */ + int applyContainerTransaction(in WindowContainerTransaction t, ITaskOrganizer organizer); /** Creates a persistent root task in WM for a particular windowing-mode. */ ActivityManager.RunningTaskInfo createRootTask(int displayId, int windowingMode); diff --git a/core/java/android/app/admin/DevicePolicyKeyguardService.java b/core/java/android/app/admin/DevicePolicyKeyguardService.java index c2a76c5014c0..2ac5ebfc7c84 100644 --- a/core/java/android/app/admin/DevicePolicyKeyguardService.java +++ b/core/java/android/app/admin/DevicePolicyKeyguardService.java @@ -22,7 +22,7 @@ import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; -import android.view.SurfaceControl; +import android.view.SurfaceControlViewHost; /** * Client interface for providing the SystemUI with secondary lockscreen information. @@ -43,14 +43,14 @@ public class DevicePolicyKeyguardService extends Service { @Override public void onSurfaceReady(@Nullable IBinder hostInputToken, IKeyguardCallback callback) { mCallback = callback; - SurfaceControl surfaceControl = + SurfaceControlViewHost.SurfacePackage surfacePackage = DevicePolicyKeyguardService.this.onSurfaceReady(hostInputToken); if (mCallback != null) { try { - mCallback.onSurfaceControlCreated(surfaceControl); + mCallback.onRemoteContentReady(surfacePackage); } catch (RemoteException e) { - Log.e(TAG, "Failed to return created SurfaceControl", e); + Log.e(TAG, "Failed to return created SurfacePackage", e); } } } @@ -65,11 +65,11 @@ public class DevicePolicyKeyguardService extends Service { /** * Called by keyguard once the host surface for the secondary lockscreen is ready to display * remote content. - * @return the {@link SurfaceControl} for the Surface the secondary lockscreen content is - * attached to. + * @return the {@link SurfaceControlViewHost.SurfacePackage} for the Surface the + * secondary lockscreen content is attached to. */ @Nullable - public SurfaceControl onSurfaceReady(@Nullable IBinder hostInputToken) { + public SurfaceControlViewHost.SurfacePackage onSurfaceReady(@Nullable IBinder hostInputToken) { return null; } diff --git a/core/java/android/app/admin/IKeyguardCallback.aidl b/core/java/android/app/admin/IKeyguardCallback.aidl index 81e7d4dee902..856033dc8129 100644 --- a/core/java/android/app/admin/IKeyguardCallback.aidl +++ b/core/java/android/app/admin/IKeyguardCallback.aidl @@ -15,13 +15,13 @@ */ package android.app.admin; -import android.view.SurfaceControl; +import android.view.SurfaceControlViewHost; /** * Internal IPC interface for informing the keyguard of events on the secondary lockscreen. * @hide */ interface IKeyguardCallback { - oneway void onSurfaceControlCreated(in SurfaceControl remoteSurfaceControl); + oneway void onRemoteContentReady(in SurfaceControlViewHost.SurfacePackage surfacePackage); oneway void onDismiss(); } diff --git a/core/java/android/app/compat/CompatChanges.java b/core/java/android/app/compat/CompatChanges.java new file mode 100644 index 000000000000..5b367894ce69 --- /dev/null +++ b/core/java/android/app/compat/CompatChanges.java @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2020 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.compat; + +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.compat.Compatibility; +import android.content.Context; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.UserHandle; + +import com.android.internal.compat.IPlatformCompat; + +/** + * CompatChanges APIs - to be used by platform code only (including mainline + * modules). + * + * @hide + */ +@SystemApi +public final class CompatChanges { + private CompatChanges() {} + + /** + * Query if a given compatibility change is enabled for the current process. This method is + * intended to be called by code running inside a process of the affected app only. + * + * <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. + * + * @param changeId The ID of the compatibility change in question. + * @return {@code true} if the change is enabled for the current app. + */ + public static boolean isChangeEnabled(long changeId) { + return Compatibility.isChangeEnabled(changeId); + } + + /** + * Same as {@code #isChangeEnabled(long)}, except this version should be called on behalf of an + * app from a different process that's performing work for the app. + * + * <p> Note that this involves a binder call to the system server (unless running in the system + * server). If the binder call fails, a {@code RuntimeException} will be thrown. + * + * <p> Caller must have android.permission.READ_COMPAT_CHANGE_CONFIG permission. If it + * doesn't, a {@code RuntimeException} will be thrown. + * + * @param changeId The ID of the compatibility change in question. + * @param packageName The package name of the app in question. + * @param user The user that the operation is done for. + * @return {@code true} if the change is enabled for the current app. + */ + public static boolean isChangeEnabled(long changeId, @NonNull String packageName, + @NonNull UserHandle user) { + IPlatformCompat platformCompat = IPlatformCompat.Stub.asInterface( + ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)); + try { + return platformCompat.isChangeEnabledByPackageName(changeId, packageName, + user.getIdentifier()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Same as {@code #isChangeEnabled(long)}, except this version should be called on behalf of an + * app from a different process that's performing work for the app. + * + * <p> Note that this involves a binder call to the system server (unless running in the system + * server). If the binder call fails, {@code RuntimeException} will be thrown. + * + * <p> Caller must have android.permission.READ_COMPAT_CHANGE_CONFIG permission. If it + * doesn't, a {@code RuntimeException} will be thrown. + * + * <p> Returns {@code true} if there are no installed packages for the required UID, or if the + * change is enabled for ALL of the installed packages associated with the provided UID. Please + * use a more specific API if you want a different behaviour for multi-package UIDs. + * + * @param changeId The ID of the compatibility change in question. + * @param uid The UID of the app in question. + * @return {@code true} if the change is enabled for the current app. + */ + public static boolean isChangeEnabled(long changeId, int uid) { + IPlatformCompat platformCompat = IPlatformCompat.Stub.asInterface( + ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)); + try { + return platformCompat.isChangeEnabledByUid(changeId, uid); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } +} diff --git a/core/java/android/app/timedetector/ITimeDetectorService.aidl b/core/java/android/app/timedetector/ITimeDetectorService.aidl index de8f4700de2d..5ead0c90c80e 100644 --- a/core/java/android/app/timedetector/ITimeDetectorService.aidl +++ b/core/java/android/app/timedetector/ITimeDetectorService.aidl @@ -18,7 +18,7 @@ package android.app.timedetector; import android.app.timedetector.ManualTimeSuggestion; import android.app.timedetector.NetworkTimeSuggestion; -import android.app.timedetector.PhoneTimeSuggestion; +import android.app.timedetector.TelephonyTimeSuggestion; /** * System private API to communicate with time detector service. @@ -34,7 +34,7 @@ import android.app.timedetector.PhoneTimeSuggestion; * {@hide} */ interface ITimeDetectorService { - void suggestPhoneTime(in PhoneTimeSuggestion timeSuggestion); void suggestManualTime(in ManualTimeSuggestion timeSuggestion); void suggestNetworkTime(in NetworkTimeSuggestion timeSuggestion); + void suggestTelephonyTime(in TelephonyTimeSuggestion timeSuggestion); } diff --git a/core/java/android/app/timedetector/PhoneTimeSuggestion.aidl b/core/java/android/app/timedetector/TelephonyTimeSuggestion.aidl index f5e240549a9a..d9b038692c62 100644 --- a/core/java/android/app/timedetector/PhoneTimeSuggestion.aidl +++ b/core/java/android/app/timedetector/TelephonyTimeSuggestion.aidl @@ -16,4 +16,4 @@ package android.app.timedetector; -parcelable PhoneTimeSuggestion; +parcelable TelephonyTimeSuggestion; diff --git a/core/java/android/app/timedetector/PhoneTimeSuggestion.java b/core/java/android/app/timedetector/TelephonyTimeSuggestion.java index 0133a4472686..c0e8957727cf 100644 --- a/core/java/android/app/timedetector/PhoneTimeSuggestion.java +++ b/core/java/android/app/timedetector/TelephonyTimeSuggestion.java @@ -50,17 +50,17 @@ import java.util.Objects; * * @hide */ -public final class PhoneTimeSuggestion implements Parcelable { +public final class TelephonyTimeSuggestion implements Parcelable { /** @hide */ - public static final @NonNull Parcelable.Creator<PhoneTimeSuggestion> CREATOR = - new Parcelable.Creator<PhoneTimeSuggestion>() { - public PhoneTimeSuggestion createFromParcel(Parcel in) { - return PhoneTimeSuggestion.createFromParcel(in); + public static final @NonNull Parcelable.Creator<TelephonyTimeSuggestion> CREATOR = + new Parcelable.Creator<TelephonyTimeSuggestion>() { + public TelephonyTimeSuggestion createFromParcel(Parcel in) { + return TelephonyTimeSuggestion.createFromParcel(in); } - public PhoneTimeSuggestion[] newArray(int size) { - return new PhoneTimeSuggestion[size]; + public TelephonyTimeSuggestion[] newArray(int size) { + return new TelephonyTimeSuggestion[size]; } }; @@ -68,15 +68,15 @@ public final class PhoneTimeSuggestion implements Parcelable { @Nullable private final TimestampedValue<Long> mUtcTime; @Nullable private ArrayList<String> mDebugInfo; - private PhoneTimeSuggestion(Builder builder) { + private TelephonyTimeSuggestion(Builder builder) { mSlotIndex = builder.mSlotIndex; mUtcTime = builder.mUtcTime; mDebugInfo = builder.mDebugInfo != null ? new ArrayList<>(builder.mDebugInfo) : null; } - private static PhoneTimeSuggestion createFromParcel(Parcel in) { + private static TelephonyTimeSuggestion createFromParcel(Parcel in) { int slotIndex = in.readInt(); - PhoneTimeSuggestion suggestion = new PhoneTimeSuggestion.Builder(slotIndex) + TelephonyTimeSuggestion suggestion = new TelephonyTimeSuggestion.Builder(slotIndex) .setUtcTime(in.readParcelable(null /* classLoader */)) .build(); @SuppressWarnings("unchecked") @@ -102,7 +102,7 @@ public final class PhoneTimeSuggestion implements Parcelable { /** * Returns an identifier for the source of this suggestion. * - * <p>See {@link PhoneTimeSuggestion} for more information about {@code slotIndex}. + * <p>See {@link TelephonyTimeSuggestion} for more information about {@code slotIndex}. */ public int getSlotIndex() { return mSlotIndex; @@ -111,7 +111,7 @@ public final class PhoneTimeSuggestion implements Parcelable { /** * Returns the suggested time or {@code null} if there isn't one. * - * <p>See {@link PhoneTimeSuggestion} for more information about {@code utcTime}. + * <p>See {@link TelephonyTimeSuggestion} for more information about {@code utcTime}. */ @Nullable public TimestampedValue<Long> getUtcTime() { @@ -121,7 +121,7 @@ public final class PhoneTimeSuggestion implements Parcelable { /** * Returns debug metadata for the suggestion. * - * <p>See {@link PhoneTimeSuggestion} for more information about {@code debugInfo}. + * <p>See {@link TelephonyTimeSuggestion} for more information about {@code debugInfo}. */ @NonNull public List<String> getDebugInfo() { @@ -132,7 +132,7 @@ public final class PhoneTimeSuggestion implements Parcelable { /** * Associates information with the instance that can be useful for debugging / logging. * - * <p>See {@link PhoneTimeSuggestion} for more information about {@code debugInfo}. + * <p>See {@link TelephonyTimeSuggestion} for more information about {@code debugInfo}. */ public void addDebugInfo(@NonNull String debugInfo) { if (mDebugInfo == null) { @@ -144,7 +144,7 @@ public final class PhoneTimeSuggestion implements Parcelable { /** * Associates information with the instance that can be useful for debugging / logging. * - * <p>See {@link PhoneTimeSuggestion} for more information about {@code debugInfo}. + * <p>See {@link TelephonyTimeSuggestion} for more information about {@code debugInfo}. */ public void addDebugInfo(@NonNull List<String> debugInfo) { if (mDebugInfo == null) { @@ -161,7 +161,7 @@ public final class PhoneTimeSuggestion implements Parcelable { if (o == null || getClass() != o.getClass()) { return false; } - PhoneTimeSuggestion that = (PhoneTimeSuggestion) o; + TelephonyTimeSuggestion that = (TelephonyTimeSuggestion) o; return mSlotIndex == that.mSlotIndex && Objects.equals(mUtcTime, that.mUtcTime); } @@ -173,7 +173,7 @@ public final class PhoneTimeSuggestion implements Parcelable { @Override public String toString() { - return "PhoneTimeSuggestion{" + return "TelephonyTimeSuggestion{" + "mSlotIndex='" + mSlotIndex + '\'' + ", mUtcTime=" + mUtcTime + ", mDebugInfo=" + mDebugInfo @@ -181,7 +181,7 @@ public final class PhoneTimeSuggestion implements Parcelable { } /** - * Builds {@link PhoneTimeSuggestion} instances. + * Builds {@link TelephonyTimeSuggestion} instances. * * @hide */ @@ -193,7 +193,7 @@ public final class PhoneTimeSuggestion implements Parcelable { /** * Creates a builder with the specified {@code slotIndex}. * - * <p>See {@link PhoneTimeSuggestion} for more information about {@code slotIndex}. + * <p>See {@link TelephonyTimeSuggestion} for more information about {@code slotIndex}. */ public Builder(int slotIndex) { mSlotIndex = slotIndex; @@ -202,7 +202,7 @@ public final class PhoneTimeSuggestion implements Parcelable { /** * Returns the builder for call chaining. * - * <p>See {@link PhoneTimeSuggestion} for more information about {@code utcTime}. + * <p>See {@link TelephonyTimeSuggestion} for more information about {@code utcTime}. */ @NonNull public Builder setUtcTime(@Nullable TimestampedValue<Long> utcTime) { @@ -218,7 +218,7 @@ public final class PhoneTimeSuggestion implements Parcelable { /** * Returns the builder for call chaining. * - * <p>See {@link PhoneTimeSuggestion} for more information about {@code debugInfo}. + * <p>See {@link TelephonyTimeSuggestion} for more information about {@code debugInfo}. */ @NonNull public Builder addDebugInfo(@NonNull String debugInfo) { @@ -229,10 +229,10 @@ public final class PhoneTimeSuggestion implements Parcelable { return this; } - /** Returns the {@link PhoneTimeSuggestion}. */ + /** Returns the {@link TelephonyTimeSuggestion}. */ @NonNull - public PhoneTimeSuggestion build() { - return new PhoneTimeSuggestion(this); + public TelephonyTimeSuggestion build() { + return new TelephonyTimeSuggestion(this); } } } diff --git a/core/java/android/app/timedetector/TimeDetector.java b/core/java/android/app/timedetector/TimeDetector.java index df4f513ab095..84ad495da09b 100644 --- a/core/java/android/app/timedetector/TimeDetector.java +++ b/core/java/android/app/timedetector/TimeDetector.java @@ -45,12 +45,12 @@ public interface TimeDetector { } /** - * Suggests the current phone-signal derived time to the detector. The detector may ignore the - * signal if better signals are available such as those that come from more reliable sources or - * were determined more recently. + * Suggests a telephony-signal derived time to the detector. The detector may ignore the signal + * if better signals are available such as those that come from more reliable sources or were + * determined more recently. */ - @RequiresPermission(android.Manifest.permission.SUGGEST_PHONE_TIME_AND_ZONE) - void suggestPhoneTime(@NonNull PhoneTimeSuggestion timeSuggestion); + @RequiresPermission(android.Manifest.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE) + void suggestTelephonyTime(@NonNull TelephonyTimeSuggestion timeSuggestion); /** * Suggests the user's manually entered current time to the detector. diff --git a/core/java/android/app/timedetector/TimeDetectorImpl.java b/core/java/android/app/timedetector/TimeDetectorImpl.java index 1683817740c3..c1d66672f9d2 100644 --- a/core/java/android/app/timedetector/TimeDetectorImpl.java +++ b/core/java/android/app/timedetector/TimeDetectorImpl.java @@ -40,12 +40,12 @@ public final class TimeDetectorImpl implements TimeDetector { } @Override - public void suggestPhoneTime(@NonNull PhoneTimeSuggestion timeSuggestion) { + public void suggestTelephonyTime(@NonNull TelephonyTimeSuggestion timeSuggestion) { if (DEBUG) { - Log.d(TAG, "suggestPhoneTime called: " + timeSuggestion); + Log.d(TAG, "suggestTelephonyTime called: " + timeSuggestion); } try { - mITimeDetectorService.suggestPhoneTime(timeSuggestion); + mITimeDetectorService.suggestTelephonyTime(timeSuggestion); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl b/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl index df6438319ab7..b06f4b8ecdb8 100644 --- a/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl +++ b/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl @@ -17,7 +17,7 @@ package android.app.timezonedetector; import android.app.timezonedetector.ManualTimeZoneSuggestion; -import android.app.timezonedetector.PhoneTimeZoneSuggestion; +import android.app.timezonedetector.TelephonyTimeZoneSuggestion; /** * System private API to communicate with time zone detector service. @@ -34,5 +34,5 @@ import android.app.timezonedetector.PhoneTimeZoneSuggestion; */ interface ITimeZoneDetectorService { void suggestManualTimeZone(in ManualTimeZoneSuggestion timeZoneSuggestion); - void suggestPhoneTimeZone(in PhoneTimeZoneSuggestion timeZoneSuggestion); + void suggestTelephonyTimeZone(in TelephonyTimeZoneSuggestion timeZoneSuggestion); } diff --git a/core/java/android/app/timezonedetector/PhoneTimeZoneSuggestion.aidl b/core/java/android/app/timezonedetector/TelephonyTimeZoneSuggestion.aidl index 3ad903bb5949..b57ad20734be 100644 --- a/core/java/android/app/timezonedetector/PhoneTimeZoneSuggestion.aidl +++ b/core/java/android/app/timezonedetector/TelephonyTimeZoneSuggestion.aidl @@ -16,4 +16,4 @@ package android.app.timezonedetector; -parcelable PhoneTimeZoneSuggestion; +parcelable TelephonyTimeZoneSuggestion; diff --git a/core/java/android/app/timezonedetector/PhoneTimeZoneSuggestion.java b/core/java/android/app/timezonedetector/TelephonyTimeZoneSuggestion.java index 9147b4462492..150c01d59899 100644 --- a/core/java/android/app/timezonedetector/PhoneTimeZoneSuggestion.java +++ b/core/java/android/app/timezonedetector/TelephonyTimeZoneSuggestion.java @@ -56,18 +56,18 @@ import java.util.Objects; * * @hide */ -public final class PhoneTimeZoneSuggestion implements Parcelable { +public final class TelephonyTimeZoneSuggestion implements Parcelable { /** @hide */ @NonNull - public static final Creator<PhoneTimeZoneSuggestion> CREATOR = - new Creator<PhoneTimeZoneSuggestion>() { - public PhoneTimeZoneSuggestion createFromParcel(Parcel in) { - return PhoneTimeZoneSuggestion.createFromParcel(in); + public static final Creator<TelephonyTimeZoneSuggestion> CREATOR = + new Creator<TelephonyTimeZoneSuggestion>() { + public TelephonyTimeZoneSuggestion createFromParcel(Parcel in) { + return TelephonyTimeZoneSuggestion.createFromParcel(in); } - public PhoneTimeZoneSuggestion[] newArray(int size) { - return new PhoneTimeZoneSuggestion[size]; + public TelephonyTimeZoneSuggestion[] newArray(int size) { + return new TelephonyTimeZoneSuggestion[size]; } }; @@ -76,7 +76,7 @@ public final class PhoneTimeZoneSuggestion implements Parcelable { * the same {@code slotIndex}. */ @NonNull - public static PhoneTimeZoneSuggestion createEmptySuggestion( + public static TelephonyTimeZoneSuggestion createEmptySuggestion( int slotIndex, @NonNull String debugInfo) { return new Builder(slotIndex).addDebugInfo(debugInfo).build(); } @@ -144,7 +144,7 @@ public final class PhoneTimeZoneSuggestion implements Parcelable { @Quality private final int mQuality; @Nullable private List<String> mDebugInfo; - private PhoneTimeZoneSuggestion(Builder builder) { + private TelephonyTimeZoneSuggestion(Builder builder) { mSlotIndex = builder.mSlotIndex; mZoneId = builder.mZoneId; mMatchType = builder.mMatchType; @@ -153,15 +153,16 @@ public final class PhoneTimeZoneSuggestion implements Parcelable { } @SuppressWarnings("unchecked") - private static PhoneTimeZoneSuggestion createFromParcel(Parcel in) { + private static TelephonyTimeZoneSuggestion createFromParcel(Parcel in) { // Use the Builder so we get validation during build(). int slotIndex = in.readInt(); - PhoneTimeZoneSuggestion suggestion = new Builder(slotIndex) + TelephonyTimeZoneSuggestion suggestion = new Builder(slotIndex) .setZoneId(in.readString()) .setMatchType(in.readInt()) .setQuality(in.readInt()) .build(); - List<String> debugInfo = in.readArrayList(PhoneTimeZoneSuggestion.class.getClassLoader()); + List<String> debugInfo = + in.readArrayList(TelephonyTimeZoneSuggestion.class.getClassLoader()); if (debugInfo != null) { suggestion.addDebugInfo(debugInfo); } @@ -185,7 +186,7 @@ public final class PhoneTimeZoneSuggestion implements Parcelable { /** * Returns an identifier for the source of this suggestion. * - * <p>See {@link PhoneTimeZoneSuggestion} for more information about {@code slotIndex}. + * <p>See {@link TelephonyTimeZoneSuggestion} for more information about {@code slotIndex}. */ public int getSlotIndex() { return mSlotIndex; @@ -195,7 +196,7 @@ public final class PhoneTimeZoneSuggestion implements Parcelable { * Returns the suggested time zone Olson ID, e.g. "America/Los_Angeles". {@code null} means that * the caller is no longer sure what the current time zone is. * - * <p>See {@link PhoneTimeZoneSuggestion} for more information about {@code zoneId}. + * <p>See {@link TelephonyTimeZoneSuggestion} for more information about {@code zoneId}. */ @Nullable public String getZoneId() { @@ -206,7 +207,7 @@ public final class PhoneTimeZoneSuggestion implements Parcelable { * Returns information about how the suggestion was determined which could be used to rank * suggestions when several are available from different sources. * - * <p>See {@link PhoneTimeZoneSuggestion} for more information about {@code matchType}. + * <p>See {@link TelephonyTimeZoneSuggestion} for more information about {@code matchType}. */ @MatchType public int getMatchType() { @@ -216,7 +217,7 @@ public final class PhoneTimeZoneSuggestion implements Parcelable { /** * Returns information about the likelihood of the suggested zone being correct. * - * <p>See {@link PhoneTimeZoneSuggestion} for more information about {@code quality}. + * <p>See {@link TelephonyTimeZoneSuggestion} for more information about {@code quality}. */ @Quality public int getQuality() { @@ -226,7 +227,7 @@ public final class PhoneTimeZoneSuggestion implements Parcelable { /** * Returns debug metadata for the suggestion. * - * <p>See {@link PhoneTimeZoneSuggestion} for more information about {@code debugInfo}. + * <p>See {@link TelephonyTimeZoneSuggestion} for more information about {@code debugInfo}. */ @NonNull public List<String> getDebugInfo() { @@ -237,7 +238,7 @@ public final class PhoneTimeZoneSuggestion implements Parcelable { /** * Associates information with the instance that can be useful for debugging / logging. * - * <p>See {@link PhoneTimeZoneSuggestion} for more information about {@code debugInfo}. + * <p>See {@link TelephonyTimeZoneSuggestion} for more information about {@code debugInfo}. */ public void addDebugInfo(@NonNull String debugInfo) { if (mDebugInfo == null) { @@ -249,7 +250,7 @@ public final class PhoneTimeZoneSuggestion implements Parcelable { /** * Associates information with the instance that can be useful for debugging / logging. * - * <p>See {@link PhoneTimeZoneSuggestion} for more information about {@code debugInfo}. + * <p>See {@link TelephonyTimeZoneSuggestion} for more information about {@code debugInfo}. */ public void addDebugInfo(@NonNull List<String> debugInfo) { if (mDebugInfo == null) { @@ -266,7 +267,7 @@ public final class PhoneTimeZoneSuggestion implements Parcelable { if (o == null || getClass() != o.getClass()) { return false; } - PhoneTimeZoneSuggestion that = (PhoneTimeZoneSuggestion) o; + TelephonyTimeZoneSuggestion that = (TelephonyTimeZoneSuggestion) o; return mSlotIndex == that.mSlotIndex && mMatchType == that.mMatchType && mQuality == that.mQuality @@ -280,7 +281,7 @@ public final class PhoneTimeZoneSuggestion implements Parcelable { @Override public String toString() { - return "PhoneTimeZoneSuggestion{" + return "TelephonyTimeZoneSuggestion{" + "mSlotIndex=" + mSlotIndex + ", mZoneId='" + mZoneId + '\'' + ", mMatchType=" + mMatchType @@ -290,7 +291,7 @@ public final class PhoneTimeZoneSuggestion implements Parcelable { } /** - * Builds {@link PhoneTimeZoneSuggestion} instances. + * Builds {@link TelephonyTimeZoneSuggestion} instances. * * @hide */ @@ -304,7 +305,7 @@ public final class PhoneTimeZoneSuggestion implements Parcelable { /** * Creates a builder with the specified {@code slotIndex}. * - * <p>See {@link PhoneTimeZoneSuggestion} for more information about {@code slotIndex}. + * <p>See {@link TelephonyTimeZoneSuggestion} for more information about {@code slotIndex}. */ public Builder(int slotIndex) { mSlotIndex = slotIndex; @@ -313,7 +314,7 @@ public final class PhoneTimeZoneSuggestion implements Parcelable { /** * Returns the builder for call chaining. * - * <p>See {@link PhoneTimeZoneSuggestion} for more information about {@code zoneId}. + * <p>See {@link TelephonyTimeZoneSuggestion} for more information about {@code zoneId}. */ @NonNull public Builder setZoneId(@Nullable String zoneId) { @@ -324,7 +325,7 @@ public final class PhoneTimeZoneSuggestion implements Parcelable { /** * Returns the builder for call chaining. * - * <p>See {@link PhoneTimeZoneSuggestion} for more information about {@code matchType}. + * <p>See {@link TelephonyTimeZoneSuggestion} for more information about {@code matchType}. */ @NonNull public Builder setMatchType(@MatchType int matchType) { @@ -335,7 +336,7 @@ public final class PhoneTimeZoneSuggestion implements Parcelable { /** * Returns the builder for call chaining. * - * <p>See {@link PhoneTimeZoneSuggestion} for more information about {@code quality}. + * <p>See {@link TelephonyTimeZoneSuggestion} for more information about {@code quality}. */ @NonNull public Builder setQuality(@Quality int quality) { @@ -346,7 +347,7 @@ public final class PhoneTimeZoneSuggestion implements Parcelable { /** * Returns the builder for call chaining. * - * <p>See {@link PhoneTimeZoneSuggestion} for more information about {@code debugInfo}. + * <p>See {@link TelephonyTimeZoneSuggestion} for more information about {@code debugInfo}. */ @NonNull public Builder addDebugInfo(@NonNull String debugInfo) { @@ -384,11 +385,11 @@ public final class PhoneTimeZoneSuggestion implements Parcelable { } } - /** Returns the {@link PhoneTimeZoneSuggestion}. */ + /** Returns the {@link TelephonyTimeZoneSuggestion}. */ @NonNull - public PhoneTimeZoneSuggestion build() { + public TelephonyTimeZoneSuggestion build() { validate(); - return new PhoneTimeZoneSuggestion(this); + return new TelephonyTimeZoneSuggestion(this); } } } diff --git a/core/java/android/app/timezonedetector/TimeZoneDetector.java b/core/java/android/app/timezonedetector/TimeZoneDetector.java index 6a3953eccb2d..20761ad2d447 100644 --- a/core/java/android/app/timezonedetector/TimeZoneDetector.java +++ b/core/java/android/app/timezonedetector/TimeZoneDetector.java @@ -47,8 +47,8 @@ public interface TimeZoneDetector { * * @hide */ - @RequiresPermission(android.Manifest.permission.SUGGEST_PHONE_TIME_AND_ZONE) - void suggestPhoneTimeZone(@NonNull PhoneTimeZoneSuggestion timeZoneSuggestion); + @RequiresPermission(android.Manifest.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE) + void suggestTelephonyTimeZone(@NonNull TelephonyTimeZoneSuggestion timeZoneSuggestion); /** * Suggests the current time zone, determined for the user's manually information, to the diff --git a/core/java/android/app/timezonedetector/TimeZoneDetectorImpl.java b/core/java/android/app/timezonedetector/TimeZoneDetectorImpl.java index 27b8374db172..0ada88500193 100644 --- a/core/java/android/app/timezonedetector/TimeZoneDetectorImpl.java +++ b/core/java/android/app/timezonedetector/TimeZoneDetectorImpl.java @@ -40,12 +40,12 @@ public final class TimeZoneDetectorImpl implements TimeZoneDetector { } @Override - public void suggestPhoneTimeZone(@NonNull PhoneTimeZoneSuggestion timeZoneSuggestion) { + public void suggestTelephonyTimeZone(@NonNull TelephonyTimeZoneSuggestion timeZoneSuggestion) { if (DEBUG) { - Log.d(TAG, "suggestPhoneTimeZone called: " + timeZoneSuggestion); + Log.d(TAG, "suggestTelephonyTimeZone called: " + timeZoneSuggestion); } try { - mITimeZoneDetectorService.suggestPhoneTimeZone(timeZoneSuggestion); + mITimeZoneDetectorService.suggestTelephonyTimeZone(timeZoneSuggestion); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index 2e93d43f7c15..01ccb86fb129 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -1861,15 +1861,19 @@ public final class BluetoothAdapter { } /** - * Connects all enabled and supported bluetooth profiles between the local and remote device + * Connects all enabled and supported bluetooth profiles between the local and remote device. + * Connection is asynchronous and you should listen to each profile's broadcast intent + * ACTION_CONNECTION_STATE_CHANGED to verify whether connection was successful. For example, + * to verify a2dp is connected, you would listen for + * {@link BluetoothA2dp#ACTION_CONNECTION_STATE_CHANGED} * * @param device is the remote device with which to connect these profiles - * @return true if all profiles successfully connected, false if an error occurred + * @return true if message sent to try to connect all profiles, false if an error occurred * * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean connectAllEnabledProfiles(@NonNull BluetoothDevice device) { try { mServiceLock.readLock().lock(); @@ -1886,15 +1890,19 @@ public final class BluetoothAdapter { } /** - * Disconnects all enabled and supported bluetooth profiles between the local and remote device + * Disconnects all enabled and supported bluetooth profiles between the local and remote device. + * Disconnection is asynchronous and you should listen to each profile's broadcast intent + * ACTION_CONNECTION_STATE_CHANGED to verify whether disconnection was successful. For example, + * to verify a2dp is disconnected, you would listen for + * {@link BluetoothA2dp#ACTION_CONNECTION_STATE_CHANGED} * * @param device is the remote device with which to disconnect these profiles - * @return true if all profiles successfully disconnected, false if an error occurred + * @return true if message sent to try to disconnect all profiles, false if an error occurred * * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean disconnectAllEnabledProfiles(@NonNull BluetoothDevice device) { try { mServiceLock.readLock().lock(); diff --git a/core/java/android/content/integrity/AtomicFormula.java b/core/java/android/content/integrity/AtomicFormula.java index 439d53661b8e..d25f413bb65f 100644 --- a/core/java/android/content/integrity/AtomicFormula.java +++ b/core/java/android/content/integrity/AtomicFormula.java @@ -332,9 +332,12 @@ public abstract class AtomicFormula extends IntegrityFormula { * Constructs a new {@link StringAtomicFormula} together with handling the necessary * hashing for the given key. * - * <p> The value will be hashed with SHA256 and the hex digest will be computed; for - * all cases except when the key is PACKAGE_NAME or INSTALLER_NAME and the value - * is less than 33 characters. + * <p> The value will be automatically hashed with SHA256 and the hex digest will be + * computed when the key is PACKAGE_NAME or INSTALLER_NAME and the value is more than 32 + * characters. + * + * <p> The APP_CERTIFICATES and INSTALLER_CERTIFICATES are always delivered in hashed + * form. So the isHashedValue is set to true by default. * * @throws IllegalArgumentException if {@code key} cannot be used with string value. */ @@ -348,7 +351,10 @@ public abstract class AtomicFormula extends IntegrityFormula { String.format( "Key %s cannot be used with StringAtomicFormula", keyToString(key))); mValue = hashValue(key, value); - mIsHashedValue = !mValue.equals(value); + mIsHashedValue = + key == APP_CERTIFICATE || key == INSTALLER_CERTIFICATE + ? true + : !mValue.equals(value); } StringAtomicFormula(Parcel in) { diff --git a/core/java/android/content/pm/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java index de153d00b48b..edc20d9f65ad 100644 --- a/core/java/android/content/pm/CrossProfileApps.java +++ b/core/java/android/content/pm/CrossProfileApps.java @@ -323,6 +323,7 @@ public class CrossProfileApps { */ @RequiresPermission( allOf={android.Manifest.permission.MANAGE_APP_OPS_MODES, + android.Manifest.permission.UPDATE_APP_OPS_STATS, android.Manifest.permission.INTERACT_ACROSS_USERS}) public void setInteractAcrossProfilesAppOp(@NonNull String packageName, @Mode int newMode) { try { @@ -363,6 +364,7 @@ public class CrossProfileApps { */ @RequiresPermission( allOf={android.Manifest.permission.MANAGE_APP_OPS_MODES, + android.Manifest.permission.UPDATE_APP_OPS_STATS, android.Manifest.permission.INTERACT_ACROSS_USERS}) public void resetInteractAcrossProfilesAppOps( @NonNull Collection<String> previousCrossProfilePackages, diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl index 50bb3c721763..04923590b413 100644 --- a/core/java/android/content/pm/ILauncherApps.aidl +++ b/core/java/android/content/pm/ILauncherApps.aidl @@ -20,11 +20,13 @@ import android.app.IApplicationThread; import android.content.ComponentName; import android.content.Intent; import android.content.IntentSender; +import android.content.LocusId; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.IOnAppsChangedListener; import android.content.pm.LauncherApps; import android.content.pm.IPackageInstallerCallback; +import android.content.pm.IShortcutChangeCallback; import android.content.pm.PackageInstaller; import android.content.pm.ParceledListSlice; import android.content.pm.ResolveInfo; @@ -66,7 +68,8 @@ interface ILauncherApps { in UserHandle user); ParceledListSlice getShortcuts(String callingPackage, long changedSince, String packageName, - in List shortcutIds, in ComponentName componentName, int flags, in UserHandle user); + in List shortcutIds, in List<LocusId> locusIds, in ComponentName componentName, + int flags, in UserHandle user); void pinShortcuts(String callingPackage, String packageName, in List<String> shortcutIds, in UserHandle user); boolean startShortcut(String callingPackage, String packageName, String id, @@ -89,4 +92,10 @@ interface ILauncherApps { void registerPackageInstallerCallback(String callingPackage, in IPackageInstallerCallback callback); ParceledListSlice getAllSessions(String callingPackage); + + void registerShortcutChangeCallback(String callingPackage, long changedSince, + String packageName, in List shortcutIds, in List<LocusId> locusIds, + in ComponentName componentName, int flags, in IShortcutChangeCallback callback, + int callbackId); + void unregisterShortcutChangeCallback(String callingPackage, int callbackId); } diff --git a/core/java/android/content/pm/IShortcutChangeCallback.aidl b/core/java/android/content/pm/IShortcutChangeCallback.aidl new file mode 100644 index 000000000000..fed4e4a796f9 --- /dev/null +++ b/core/java/android/content/pm/IShortcutChangeCallback.aidl @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2020, 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.content.pm; + +import android.content.pm.ParceledListSlice; +import android.content.pm.ShortcutInfo; +import android.os.UserHandle; + +import java.util.List; + +/** + * Interface for LauncherApps#ShortcutChangeCallbackProxy. + * + * @hide + */ +oneway interface IShortcutChangeCallback +{ + void onShortcutsAddedOrUpdated(String packageName, in List<ShortcutInfo> shortcuts, + in UserHandle user); + + void onShortcutsRemoved(String packageName, in List<ShortcutInfo> shortcuts, + in UserHandle user); +}
\ No newline at end of file diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java index cea0b6b5f3ad..73c9e4d843b7 100644 --- a/core/java/android/content/pm/LauncherApps.java +++ b/core/java/android/content/pm/LauncherApps.java @@ -34,6 +34,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentSender; +import android.content.LocusId; import android.content.pm.PackageInstaller.SessionCallback; import android.content.pm.PackageInstaller.SessionCallbackDelegate; import android.content.pm.PackageInstaller.SessionInfo; @@ -61,15 +62,21 @@ import android.os.UserHandle; import android.os.UserManager; import android.util.DisplayMetrics; import android.util.Log; +import android.util.Pair; + +import com.android.internal.util.function.pooled.PooledLambda; import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.concurrent.Executor; @@ -152,6 +159,9 @@ public class LauncherApps { private final List<CallbackMessageHandler> mCallbacks = new ArrayList<>(); private final List<SessionCallbackDelegate> mDelegates = new ArrayList<>(); + private final Map<Integer, Pair<Executor, ShortcutChangeCallback>> + mShortcutChangeCallbacks = new HashMap<>(); + /** * Callbacks for package changes to this and related managed profiles. */ @@ -406,6 +416,9 @@ public class LauncherApps { List<String> mShortcutIds; @Nullable + List<LocusId> mLocusIds; + + @Nullable ComponentName mActivity; @QueryFlags @@ -442,6 +455,19 @@ public class LauncherApps { } /** + * If non-null, return only the specified shortcuts by locus ID. When setting this field, + * a package name must also be set with {@link #setPackage}. + * + * @hide + */ + @SystemApi + @NonNull + public ShortcutQuery setLocusIds(@Nullable List<LocusId> locusIds) { + mLocusIds = locusIds; + return this; + } + + /** * If non-null, returns only shortcuts associated with the activity; i.e. * {@link ShortcutInfo}s whose {@link ShortcutInfo#getActivity()} are equal * to {@code activity}. @@ -469,6 +495,95 @@ public class LauncherApps { } } + /** + * Callbacks for shortcut changes to this and related managed profiles. + * + * @hide + */ + public interface ShortcutChangeCallback { + /** + * Indicates that one or more shortcuts, that match the {@link ShortcutQuery} used to + * register this callback, have been added or updated. + * @see LauncherApps#registerShortcutChangeCallback(ShortcutChangeCallback, ShortcutQuery) + * + * <p>Only the applications that are allowed to access the shortcut information, + * as defined in {@link #hasShortcutHostPermission()}, will receive it. + * + * @param packageName The name of the package that has the shortcuts. + * @param shortcuts Shortcuts from the package that have updated or added. Only "key" + * information will be provided, as defined in {@link ShortcutInfo#hasKeyFieldsOnly()}. + * @param user The UserHandle of the profile that generated the change. + * + * @see ShortcutManager + */ + default void onShortcutsAddedOrUpdated(@NonNull String packageName, + @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user) {} + + /** + * Indicates that one or more shortcuts, that match the {@link ShortcutQuery} used to + * register this callback, have been removed. + * @see LauncherApps#registerShortcutChangeCallback(ShortcutChangeCallback, ShortcutQuery) + * + * <p>Only the applications that are allowed to access the shortcut information, + * as defined in {@link #hasShortcutHostPermission()}, will receive it. + * + * @param packageName The name of the package that has the shortcuts. + * @param shortcuts Shortcuts from the package that have been removed. Only "key" + * information will be provided, as defined in {@link ShortcutInfo#hasKeyFieldsOnly()}. + * @param user The UserHandle of the profile that generated the change. + * + * @see ShortcutManager + */ + default void onShortcutsRemoved(@NonNull String packageName, + @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user) {} + } + + /** + * Callback proxy class for {@link ShortcutChangeCallback} + * + * @hide + */ + private static class ShortcutChangeCallbackProxy extends + android.content.pm.IShortcutChangeCallback.Stub { + private final WeakReference<Pair<Executor, ShortcutChangeCallback>> mRemoteReferences; + + ShortcutChangeCallbackProxy(Pair<Executor, ShortcutChangeCallback> remoteReferences) { + mRemoteReferences = new WeakReference<>(remoteReferences); + } + + @Override + public void onShortcutsAddedOrUpdated(@NonNull String packageName, + @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user) { + Pair<Executor, ShortcutChangeCallback> remoteReferences = mRemoteReferences.get(); + if (remoteReferences == null) { + // Binder is dead. + return; + } + + final Executor executor = remoteReferences.first; + final ShortcutChangeCallback callback = remoteReferences.second; + executor.execute( + PooledLambda.obtainRunnable(ShortcutChangeCallback::onShortcutsAddedOrUpdated, + callback, packageName, shortcuts, user).recycleOnUse()); + } + + @Override + public void onShortcutsRemoved(@NonNull String packageName, + @NonNull List<ShortcutInfo> shortcuts, @NonNull UserHandle user) { + Pair<Executor, ShortcutChangeCallback> remoteReferences = mRemoteReferences.get(); + if (remoteReferences == null) { + // Binder is dead. + return; + } + + final Executor executor = remoteReferences.first; + final ShortcutChangeCallback callback = remoteReferences.second; + executor.execute( + PooledLambda.obtainRunnable(ShortcutChangeCallback::onShortcutsRemoved, + callback, packageName, shortcuts, user).recycleOnUse()); + } + } + /** @hide */ public LauncherApps(Context context, ILauncherApps service) { mContext = context; @@ -924,8 +1039,8 @@ public class LauncherApps { // changed callback, but that only returns shortcuts with the "key" information, so // that won't return disabled message. return maybeUpdateDisabledMessage(mService.getShortcuts(mContext.getPackageName(), - query.mChangedSince, query.mPackage, query.mShortcutIds, query.mActivity, - query.mQueryFlags, user) + query.mChangedSince, query.mPackage, query.mShortcutIds, query.mLocusIds, + query.mActivity, query.mQueryFlags, user) .getList()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -1560,6 +1675,63 @@ public class LauncherApps { } /** + * Register a callback to watch for shortcut change events in this user and managed profiles. + * + * @param callback The callback to register. + * @param query {@link ShortcutQuery} to match and filter the shortcut events. Only matching + * shortcuts will be returned by the callback. + * @param executor {@link Executor} to handle the callbacks. To dispatch callbacks to the main + * thread of your application, you can use {@link android.content.Context#getMainExecutor()}. + * + * @hide + */ + public void registerShortcutChangeCallback(@NonNull ShortcutChangeCallback callback, + @NonNull ShortcutQuery query, @NonNull @CallbackExecutor Executor executor) { + Objects.requireNonNull(callback, "Callback cannot be null"); + Objects.requireNonNull(query, "Query cannot be null"); + Objects.requireNonNull(executor, "Executor cannot be null"); + + synchronized (mShortcutChangeCallbacks) { + final int callbackId = callback.hashCode(); + final Pair<Executor, ShortcutChangeCallback> state = new Pair<>(executor, callback); + mShortcutChangeCallbacks.put(callbackId, state); + try { + mService.registerShortcutChangeCallback(mContext.getPackageName(), + query.mChangedSince, query.mPackage, query.mShortcutIds, query.mLocusIds, + query.mActivity, query.mQueryFlags, new ShortcutChangeCallbackProxy(state), + callbackId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + + /** + * Unregisters a callback that was previously registered. + * @see #registerShortcutChangeCallback(ShortcutChangeCallback, ShortcutQuery, Executor) + * + * @param callback Callback to be unregistered. + * + * @hide + */ + public void unregisterShortcutChangeCallback(@NonNull ShortcutChangeCallback callback) { + Objects.requireNonNull(callback, "Callback cannot be null"); + + synchronized (mShortcutChangeCallbacks) { + final int callbackId = callback.hashCode(); + if (mShortcutChangeCallbacks.containsKey(callbackId)) { + mShortcutChangeCallbacks.remove(callbackId); + try { + mService.unregisterShortcutChangeCallback(mContext.getPackageName(), + callbackId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + } + + /** * A helper method to extract a {@link PinItemRequest} set to * the {@link #EXTRA_PIN_ITEM_REQUEST} extra. */ diff --git a/core/java/android/content/pm/ShortcutServiceInternal.java b/core/java/android/content/pm/ShortcutServiceInternal.java index e6f682d22b14..a11a1dd5a68b 100644 --- a/core/java/android/content/pm/ShortcutServiceInternal.java +++ b/core/java/android/content/pm/ShortcutServiceInternal.java @@ -23,6 +23,7 @@ import android.appwidget.AppWidgetProviderInfo; import android.content.ComponentName; import android.content.Intent; import android.content.IntentSender; +import android.content.LocusId; import android.content.pm.LauncherApps.ShortcutQuery; import android.os.Bundle; import android.os.ParcelFileDescriptor; @@ -45,8 +46,8 @@ public abstract class ShortcutServiceInternal { getShortcuts(int launcherUserId, @NonNull String callingPackage, long changedSince, @Nullable String packageName, @Nullable List<String> shortcutIds, - @Nullable ComponentName componentName, @ShortcutQuery.QueryFlags int flags, - int userId, int callingPid, int callingUid); + @Nullable List<LocusId> locusIds, @Nullable ComponentName componentName, + @ShortcutQuery.QueryFlags int flags, int userId, int callingPid, int callingUid); public abstract boolean isPinnedByCaller(int launcherUserId, @NonNull String callingPackage, diff --git a/core/java/android/content/pm/TEST_MAPPING b/core/java/android/content/pm/TEST_MAPPING index 0549c34cc034..6f30ecd9b281 100644 --- a/core/java/android/content/pm/TEST_MAPPING +++ b/core/java/android/content/pm/TEST_MAPPING @@ -15,5 +15,15 @@ "name": "FrameworksInstantAppResolverTests", "file_patterns": ["(/|^)InstantApp[^/]*"] } + ], + "postsubmit": [ + { + "name": "CtsAppSecurityHostTestCases", + "options": [ + { + "include-filter": "android.appsecurity.cts.AppSecurityTests#testPermissionDiffCert" + } + ] + } ] } diff --git a/core/java/android/hardware/biometrics/BiometricNativeHandleUtils.java b/core/java/android/hardware/biometrics/BiometricNativeHandleUtils.java new file mode 100644 index 000000000000..5544eaeca7f3 --- /dev/null +++ b/core/java/android/hardware/biometrics/BiometricNativeHandleUtils.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2020 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.hardware.biometrics; + +import android.os.NativeHandle; +import android.os.ParcelFileDescriptor; + +import java.io.IOException; + +/** + * A class that contains utilities for IBiometricNativeHandle. + * + * @hide + */ +public final class BiometricNativeHandleUtils { + + private BiometricNativeHandleUtils() { + } + + /** + * Converts a {@link NativeHandle} into an {@link IBiometricNativeHandle} by duplicating the + * underlying file descriptors. + * + * Both the original and new handle must be closed after use. + * + * @param h {@link NativeHandle}. Usually used to identify a WindowManager window. Can be null. + * @return A {@link IBiometricNativeHandle} representation of {@code h}. Will be null if + * {@code h} or its raw file descriptors are null. + */ + public static IBiometricNativeHandle dup(NativeHandle h) { + IBiometricNativeHandle handle = null; + if (h != null && h.getFileDescriptors() != null && h.getInts() != null) { + handle = new IBiometricNativeHandle(); + handle.ints = h.getInts().clone(); + handle.fds = new ParcelFileDescriptor[h.getFileDescriptors().length]; + for (int i = 0; i < h.getFileDescriptors().length; ++i) { + try { + handle.fds[i] = ParcelFileDescriptor.dup(h.getFileDescriptors()[i]); + } catch (IOException e) { + return null; + } + } + } + return handle; + } + + /** + * Closes the handle's file descriptors. + * + * @param h {@link IBiometricNativeHandle} handle. + */ + public static void close(IBiometricNativeHandle h) { + if (h != null) { + for (ParcelFileDescriptor fd : h.fds) { + if (fd != null) { + try { + fd.close(); + } catch (IOException e) { + // do nothing. + } + } + } + } + } +} diff --git a/core/java/android/hardware/biometrics/IBiometricNativeHandle.aidl b/core/java/android/hardware/biometrics/IBiometricNativeHandle.aidl new file mode 100644 index 000000000000..6dcdc1be3a50 --- /dev/null +++ b/core/java/android/hardware/biometrics/IBiometricNativeHandle.aidl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2020 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.hardware.biometrics; + +/** + * Representation of a native handle. + * Copied from /common/aidl/android/hardware/common/NativeHandle.aidl + * @hide + */ +parcelable IBiometricNativeHandle { + ParcelFileDescriptor[] fds; + int[] ints; +} diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java index 55ebe285af1e..6bda46b0b692 100644 --- a/core/java/android/hardware/face/FaceManager.java +++ b/core/java/android/hardware/face/FaceManager.java @@ -29,7 +29,9 @@ import android.content.Context; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricFaceConstants; +import android.hardware.biometrics.BiometricNativeHandleUtils; import android.hardware.biometrics.CryptoObject; +import android.hardware.biometrics.IBiometricNativeHandle; import android.hardware.biometrics.IBiometricServiceLockoutResetCallback; import android.os.Binder; import android.os.CancellationSignal; @@ -38,6 +40,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.IRemoteCallback; import android.os.Looper; +import android.os.NativeHandle; import android.os.PowerManager; import android.os.RemoteException; import android.os.Trace; @@ -245,6 +248,19 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan } /** + * Defaults to {@link FaceManager#enroll(int, byte[], CancellationSignal, EnrollmentCallback, + * int[], NativeHandle)} with {@code windowId} set to null. + * + * @see FaceManager#enroll(int, byte[], CancellationSignal, EnrollmentCallback, int[], + * NativeHandle) + */ + @RequiresPermission(MANAGE_BIOMETRIC) + public void enroll(int userId, byte[] token, CancellationSignal cancel, + EnrollmentCallback callback, int[] disabledFeatures) { + enroll(userId, token, cancel, callback, disabledFeatures, null /* windowId */); + } + + /** * Request face authentication enrollment. This call operates the face authentication hardware * and starts capturing images. Progress will be indicated by callbacks to the * {@link EnrollmentCallback} object. It terminates when @@ -259,11 +275,13 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan * @param flags optional flags * @param userId the user to whom this face will belong to * @param callback an object to receive enrollment events + * @param windowId optional ID of a camera preview window for a single-camera device. Must be + * null if not used. * @hide */ @RequiresPermission(MANAGE_BIOMETRIC) public void enroll(int userId, byte[] token, CancellationSignal cancel, - EnrollmentCallback callback, int[] disabledFeatures) { + EnrollmentCallback callback, int[] disabledFeatures, @Nullable NativeHandle windowId) { if (callback == null) { throw new IllegalArgumentException("Must supply an enrollment callback"); } @@ -278,20 +296,72 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan } if (mService != null) { + IBiometricNativeHandle handle = BiometricNativeHandleUtils.dup(windowId); try { mEnrollmentCallback = callback; Trace.beginSection("FaceManager#enroll"); mService.enroll(userId, mToken, token, mServiceReceiver, - mContext.getOpPackageName(), disabledFeatures); + mContext.getOpPackageName(), disabledFeatures, handle); } catch (RemoteException e) { Log.w(TAG, "Remote exception in enroll: ", e); - if (callback != null) { - // Though this may not be a hardware issue, it will cause apps to give up or - // try again later. - callback.onEnrollmentError(FACE_ERROR_HW_UNAVAILABLE, - getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE, + // Though this may not be a hardware issue, it will cause apps to give up or + // try again later. + callback.onEnrollmentError(FACE_ERROR_HW_UNAVAILABLE, + getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE, + 0 /* vendorCode */)); + } finally { + Trace.endSection(); + BiometricNativeHandleUtils.close(handle); + } + } + } + + /** + * Request face authentication enrollment for a remote client, for example Android Auto. + * This call operates the face authentication hardware and starts capturing images. + * Progress will be indicated by callbacks to the + * {@link EnrollmentCallback} object. It terminates when + * {@link EnrollmentCallback#onEnrollmentError(int, CharSequence)} or + * {@link EnrollmentCallback#onEnrollmentProgress(int) is called with remaining == 0, at + * which point the object is no longer valid. The operation can be canceled by using the + * provided cancel object. + * + * @param token a unique token provided by a recent creation or verification of device + * credentials (e.g. pin, pattern or password). + * @param cancel an object that can be used to cancel enrollment + * @param userId the user to whom this face will belong to + * @param callback an object to receive enrollment events + * @hide + */ + @RequiresPermission(MANAGE_BIOMETRIC) + public void enrollRemotely(int userId, byte[] token, CancellationSignal cancel, + EnrollmentCallback callback, int[] disabledFeatures) { + if (callback == null) { + throw new IllegalArgumentException("Must supply an enrollment callback"); + } + + if (cancel != null) { + if (cancel.isCanceled()) { + Log.w(TAG, "enrollRemotely is already canceled."); + return; + } else { + cancel.setOnCancelListener(new OnEnrollCancelListener()); + } + } + + if (mService != null) { + try { + mEnrollmentCallback = callback; + Trace.beginSection("FaceManager#enrollRemotely"); + mService.enrollRemotely(userId, mToken, token, mServiceReceiver, + mContext.getOpPackageName(), disabledFeatures); + } catch (RemoteException e) { + Log.w(TAG, "Remote exception in enrollRemotely: ", e); + // Though this may not be a hardware issue, it will cause apps to give up or + // try again later. + callback.onEnrollmentError(FACE_ERROR_HW_UNAVAILABLE, + getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */)); - } } finally { Trace.endSection(); } diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl index 68a4aef7af08..8ba24735c039 100644 --- a/core/java/android/hardware/face/IFaceService.aidl +++ b/core/java/android/hardware/face/IFaceService.aidl @@ -15,6 +15,7 @@ */ package android.hardware.face; +import android.hardware.biometrics.IBiometricNativeHandle; import android.hardware.biometrics.IBiometricServiceReceiverInternal; import android.hardware.biometrics.IBiometricServiceLockoutResetCallback; import android.hardware.face.IFaceServiceReceiver; @@ -51,6 +52,10 @@ interface IFaceService { // Start face enrollment void enroll(int userId, IBinder token, in byte [] cryptoToken, IFaceServiceReceiver receiver, + String opPackageName, in int [] disabledFeatures, in IBiometricNativeHandle windowId); + + // Start remote face enrollment + void enrollRemotely(int userId, IBinder token, in byte [] cryptoToken, IFaceServiceReceiver receiver, String opPackageName, in int [] disabledFeatures); // Cancel enrollment in progress diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index ff9d14510d4b..f6717c77f90e 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -32,7 +32,9 @@ import android.content.Context; import android.content.pm.PackageManager; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricFingerprintConstants; +import android.hardware.biometrics.BiometricNativeHandleUtils; import android.hardware.biometrics.BiometricPrompt; +import android.hardware.biometrics.IBiometricNativeHandle; import android.hardware.biometrics.IBiometricServiceLockoutResetCallback; import android.os.Binder; import android.os.CancellationSignal; @@ -41,6 +43,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.IRemoteCallback; import android.os.Looper; +import android.os.NativeHandle; import android.os.PowerManager; import android.os.RemoteException; import android.os.UserHandle; @@ -403,15 +406,33 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing } /** + * Defaults to {@link FingerprintManager#authenticate(CryptoObject, CancellationSignal, int, + * AuthenticationCallback, Handler, int, NativeHandle)} with {@code windowId} set to null. + * + * @see FingerprintManager#authenticate(CryptoObject, CancellationSignal, int, + * AuthenticationCallback, Handler, int, NativeHandle) + * + * @hide + */ + @RequiresPermission(anyOf = {USE_BIOMETRIC, USE_FINGERPRINT}) + public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel, + int flags, @NonNull AuthenticationCallback callback, Handler handler, int userId) { + authenticate(crypto, cancel, flags, callback, handler, userId, null /* windowId */); + } + + /** * Per-user version, see {@link FingerprintManager#authenticate(CryptoObject, * CancellationSignal, int, AuthenticationCallback, Handler)}. This version does not * display the BiometricPrompt. * @param userId the user ID that the fingerprint hardware will authenticate for. + * @param windowId for optical fingerprint sensors that require active illumination by the OLED + * display. Should be null for devices that don't require illumination. * @hide */ @RequiresPermission(anyOf = {USE_BIOMETRIC, USE_FINGERPRINT}) public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel, - int flags, @NonNull AuthenticationCallback callback, Handler handler, int userId) { + int flags, @NonNull AuthenticationCallback callback, Handler handler, int userId, + @Nullable NativeHandle windowId) { if (callback == null) { throw new IllegalArgumentException("Must supply an authentication callback"); } @@ -425,26 +446,44 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing } } - if (mService != null) try { - useHandler(handler); - mAuthenticationCallback = callback; - mCryptoObject = crypto; - long sessionId = crypto != null ? crypto.getOpId() : 0; - mService.authenticate(mToken, sessionId, userId, mServiceReceiver, flags, - mContext.getOpPackageName()); - } catch (RemoteException e) { - Slog.w(TAG, "Remote exception while authenticating: ", e); - if (callback != null) { + if (mService != null) { + IBiometricNativeHandle handle = BiometricNativeHandleUtils.dup(windowId); + try { + useHandler(handler); + mAuthenticationCallback = callback; + mCryptoObject = crypto; + long sessionId = crypto != null ? crypto.getOpId() : 0; + mService.authenticate(mToken, sessionId, userId, mServiceReceiver, flags, + mContext.getOpPackageName(), handle); + } catch (RemoteException e) { + Slog.w(TAG, "Remote exception while authenticating: ", e); // Though this may not be a hardware issue, it will cause apps to give up or try // again later. callback.onAuthenticationError(FINGERPRINT_ERROR_HW_UNAVAILABLE, getErrorString(mContext, FINGERPRINT_ERROR_HW_UNAVAILABLE, - 0 /* vendorCode */)); + 0 /* vendorCode */)); + } finally { + BiometricNativeHandleUtils.close(handle); } } } /** + * Defaults to {@link FingerprintManager#enroll(byte[], CancellationSignal, int, int, + * EnrollmentCallback, NativeHandle)} with {@code windowId} set to null. + * + * @see FingerprintManager#enroll(byte[], CancellationSignal, int, int, EnrollmentCallback, + * NativeHandle) + * + * @hide + */ + @RequiresPermission(MANAGE_FINGERPRINT) + public void enroll(byte [] token, CancellationSignal cancel, int flags, + int userId, EnrollmentCallback callback) { + enroll(token, cancel, flags, userId, callback, null /* windowId */); + } + + /** * Request fingerprint enrollment. This call warms up the fingerprint hardware * and starts scanning for fingerprints. Progress will be indicated by callbacks to the * {@link EnrollmentCallback} object. It terminates when @@ -462,7 +501,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing */ @RequiresPermission(MANAGE_FINGERPRINT) public void enroll(byte [] token, CancellationSignal cancel, int flags, - int userId, EnrollmentCallback callback) { + int userId, EnrollmentCallback callback, @Nullable NativeHandle windowId) { if (userId == UserHandle.USER_CURRENT) { userId = getCurrentUserId(); } @@ -479,18 +518,21 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing } } - if (mService != null) try { - mEnrollmentCallback = callback; - mService.enroll(mToken, token, userId, mServiceReceiver, flags, - mContext.getOpPackageName()); - } catch (RemoteException e) { - Slog.w(TAG, "Remote exception in enroll: ", e); - if (callback != null) { + if (mService != null) { + IBiometricNativeHandle handle = BiometricNativeHandleUtils.dup(windowId); + try { + mEnrollmentCallback = callback; + mService.enroll(mToken, token, userId, mServiceReceiver, flags, + mContext.getOpPackageName(), handle); + } catch (RemoteException e) { + Slog.w(TAG, "Remote exception in enroll: ", e); // Though this may not be a hardware issue, it will cause apps to give up or try // again later. callback.onEnrollmentError(FINGERPRINT_ERROR_HW_UNAVAILABLE, getErrorString(mContext, FINGERPRINT_ERROR_HW_UNAVAILABLE, - 0 /* vendorCode */)); + 0 /* vendorCode */)); + } finally { + BiometricNativeHandleUtils.close(handle); } } } diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl index 1a7e12856753..f2ffd08d5bc8 100644 --- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl +++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl @@ -15,6 +15,7 @@ */ package android.hardware.fingerprint; +import android.hardware.biometrics.IBiometricNativeHandle; import android.hardware.biometrics.IBiometricServiceReceiverInternal; import android.hardware.biometrics.IBiometricServiceLockoutResetCallback; import android.hardware.fingerprint.IFingerprintClientActiveCallback; @@ -31,7 +32,8 @@ interface IFingerprintService { // USE_FINGERPRINT/USE_BIOMETRIC permission. This is effectively deprecated, since it only comes // through FingerprintManager now. void authenticate(IBinder token, long sessionId, int userId, - IFingerprintServiceReceiver receiver, int flags, String opPackageName); + IFingerprintServiceReceiver receiver, int flags, String opPackageName, + in IBiometricNativeHandle windowId); // This method prepares the service to start authenticating, but doesn't start authentication. // This is protected by the MANAGE_BIOMETRIC signatuer permission. This method should only be @@ -40,7 +42,7 @@ interface IFingerprintService { // startPreparedClient(). void prepareForAuthentication(IBinder token, long sessionId, int userId, IBiometricServiceReceiverInternal wrapperReceiver, String opPackageName, int cookie, - int callingUid, int callingPid, int callingUserId); + int callingUid, int callingPid, int callingUserId, in IBiometricNativeHandle windowId); // Starts authentication with the previously prepared client. void startPreparedClient(int cookie); @@ -55,7 +57,7 @@ interface IFingerprintService { // Start fingerprint enrollment void enroll(IBinder token, in byte [] cryptoToken, int groupId, IFingerprintServiceReceiver receiver, - int flags, String opPackageName); + int flags, String opPackageName, in IBiometricNativeHandle windowId); // Cancel enrollment in progress void cancelEnrollment(IBinder token); diff --git a/core/java/android/hardware/soundtrigger/ConversionUtil.java b/core/java/android/hardware/soundtrigger/ConversionUtil.java index a30fd6b51e76..dbf33cade60c 100644 --- a/core/java/android/hardware/soundtrigger/ConversionUtil.java +++ b/core/java/android/hardware/soundtrigger/ConversionUtil.java @@ -17,7 +17,6 @@ package android.hardware.soundtrigger; import android.annotation.Nullable; -import android.hardware.soundtrigger.ModelParams; import android.media.AudioFormat; import android.media.audio.common.AudioConfig; import android.media.soundtrigger_middleware.AudioCapabilities; @@ -333,20 +332,22 @@ class ConversionUtil { public static int aidl2apiAudioCapabilities(int aidlCapabilities) { int result = 0; if ((aidlCapabilities & AudioCapabilities.ECHO_CANCELLATION) != 0) { - result |= SoundTrigger.ModuleProperties.CAPABILITY_ECHO_CANCELLATION; + result |= SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_ECHO_CANCELLATION; } if ((aidlCapabilities & AudioCapabilities.NOISE_SUPPRESSION) != 0) { - result |= SoundTrigger.ModuleProperties.CAPABILITY_NOISE_SUPPRESSION; + result |= SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_NOISE_SUPPRESSION; } return result; } public static int api2aidlAudioCapabilities(int apiCapabilities) { int result = 0; - if ((apiCapabilities & SoundTrigger.ModuleProperties.CAPABILITY_ECHO_CANCELLATION) != 0) { + if ((apiCapabilities & SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_ECHO_CANCELLATION) + != 0) { result |= AudioCapabilities.ECHO_CANCELLATION; } - if ((apiCapabilities & SoundTrigger.ModuleProperties.CAPABILITY_NOISE_SUPPRESSION) != 0) { + if ((apiCapabilities & SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_NOISE_SUPPRESSION) + != 0) { result |= AudioCapabilities.NOISE_SUPPRESSION; } return result; diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java index d505ae59dfaf..a74871d29041 100644 --- a/core/java/android/hardware/soundtrigger/SoundTrigger.java +++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java @@ -97,8 +97,8 @@ public class SoundTrigger { */ @Retention(RetentionPolicy.SOURCE) @IntDef(flag = true, prefix = { "AUDIO_CAPABILITY_" }, value = { - CAPABILITY_ECHO_CANCELLATION, - CAPABILITY_NOISE_SUPPRESSION + AUDIO_CAPABILITY_ECHO_CANCELLATION, + AUDIO_CAPABILITY_NOISE_SUPPRESSION }) public @interface AudioCapabilities {} @@ -106,12 +106,12 @@ public class SoundTrigger { * If set the underlying module supports AEC. * Describes bit field {@link ModuleProperties#audioCapabilities} */ - public static final int CAPABILITY_ECHO_CANCELLATION = 0x1; + public static final int AUDIO_CAPABILITY_ECHO_CANCELLATION = 0x1; /** * If set, the underlying module supports noise suppression. * Describes bit field {@link ModuleProperties#audioCapabilities} */ - public static final int CAPABILITY_NOISE_SUPPRESSION = 0x2; + public static final int AUDIO_CAPABILITY_NOISE_SUPPRESSION = 0x2; /** Unique module ID provided by the native service */ public final int id; @@ -735,22 +735,40 @@ public class SoundTrigger { /** * The inclusive start of supported range. */ - public final int start; + private final int mStart; /** * The inclusive end of supported range. */ - public final int end; + private final int mEnd; ModelParamRange(int start, int end) { - this.start = start; - this.end = end; + this.mStart = start; + this.mEnd = end; } /** @hide */ private ModelParamRange(@NonNull Parcel in) { - this.start = in.readInt(); - this.end = in.readInt(); + this.mStart = in.readInt(); + this.mEnd = in.readInt(); + } + + /** + * Get the beginning of the param range + * + * @return The inclusive start of the supported range. + */ + public int getStart() { + return mStart; + } + + /** + * Get the end of the param range + * + * @return The inclusive end of the supported range. + */ + public int getEnd() { + return mEnd; } @NonNull @@ -780,8 +798,8 @@ public class SoundTrigger { public int hashCode() { final int prime = 31; int result = 1; - result = prime * result + (start); - result = prime * result + (end); + result = prime * result + (mStart); + result = prime * result + (mEnd); return result; } @@ -797,10 +815,10 @@ public class SoundTrigger { return false; } ModelParamRange other = (ModelParamRange) obj; - if (start != other.start) { + if (mStart != other.mStart) { return false; } - if (end != other.end) { + if (mEnd != other.mEnd) { return false; } return true; @@ -808,14 +826,14 @@ public class SoundTrigger { @Override public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeInt(start); - dest.writeInt(end); + dest.writeInt(mStart); + dest.writeInt(mEnd); } @Override @NonNull public String toString() { - return "ModelParamRange [start=" + start + ", end=" + end + "]"; + return "ModelParamRange [start=" + mStart + ", end=" + mEnd + "]"; } } diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index fa12c08f2277..f644f148a5ad 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -3750,6 +3750,7 @@ public class ConnectivityManager { checkCallbackNotNull(callback); Preconditions.checkArgument(action == REQUEST || need != null, "null NetworkCapabilities"); final NetworkRequest request; + final String callingPackageName = mContext.getOpPackageName(); try { synchronized(sCallbacks) { if (callback.networkRequest != null @@ -3761,10 +3762,11 @@ public class ConnectivityManager { Messenger messenger = new Messenger(handler); Binder binder = new Binder(); if (action == LISTEN) { - request = mService.listenForNetwork(need, messenger, binder); + request = mService.listenForNetwork( + need, messenger, binder, callingPackageName); } else { request = mService.requestNetwork( - need, messenger, timeoutMs, binder, legacyType); + need, messenger, timeoutMs, binder, legacyType, callingPackageName); } if (request != null) { sCallbacks.put(request, callback); @@ -4037,8 +4039,10 @@ public class ConnectivityManager { @NonNull PendingIntent operation) { printStackTrace(); checkPendingIntentNotNull(operation); + final String callingPackageName = mContext.getOpPackageName(); try { - mService.pendingRequestForNetwork(request.networkCapabilities, operation); + mService.pendingRequestForNetwork( + request.networkCapabilities, operation, callingPackageName); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (ServiceSpecificException e) { @@ -4150,8 +4154,10 @@ public class ConnectivityManager { @NonNull PendingIntent operation) { printStackTrace(); checkPendingIntentNotNull(operation); + final String callingPackageName = mContext.getOpPackageName(); try { - mService.pendingListenForNetwork(request.networkCapabilities, operation); + mService.pendingListenForNetwork( + request.networkCapabilities, operation, callingPackageName); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } catch (ServiceSpecificException e) { diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index 0fae607ca6ca..1c7628f6ad0a 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -167,18 +167,19 @@ interface IConnectivityManager in int factorySerialNumber); NetworkRequest requestNetwork(in NetworkCapabilities networkCapabilities, - in Messenger messenger, int timeoutSec, in IBinder binder, int legacy); + in Messenger messenger, int timeoutSec, in IBinder binder, int legacy, + String callingPackageName); NetworkRequest pendingRequestForNetwork(in NetworkCapabilities networkCapabilities, - in PendingIntent operation); + in PendingIntent operation, String callingPackageName); void releasePendingNetworkRequest(in PendingIntent operation); NetworkRequest listenForNetwork(in NetworkCapabilities networkCapabilities, - in Messenger messenger, in IBinder binder); + in Messenger messenger, in IBinder binder, String callingPackageName); void pendingListenForNetwork(in NetworkCapabilities networkCapabilities, - in PendingIntent operation); + in PendingIntent operation, String callingPackageName); void releaseNetworkRequest(in NetworkRequest networkRequest); diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java index cf5f2259af44..f8b51dd9906b 100644 --- a/core/java/android/net/NetworkCapabilities.java +++ b/core/java/android/net/NetworkCapabilities.java @@ -27,6 +27,7 @@ import android.os.Build; import android.os.Parcel; import android.os.Parcelable; import android.os.Process; +import android.text.TextUtils; import android.util.ArraySet; import android.util.proto.ProtoOutputStream; @@ -63,6 +64,16 @@ public final class NetworkCapabilities implements Parcelable { // Set to true when private DNS is broken. private boolean mPrivateDnsBroken; + /** + * Uid of the app making the request. + */ + private int mRequestorUid; + + /** + * Package name of the app making the request. + */ + private String mRequestorPackageName; + public NetworkCapabilities() { clearAll(); mNetworkCapabilities = DEFAULT_CAPABILITIES; @@ -89,6 +100,8 @@ public final class NetworkCapabilities implements Parcelable { mOwnerUid = Process.INVALID_UID; mSSID = null; mPrivateDnsBroken = false; + mRequestorUid = Process.INVALID_UID; + mRequestorPackageName = null; } /** @@ -109,6 +122,8 @@ public final class NetworkCapabilities implements Parcelable { mUnwantedNetworkCapabilities = nc.mUnwantedNetworkCapabilities; mSSID = nc.mSSID; mPrivateDnsBroken = nc.mPrivateDnsBroken; + mRequestorUid = nc.mRequestorUid; + mRequestorPackageName = nc.mRequestorPackageName; } /** @@ -810,7 +825,7 @@ public final class NetworkCapabilities implements Parcelable { } /** - * UID of the app that owns this network, or INVALID_UID if none/unknown. + * UID of the app that owns this network, or Process#INVALID_UID if none/unknown. * * <p>This field keeps track of the UID of the app that created this network and is in charge of * its lifecycle. This could be the UID of apps such as the Wifi network suggestor, the running @@ -821,8 +836,9 @@ public final class NetworkCapabilities implements Parcelable { /** * Set the UID of the owner app. */ - public void setOwnerUid(final int uid) { + public @NonNull NetworkCapabilities setOwnerUid(final int uid) { mOwnerUid = uid; + return this; } /** @@ -865,9 +881,11 @@ public final class NetworkCapabilities implements Parcelable { * @hide */ @SystemApi - public void setAdministratorUids(@NonNull final List<Integer> administratorUids) { + public @NonNull NetworkCapabilities setAdministratorUids( + @NonNull final List<Integer> administratorUids) { mAdministratorUids.clear(); mAdministratorUids.addAll(administratorUids); + return this; } /** @@ -1385,6 +1403,7 @@ public final class NetworkCapabilities implements Parcelable { combineSignalStrength(nc); combineUids(nc); combineSSIDs(nc); + combineRequestor(nc); } /** @@ -1404,7 +1423,8 @@ public final class NetworkCapabilities implements Parcelable { && satisfiedBySpecifier(nc) && (onlyImmutable || satisfiedBySignalStrength(nc)) && (onlyImmutable || satisfiedByUids(nc)) - && (onlyImmutable || satisfiedBySSID(nc))); + && (onlyImmutable || satisfiedBySSID(nc))) + && (onlyImmutable || satisfiedByRequestor(nc)); } /** @@ -1488,7 +1508,7 @@ public final class NetworkCapabilities implements Parcelable { public boolean equals(@Nullable Object obj) { if (obj == null || (obj instanceof NetworkCapabilities == false)) return false; NetworkCapabilities that = (NetworkCapabilities) obj; - return (equalsNetCapabilities(that) + return equalsNetCapabilities(that) && equalsTransportTypes(that) && equalsLinkBandwidths(that) && equalsSignalStrength(that) @@ -1496,7 +1516,8 @@ public final class NetworkCapabilities implements Parcelable { && equalsTransportInfo(that) && equalsUids(that) && equalsSSID(that) - && equalsPrivateDnsBroken(that)); + && equalsPrivateDnsBroken(that) + && equalsRequestor(that); } @Override @@ -1514,7 +1535,9 @@ public final class NetworkCapabilities implements Parcelable { + Objects.hashCode(mUids) * 31 + Objects.hashCode(mSSID) * 37 + Objects.hashCode(mTransportInfo) * 41 - + Objects.hashCode(mPrivateDnsBroken) * 43; + + Objects.hashCode(mPrivateDnsBroken) * 43 + + Objects.hashCode(mRequestorUid) * 47 + + Objects.hashCode(mRequestorPackageName) * 53; } @Override @@ -1537,6 +1560,8 @@ public final class NetworkCapabilities implements Parcelable { dest.writeBoolean(mPrivateDnsBroken); dest.writeList(mAdministratorUids); dest.writeInt(mOwnerUid); + dest.writeInt(mRequestorUid); + dest.writeString(mRequestorPackageName); } public static final @android.annotation.NonNull Creator<NetworkCapabilities> CREATOR = @@ -1559,6 +1584,8 @@ public final class NetworkCapabilities implements Parcelable { netCap.mPrivateDnsBroken = in.readBoolean(); netCap.setAdministratorUids(in.readArrayList(null)); netCap.mOwnerUid = in.readInt(); + netCap.mRequestorUid = in.readInt(); + netCap.mRequestorPackageName = in.readString(); return netCap; } @Override @@ -1624,6 +1651,9 @@ public final class NetworkCapabilities implements Parcelable { sb.append(" Private DNS is broken"); } + sb.append(" RequestorUid: ").append(mRequestorUid); + sb.append(" RequestorPackageName: ").append(mRequestorPackageName); + sb.append("]"); return sb.toString(); } @@ -1632,6 +1662,7 @@ public final class NetworkCapabilities implements Parcelable { private interface NameOf { String nameOf(int value); } + /** * @hide */ @@ -1799,4 +1830,120 @@ public final class NetworkCapabilities implements Parcelable { private boolean equalsPrivateDnsBroken(NetworkCapabilities nc) { return mPrivateDnsBroken == nc.mPrivateDnsBroken; } + + /** + * Set the uid of the app making the request. + * + * Note: This works only for {@link NetworkAgent} instances. Any capabilities passed in + * via the public {@link ConnectivityManager} API's will have this field overwritten. + * + * @param uid UID of the app. + * @hide + */ + @SystemApi + public @NonNull NetworkCapabilities setRequestorUid(int uid) { + mRequestorUid = uid; + return this; + } + + /** + * @return the uid of the app making the request. + * + * Note: This could return {@link Process#INVALID_UID} if the {@link NetworkRequest} + * object was not obtained from {@link ConnectivityManager}. + * @hide + */ + public int getRequestorUid() { + return mRequestorUid; + } + + /** + * Set the package name of the app making the request. + * + * Note: This works only for {@link NetworkAgent} instances. Any capabilities passed in + * via the public {@link ConnectivityManager} API's will have this field overwritten. + * + * @param packageName package name of the app. + * @hide + */ + @SystemApi + public @NonNull NetworkCapabilities setRequestorPackageName(@NonNull String packageName) { + mRequestorPackageName = packageName; + return this; + } + + /** + * @return the package name of the app making the request. + * + * Note: This could return {@code null} if the {@link NetworkRequest} object was not obtained + * from {@link ConnectivityManager}. + * @hide + */ + @Nullable + public String getRequestorPackageName() { + return mRequestorPackageName; + } + + /** + * Set the uid and package name of the app making the request. + * + * Note: This is intended to be only invoked from within connectivitiy service. + * + * @param uid UID of the app. + * @param packageName package name of the app. + * @hide + */ + public @NonNull NetworkCapabilities setRequestorUidAndPackageName( + int uid, @NonNull String packageName) { + return setRequestorUid(uid).setRequestorPackageName(packageName); + } + + /** + * Test whether the passed NetworkCapabilities satisfies the requestor restrictions of this + * capabilities. + * + * This method is called on the NetworkCapabilities embedded in a request with the + * capabilities of an available network. If the available network, sets a specific + * requestor (by uid and optionally package name), then this will only match a request from the + * same app. If either of the capabilities have an unset uid or package name, then it matches + * everything. + * <p> + * nc is assumed nonnull. Else, NPE. + */ + private boolean satisfiedByRequestor(NetworkCapabilities nc) { + // No uid set, matches everything. + if (mRequestorUid == Process.INVALID_UID || nc.mRequestorUid == Process.INVALID_UID) { + return true; + } + // uids don't match. + if (mRequestorUid != nc.mRequestorUid) return false; + // No package names set, matches everything + if (null == nc.mRequestorPackageName || null == mRequestorPackageName) return true; + // check for package name match. + return TextUtils.equals(mRequestorPackageName, nc.mRequestorPackageName); + } + + /** + * Combine requestor info of the capabilities. + * <p> + * This is only legal if either the requestor info of this object is reset, or both info are + * equal. + * nc is assumed nonnull. + */ + private void combineRequestor(@NonNull NetworkCapabilities nc) { + if (mRequestorUid != Process.INVALID_UID && mRequestorUid != nc.mOwnerUid) { + throw new IllegalStateException("Can't combine two uids"); + } + if (mRequestorPackageName != null + && !mRequestorPackageName.equals(nc.mRequestorPackageName)) { + throw new IllegalStateException("Can't combine two package names"); + } + setRequestorUid(nc.mRequestorUid); + setRequestorPackageName(nc.mRequestorPackageName); + } + + private boolean equalsRequestor(NetworkCapabilities nc) { + return mRequestorUid == nc.mRequestorUid + && TextUtils.equals(mRequestorPackageName, nc.mRequestorPackageName); + } } diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java index 301d20340643..964f13f39ec6 100644 --- a/core/java/android/net/NetworkRequest.java +++ b/core/java/android/net/NetworkRequest.java @@ -380,6 +380,7 @@ public class NetworkRequest implements Parcelable { dest.writeInt(requestId); dest.writeString(type.name()); } + public static final @android.annotation.NonNull Creator<NetworkRequest> CREATOR = new Creator<NetworkRequest>() { public NetworkRequest createFromParcel(Parcel in) { @@ -494,6 +495,31 @@ public class NetworkRequest implements Parcelable { return networkCapabilities.getNetworkSpecifier(); } + /** + * @return the uid of the app making the request. + * + * Note: This could return {@link Process#INVALID_UID} if the {@link NetworkRequest} object was + * not obtained from {@link ConnectivityManager}. + * @hide + */ + @SystemApi + public int getRequestorUid() { + return networkCapabilities.getRequestorUid(); + } + + /** + * @return the package name of the app making the request. + * + * Note: This could return {@code null} if the {@link NetworkRequest} object was not obtained + * from {@link ConnectivityManager}. + * @hide + */ + @SystemApi + @Nullable + public String getRequestorPackageName() { + return networkCapabilities.getRequestorPackageName(); + } + public String toString() { return "NetworkRequest [ " + type + " id=" + requestId + (legacyType != ConnectivityManager.TYPE_NONE ? ", legacyType=" + legacyType : "") + diff --git a/core/java/android/net/NetworkSpecifier.java b/core/java/android/net/NetworkSpecifier.java index cf31d217c967..2dd0c4e207fe 100644 --- a/core/java/android/net/NetworkSpecifier.java +++ b/core/java/android/net/NetworkSpecifier.java @@ -39,23 +39,6 @@ public abstract class NetworkSpecifier { /** * Optional method which can be overridden by concrete implementations of NetworkSpecifier to - * check a self-reported UID. A concrete implementation may contain a UID which would be self- - * reported by the caller (since NetworkSpecifier implementations should be non-mutable). This - * function is called by ConnectivityService and is passed the actual UID of the caller - - * allowing the verification of the self-reported UID. In cases of mismatch the implementation - * should throw a SecurityException. - * - * @param requestorUid The UID of the requestor as obtained from its binder. - * - * @hide - */ - @SystemApi - public void assertValidFromUid(int requestorUid) { - // empty - } - - /** - * Optional method which can be overridden by concrete implementations of NetworkSpecifier to * perform any redaction of information from the NetworkSpecifier, e.g. if it contains * sensitive information. The default implementation simply returns the object itself - i.e. * no information is redacted. A concrete implementation may return a modified (copy) of the diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index a29173469ffb..70b2db70a9e8 100755 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -240,6 +240,13 @@ public class Build { public static final String RELEASE = getString("ro.build.version.release"); /** + * The version string we show to the user; may be {@link #RELEASE} or + * {@link #CODENAME} if not a final release build. + */ + @NonNull public static final String RELEASE_OR_CODENAME = getString( + "ro.build.version.release_or_codename"); + + /** * The base OS build the product is based on. */ public static final String BASE_OS = SystemProperties.get("ro.build.version.base_os", ""); diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java index 44f12a6adf60..21a1e0f0a108 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -34,9 +34,11 @@ import android.text.TextUtils; import android.util.Log; import java.io.File; +import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedList; +import java.util.List; /** * Provides access to environment variables. @@ -539,12 +541,21 @@ public class Environment { @SystemApi public static @NonNull Collection<File> getInternalMediaDirectories() { final ArrayList<File> res = new ArrayList<>(); - res.add(new File(Environment.getRootDirectory(), "media")); - res.add(new File(Environment.getOemDirectory(), "media")); - res.add(new File(Environment.getProductDirectory(), "media")); + addCanonicalFile(res, new File(Environment.getRootDirectory(), "media")); + addCanonicalFile(res, new File(Environment.getOemDirectory(), "media")); + addCanonicalFile(res, new File(Environment.getProductDirectory(), "media")); return res; } + private static void addCanonicalFile(List<File> list, File file) { + try { + list.add(file.getCanonicalFile()); + } catch (IOException e) { + Log.w(TAG, "Failed to resolve " + file + ": " + e); + list.add(file); + } + } + /** * Return the primary shared/external storage directory. This directory may * not currently be accessible if it has been mounted by the user on their diff --git a/core/java/android/os/incremental/IncrementalManager.java b/core/java/android/os/incremental/IncrementalManager.java index 8990bdf06a6f..35fa37a491de 100644 --- a/core/java/android/os/incremental/IncrementalManager.java +++ b/core/java/android/os/incremental/IncrementalManager.java @@ -23,8 +23,6 @@ import android.annotation.SystemService; import android.content.Context; import android.content.pm.DataLoaderParams; import android.os.RemoteException; -import android.system.ErrnoException; -import android.system.Os; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; @@ -193,116 +191,54 @@ public final class IncrementalManager { } /** - * Renames an Incremental path to a new path. If source path is a file, make a link from the old - * Incremental file to the new one. If source path is a dir, unbind old dir from Incremental - * Storage and bind the new one. - * <ol> - * <li> For renaming a dir, dest dir will be created if not exists, and does not need to - * be on the same Incremental storage as the source. </li> - * <li> For renaming a file, dest file must be on the same Incremental storage as source. - * </li> - * </ol> + * Set up an app's code path. The expected outcome of this method is: + * 1) The actual apk directory under /data/incremental is bind-mounted to the parent directory + * of {@code afterCodeFile}. + * 2) All the files under {@code beforeCodeFile} will show up under {@code afterCodeFile}. * - * @param sourcePath Absolute path to the source. Should be the same type as the destPath (file - * or dir). Expected to already exist and is an Incremental path. - * @param destPath Absolute path to the destination. - * @throws IllegalArgumentException when 1) source does not exist, or 2) source and dest type - * mismatch (one is file and the other is dir), or 3) source - * path is not on Incremental File System, - * @throws IOException when 1) cannot find the root path of the Incremental storage - * of source, or 2) cannot retrieve the Incremental storage - * instance of the source, or 3) renaming a file, but dest is - * not on the same Incremental Storage, or 4) renaming a dir, - * dest dir does not exist but fails to be created. - * <p> - * TODO(b/136132412): add unit tests + * @param beforeCodeFile Path that is currently bind-mounted and have APKs under it. + * Should no longer have any APKs after this method is called. + * Example: /data/app/vmdl*tmp + * @param afterCodeFile Path that should will have APKs after this method is called. Its parent + * directory should be bind-mounted to a directory under /data/incremental. + * Example: /data/app/~~[randomStringA]/[packageName]-[randomStringB] + * @throws IllegalArgumentException + * @throws IOException + * TODO(b/147371381): add unit tests */ - public void rename(@NonNull String sourcePath, @NonNull String destPath) throws IOException { - final File source = new File(sourcePath); - final File dest = new File(destPath); - if (!source.exists()) { - throw new IllegalArgumentException("Path not exist: " + sourcePath); + public void renameCodePath(File beforeCodeFile, File afterCodeFile) + throws IllegalArgumentException, IOException { + final String beforeCodePath = beforeCodeFile.getAbsolutePath(); + final String afterCodePathParent = afterCodeFile.getParentFile().getAbsolutePath(); + if (!isIncrementalPath(beforeCodePath)) { + throw new IllegalArgumentException("Not an Incremental path: " + beforeCodePath); } - if (dest.exists()) { - throw new IllegalArgumentException("Target path already exists: " + destPath); + final String afterCodePathName = afterCodeFile.getName(); + final Path apkStoragePath = Paths.get(beforeCodePath); + if (apkStoragePath == null || apkStoragePath.toAbsolutePath() == null) { + throw new IOException("Invalid source storage path for: " + beforeCodePath); } - if (source.isDirectory() && dest.exists() && dest.isFile()) { - throw new IllegalArgumentException( - "Trying to rename a dir but destination is a file: " + destPath); - } - if (source.isFile() && dest.exists() && dest.isDirectory()) { - throw new IllegalArgumentException( - "Trying to rename a file but destination is a dir: " + destPath); - } - if (!isIncrementalPath(sourcePath)) { - throw new IllegalArgumentException("Not an Incremental path: " + sourcePath); - } - - Path storagePath = Paths.get(sourcePath); - if (source.isFile()) { - storagePath = getStoragePathForFile(source); - } - if (storagePath == null || storagePath.toAbsolutePath() == null) { - throw new IOException("Invalid source storage path for: " + sourcePath); - } - final IncrementalStorage storage = openStorage(storagePath.toAbsolutePath().toString()); - if (storage == null) { + final IncrementalStorage apkStorage = + openStorage(apkStoragePath.toAbsolutePath().toString()); + if (apkStorage == null) { throw new IOException("Failed to retrieve storage from Incremental Service."); } - - if (source.isFile()) { - renameFile(storage, storagePath, source, dest); - } else { - renameDir(storage, storagePath, source, dest); - } - } - - private void renameFile(IncrementalStorage storage, Path storagePath, - File source, File dest) throws IOException { - Path sourcePath = source.toPath(); - Path destPath = dest.toPath(); - if (!sourcePath.startsWith(storagePath)) { - throw new IOException("Path: " + source.getAbsolutePath() + " is not on storage at: " - + storagePath.toString()); - } - if (!destPath.startsWith(storagePath)) { - throw new IOException("Path: " + dest.getAbsolutePath() + " is not on storage at: " - + storagePath.toString()); + final IncrementalStorage linkedApkStorage = createStorage(afterCodePathParent, apkStorage, + IncrementalManager.CREATE_MODE_CREATE + | IncrementalManager.CREATE_MODE_PERMANENT_BIND); + if (linkedApkStorage == null) { + throw new IOException("Failed to create linked storage at dir: " + afterCodePathParent); } - final Path sourceRelativePath = storagePath.relativize(sourcePath); - final Path destRelativePath = storagePath.relativize(destPath); - storage.moveFile(sourceRelativePath.toString(), destRelativePath.toString()); - - } - - private void renameDir(IncrementalStorage storage, Path storagePath, - File source, File dest) throws IOException { - Path destPath = dest.toPath(); - boolean usedMkdir = false; - try { - Os.mkdir(dest.getAbsolutePath(), 0755); - usedMkdir = true; - } catch (ErrnoException e) { - // Traditional mkdir fails but maybe we can create it on Incremental File System if - // the dest path is on the same Incremental storage as the source. - if (destPath.startsWith(storagePath)) { - storage.makeDirectories(storagePath.relativize(destPath).toString()); - } else { - throw new IOException("Failed to create directory: " + dest.getAbsolutePath(), e); - } - } - try { - storage.moveDir(source.getAbsolutePath(), dest.getAbsolutePath()); - } catch (Exception ex) { - if (usedMkdir) { - try { - Os.remove(dest.getAbsolutePath()); - } catch (ErrnoException ignored) { - } + linkedApkStorage.makeDirectory(afterCodePathName); + File[] files = beforeCodeFile.listFiles(); + for (int i = 0; i < files.length; i++) { + if (files[i].isFile()) { + String fileName = files[i].getName(); + apkStorage.makeLink( + fileName, linkedApkStorage, afterCodePathName + "/" + fileName); } - throw new IOException( - "Failed to move " + source.getAbsolutePath() + " to " + dest.getAbsolutePath()); } + apkStorage.unBind(beforeCodePath); } /** diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java index b6f5138a6582..adfa885f121e 100644 --- a/core/java/android/provider/Telephony.java +++ b/core/java/android/provider/Telephony.java @@ -5205,6 +5205,7 @@ public final class Telephony { /** * TelephonyProvider column name for allowed network types. Indicate which network types * are allowed. Default is -1. + * <P>Type: BIGINT (long) </P> */ public static final String ALLOWED_NETWORK_TYPES = "allowed_network_types"; } diff --git a/core/java/android/service/textclassifier/TextClassifierService.java b/core/java/android/service/textclassifier/TextClassifierService.java index 848868a5532f..3ff6f549e337 100644 --- a/core/java/android/service/textclassifier/TextClassifierService.java +++ b/core/java/android/service/textclassifier/TextClassifierService.java @@ -41,6 +41,7 @@ import android.util.Slog; import android.view.textclassifier.ConversationActions; import android.view.textclassifier.SelectionEvent; import android.view.textclassifier.TextClassification; +import android.view.textclassifier.TextClassificationConstants; import android.view.textclassifier.TextClassificationContext; import android.view.textclassifier.TextClassificationManager; import android.view.textclassifier.TextClassificationSessionId; @@ -404,22 +405,27 @@ public abstract class TextClassifierService extends Service { */ @NonNull public static TextClassifier getDefaultTextClassifierImplementation(@NonNull Context context) { - final String defaultTextClassifierPackageName = - context.getPackageManager().getDefaultTextClassifierPackageName(); - if (TextUtils.isEmpty(defaultTextClassifierPackageName)) { - return TextClassifier.NO_OP; - } - if (defaultTextClassifierPackageName.equals(context.getPackageName())) { - throw new RuntimeException( - "The default text classifier itself should not call the" - + "getDefaultTextClassifierImplementation() method."); - } final TextClassificationManager tcm = context.getSystemService(TextClassificationManager.class); - if (tcm != null) { + if (tcm == null) { + return TextClassifier.NO_OP; + } + TextClassificationConstants settings = new TextClassificationConstants(); + if (settings.getUseDefaultTextClassifierAsDefaultImplementation()) { + final String defaultTextClassifierPackageName = + context.getPackageManager().getDefaultTextClassifierPackageName(); + if (TextUtils.isEmpty(defaultTextClassifierPackageName)) { + return TextClassifier.NO_OP; + } + if (defaultTextClassifierPackageName.equals(context.getPackageName())) { + throw new RuntimeException( + "The default text classifier itself should not call the" + + "getDefaultTextClassifierImplementation() method."); + } return tcm.getTextClassifier(TextClassifier.DEFAULT_SERVICE); + } else { + return tcm.getTextClassifier(TextClassifier.LOCAL); } - return TextClassifier.NO_OP; } /** @hide **/ diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java index 1966f17aaf35..a88d389178bf 100644 --- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java +++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java @@ -181,14 +181,14 @@ public class AlwaysOnHotwordDetector { * Returned by {@link #getSupportedAudioCapabilities()} */ public static final int AUDIO_CAPABILITY_ECHO_CANCELLATION = - SoundTrigger.ModuleProperties.CAPABILITY_ECHO_CANCELLATION; + SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_ECHO_CANCELLATION; /** * If set, the underlying module supports noise suppression. * Returned by {@link #getSupportedAudioCapabilities()} */ public static final int AUDIO_CAPABILITY_NOISE_SUPPRESSION = - SoundTrigger.ModuleProperties.CAPABILITY_NOISE_SUPPRESSION; + SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_NOISE_SUPPRESSION; /** @hide */ @Retention(RetentionPolicy.SOURCE) @@ -249,21 +249,21 @@ public class AlwaysOnHotwordDetector { } /** - * The inclusive start of supported range. + * Get the beginning of the param range * - * @return start of range + * @return The inclusive start of the supported range. */ - public int start() { - return mModelParamRange.start; + public int getStart() { + return mModelParamRange.getStart(); } /** - * The inclusive end of supported range. + * Get the end of the param range * - * @return end of range + * @return The inclusive end of the supported range. */ - public int end() { - return mModelParamRange.end; + public int getEnd() { + return mModelParamRange.getEnd(); } @Override diff --git a/core/java/android/timezone/CountryTimeZones.java b/core/java/android/timezone/CountryTimeZones.java index ab2c4fc1bf23..ee3a8a79d5d7 100644 --- a/core/java/android/timezone/CountryTimeZones.java +++ b/core/java/android/timezone/CountryTimeZones.java @@ -18,7 +18,6 @@ package android.timezone; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.SuppressLint; import android.icu.util.TimeZone; import java.util.ArrayList; @@ -206,27 +205,47 @@ public final class CountryTimeZones { } /** - * Returns a time zone for the country, if there is one, that matches the desired properties. If - * there are multiple matches and the {@code bias} is one of them then it is returned, otherwise - * an arbitrary match is returned based on the {@link #getEffectiveTimeZoneMappingsAt(long)} - * ordering. + * Returns a time zone for the country, if there is one, that matches the supplied properties. + * If there are multiple matches and the {@code bias} is one of them then it is returned, + * otherwise an arbitrary match is returned based on the {@link + * #getEffectiveTimeZoneMappingsAt(long)} ordering. * + * @param whenMillis the UTC time to match against + * @param bias the time zone to prefer, can be {@code null} to indicate there is no preference * @param totalOffsetMillis the offset from UTC at {@code whenMillis} * @param isDst the Daylight Savings Time state at {@code whenMillis}. {@code true} means DST, - * {@code false} means not DST, {@code null} means unknown - * @param dstOffsetMillis the part of {@code totalOffsetMillis} contributed by DST, only used if - * {@code isDst} is {@code true}. The value can be {@code null} if the DST offset is - * unknown - * @param whenMillis the UTC time to match against - * @param bias the time zone to prefer, can be {@code null} + * {@code false} means not DST + * @return an {@link OffsetResult} with information about a matching zone, or {@code null} if + * there is no match */ @Nullable - public OffsetResult lookupByOffsetWithBias(int totalOffsetMillis, @Nullable Boolean isDst, - @SuppressLint("AutoBoxing") @Nullable Integer dstOffsetMillis, long whenMillis, - @Nullable TimeZone bias) { + public OffsetResult lookupByOffsetWithBias(long whenMillis, @Nullable TimeZone bias, + int totalOffsetMillis, boolean isDst) { libcore.timezone.CountryTimeZones.OffsetResult delegateOffsetResult = mDelegate.lookupByOffsetWithBias( - totalOffsetMillis, isDst, dstOffsetMillis, whenMillis, bias); + whenMillis, bias, totalOffsetMillis, isDst); + return delegateOffsetResult == null ? null : + new OffsetResult( + delegateOffsetResult.getTimeZone(), delegateOffsetResult.isOnlyMatch()); + } + + /** + * Returns a time zone for the country, if there is one, that matches the supplied properties. + * If there are multiple matches and the {@code bias} is one of them then it is returned, + * otherwise an arbitrary match is returned based on the {@link + * #getEffectiveTimeZoneMappingsAt(long)} ordering. + * + * @param whenMillis the UTC time to match against + * @param bias the time zone to prefer, can be {@code null} to indicate there is no preference + * @param totalOffsetMillis the offset from UTC at {@code whenMillis} + * @return an {@link OffsetResult} with information about a matching zone, or {@code null} if + * there is no match + */ + @Nullable + public OffsetResult lookupByOffsetWithBias(long whenMillis, @Nullable TimeZone bias, + int totalOffsetMillis) { + libcore.timezone.CountryTimeZones.OffsetResult delegateOffsetResult = + mDelegate.lookupByOffsetWithBias(whenMillis, bias, totalOffsetMillis); return delegateOffsetResult == null ? null : new OffsetResult( delegateOffsetResult.getTimeZone(), delegateOffsetResult.isOnlyMatch()); diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java index 36f4e5361ea6..4b3afbaada64 100644 --- a/core/java/android/util/TimeUtils.java +++ b/core/java/android/util/TimeUtils.java @@ -80,7 +80,7 @@ public class TimeUtils { return null; } CountryTimeZones.OffsetResult offsetResult = countryTimeZones.lookupByOffsetWithBias( - offsetMillis, isDst, null /* dstOffsetMillis */, whenMillis, bias); + whenMillis, bias, offsetMillis, isDst); return offsetResult != null ? offsetResult.getTimeZone() : null; } diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index 22d6f37dbd95..54de1bb3739d 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -190,9 +190,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation onAnimationFinish(); } }); - setStartingAnimation(true); mAnimator.start(); - setStartingAnimation(false); } @Override @@ -203,9 +201,6 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } } - protected void setStartingAnimation(boolean startingAnimation) { - } - protected void onAnimationFinish() { mController.finish(mShow); } @@ -239,16 +234,6 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation final @AnimationType int type; } - private class DefaultAnimationControlListener extends InternalAnimationControlListener { - DefaultAnimationControlListener(boolean show) { - super(show); - } - - @Override - protected void setStartingAnimation(boolean startingAnimation) { - mStartingAnimation = startingAnimation; - } - } /** * Represents a control request that we had to defer because we are waiting for the IME to * process our show request. @@ -822,7 +807,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation return; } - final DefaultAnimationControlListener listener = new DefaultAnimationControlListener(show); + final InternalAnimationControlListener listener = + new InternalAnimationControlListener(show); // Show/hide animations always need to be relative to the display frame, in order that shown // and hidden state insets are correct. controlAnimationUnchecked( @@ -878,7 +864,9 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation return true; } mViewRoot.mView.dispatchWindowInsetsAnimationStart(animation, bounds); + mStartingAnimation = true; listener.onReady(controller, types); + mStartingAnimation = false; return true; } }); diff --git a/core/java/android/view/SurfaceControlViewHost.aidl b/core/java/android/view/SurfaceControlViewHost.aidl new file mode 100644 index 000000000000..3b31ab834a51 --- /dev/null +++ b/core/java/android/view/SurfaceControlViewHost.aidl @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +parcelable SurfaceControlViewHost.SurfacePackage;
\ No newline at end of file diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 75d5538faedd..b1c354f6f717 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -1570,7 +1570,8 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall private void reparentSurfacePackage(SurfaceControl.Transaction t, SurfaceControlViewHost.SurfacePackage p) { // TODO: Link accessibility IDs here. - t.reparent(p.getSurfaceControl(), mSurfaceControl); + final SurfaceControl sc = p.getSurfaceControl(); + t.reparent(sc, mSurfaceControl).show(sc); } /** diff --git a/core/java/android/view/textclassifier/TextClassificationConstants.java b/core/java/android/view/textclassifier/TextClassificationConstants.java index ed69513b7f69..3d5ac58e7704 100644 --- a/core/java/android/view/textclassifier/TextClassificationConstants.java +++ b/core/java/android/view/textclassifier/TextClassificationConstants.java @@ -17,6 +17,7 @@ package android.view.textclassifier; import android.annotation.Nullable; +import android.content.Context; import android.provider.DeviceConfig; import com.android.internal.annotations.VisibleForTesting; @@ -167,6 +168,16 @@ public final class TextClassificationConstants { static final String TEXT_CLASSIFIER_SERVICE_PACKAGE_OVERRIDE = "textclassifier_service_package_override"; + /** + * Whether to use the default system text classifier as the default text classifier + * implementation. The local text classifier is used if it is {@code false}. + * + * @see android.service.textclassifier.TextClassifierService#getDefaultTextClassifierImplementation(Context) + */ + // TODO: Once the system health experiment is done, remove this together with local TC. + private static final String USE_DEFAULT_SYSTEM_TEXT_CLASSIFIER_AS_DEFAULT_IMPL = + "use_default_system_text_classifier_as_default_impl"; + private static final String DEFAULT_TEXT_CLASSIFIER_SERVICE_PACKAGE_OVERRIDE = null; private static final boolean LOCAL_TEXT_CLASSIFIER_ENABLED_DEFAULT = true; private static final boolean SYSTEM_TEXT_CLASSIFIER_ENABLED_DEFAULT = true; @@ -209,7 +220,8 @@ public final class TextClassificationConstants { private static final boolean TEMPLATE_INTENT_FACTORY_ENABLED_DEFAULT = true; private static final boolean TRANSLATE_IN_CLASSIFICATION_ENABLED_DEFAULT = true; private static final boolean DETECT_LANGUAGES_FROM_TEXT_ENABLED_DEFAULT = true; - private static final float[] LANG_ID_CONTEXT_SETTINGS_DEFAULT = new float[] {20f, 1.0f, 0.4f}; + private static final float[] LANG_ID_CONTEXT_SETTINGS_DEFAULT = new float[]{20f, 1.0f, 0.4f}; + private static final boolean USE_DEFAULT_SYSTEM_TEXT_CLASSIFIER_AS_DEFAULT_IMPL_DEFAULT = true; @Nullable public String getTextClassifierServicePackageOverride() { @@ -331,6 +343,13 @@ public final class TextClassificationConstants { LANG_ID_CONTEXT_SETTINGS, LANG_ID_CONTEXT_SETTINGS_DEFAULT); } + public boolean getUseDefaultTextClassifierAsDefaultImplementation() { + return DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_TEXTCLASSIFIER, + USE_DEFAULT_SYSTEM_TEXT_CLASSIFIER_AS_DEFAULT_IMPL, + USE_DEFAULT_SYSTEM_TEXT_CLASSIFIER_AS_DEFAULT_IMPL_DEFAULT); + } + void dump(IndentingPrintWriter pw) { pw.println("TextClassificationConstants:"); pw.increaseIndent(); @@ -378,6 +397,8 @@ public final class TextClassificationConstants { .println(); pw.printPair("textclassifier_service_package_override", getTextClassifierServicePackageOverride()).println(); + pw.printPair("use_default_system_text_classifier_as_default_impl", + getUseDefaultTextClassifierAsDefaultImplementation()).println(); pw.decreaseIndent(); } diff --git a/core/java/android/view/textclassifier/TextClassificationManager.java b/core/java/android/view/textclassifier/TextClassificationManager.java index a6c83a1cfa76..dfbec9b67027 100644 --- a/core/java/android/view/textclassifier/TextClassificationManager.java +++ b/core/java/android/view/textclassifier/TextClassificationManager.java @@ -25,7 +25,6 @@ import android.content.Context; import android.os.ServiceManager; import android.provider.DeviceConfig; import android.provider.DeviceConfig.Properties; -import android.util.SparseArray; import android.view.textclassifier.TextClassifier.TextClassifierType; import com.android.internal.annotations.GuardedBy; @@ -62,8 +61,6 @@ public final class TextClassificationManager { @Nullable private TextClassifier mLocalTextClassifier; @GuardedBy("mLock") - private SparseArray<TextClassifier> mSystemTextClassifiers = new SparseArray<>(); - @GuardedBy("mLock") private TextClassificationSessionFactory mSessionFactory; @GuardedBy("mLock") private TextClassificationConstants mSettings; @@ -207,23 +204,17 @@ public final class TextClassificationManager { /** @hide */ private TextClassifier getSystemTextClassifier(@TextClassifierType int type) { synchronized (mLock) { - if (mSystemTextClassifiers.get(type) == null - && getSettings().isSystemTextClassifierEnabled()) { + if (getSettings().isSystemTextClassifierEnabled()) { try { - mSystemTextClassifiers.put( - type, - new SystemTextClassifier( - mContext, - getSettings(), - /* useDefault= */ type == TextClassifier.DEFAULT_SERVICE)); - Log.d(LOG_TAG, "Initialized SystemTextClassifier, type = " + type); + Log.d(LOG_TAG, "Initializing SystemTextClassifier, type = " + type); + return new SystemTextClassifier( + mContext, + getSettings(), + /* useDefault= */ type == TextClassifier.DEFAULT_SERVICE); } catch (ServiceManager.ServiceNotFoundException e) { Log.e(LOG_TAG, "Could not initialize SystemTextClassifier", e); } } - if (mSystemTextClassifiers.get(type) != null) { - return mSystemTextClassifiers.get(type); - } return TextClassifier.NO_OP; } } @@ -263,7 +254,6 @@ public final class TextClassificationManager { private void invalidateTextClassifiers() { synchronized (mLock) { mLocalTextClassifier = null; - mSystemTextClassifiers.clear(); } } diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 7fd41508dfc3..4e4a9837b32c 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -454,15 +454,17 @@ public class Editor { aspectRatio = 5.5f; } - final Paint.FontMetrics fontMetrics = mTextView.getPaint().getFontMetrics(); - final float sourceHeight = fontMetrics.descent - fontMetrics.ascent; + final Layout layout = mTextView.getLayout(); + final int line = layout.getLineForOffset(mTextView.getSelectionStart()); + final int sourceHeight = + layout.getLineBottomWithoutSpacing(line) - layout.getLineTop(line); // Slightly increase the height to avoid tooLargeTextForMagnifier() returns true. int height = (int)(sourceHeight * zoom) + 2; int width = (int)(aspectRatio * height); params.setFishEyeStyle() .setSize(width, height) - .setSourceSize(width, Math.round(sourceHeight)) + .setSourceSize(width, sourceHeight) .setElevation(0) .setInitialZoom(zoom) .setClippingEnabled(false); @@ -5041,7 +5043,7 @@ public class Editor { // Vertically snap to middle of current line. showPosInView.y = ((mTextView.getLayout().getLineTop(lineNumber) - + mTextView.getLayout().getLineBottom(lineNumber)) / 2.0f + + mTextView.getLayout().getLineBottomWithoutSpacing(lineNumber)) / 2.0f + mTextView.getTotalPaddingTop() - mTextView.getScrollY()) * mTextViewScaleY; return true; } diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java index 78d4e61ea953..a2c70b9afbee 100644 --- a/core/java/android/widget/Toast.java +++ b/core/java/android/widget/Toast.java @@ -148,6 +148,9 @@ public class Toast { @Nullable private CharSequence mText; + // TODO(b/144152069): Remove this after assessing impact on dogfood. + private boolean mIsCustomToast; + /** * Construct an empty Toast object. You must call {@link #setView} before you * can call {@link #show}. @@ -214,7 +217,8 @@ public class Toast { service.enqueueTextToast(pkg, mToken, mText, mDuration, displayId, callback); } } else { - service.enqueueToast(pkg, mToken, tn, mDuration, displayId); + service.enqueueTextOrCustomToast(pkg, mToken, tn, mDuration, displayId, + mIsCustomToast); } } catch (RemoteException e) { // Empty @@ -252,12 +256,17 @@ public class Toast { */ @Deprecated public void setView(View view) { + mIsCustomToast = true; mNextView = view; } /** * Return the view. * + * <p><strong>Warning:</strong> Starting from Android {@link Build.VERSION_CODES#R}, for apps + * targeting API level {@link Build.VERSION_CODES#R} or higher that haven't called {@link + * #setView(View)} with a non-{@code null} view, this method will return {@code null}. + * * @see #setView * @deprecated Custom toast views are deprecated. Apps can create a standard text toast with the * {@link #makeText(Context, CharSequence, int)} method, or use a @@ -266,6 +275,7 @@ public class Toast { * targeting API level {@link Build.VERSION_CODES#R} or higher that are in the background * will not have custom toast views displayed. */ + @Deprecated public View getView() { return mNextView; } @@ -292,6 +302,10 @@ public class Toast { /** * Set the margins of the view. * + * <p><strong>Warning:</strong> Starting from Android {@link Build.VERSION_CODES#R}, for apps + * targeting API level {@link Build.VERSION_CODES#R} or higher, this method is a no-op when + * called on text toasts. + * * @param horizontalMargin The horizontal margin, in percentage of the * container width, between the container's edges and the * notification @@ -300,30 +314,59 @@ public class Toast { * notification */ public void setMargin(float horizontalMargin, float verticalMargin) { + if (isSystemRenderedTextToast()) { + Log.e(TAG, "setMargin() shouldn't be called on text toasts, the values won't be used"); + } mTN.mHorizontalMargin = horizontalMargin; mTN.mVerticalMargin = verticalMargin; } /** * Return the horizontal margin. + * + * <p><strong>Warning:</strong> Starting from Android {@link Build.VERSION_CODES#R}, for apps + * targeting API level {@link Build.VERSION_CODES#R} or higher, this method shouldn't be called + * on text toasts as its return value may not reflect actual value since text toasts are not + * rendered by the app anymore. */ public float getHorizontalMargin() { + if (isSystemRenderedTextToast()) { + Log.e(TAG, "getHorizontalMargin() shouldn't be called on text toasts, the result may " + + "not reflect actual values."); + } return mTN.mHorizontalMargin; } /** * Return the vertical margin. + * + * <p><strong>Warning:</strong> Starting from Android {@link Build.VERSION_CODES#R}, for apps + * targeting API level {@link Build.VERSION_CODES#R} or higher, this method shouldn't be called + * on text toasts as its return value may not reflect actual value since text toasts are not + * rendered by the app anymore. */ public float getVerticalMargin() { + if (isSystemRenderedTextToast()) { + Log.e(TAG, "getVerticalMargin() shouldn't be called on text toasts, the result may not" + + " reflect actual values."); + } return mTN.mVerticalMargin; } /** * Set the location at which the notification should appear on the screen. + * + * <p><strong>Warning:</strong> Starting from Android {@link Build.VERSION_CODES#R}, for apps + * targeting API level {@link Build.VERSION_CODES#R} or higher, this method is a no-op when + * called on text toasts. + * * @see android.view.Gravity * @see #getGravity */ public void setGravity(int gravity, int xOffset, int yOffset) { + if (isSystemRenderedTextToast()) { + Log.e(TAG, "setGravity() shouldn't be called on text toasts, the values won't be used"); + } mTN.mGravity = gravity; mTN.mX = xOffset; mTN.mY = yOffset; @@ -331,27 +374,59 @@ public class Toast { /** * Get the location at which the notification should appear on the screen. + * + * <p><strong>Warning:</strong> Starting from Android {@link Build.VERSION_CODES#R}, for apps + * targeting API level {@link Build.VERSION_CODES#R} or higher, this method shouldn't be called + * on text toasts as its return value may not reflect actual value since text toasts are not + * rendered by the app anymore. + * * @see android.view.Gravity * @see #getGravity */ public int getGravity() { + if (isSystemRenderedTextToast()) { + Log.e(TAG, "getGravity() shouldn't be called on text toasts, the result may not reflect" + + " actual values."); + } return mTN.mGravity; } /** * Return the X offset in pixels to apply to the gravity's location. + * + * <p><strong>Warning:</strong> Starting from Android {@link Build.VERSION_CODES#R}, for apps + * targeting API level {@link Build.VERSION_CODES#R} or higher, this method shouldn't be called + * on text toasts as its return value may not reflect actual value since text toasts are not + * rendered by the app anymore. */ public int getXOffset() { + if (isSystemRenderedTextToast()) { + Log.e(TAG, "getXOffset() shouldn't be called on text toasts, the result may not reflect" + + " actual values."); + } return mTN.mX; } /** * Return the Y offset in pixels to apply to the gravity's location. + * + * <p><strong>Warning:</strong> Starting from Android {@link Build.VERSION_CODES#R}, for apps + * targeting API level {@link Build.VERSION_CODES#R} or higher, this method shouldn't be called + * on text toasts as its return value may not reflect actual value since text toasts are not + * rendered by the app anymore. */ public int getYOffset() { + if (isSystemRenderedTextToast()) { + Log.e(TAG, "getYOffset() shouldn't be called on text toasts, the result may not reflect" + + " actual values."); + } return mTN.mY; } + private boolean isSystemRenderedTextToast() { + return Compatibility.isChangeEnabled(CHANGE_TEXT_TOASTS_IN_THE_SYSTEM) && mNextView == null; + } + /** * Adds a callback to be notified when the toast is shown or hidden. * @@ -385,7 +460,7 @@ public class Toast { } /** - * Make a standard toast that just contains a text view. + * Make a standard toast that just contains text. * * @param context The context to use. Usually your {@link android.app.Application} * or {@link android.app.Activity} object. @@ -428,7 +503,7 @@ public class Toast { } /** - * Make a standard toast that just contains a text view with the text from a resource. + * Make a standard toast that just contains text from a resource. * * @param context The context to use. Usually your {@link android.app.Application} * or {@link android.app.Activity} object. diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index a43e4fe118a9..65cad834d5be 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -2424,6 +2424,10 @@ public class ChooserActivity extends ResolverActivity implements offset += findViewById(R.id.content_preview_container).getHeight(); } + if (hasWorkProfile() && ENABLE_TABBED_VIEW) { + offset += findViewById(R.id.tabs).getHeight(); + } + int directShareHeight = 0; rowsToShow = Math.min(4, rowsToShow); for (int i = 0, childCount = recyclerView.getChildCount(); diff --git a/core/java/com/android/internal/net/VpnConfig.java b/core/java/com/android/internal/net/VpnConfig.java index f5a19fe73a86..6d2d7356a2e8 100644 --- a/core/java/com/android/internal/net/VpnConfig.java +++ b/core/java/com/android/internal/net/VpnConfig.java @@ -52,6 +52,7 @@ public class VpnConfig implements Parcelable { public static final String DIALOGS_PACKAGE = "com.android.vpndialogs"; + // TODO: Rename this to something that encompasses Settings-based Platform VPNs as well. public static final String LEGACY_VPN = "[Legacy VPN]"; public static Intent getIntentForConfirmation() { diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java index c6ed624d73ac..518911e652f6 100644 --- a/core/java/com/android/internal/os/RuntimeInit.java +++ b/core/java/com/android/internal/os/RuntimeInit.java @@ -323,7 +323,7 @@ public class RuntimeInit { result.append(System.getProperty("java.vm.version")); // such as 1.1.0 result.append(" (Linux; U; Android "); - String version = Build.VERSION.RELEASE; // "1.0" or "3.4b5" + String version = Build.VERSION.RELEASE_OR_CODENAME; // "1.0" or "3.4b5" result.append(version.length() > 0 ? version : "1.0"); // add the model for the release build diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 9758673d678b..35eb0fc986d7 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -208,6 +208,7 @@ cc_library_shared { static_libs: [ "libasync_safe", + "libbinderthreadstateutils", "libdmabufinfo", "libgif", "libseccomp_policy", diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp index aa209cb3899e..38fb8bdc1f7a 100644 --- a/core/jni/android/graphics/Graphics.cpp +++ b/core/jni/android/graphics/Graphics.cpp @@ -569,8 +569,8 @@ bool RecyclingClippingPixelAllocator::allocPixelRef(SkBitmap* bitmap) { // mRecycledBitmap specifies the width and height of the bitmap that we // want to reuse. Neither can be changed. We will try to find a way // to reuse the memory. - const int maxWidth = SkTMax(bitmap->width(), mRecycledBitmap->info().width()); - const int maxHeight = SkTMax(bitmap->height(), mRecycledBitmap->info().height()); + const int maxWidth = std::max(bitmap->width(), mRecycledBitmap->info().width()); + const int maxHeight = std::max(bitmap->height(), mRecycledBitmap->info().height()); const SkImageInfo maxInfo = bitmap->info().makeWH(maxWidth, maxHeight); const size_t rowBytes = maxInfo.minRowBytes(); const size_t bytesNeeded = maxInfo.computeByteSize(rowBytes); diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp index c269d1cf4295..14d74878b2ea 100644 --- a/core/jni/android_util_Binder.cpp +++ b/core/jni/android_util_Binder.cpp @@ -37,6 +37,7 @@ #include <binder/Parcel.h> #include <binder/ProcessState.h> #include <binder/Stability.h> +#include <binderthreadstate/CallerUtils.h> #include <cutils/atomic.h> #include <log/log.h> #include <utils/KeyedVector.h> @@ -950,7 +951,7 @@ static jint android_os_Binder_getCallingUid() static jboolean android_os_Binder_isHandlingTransaction() { - return IPCThreadState::self()->isServingCall(); + return getCurrentServingCall() == BinderCallType::BINDER; } static jlong android_os_Binder_clearCallingIdentity() diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp index 69ca17c08257..5a8225c1f21a 100644 --- a/core/jni/android_view_ThreadedRenderer.cpp +++ b/core/jni/android_view_ThreadedRenderer.cpp @@ -147,10 +147,12 @@ static jlong android_view_ThreadedRenderer_createRootRenderNode(JNIEnv* env, job } static jlong android_view_ThreadedRenderer_createProxy(JNIEnv* env, jobject clazz, - jboolean translucent, jlong rootRenderNodePtr) { + jboolean translucent, jboolean isWideGamut, jlong rootRenderNodePtr) { RootRenderNode* rootRenderNode = reinterpret_cast<RootRenderNode*>(rootRenderNodePtr); ContextFactoryImpl factory(rootRenderNode); - return (jlong) new RenderProxy(translucent, rootRenderNode, &factory); + RenderProxy* proxy = new RenderProxy(translucent, rootRenderNode, &factory); + proxy->setWideGamut(isWideGamut); + return (jlong) proxy; } static void android_view_ThreadedRenderer_deleteProxy(JNIEnv* env, jobject clazz, @@ -627,7 +629,7 @@ static const JNINativeMethod gMethods[] = { { "nSetProcessStatsBuffer", "(I)V", (void*) android_view_ThreadedRenderer_setProcessStatsBuffer }, { "nGetRenderThreadTid", "(J)I", (void*) android_view_ThreadedRenderer_getRenderThreadTid }, { "nCreateRootRenderNode", "()J", (void*) android_view_ThreadedRenderer_createRootRenderNode }, - { "nCreateProxy", "(ZJ)J", (void*) android_view_ThreadedRenderer_createProxy }, + { "nCreateProxy", "(ZZJ)J", (void*) android_view_ThreadedRenderer_createProxy }, { "nDeleteProxy", "(J)V", (void*) android_view_ThreadedRenderer_deleteProxy }, { "nLoadSystemProperties", "(J)Z", (void*) android_view_ThreadedRenderer_loadSystemProperties }, { "nSetName", "(JLjava/lang/String;)V", (void*) android_view_ThreadedRenderer_setName }, diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto index a6e08d2cecc1..fc0a2ef6041a 100644 --- a/core/proto/android/app/settings_enums.proto +++ b/core/proto/android/app/settings_enums.proto @@ -2562,7 +2562,7 @@ enum PageId { // CATEGORY: SETTINGS // OS: R OPEN_SUPPORTED_LINKS = 1824; - + // OPEN: Settings > Display > Dark theme > Set start time dialog DIALOG_DARK_THEME_SET_START_TIME = 1825; @@ -2573,4 +2573,9 @@ enum PageId { // CATEGORY: SETTINGS // OS: R VIBRATE_FOR_CALLS = 1827; + + // OPEN: Settings > Connected devices > Connection preferences > NFC + // CATEGORY: SETTINGS + // OS: R + CONNECTION_DEVICE_ADVANCED_NFC = 1828; } diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto index 8adcc9ed905d..bf4cdee72b2d 100644 --- a/core/proto/android/os/incident.proto +++ b/core/proto/android/os/incident.proto @@ -52,6 +52,7 @@ import "frameworks/base/core/proto/android/service/package.proto"; import "frameworks/base/core/proto/android/service/print.proto"; import "frameworks/base/core/proto/android/service/procstats.proto"; import "frameworks/base/core/proto/android/service/restricted_image.proto"; +import "frameworks/base/core/proto/android/service/sensor_service.proto"; import "frameworks/base/core/proto/android/service/usb.proto"; import "frameworks/base/core/proto/android/util/event_log_tags.proto"; import "frameworks/base/core/proto/android/util/log.proto"; @@ -492,6 +493,11 @@ message IncidentProto { (section).args = "contexthub --proto" ]; + optional android.service.SensorServiceProto sensor_service = 3053 [ + (section).type = SECTION_DUMPSYS, + (section).args = "sensorservice --proto" + ]; + // Reserved for OEMs. extensions 50000 to 100000; } diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto index 0a2fd7093a49..2d2ead455a4d 100644 --- a/core/proto/android/server/activitymanagerservice.proto +++ b/core/proto/android/server/activitymanagerservice.proto @@ -27,7 +27,6 @@ import "frameworks/base/core/proto/android/content/component_name.proto"; import "frameworks/base/core/proto/android/content/configuration.proto"; import "frameworks/base/core/proto/android/content/intent.proto"; import "frameworks/base/core/proto/android/content/package_item_info.proto"; -import "frameworks/base/core/proto/android/graphics/rect.proto"; import "frameworks/base/core/proto/android/internal/processstats.proto"; import "frameworks/base/core/proto/android/os/bundle.proto"; import "frameworks/base/core/proto/android/os/looper.proto"; diff --git a/core/proto/android/server/notificationhistory.proto b/core/proto/android/server/notificationhistory.proto index 1e6ee3f1a3a8..6749719be124 100644 --- a/core/proto/android/server/notificationhistory.proto +++ b/core/proto/android/server/notificationhistory.proto @@ -17,8 +17,6 @@ syntax = "proto2"; package com.android.server.notification; -import "frameworks/base/core/proto/android/server/enums.proto"; - option java_multiple_files = true; // On disk data store for historical notifications diff --git a/core/proto/android/service/sensor_service.proto b/core/proto/android/service/sensor_service.proto new file mode 100644 index 000000000000..8598f86a7f28 --- /dev/null +++ b/core/proto/android/service/sensor_service.proto @@ -0,0 +1,246 @@ +/* + * Copyright (C) 2020 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. + */ + +syntax = "proto2"; +package android.service; + +import "frameworks/base/core/proto/android/privacy.proto"; + +option java_multiple_files = true; + +/* + * Notes: + * 1. When using ProtoOutputStream to write this proto message, must call + * token = ProtoOutputStream#start(fieldId) before and ProtoOutputStream#end(token) after + * writing a nested message. + * 2. Never reuse a proto field number. When removing a field, mark it as reserved. + */ + +// Proto dump of android::SensorService. dumpsys sensorservice --proto +message SensorServiceProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + enum OperatingModeEnum { + OP_MODE_UNKNOWN = 0; + OP_MODE_NORMAL = 1; + OP_MODE_RESTRICTED = 2; + OP_MODE_DATA_INJECTION = 3; + } + + optional int64 current_time_ms = 1; + optional SensorDeviceProto sensor_device = 2; + optional SensorListProto sensors = 3; + optional SensorFusionProto fusion_state = 4; + optional SensorEventsProto sensor_events = 5; + repeated ActiveSensorProto active_sensors = 6; + optional int32 socket_buffer_size = 7; + optional int32 socket_buffer_size_in_events = 8; + optional bool wake_lock_acquired = 9; + optional OperatingModeEnum operating_mode = 10; + // Non-empty only if operating_mode is RESTRICTED or DATA_INJECTION. + optional string whitelisted_package = 11; + optional bool sensor_privacy = 12; + repeated SensorEventConnectionProto active_connections = 13; + repeated SensorDirectConnectionProto direct_connections = 14; + repeated SensorRegistrationInfoProto previous_registrations = 15; +} + +// Proto dump of android::SensorDevice +message SensorDeviceProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + optional bool initialized = 1; + optional int32 total_sensors = 2; + optional int32 active_sensors = 3; + + message SensorProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + optional int32 handle = 1; + optional int32 active_count = 2; + repeated float sampling_period_ms = 3; + optional float sampling_period_selected = 4; + repeated float batching_period_ms = 5; + optional float batching_period_selected = 6; + } + repeated SensorProto sensors = 4; +} + +// Proto dump of android::SensorServiceUtil::SensorList +message SensorListProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + enum ReportingModeEnum { + RM_UNKNOWN = 0; + RM_CONTINUOUS = 1; + RM_ON_CHANGE = 2; + RM_ONE_SHOT = 3; + RM_SPECIAL_TRIGGER = 4; + } + + message SensorProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + optional int32 handle = 1; + optional string name = 2; + optional string vendor = 3; + optional int32 version = 4; + optional string string_type = 5; + optional int32 type = 6; + optional string required_permission = 7; + optional int32 flags = 8; + optional ReportingModeEnum reporting_mode = 9; + optional int32 max_delay_us = 10; + optional int32 min_delay_us = 11; + optional int32 fifo_max_event_count = 12; + optional int32 fifo_reserved_event_count = 13; + optional bool is_wakeup = 14; + optional bool data_injection_supported = 15; + optional bool is_dynamic = 16; + optional bool has_additional_info = 17; + optional int32 highest_rate_level = 18; + optional bool ashmem = 19; + optional bool gralloc = 20; + optional float min_value = 21; + optional float max_value = 22; + optional float resolution = 23; + optional float power_usage = 24; + } + repeated SensorProto sensors = 1; +} + + +// Proto dump of android::SensorFusion +message SensorFusionProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + message FusionProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + optional bool enabled = 1; + optional int32 num_clients = 2; + optional float estimated_gyro_rate = 3; + optional float attitude_x = 4; + optional float attitude_y = 5; + optional float attitude_z = 6; + optional float attitude_w = 7; + optional float attitude_length = 8; + optional float bias_x = 9; + optional float bias_y = 10; + optional float bias_z = 11; + } + optional FusionProto fusion_9axis = 1; + optional FusionProto fusion_nomag = 2; + optional FusionProto fusion_nogyro = 3; +} + +// Proto dump of android::SensorServiceUtil::RecentEventLogger +message SensorEventsProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + message Event { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + optional float timestamp_sec = 1; + optional int64 wall_timestamp_ms = 2; + optional bool masked = 3; + optional int64 int64_data = 4; + repeated float float_array = 5; + } + + message RecentEventsLog { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + optional string name = 1; + optional int32 recent_events_count = 2; + repeated Event events = 3; + } + repeated RecentEventsLog recent_events_logs = 1; +} + +message ActiveSensorProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + optional string name = 1; + optional int32 handle = 2; + optional int32 num_connections = 3; +} + +// Proto dump of android::SensorService::SensorDirectConnection +message SensorDirectConnectionProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + message SensorProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + optional int32 sensor = 1; + optional int32 rate = 2; + } + + optional string package_name = 1; + optional int32 hal_channel_handle = 2; + optional int32 num_sensor_activated = 3; + repeated SensorProto sensors = 4; +} + +// Proto dump of android::SensorService::SensorEventConnection +message SensorEventConnectionProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + message FlushInfoProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + optional string sensor_name = 1; + optional int32 sensor_handle = 2; + optional bool first_flush_pending = 3; + optional int32 pending_flush_events_to_send = 4; + } + + enum OperatingModeEnum { + OP_MODE_UNKNOWN = 0; + OP_MODE_NORMAL = 1; + OP_MODE_RESTRICTED = 2; + OP_MODE_DATA_INJECTION = 3; + } + + optional OperatingModeEnum operating_mode = 1; + optional string package_name = 2; + optional int32 wake_lock_ref_count = 3; + optional int32 uid = 4; + optional int32 cache_size = 5; + optional int32 max_cache_size = 6; + repeated FlushInfoProto flush_infos = 7; + optional int32 events_received = 8; + optional int32 events_sent = 9; + optional int32 events_cache = 10; + optional int32 events_dropped = 11; + optional int32 total_acks_needed = 12; + optional int32 total_acks_received = 13; +} + +// Proto dump of android::SensorService::SensorRegistrationInfo +message SensorRegistrationInfoProto { + option (android.msg_privacy).dest = DEST_AUTOMATIC; + + optional int64 timestamp_sec = 1; + optional int32 sensor_handle = 2; + optional string package_name = 3; + optional int32 pid = 4; + optional int32 uid = 5; + optional int64 sampling_rate_us = 6; + optional int64 max_report_latency_us = 7; + optional bool activated = 8; +} diff --git a/core/proto/android/stats/devicepolicy/device_policy_enums.proto b/core/proto/android/stats/devicepolicy/device_policy_enums.proto index 0f03e69e6c93..d1392a5e0f31 100644 --- a/core/proto/android/stats/devicepolicy/device_policy_enums.proto +++ b/core/proto/android/stats/devicepolicy/device_policy_enums.proto @@ -157,4 +157,6 @@ enum EventId { SET_FACTORY_RESET_PROTECTION = 130; SET_COMMON_CRITERIA_MODE = 131; ALLOW_MODIFICATION_OF_ADMIN_CONFIGURED_NETWORKS = 132; + SET_TIME = 133; + SET_TIME_ZONE = 134; } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 7a302566a872..de0d172ecf04 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2605,7 +2605,7 @@ <p>Not for use by third-party applications. @hide --> - <permission android:name="android.permission.SUGGEST_PHONE_TIME_AND_ZONE" + <permission android:name="android.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE" android:protectionLevel="signature|telephony" /> <!-- Allows applications like settings to suggest the user's manually chosen time / time zone. @@ -3448,6 +3448,14 @@ <permission android:name="android.permission.TUNER_RESOURCE_ACCESS" android:protectionLevel="signature|privileged" /> + <!-- This permission is required by Media Resource Manager Service when + accessing its overridePid Api. + <p>Protection level: signature + <p>Not for use by third-party applications. + @hide --> + <permission android:name="android.permission.MEDIA_RESOURCE_OVERRIDE_PID" + android:protectionLevel="signature" /> + <!-- Must be required by a {@link android.media.routing.MediaRouteService} to ensure that only the system can interact with it. @hide --> @@ -4057,6 +4065,11 @@ <permission android:name="android.permission.STATSCOMPANION" android:protectionLevel="signature" /> + <!--@SystemApi @hide Allows an application to register stats pull atom callbacks. + <p>Not for use by third-party applications.--> + <permission android:name="android.permission.REGISTER_STATS_PULL_ATOM" + android:protectionLevel="signature|privileged" /> + <!-- @SystemApi Allows an application to control the backup and restore process. <p>Not for use by third-party applications. @hide pending API council --> @@ -4865,6 +4878,19 @@ <permission android:name="android.permission.ACCESS_SHARED_LIBRARIES" android:protectionLevel="signature|installer" /> + <!-- Allows an app to log compat change usage. + @hide <p>Not for use by third-party applications.</p> --> + <permission android:name="android.permission.LOG_COMPAT_CHANGE" + android:protectionLevel="signature|privileged" /> + <!-- Allows an app to read compat change config. + @hide <p>Not for use by third-party applications.</p> --> + <permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG" + android:protectionLevel="signature|privileged" /> + <!-- Allows an app to override compat change config. + @hide <p>Not for use by third-party applications.</p> --> + <permission android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG" + android:protectionLevel="signature|privileged" /> + <!-- Allows input events to be monitored. Very dangerous! @hide --> <permission android:name="android.permission.MONITOR_INPUT" android:protectionLevel="signature" /> @@ -5302,6 +5328,10 @@ android:permission="android.permission.BIND_JOB_SERVICE" > </service> + <service android:name="com.android.server.blob.BlobStoreIdleJobService" + android:permission="android.permission.BIND_JOB_SERVICE"> + </service> + <service android:name="com.android.server.pm.PackageManagerShellCommandDataLoader"> <intent-filter> <action android:name="android.intent.action.LOAD_DATA" /> diff --git a/core/res/res/layout/accessibility_button_chooser_item.xml b/core/res/res/layout/accessibility_button_chooser_item.xml index d19e313055ae..d6fd7aa30e6e 100644 --- a/core/res/res/layout/accessibility_button_chooser_item.xml +++ b/core/res/res/layout/accessibility_button_chooser_item.xml @@ -20,6 +20,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" + android:clickable="true" android:gravity="center" android:paddingStart="16dp" android:paddingEnd="16dp" diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index b2d08a9730fc..31e68e88c0a8 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -4343,4 +4343,7 @@ Determines whether the specified key groups can be used to wake up the device. --> <bool name="config_wakeOnDpadKeyPress">true</bool> <bool name="config_wakeOnAssistKeyPress">true</bool> + + <!-- Whether to default to an expanded list of users on the lock screen user switcher. --> + <bool name="config_expandLockScreenUserSwitcher">false</bool> </resources> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index c59d25f10b0b..2453bb18577f 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3863,4 +3863,7 @@ <!-- Toast message for background started foreground service while-in-use permission restriction feature --> <java-symbol type="string" name="allow_while_in_use_permission_in_fgs" /> + + <!-- Whether to expand the lock screen user switcher by default --> + <java-symbol type="bool" name="config_expandLockScreenUserSwitcher" /> </resources> diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml index 0d7ac089ac4a..70917e76f8b4 100644 --- a/core/res/res/xml/sms_short_codes.xml +++ b/core/res/res/xml/sms_short_codes.xml @@ -34,7 +34,7 @@ http://smscoin.net/software/engine/WordPress/Paid+SMS-registration/ --> <!-- Arab Emirates --> - <shortcode country="ae" pattern="\\d{1,5}" free="3214|1017" /> + <shortcode country="ae" pattern="\\d{1,5}" free="1017|1355|3214" /> <!-- Albania: 5 digits, known short codes listed --> <shortcode country="al" pattern="\\d{5}" premium="15191|55[56]00" /> @@ -184,7 +184,7 @@ <shortcode country="mk" pattern="\\d{1,6}" free="129005|122" /> <!-- Mexico: 4-5 digits (not confirmed), known premium codes listed --> - <shortcode country="mx" pattern="\\d{4,5}" premium="53035|7766" free="46645|5050|26259|50025|50052|9963|76551" /> + <shortcode country="mx" pattern="\\d{4,5}" premium="53035|7766" free="26259|46645|50025|50052|5050|76551|88778|9963" /> <!-- Malaysia: 5 digits: http://www.skmm.gov.my/attachment/Consumer_Regulation/Mobile_Content_Services_FAQs.pdf --> <shortcode country="my" pattern="\\d{5}" premium="32298|33776" free="22099|28288" /> @@ -199,7 +199,7 @@ <shortcode country="no" pattern="\\d{4,5}" premium="2201|222[67]" free="2171" /> <!-- New Zealand: 3-4 digits, known premium codes listed --> - <shortcode country="nz" pattern="\\d{3,4}" premium="3903|8995|4679" free="1737|2141|3067|3068|3110|4006|4053|4061|4062|4202|4300|4334|4412|4575|5626|8006|8681" /> + <shortcode country="nz" pattern="\\d{3,4}" premium="3903|8995|4679" free="1737|176|2141|3067|3068|3110|4006|4053|4061|4062|4202|4300|4334|4412|4575|5626|8006|8681" /> <!-- Peru: 4-5 digits (not confirmed), known premium codes listed --> <shortcode country="pe" pattern="\\d{4,5}" free="9963" /> @@ -267,4 +267,7 @@ <!-- Mayotte (French Territory): 1-5 digits (not confirmed) --> <shortcode country="yt" pattern="\\d{1,5}" free="38600,36300,36303,959" /> + <!-- South Africa --> + <shortcode country="za" pattern="\d{1,5}" free="44136" /> + </shortcodes> diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp index 38454ecb6111..9f15fafe2bf1 100644 --- a/core/tests/coretests/Android.bp +++ b/core/tests/coretests/Android.bp @@ -38,6 +38,7 @@ android_test { "mockito-target-minus-junit4", "ub-uiautomator", "platform-test-annotations", + "platform-compat-test-rules", "truth-prebuilt", "print-test-util-lib", "testng", diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml index ee93b397bedf..59335a595334 100644 --- a/core/tests/coretests/AndroidManifest.xml +++ b/core/tests/coretests/AndroidManifest.xml @@ -111,6 +111,10 @@ <uses-permission android:name="android.permission.MOVE_PACKAGE" /> <uses-permission android:name="android.permission.PACKAGE_VERIFICATION_AGENT" /> + <!-- gating and logging permissions --> + <uses-permission android:name="android.permission.LOG_COMPAT_CHANGE" /> + <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG" /> + <!-- os storage test permissions --> <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" /> <uses-permission android:name="android.permission.ASEC_ACCESS" /> diff --git a/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java b/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java index 6c5d5485664e..02be557c01bd 100644 --- a/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java +++ b/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java @@ -24,6 +24,9 @@ import android.app.ActivityManager.TaskDescription; import android.content.Context; import android.content.pm.ConfigurationInfo; import android.content.res.Configuration; +import android.graphics.Bitmap; +import android.os.Parcel; +import android.os.Parcelable; import android.test.AndroidTestCase; import androidx.test.filters.SmallTest; @@ -137,21 +140,7 @@ public class ActivityManagerTest extends AndroidTestCase { // Must overwrite all the fields td2.copyFrom(td1); - assertEquals(td1.getLabel(), td2.getLabel()); - assertEquals(td1.getInMemoryIcon(), td2.getInMemoryIcon()); - assertEquals(td1.getIconFilename(), td2.getIconFilename()); - assertEquals(td1.getIconResource(), td2.getIconResource()); - assertEquals(td1.getPrimaryColor(), td2.getPrimaryColor()); - assertEquals(td1.getBackgroundColor(), td2.getBackgroundColor()); - assertEquals(td1.getStatusBarColor(), td2.getStatusBarColor()); - assertEquals(td1.getNavigationBarColor(), td2.getNavigationBarColor()); - assertEquals(td1.getEnsureStatusBarContrastWhenTransparent(), - td2.getEnsureStatusBarContrastWhenTransparent()); - assertEquals(td1.getEnsureNavigationBarContrastWhenTransparent(), - td2.getEnsureNavigationBarContrastWhenTransparent()); - assertEquals(td1.getResizeMode(), td2.getResizeMode()); - assertEquals(td1.getMinWidth(), td2.getMinWidth()); - assertEquals(td1.getMinHeight(), td2.getMinHeight()); + assertTaskDescriptionEqual(td1, td2, true, true); } @SmallTest @@ -191,44 +180,101 @@ public class ActivityManagerTest extends AndroidTestCase { // Must overwrite all public and hidden fields, since other has all fields set. td2.copyFromPreserveHiddenFields(td1); - assertEquals(td1.getLabel(), td2.getLabel()); - assertEquals(td1.getInMemoryIcon(), td2.getInMemoryIcon()); - assertEquals(td1.getIconFilename(), td2.getIconFilename()); - assertEquals(td1.getIconResource(), td2.getIconResource()); - assertEquals(td1.getPrimaryColor(), td2.getPrimaryColor()); - assertEquals(td1.getBackgroundColor(), td2.getBackgroundColor()); - assertEquals(td1.getStatusBarColor(), td2.getStatusBarColor()); - assertEquals(td1.getNavigationBarColor(), td2.getNavigationBarColor()); - assertEquals(td1.getEnsureStatusBarContrastWhenTransparent(), - td2.getEnsureStatusBarContrastWhenTransparent()); - assertEquals(td1.getEnsureNavigationBarContrastWhenTransparent(), - td2.getEnsureNavigationBarContrastWhenTransparent()); - assertEquals(td1.getResizeMode(), td2.getResizeMode()); - assertEquals(td1.getMinWidth(), td2.getMinWidth()); - assertEquals(td1.getMinHeight(), td2.getMinHeight()); + assertTaskDescriptionEqual(td1, td2, true, true); TaskDescription td3 = new TaskDescription(); // Must overwrite only public fields, and preserve hidden fields. td2.copyFromPreserveHiddenFields(td3); - // Overwritten fields - assertEquals(td3.getLabel(), td2.getLabel()); - assertEquals(td3.getInMemoryIcon(), td2.getInMemoryIcon()); - assertEquals(td3.getIconFilename(), td2.getIconFilename()); - assertEquals(td3.getIconResource(), td2.getIconResource()); - assertEquals(td3.getPrimaryColor(), td2.getPrimaryColor()); - assertEquals(td3.getEnsureStatusBarContrastWhenTransparent(), - td2.getEnsureStatusBarContrastWhenTransparent()); - assertEquals(td3.getEnsureNavigationBarContrastWhenTransparent(), - td2.getEnsureNavigationBarContrastWhenTransparent()); - - // Preserved fields - assertEquals(td1.getBackgroundColor(), td2.getBackgroundColor()); - assertEquals(td1.getStatusBarColor(), td2.getStatusBarColor()); - assertEquals(td1.getNavigationBarColor(), td2.getNavigationBarColor()); - assertEquals(td1.getResizeMode(), td2.getResizeMode()); - assertEquals(td1.getMinWidth(), td2.getMinWidth()); - assertEquals(td1.getMinHeight(), td2.getMinHeight()); + assertTaskDescriptionEqual(td3, td2, true, false); + assertTaskDescriptionEqual(td1, td2, false, true); + } + + @SmallTest + public void testTaskDescriptionParceling() throws Exception { + TaskDescription tdBitmapNull = new TaskDescription( + "test label", // label + null, // bitmap + 21, // iconRes + "dummy file", // iconFilename + 0x111111, // colorPrimary + 0x222222, // colorBackground + 0x333333, // statusBarColor + 0x444444, // navigationBarColor + false, // ensureStatusBarContrastWhenTransparent + false, // ensureNavigationBarContrastWhenTransparent + RESIZE_MODE_UNRESIZEABLE, // resizeMode + 10, // minWidth + 20 // minHeight + ); + + // Normal parceling should keep everything the same. + TaskDescription tdParcelled = new TaskDescription(parcelingRoundTrip(tdBitmapNull)); + assertTaskDescriptionEqual(tdBitmapNull, tdParcelled, true, true); + + Bitmap recycledBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888); + recycledBitmap.recycle(); + assertTrue(recycledBitmap.isRecycled()); + TaskDescription tdBitmapRecycled = new TaskDescription( + "test label", // label + recycledBitmap, // bitmap + 21, // iconRes + "dummy file", // iconFilename + 0x111111, // colorPrimary + 0x222222, // colorBackground + 0x333333, // statusBarColor + 0x444444, // navigationBarColor + false, // ensureStatusBarContrastWhenTransparent + false, // ensureNavigationBarContrastWhenTransparent + RESIZE_MODE_UNRESIZEABLE, // resizeMode + 10, // minWidth + 20 // minHeight + ); + // Recycled bitmap will be ignored while parceling. + tdParcelled = new TaskDescription(parcelingRoundTrip(tdBitmapRecycled)); + assertTaskDescriptionEqual(tdBitmapNull, tdParcelled, true, true); + + } + + private void assertTaskDescriptionEqual(TaskDescription td1, TaskDescription td2, + boolean checkOverwrittenFields, boolean checkPreservedFields) { + if (checkOverwrittenFields) { + assertEquals(td1.getLabel(), td2.getLabel()); + assertEquals(td1.getInMemoryIcon(), td2.getInMemoryIcon()); + assertEquals(td1.getIconFilename(), td2.getIconFilename()); + assertEquals(td1.getIconResource(), td2.getIconResource()); + assertEquals(td1.getPrimaryColor(), td2.getPrimaryColor()); + assertEquals(td1.getEnsureStatusBarContrastWhenTransparent(), + td2.getEnsureStatusBarContrastWhenTransparent()); + assertEquals(td1.getEnsureNavigationBarContrastWhenTransparent(), + td2.getEnsureNavigationBarContrastWhenTransparent()); + } + if (checkPreservedFields) { + assertEquals(td1.getBackgroundColor(), td2.getBackgroundColor()); + assertEquals(td1.getStatusBarColor(), td2.getStatusBarColor()); + assertEquals(td1.getNavigationBarColor(), td2.getNavigationBarColor()); + assertEquals(td1.getResizeMode(), td2.getResizeMode()); + assertEquals(td1.getMinWidth(), td2.getMinWidth()); + assertEquals(td1.getMinHeight(), td2.getMinHeight()); + } + } + + private <T extends Parcelable> T parcelingRoundTrip(final T in) throws Exception { + final Parcel p = Parcel.obtain(); + in.writeToParcel(p, /* flags */ 0); + p.setDataPosition(0); + final byte[] marshalledData = p.marshall(); + p.recycle(); + + final Parcel q = Parcel.obtain(); + q.unmarshall(marshalledData, 0, marshalledData.length); + q.setDataPosition(0); + + final Parcelable.Creator<T> creator = (Parcelable.Creator<T>) + in.getClass().getField("CREATOR").get(null); // static object, so null receiver + final T unmarshalled = (T) creator.createFromParcel(q); + q.recycle(); + return unmarshalled; } // If any entries in appear in the list, sanity check them against all running applications diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java index c986db8b2a83..c328d720426d 100644 --- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java +++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java @@ -504,15 +504,17 @@ public class ActivityThreadTest { } @Override - public void onPictureInPictureRequested() { + public boolean onPictureInPictureRequested() { mPipRequested = true; if (getIntent().getBooleanExtra(PIP_REQUESTED_OVERRIDE_ENTER, false)) { enterPictureInPictureMode(new PictureInPictureParams.Builder().build()); mPipEntered = true; + return true; } else if (getIntent().getBooleanExtra(PIP_REQUESTED_OVERRIDE_SKIP, false)) { mPipEnterSkipped = true; + return false; } - super.onPictureInPictureRequested(); + return super.onPictureInPictureRequested(); } boolean pipRequested() { diff --git a/core/tests/coretests/src/android/app/compat/CompatChangesTest.java b/core/tests/coretests/src/android/app/compat/CompatChangesTest.java new file mode 100644 index 000000000000..fbd02eddbcf8 --- /dev/null +++ b/core/tests/coretests/src/android/app/compat/CompatChangesTest.java @@ -0,0 +1,81 @@ +/* + * 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.compat; + +import static com.google.common.truth.Truth.assertThat; + +import android.app.Instrumentation; +import android.compat.testing.PlatformCompatChangeRule; +import android.os.Process; +import android.os.UserHandle; +import android.platform.test.annotations.Presubmit; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges; +import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestRule; +import org.junit.runner.RunWith; + +/** + * {@link CompatChanges} tests. + */ +@RunWith(AndroidJUnit4.class) +@SmallTest +@Presubmit +public class CompatChangesTest { + static final long CHANGE_ID = 1L; + + private Instrumentation mInstrumentation; + + @Rule + public TestRule compatChangeRule = new PlatformCompatChangeRule(); + + @Before + public void setup() { + mInstrumentation = InstrumentationRegistry.getInstrumentation(); + } + + + private String getPackageName() { + return mInstrumentation.getTargetContext().getPackageName(); + } + + @Test + @EnableCompatChanges(CHANGE_ID) + public void testEnabledChange() { + assertThat(CompatChanges.isChangeEnabled(CHANGE_ID)).isTrue(); + assertThat(CompatChanges.isChangeEnabled(CHANGE_ID, Process.myUid())).isTrue(); + assertThat(CompatChanges.isChangeEnabled(CHANGE_ID, getPackageName(), + UserHandle.of(UserHandle.myUserId()))).isTrue(); + } + + @Test + @DisableCompatChanges(CHANGE_ID) + public void testDisabledChange() { + assertThat(CompatChanges.isChangeEnabled(CHANGE_ID)).isFalse(); + assertThat(CompatChanges.isChangeEnabled(CHANGE_ID, Process.myUid())).isFalse(); + assertThat(CompatChanges.isChangeEnabled(CHANGE_ID, getPackageName(), + UserHandle.of(UserHandle.myUserId()))).isFalse(); + } +} diff --git a/core/tests/coretests/src/android/app/timedetector/PhoneTimeSuggestionTest.java b/core/tests/coretests/src/android/app/timedetector/TelephonyTimeSuggestionTest.java index d17b63597a21..4b64dfc84fb7 100644 --- a/core/tests/coretests/src/android/app/timedetector/PhoneTimeSuggestionTest.java +++ b/core/tests/coretests/src/android/app/timedetector/TelephonyTimeSuggestionTest.java @@ -26,44 +26,45 @@ import android.os.TimestampedValue; import org.junit.Test; -public class PhoneTimeSuggestionTest { +public class TelephonyTimeSuggestionTest { private static final int SLOT_INDEX = 99999; @Test public void testEquals() { - PhoneTimeSuggestion.Builder builder1 = new PhoneTimeSuggestion.Builder(SLOT_INDEX); + TelephonyTimeSuggestion.Builder builder1 = new TelephonyTimeSuggestion.Builder(SLOT_INDEX); { - PhoneTimeSuggestion one = builder1.build(); + TelephonyTimeSuggestion one = builder1.build(); assertEquals(one, one); } - PhoneTimeSuggestion.Builder builder2 = new PhoneTimeSuggestion.Builder(SLOT_INDEX); + TelephonyTimeSuggestion.Builder builder2 = new TelephonyTimeSuggestion.Builder(SLOT_INDEX); { - PhoneTimeSuggestion one = builder1.build(); - PhoneTimeSuggestion two = builder2.build(); + TelephonyTimeSuggestion one = builder1.build(); + TelephonyTimeSuggestion two = builder2.build(); assertEquals(one, two); assertEquals(two, one); } builder1.setUtcTime(new TimestampedValue<>(1111L, 2222L)); { - PhoneTimeSuggestion one = builder1.build(); + TelephonyTimeSuggestion one = builder1.build(); assertEquals(one, one); } builder2.setUtcTime(new TimestampedValue<>(1111L, 2222L)); { - PhoneTimeSuggestion one = builder1.build(); - PhoneTimeSuggestion two = builder2.build(); + TelephonyTimeSuggestion one = builder1.build(); + TelephonyTimeSuggestion two = builder2.build(); assertEquals(one, two); assertEquals(two, one); } - PhoneTimeSuggestion.Builder builder3 = new PhoneTimeSuggestion.Builder(SLOT_INDEX + 1); + TelephonyTimeSuggestion.Builder builder3 = + new TelephonyTimeSuggestion.Builder(SLOT_INDEX + 1); builder3.setUtcTime(new TimestampedValue<>(1111L, 2222L)); { - PhoneTimeSuggestion one = builder1.build(); - PhoneTimeSuggestion three = builder3.build(); + TelephonyTimeSuggestion one = builder1.build(); + TelephonyTimeSuggestion three = builder3.build(); assertNotEquals(one, three); assertNotEquals(three, one); } @@ -72,15 +73,15 @@ public class PhoneTimeSuggestionTest { builder1.addDebugInfo("Debug info 1"); builder2.addDebugInfo("Debug info 2"); { - PhoneTimeSuggestion one = builder1.build(); - PhoneTimeSuggestion two = builder2.build(); + TelephonyTimeSuggestion one = builder1.build(); + TelephonyTimeSuggestion two = builder2.build(); assertEquals(one, two); } } @Test public void testParcelable() { - PhoneTimeSuggestion.Builder builder = new PhoneTimeSuggestion.Builder(SLOT_INDEX); + TelephonyTimeSuggestion.Builder builder = new TelephonyTimeSuggestion.Builder(SLOT_INDEX); assertRoundTripParcelable(builder.build()); builder.setUtcTime(new TimestampedValue<>(1111L, 2222L)); @@ -88,9 +89,9 @@ public class PhoneTimeSuggestionTest { // DebugInfo should also be stored (but is not checked by equals() { - PhoneTimeSuggestion suggestion1 = builder.build(); + TelephonyTimeSuggestion suggestion1 = builder.build(); builder.addDebugInfo("This is debug info"); - PhoneTimeSuggestion rtSuggestion1 = roundTripParcelable(suggestion1); + TelephonyTimeSuggestion rtSuggestion1 = roundTripParcelable(suggestion1); assertEquals(suggestion1.getDebugInfo(), rtSuggestion1.getDebugInfo()); } } diff --git a/core/tests/coretests/src/android/app/timezonedetector/PhoneTimeZoneSuggestionTest.java b/core/tests/coretests/src/android/app/timezonedetector/PhoneTimeZoneSuggestionTest.java deleted file mode 100644 index 384dbf9fab07..000000000000 --- a/core/tests/coretests/src/android/app/timezonedetector/PhoneTimeZoneSuggestionTest.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.app.timezonedetector; - -import static android.app.timezonedetector.ParcelableTestSupport.assertRoundTripParcelable; -import static android.app.timezonedetector.ParcelableTestSupport.roundTripParcelable; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertTrue; - -import org.junit.Test; - -public class PhoneTimeZoneSuggestionTest { - private static final int SLOT_INDEX = 99999; - - @Test - public void testEquals() { - PhoneTimeZoneSuggestion.Builder builder1 = new PhoneTimeZoneSuggestion.Builder(SLOT_INDEX); - { - PhoneTimeZoneSuggestion one = builder1.build(); - assertEquals(one, one); - } - - PhoneTimeZoneSuggestion.Builder builder2 = new PhoneTimeZoneSuggestion.Builder(SLOT_INDEX); - { - PhoneTimeZoneSuggestion one = builder1.build(); - PhoneTimeZoneSuggestion two = builder2.build(); - assertEquals(one, two); - assertEquals(two, one); - } - - PhoneTimeZoneSuggestion.Builder builder3 = - new PhoneTimeZoneSuggestion.Builder(SLOT_INDEX + 1); - { - PhoneTimeZoneSuggestion one = builder1.build(); - PhoneTimeZoneSuggestion three = builder3.build(); - assertNotEquals(one, three); - assertNotEquals(three, one); - } - - builder1.setZoneId("Europe/London"); - builder1.setMatchType(PhoneTimeZoneSuggestion.MATCH_TYPE_NETWORK_COUNTRY_ONLY); - builder1.setQuality(PhoneTimeZoneSuggestion.QUALITY_SINGLE_ZONE); - { - PhoneTimeZoneSuggestion one = builder1.build(); - PhoneTimeZoneSuggestion two = builder2.build(); - assertNotEquals(one, two); - } - - builder2.setZoneId("Europe/Paris"); - builder2.setMatchType(PhoneTimeZoneSuggestion.MATCH_TYPE_NETWORK_COUNTRY_ONLY); - builder2.setQuality(PhoneTimeZoneSuggestion.QUALITY_SINGLE_ZONE); - { - PhoneTimeZoneSuggestion one = builder1.build(); - PhoneTimeZoneSuggestion two = builder2.build(); - assertNotEquals(one, two); - } - - builder1.setZoneId("Europe/Paris"); - { - PhoneTimeZoneSuggestion one = builder1.build(); - PhoneTimeZoneSuggestion two = builder2.build(); - assertEquals(one, two); - } - - builder1.setMatchType(PhoneTimeZoneSuggestion.MATCH_TYPE_EMULATOR_ZONE_ID); - builder2.setMatchType(PhoneTimeZoneSuggestion.MATCH_TYPE_NETWORK_COUNTRY_ONLY); - { - PhoneTimeZoneSuggestion one = builder1.build(); - PhoneTimeZoneSuggestion two = builder2.build(); - assertNotEquals(one, two); - } - - builder1.setMatchType(PhoneTimeZoneSuggestion.MATCH_TYPE_NETWORK_COUNTRY_ONLY); - { - PhoneTimeZoneSuggestion one = builder1.build(); - PhoneTimeZoneSuggestion two = builder2.build(); - assertEquals(one, two); - } - - builder1.setQuality(PhoneTimeZoneSuggestion.QUALITY_SINGLE_ZONE); - builder2.setQuality(PhoneTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS); - { - PhoneTimeZoneSuggestion one = builder1.build(); - PhoneTimeZoneSuggestion two = builder2.build(); - assertNotEquals(one, two); - } - - builder1.setQuality(PhoneTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS); - { - PhoneTimeZoneSuggestion one = builder1.build(); - PhoneTimeZoneSuggestion two = builder2.build(); - assertEquals(one, two); - } - - // DebugInfo must not be considered in equals(). - { - PhoneTimeZoneSuggestion one = builder1.build(); - PhoneTimeZoneSuggestion two = builder2.build(); - one.addDebugInfo("Debug info 1"); - two.addDebugInfo("Debug info 2"); - assertEquals(one, two); - } - } - - @Test(expected = RuntimeException.class) - public void testBuilderValidates_emptyZone_badMatchType() { - PhoneTimeZoneSuggestion.Builder builder = new PhoneTimeZoneSuggestion.Builder(SLOT_INDEX); - // No zone ID, so match type should be left unset. - builder.setMatchType(PhoneTimeZoneSuggestion.MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET); - builder.build(); - } - - @Test(expected = RuntimeException.class) - public void testBuilderValidates_zoneSet_badMatchType() { - PhoneTimeZoneSuggestion.Builder builder = new PhoneTimeZoneSuggestion.Builder(SLOT_INDEX); - builder.setZoneId("Europe/London"); - builder.setQuality(PhoneTimeZoneSuggestion.QUALITY_SINGLE_ZONE); - builder.build(); - } - - @Test - public void testParcelable() { - PhoneTimeZoneSuggestion.Builder builder = new PhoneTimeZoneSuggestion.Builder(SLOT_INDEX); - assertRoundTripParcelable(builder.build()); - - builder.setZoneId("Europe/London"); - builder.setMatchType(PhoneTimeZoneSuggestion.MATCH_TYPE_EMULATOR_ZONE_ID); - builder.setQuality(PhoneTimeZoneSuggestion.QUALITY_SINGLE_ZONE); - PhoneTimeZoneSuggestion suggestion1 = builder.build(); - assertRoundTripParcelable(suggestion1); - - // DebugInfo should also be stored (but is not checked by equals() - String debugString = "This is debug info"; - suggestion1.addDebugInfo(debugString); - PhoneTimeZoneSuggestion suggestion1_2 = roundTripParcelable(suggestion1); - assertEquals(suggestion1, suggestion1_2); - assertTrue(suggestion1_2.getDebugInfo().contains(debugString)); - } -} diff --git a/core/tests/coretests/src/android/app/timezonedetector/TelephonyTimeZoneSuggestionTest.java b/core/tests/coretests/src/android/app/timezonedetector/TelephonyTimeZoneSuggestionTest.java new file mode 100644 index 000000000000..59d55b79157c --- /dev/null +++ b/core/tests/coretests/src/android/app/timezonedetector/TelephonyTimeZoneSuggestionTest.java @@ -0,0 +1,162 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.timezonedetector; + +import static android.app.timezonedetector.ParcelableTestSupport.assertRoundTripParcelable; +import static android.app.timezonedetector.ParcelableTestSupport.roundTripParcelable; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +public class TelephonyTimeZoneSuggestionTest { + private static final int SLOT_INDEX = 99999; + + @Test + public void testEquals() { + TelephonyTimeZoneSuggestion.Builder builder1 = + new TelephonyTimeZoneSuggestion.Builder(SLOT_INDEX); + { + TelephonyTimeZoneSuggestion one = builder1.build(); + assertEquals(one, one); + } + + TelephonyTimeZoneSuggestion.Builder builder2 = + new TelephonyTimeZoneSuggestion.Builder(SLOT_INDEX); + { + TelephonyTimeZoneSuggestion one = builder1.build(); + TelephonyTimeZoneSuggestion two = builder2.build(); + assertEquals(one, two); + assertEquals(two, one); + } + + TelephonyTimeZoneSuggestion.Builder builder3 = + new TelephonyTimeZoneSuggestion.Builder(SLOT_INDEX + 1); + { + TelephonyTimeZoneSuggestion one = builder1.build(); + TelephonyTimeZoneSuggestion three = builder3.build(); + assertNotEquals(one, three); + assertNotEquals(three, one); + } + + builder1.setZoneId("Europe/London"); + builder1.setMatchType(TelephonyTimeZoneSuggestion.MATCH_TYPE_NETWORK_COUNTRY_ONLY); + builder1.setQuality(TelephonyTimeZoneSuggestion.QUALITY_SINGLE_ZONE); + { + TelephonyTimeZoneSuggestion one = builder1.build(); + TelephonyTimeZoneSuggestion two = builder2.build(); + assertNotEquals(one, two); + } + + builder2.setZoneId("Europe/Paris"); + builder2.setMatchType(TelephonyTimeZoneSuggestion.MATCH_TYPE_NETWORK_COUNTRY_ONLY); + builder2.setQuality(TelephonyTimeZoneSuggestion.QUALITY_SINGLE_ZONE); + { + TelephonyTimeZoneSuggestion one = builder1.build(); + TelephonyTimeZoneSuggestion two = builder2.build(); + assertNotEquals(one, two); + } + + builder1.setZoneId("Europe/Paris"); + { + TelephonyTimeZoneSuggestion one = builder1.build(); + TelephonyTimeZoneSuggestion two = builder2.build(); + assertEquals(one, two); + } + + builder1.setMatchType(TelephonyTimeZoneSuggestion.MATCH_TYPE_EMULATOR_ZONE_ID); + builder2.setMatchType(TelephonyTimeZoneSuggestion.MATCH_TYPE_NETWORK_COUNTRY_ONLY); + { + TelephonyTimeZoneSuggestion one = builder1.build(); + TelephonyTimeZoneSuggestion two = builder2.build(); + assertNotEquals(one, two); + } + + builder1.setMatchType(TelephonyTimeZoneSuggestion.MATCH_TYPE_NETWORK_COUNTRY_ONLY); + { + TelephonyTimeZoneSuggestion one = builder1.build(); + TelephonyTimeZoneSuggestion two = builder2.build(); + assertEquals(one, two); + } + + builder1.setQuality(TelephonyTimeZoneSuggestion.QUALITY_SINGLE_ZONE); + builder2.setQuality( + TelephonyTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS); + { + TelephonyTimeZoneSuggestion one = builder1.build(); + TelephonyTimeZoneSuggestion two = builder2.build(); + assertNotEquals(one, two); + } + + builder1.setQuality( + TelephonyTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS); + { + TelephonyTimeZoneSuggestion one = builder1.build(); + TelephonyTimeZoneSuggestion two = builder2.build(); + assertEquals(one, two); + } + + // DebugInfo must not be considered in equals(). + { + TelephonyTimeZoneSuggestion one = builder1.build(); + TelephonyTimeZoneSuggestion two = builder2.build(); + one.addDebugInfo("Debug info 1"); + two.addDebugInfo("Debug info 2"); + assertEquals(one, two); + } + } + + @Test(expected = RuntimeException.class) + public void testBuilderValidates_emptyZone_badMatchType() { + TelephonyTimeZoneSuggestion.Builder builder = + new TelephonyTimeZoneSuggestion.Builder(SLOT_INDEX); + // No zone ID, so match type should be left unset. + builder.setMatchType(TelephonyTimeZoneSuggestion.MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET); + builder.build(); + } + + @Test(expected = RuntimeException.class) + public void testBuilderValidates_zoneSet_badMatchType() { + TelephonyTimeZoneSuggestion.Builder builder = + new TelephonyTimeZoneSuggestion.Builder(SLOT_INDEX); + builder.setZoneId("Europe/London"); + builder.setQuality(TelephonyTimeZoneSuggestion.QUALITY_SINGLE_ZONE); + builder.build(); + } + + @Test + public void testParcelable() { + TelephonyTimeZoneSuggestion.Builder builder = + new TelephonyTimeZoneSuggestion.Builder(SLOT_INDEX); + assertRoundTripParcelable(builder.build()); + + builder.setZoneId("Europe/London"); + builder.setMatchType(TelephonyTimeZoneSuggestion.MATCH_TYPE_EMULATOR_ZONE_ID); + builder.setQuality(TelephonyTimeZoneSuggestion.QUALITY_SINGLE_ZONE); + TelephonyTimeZoneSuggestion suggestion1 = builder.build(); + assertRoundTripParcelable(suggestion1); + + // DebugInfo should also be stored (but is not checked by equals() + String debugString = "This is debug info"; + suggestion1.addDebugInfo(debugString); + TelephonyTimeZoneSuggestion suggestion1_2 = roundTripParcelable(suggestion1); + assertEquals(suggestion1, suggestion1_2); + assertTrue(suggestion1_2.getDebugInfo().contains(debugString)); + } +} diff --git a/core/tests/coretests/src/android/content/integrity/AtomicFormulaTest.java b/core/tests/coretests/src/android/content/integrity/AtomicFormulaTest.java index bf782034c8f1..3273e5dd32ba 100644 --- a/core/tests/coretests/src/android/content/integrity/AtomicFormulaTest.java +++ b/core/tests/coretests/src/android/content/integrity/AtomicFormulaTest.java @@ -90,18 +90,18 @@ public class AtomicFormulaTest { } @Test - public void testValidAtomicFormula_stringValue_appCertificateAutoHashed() { + public void testValidAtomicFormula_stringValue_appCertificateIsNotAutoHashed() { String appCert = "cert"; StringAtomicFormula stringAtomicFormula = new StringAtomicFormula(AtomicFormula.APP_CERTIFICATE, appCert); assertThat(stringAtomicFormula.getKey()).isEqualTo(AtomicFormula.APP_CERTIFICATE); - assertThat(stringAtomicFormula.getValue()).doesNotMatch(appCert); + assertThat(stringAtomicFormula.getValue()).matches(appCert); assertThat(stringAtomicFormula.getIsHashedValue()).isTrue(); } @Test - public void testValidAtomicFormula_stringValue_installerCertificateAutoHashed() { + public void testValidAtomicFormula_stringValue_installerCertificateIsNotAutoHashed() { String installerCert = "cert"; StringAtomicFormula stringAtomicFormula = new StringAtomicFormula(AtomicFormula.INSTALLER_CERTIFICATE, @@ -109,7 +109,7 @@ public class AtomicFormulaTest { assertThat(stringAtomicFormula.getKey()).isEqualTo( AtomicFormula.INSTALLER_CERTIFICATE); - assertThat(stringAtomicFormula.getValue()).doesNotMatch(installerCert); + assertThat(stringAtomicFormula.getValue()).matches(installerCert); assertThat(stringAtomicFormula.getIsHashedValue()).isTrue(); } diff --git a/core/tests/coretests/src/android/content/integrity/IntegrityFormulaTest.java b/core/tests/coretests/src/android/content/integrity/IntegrityFormulaTest.java index c1806028f75b..75ef1f22b819 100644 --- a/core/tests/coretests/src/android/content/integrity/IntegrityFormulaTest.java +++ b/core/tests/coretests/src/android/content/integrity/IntegrityFormulaTest.java @@ -40,7 +40,7 @@ public class IntegrityFormulaTest { assertThat(stringAtomicFormula.getKey()).isEqualTo(AtomicFormula.PACKAGE_NAME); assertThat(stringAtomicFormula.getValue()).isEqualTo(packageName); - assertThat(stringAtomicFormula.getIsHashedValue()).isEqualTo(false); + assertThat(stringAtomicFormula.getIsHashedValue()).isFalse(); } @Test @@ -53,8 +53,8 @@ public class IntegrityFormulaTest { (AtomicFormula.StringAtomicFormula) formula; assertThat(stringAtomicFormula.getKey()).isEqualTo(AtomicFormula.APP_CERTIFICATE); - assertThat(stringAtomicFormula.getValue()).doesNotMatch(appCertificate); - assertThat(stringAtomicFormula.getIsHashedValue()).isEqualTo(true); + assertThat(stringAtomicFormula.getValue()).matches(appCertificate); + assertThat(stringAtomicFormula.getIsHashedValue()).isTrue(); } @Test @@ -68,7 +68,7 @@ public class IntegrityFormulaTest { assertThat(stringAtomicFormula.getKey()).isEqualTo(AtomicFormula.INSTALLER_NAME); assertThat(stringAtomicFormula.getValue()).isEqualTo(installerName); - assertThat(stringAtomicFormula.getIsHashedValue()).isEqualTo(false); + assertThat(stringAtomicFormula.getIsHashedValue()).isFalse(); } @Test @@ -81,8 +81,8 @@ public class IntegrityFormulaTest { (AtomicFormula.StringAtomicFormula) formula; assertThat(stringAtomicFormula.getKey()).isEqualTo(AtomicFormula.INSTALLER_CERTIFICATE); - assertThat(stringAtomicFormula.getValue()).doesNotMatch(installerCertificate); - assertThat(stringAtomicFormula.getIsHashedValue()).isEqualTo(true); + assertThat(stringAtomicFormula.getValue()).matches(installerCertificate); + assertThat(stringAtomicFormula.getIsHashedValue()).isTrue(); } @Test diff --git a/core/tests/coretests/src/android/os/BuildTest.java b/core/tests/coretests/src/android/os/BuildTest.java index decc76869a53..2295eb989108 100644 --- a/core/tests/coretests/src/android/os/BuildTest.java +++ b/core/tests/coretests/src/android/os/BuildTest.java @@ -60,7 +60,7 @@ public class BuildTest extends TestCase { assertNotEmpty("BRAND", Build.BRAND); assertNotEmpty("MODEL", Build.MODEL); assertNotEmpty("VERSION.INCREMENTAL", Build.VERSION.INCREMENTAL); - assertNotEmpty("VERSION.RELEASE", Build.VERSION.RELEASE); + assertNotEmpty("VERSION.RELEASE", Build.VERSION.RELEASE_OR_CODENAME); assertNotEmpty("TYPE", Build.TYPE); Assert.assertNotNull("TAGS", Build.TAGS); // TAGS is allowed to be empty. assertNotEmpty("FINGERPRINT", Build.FINGERPRINT); diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java index 8b8e9ea4c6ee..b71c5800161e 100644 --- a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java +++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java @@ -19,7 +19,6 @@ package android.view.accessibility; import android.accessibilityservice.AccessibilityServiceInfo; import android.accessibilityservice.IAccessibilityServiceConnection; import android.content.pm.ParceledListSlice; -import android.graphics.Bitmap; import android.graphics.Region; import android.os.Bundle; import android.os.IBinder; @@ -157,9 +156,5 @@ public class AccessibilityServiceConnectionImpl extends IAccessibilityServiceCon return -1; } - public Bitmap takeScreenshot(int displayId) { - return null; - } - - public void takeScreenshotWithCallback(int displayId, RemoteCallback callback) {} + public void takeScreenshot(int displayId, RemoteCallback callback) {} } diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java index df6b9066ea5c..ce71bebfc455 100644 --- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java +++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java @@ -38,6 +38,7 @@ import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; @@ -355,6 +356,7 @@ public class ChooserActivityTest { // enable the work tab feature flag ResolverActivity.ENABLE_TABBED_VIEW = true; + markWorkProfileUserAvailable(); Intent sendIntent = createSendTextIntent(); List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTestWithOtherProfile(2); @@ -1209,17 +1211,24 @@ public class ChooserActivityTest { // enable the work tab feature flag ResolverActivity.ENABLE_TABBED_VIEW = true; int personalProfileTargets = 3; + int otherProfileTargets = 1; List<ResolvedComponentInfo> personalResolvedComponentInfos = - createResolvedComponentsForTest(personalProfileTargets); + createResolvedComponentsForTestWithOtherProfile( + personalProfileTargets + otherProfileTargets, /* userID */ 10); int workProfileTargets = 4; List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest( workProfileTargets); when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(), Mockito.anyBoolean(), - Mockito.isA(List.class))).thenReturn(personalResolvedComponentInfos); + Mockito.isA(List.class))) + .thenReturn(new ArrayList<>(personalResolvedComponentInfos)); when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(), Mockito.anyBoolean(), - Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos); + Mockito.isA(List.class))).thenReturn(new ArrayList<>(workResolvedComponentInfos)); + when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(), + Mockito.anyBoolean(), + Mockito.isA(List.class), + eq(UserHandle.SYSTEM))).thenReturn(new ArrayList<>(personalResolvedComponentInfos)); Intent sendIntent = createSendTextIntent(); sendIntent.setType("TestType"); markWorkProfileUserAvailable(); @@ -1229,8 +1238,6 @@ public class ChooserActivityTest { waitForIdle(); assertThat(activity.getCurrentUserHandle().getIdentifier(), is(0)); - // The work list adapter must only be filled when we open the work tab - assertThat(activity.getWorkListAdapter().getCount(), is(0)); onView(withText(R.string.resolver_work_tab)).perform(click()); assertThat(activity.getCurrentUserHandle().getIdentifier(), is(10)); assertThat(activity.getPersonalListAdapter().getCount(), is(personalProfileTargets)); @@ -1243,11 +1250,22 @@ public class ChooserActivityTest { ResolverActivity.ENABLE_TABBED_VIEW = true; markWorkProfileUserAvailable(); int workProfileTargets = 4; + List<ResolvedComponentInfo> personalResolvedComponentInfos = + createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10); List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(workProfileTargets); + when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(), + Mockito.anyBoolean(), + Mockito.isA(List.class))) + .thenReturn(new ArrayList<>(personalResolvedComponentInfos)); when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(), Mockito.anyBoolean(), Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos); + when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(), + Mockito.anyBoolean(), + Mockito.isA(List.class), + eq(UserHandle.SYSTEM))) + .thenReturn(new ArrayList<>(personalResolvedComponentInfos)); Intent sendIntent = createSendTextIntent(); sendIntent.setType("TestType"); @@ -1357,6 +1375,20 @@ public class ChooserActivityTest { return infoList; } + private List<ResolvedComponentInfo> createResolvedComponentsForTestWithOtherProfile( + int numberOfResults, int userId) { + List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults); + for (int i = 0; i < numberOfResults; i++) { + if (i == 0) { + infoList.add( + ResolverDataProvider.createResolvedComponentInfoWithOtherId(i, userId)); + } else { + infoList.add(ResolverDataProvider.createResolvedComponentInfo(i)); + } + } + return infoList; + } + private List<ResolvedComponentInfo> createResolvedComponentsForTestWithUserId( int numberOfResults, int userId) { List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults); diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java index eee62bb791bf..a68b59086d42 100644 --- a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java +++ b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java @@ -16,10 +16,15 @@ package com.android.internal.app; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import android.annotation.Nullable; +import android.app.prediction.AppPredictionContext; +import android.app.prediction.AppPredictionManager; +import android.app.prediction.AppPredictor; import android.app.usage.UsageStatsManager; import android.content.ContentResolver; import android.content.Context; @@ -40,6 +45,8 @@ import com.android.internal.app.chooser.TargetInfo; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import org.mockito.Mockito; + import java.util.function.Function; public class ChooserWrapperActivity extends ChooserActivity { @@ -173,6 +180,12 @@ public class ChooserWrapperActivity extends ChooserActivity { return mMultiProfilePagerAdapter.getCurrentUserHandle(); } + @Override + public Context createContextAsUser(UserHandle user, int flags) { + // return the current context as a work profile doesn't really exist in these tests + return getApplicationContext(); + } + /** * We cannot directly mock the activity created since instrumentation creates it. * <p> diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java index 911490f30799..5f4194aa51e3 100644 --- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java +++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java @@ -225,6 +225,7 @@ public class ResolverActivityTest { // enable the work tab feature flag ResolverActivity.ENABLE_TABBED_VIEW = true; + markWorkProfileUserAvailable(); Intent sendIntent = createSendImageIntent(); List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTestWithOtherProfile(2); @@ -246,7 +247,6 @@ public class ResolverActivityTest { chosen[0] = targetInfo.getResolveInfo(); return true; }; - // Make a stable copy of the components as the original list may be modified List<ResolvedComponentInfo> stableCopy = createResolvedComponentsForTestWithOtherProfile(2); @@ -443,7 +443,7 @@ public class ResolverActivityTest { // enable the work tab feature flag ResolverActivity.ENABLE_TABBED_VIEW = true; List<ResolvedComponentInfo> personalResolvedComponentInfos = - createResolvedComponentsForTest(3); + createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10); List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4); when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(), Mockito.anyBoolean(), @@ -451,6 +451,11 @@ public class ResolverActivityTest { when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(), Mockito.anyBoolean(), Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos); + when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(), + Mockito.anyBoolean(), + Mockito.isA(List.class), + eq(UserHandle.SYSTEM))) + .thenReturn(new ArrayList<>(personalResolvedComponentInfos)); Intent sendIntent = createSendImageIntent(); markWorkProfileUserAvailable(); @@ -478,17 +483,20 @@ public class ResolverActivityTest { Mockito.anyBoolean(), Mockito.anyBoolean(), Mockito.isA(List.class), - eq(sOverrides.workProfileUserHandle))).thenReturn(new ArrayList<>(workResolvedComponentInfos)); + eq(sOverrides.workProfileUserHandle))) + .thenReturn(new ArrayList<>(workResolvedComponentInfos)); when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(), Mockito.anyBoolean(), Mockito.isA(List.class), - eq(sOverrides.workProfileUserHandle))).thenReturn(new ArrayList<>(workResolvedComponentInfos)); + eq(sOverrides.workProfileUserHandle))) + .thenReturn(new ArrayList<>(workResolvedComponentInfos)); when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(), Mockito.anyBoolean(), Mockito.isA(List.class))).thenReturn(new ArrayList<>(workResolvedComponentInfos)); when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(), Mockito.anyBoolean(), - Mockito.isA(List.class))).thenReturn(new ArrayList<>(personalResolvedComponentInfos)); + Mockito.isA(List.class))) + .thenReturn(new ArrayList<>(personalResolvedComponentInfos)); when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(), Mockito.anyBoolean(), Mockito.isA(List.class), @@ -502,7 +510,7 @@ public class ResolverActivityTest { onView(withText(R.string.resolver_work_tab)).perform(click()); assertThat(activity.getCurrentUserHandle().getIdentifier(), is(10)); - assertThat(activity.getPersonalListAdapter().getCount(), is(3)); + assertThat(activity.getPersonalListAdapter().getCount(), is(2)); } @Test @@ -511,14 +519,20 @@ public class ResolverActivityTest { ResolverActivity.ENABLE_TABBED_VIEW = true; markWorkProfileUserAvailable(); List<ResolvedComponentInfo> personalResolvedComponentInfos = - createResolvedComponentsForTest(3); + createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10); List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4); when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(), Mockito.anyBoolean(), - Mockito.isA(List.class))).thenReturn(personalResolvedComponentInfos); + Mockito.isA(List.class))) + .thenReturn(new ArrayList<>(personalResolvedComponentInfos)); when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(), Mockito.anyBoolean(), Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos); + when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(), + Mockito.anyBoolean(), + Mockito.isA(List.class), + eq(UserHandle.SYSTEM))) + .thenReturn(new ArrayList<>(personalResolvedComponentInfos)); Intent sendIntent = createSendImageIntent(); final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent); @@ -536,14 +550,20 @@ public class ResolverActivityTest { ResolverActivity.ENABLE_TABBED_VIEW = true; markWorkProfileUserAvailable(); List<ResolvedComponentInfo> personalResolvedComponentInfos = - createResolvedComponentsForTest(3); + createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10); List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4); when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(), Mockito.anyBoolean(), - Mockito.isA(List.class))).thenReturn(personalResolvedComponentInfos); + Mockito.isA(List.class))) + .thenReturn(new ArrayList<>(personalResolvedComponentInfos)); when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(), Mockito.anyBoolean(), Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos); + when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(), + Mockito.anyBoolean(), + Mockito.isA(List.class), + eq(UserHandle.SYSTEM))) + .thenReturn(new ArrayList<>(personalResolvedComponentInfos)); Intent sendIntent = createSendImageIntent(); ResolveInfo[] chosen = new ResolveInfo[1]; sOverrides.onSafelyStartCallback = targetInfo -> { @@ -587,17 +607,20 @@ public class ResolverActivityTest { Mockito.anyBoolean(), Mockito.anyBoolean(), Mockito.isA(List.class), - eq(sOverrides.workProfileUserHandle))).thenReturn(new ArrayList<>(workResolvedComponentInfos)); + eq(sOverrides.workProfileUserHandle))) + .thenReturn(new ArrayList<>(workResolvedComponentInfos)); when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(), Mockito.anyBoolean(), Mockito.isA(List.class), - eq(sOverrides.workProfileUserHandle))).thenReturn(new ArrayList<>(workResolvedComponentInfos)); + eq(sOverrides.workProfileUserHandle))) + .thenReturn(new ArrayList<>(workResolvedComponentInfos)); when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(), Mockito.anyBoolean(), Mockito.isA(List.class))).thenReturn(new ArrayList<>(workResolvedComponentInfos)); when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(), Mockito.anyBoolean(), - Mockito.isA(List.class))).thenReturn(new ArrayList<>(personalResolvedComponentInfos)); + Mockito.isA(List.class))) + .thenReturn(new ArrayList<>(personalResolvedComponentInfos)); when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(), Mockito.anyBoolean(), Mockito.isA(List.class), @@ -678,6 +701,20 @@ public class ResolverActivityTest { return infoList; } + private List<ResolvedComponentInfo> createResolvedComponentsForTestWithOtherProfile( + int numberOfResults, int userId) { + List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults); + for (int i = 0; i < numberOfResults; i++) { + if (i == 0) { + infoList.add( + ResolverDataProvider.createResolvedComponentInfoWithOtherId(i, userId)); + } else { + infoList.add(ResolverDataProvider.createResolvedComponentInfo(i)); + } + } + return infoList; + } + private void waitForIdle() { InstrumentationRegistry.getInstrumentation().waitForIdleSync(); } diff --git a/data/etc/com.android.settings.xml b/data/etc/com.android.settings.xml index a200a510cf12..fe1182ecad4f 100644 --- a/data/etc/com.android.settings.xml +++ b/data/etc/com.android.settings.xml @@ -26,6 +26,7 @@ <permission name="android.permission.DELETE_PACKAGES"/> <permission name="android.permission.FORCE_STOP_PACKAGES"/> <permission name="android.permission.LOCAL_MAC_ADDRESS"/> + <permission name="android.permission.LOG_COMPAT_CHANGE" /> <permission name="android.permission.MANAGE_DEBUGGING"/> <permission name="android.permission.MANAGE_DEVICE_ADMINS"/> <permission name="android.permission.MANAGE_FINGERPRINT"/> @@ -37,8 +38,10 @@ <permission name="android.permission.MODIFY_PHONE_STATE"/> <permission name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/> <permission name="android.permission.MOVE_PACKAGE"/> + <permission name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG" /> <permission name="android.permission.OVERRIDE_WIFI_CONFIG"/> <permission name="android.permission.PACKAGE_USAGE_STATS"/> + <permission name="android.permission.READ_COMPAT_CHANGE_CONFIG" /> <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/> <permission name="android.permission.READ_SEARCH_INDEXABLES"/> <permission name="android.permission.REBOOT"/> diff --git a/data/etc/platform.xml b/data/etc/platform.xml index da505505628e..6929d0d1879d 100644 --- a/data/etc/platform.xml +++ b/data/etc/platform.xml @@ -72,6 +72,11 @@ <group gid="net_admin" /> </permission> + <permission name="android.permission.MAINLINE_NETWORK_STACK" > + <group gid="net_admin" /> + <group gid="net_raw" /> + </permission> + <!-- The group that /cache belongs to, linked to the permission set on the applications that can access /cache --> <permission name="android.permission.ACCESS_CACHE_FILESYSTEM" > diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index b5eba090691f..f83fb3f312e9 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -169,7 +169,7 @@ applications that come with the platform <permission name="android.permission.START_ACTIVITIES_FROM_BACKGROUND"/> <permission name="android.permission.STATUS_BAR"/> <permission name="android.permission.STOP_APP_SWITCHES"/> - <permission name="android.permission.SUGGEST_PHONE_TIME_AND_ZONE"/> + <permission name="android.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE"/> <permission name="android.permission.UPDATE_APP_OPS_STATS"/> <permission name="android.permission.UPDATE_DEVICE_STATS"/> <permission name="android.permission.UPDATE_LOCK"/> @@ -366,6 +366,10 @@ applications that come with the platform <permission name="android.permission.CONTROL_INCALL_EXPERIENCE"/> <!-- Permission required for Tethering CTS tests. --> <permission name="android.permission.TETHER_PRIVILEGED"/> + <!-- Permissions required for ganting and logging --> + <permission name="android.permission.LOG_COMPAT_CHANGE" /> + <permission name="android.permission.READ_COMPAT_CHANGE_CONFIG" /> + <permission name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG" /> <!-- Permissions required to test ambient display. --> <permission name="android.permission.READ_DREAM_STATE" /> <permission name="android.permission.WRITE_DREAM_STATE" /> diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java index 3b864139cf71..d08bfcf45a5c 100644 --- a/graphics/java/android/graphics/HardwareRenderer.java +++ b/graphics/java/android/graphics/HardwareRenderer.java @@ -157,7 +157,7 @@ public class HardwareRenderer { public HardwareRenderer() { mRootNode = RenderNode.adopt(nCreateRootRenderNode()); mRootNode.setClipToBounds(false); - mNativeProxy = nCreateProxy(!mOpaque, mRootNode.mNativeRenderNode); + mNativeProxy = nCreateProxy(!mOpaque, mIsWideGamut, mRootNode.mNativeRenderNode); if (mNativeProxy == 0) { throw new OutOfMemoryError("Unable to create hardware renderer"); } @@ -1085,7 +1085,8 @@ public class HardwareRenderer { private static native long nCreateRootRenderNode(); - private static native long nCreateProxy(boolean translucent, long rootRenderNode); + private static native long nCreateProxy(boolean translucent, boolean isWideGamut, + long rootRenderNode); private static native void nDeleteProxy(long nativeProxy); diff --git a/libs/hwui/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp index 3681c69e912b..a3d552faeb0a 100644 --- a/libs/hwui/HardwareBitmapUploader.cpp +++ b/libs/hwui/HardwareBitmapUploader.cpp @@ -187,7 +187,9 @@ private: EGLSyncKHR fence = mUploadThread->queue().runSync([&]() -> EGLSyncKHR { AutoSkiaGlTexture glTexture; glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, autoImage.image); - GL_CHECKPOINT(MODERATE); + if (GLUtils::dumpGLErrors()) { + return EGL_NO_SYNC_KHR; + } // glTexSubImage2D is synchronous in sense that it memcpy() from pointer that we // provide. @@ -195,19 +197,26 @@ private: // when we first use it in drawing glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bitmap.width(), bitmap.height(), format.format, format.type, bitmap.getPixels()); - GL_CHECKPOINT(MODERATE); + if (GLUtils::dumpGLErrors()) { + return EGL_NO_SYNC_KHR; + } EGLSyncKHR uploadFence = eglCreateSyncKHR(eglGetCurrentDisplay(), EGL_SYNC_FENCE_KHR, NULL); - LOG_ALWAYS_FATAL_IF(uploadFence == EGL_NO_SYNC_KHR, - "Could not create sync fence %#x", eglGetError()); + if (uploadFence == EGL_NO_SYNC_KHR) { + ALOGW("Could not create sync fence %#x", eglGetError()); + }; glFlush(); + GLUtils::dumpGLErrors(); return uploadFence; }); + if (fence == EGL_NO_SYNC_KHR) { + return false; + } EGLint waitStatus = eglClientWaitSyncKHR(display, fence, 0, FENCE_TIMEOUT); - LOG_ALWAYS_FATAL_IF(waitStatus != EGL_CONDITION_SATISFIED_KHR, - "Failed to wait for the fence %#x", eglGetError()); + ALOGE_IF(waitStatus != EGL_CONDITION_SATISFIED_KHR, + "Failed to wait for the fence %#x", eglGetError()); eglDestroySyncKHR(display, fence); } diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp index e7efe2ff798b..8d5acc631274 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp @@ -171,17 +171,15 @@ static void setBufferCount(ANativeWindow* window, uint32_t extraBuffers) { } bool SkiaOpenGLPipeline::setSurface(ANativeWindow* surface, SwapBehavior swapBehavior, - ColorMode colorMode, uint32_t extraBuffers) { + uint32_t extraBuffers) { if (mEglSurface != EGL_NO_SURFACE) { mEglManager.destroySurface(mEglSurface); mEglSurface = EGL_NO_SURFACE; } - setSurfaceColorProperties(colorMode); - if (surface) { mRenderThread.requireGlContext(); - auto newSurface = mEglManager.createSurface(surface, colorMode, mSurfaceColorSpace); + auto newSurface = mEglManager.createSurface(surface, mColorMode, mSurfaceColorSpace); if (!newSurface) { return false; } diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h index 3fe0f92b1924..e482cad6c953 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h @@ -44,7 +44,7 @@ public: FrameInfo* currentFrameInfo, bool* requireSwap) override; DeferredLayerUpdater* createTextureLayer() override; bool setSurface(ANativeWindow* surface, renderthread::SwapBehavior swapBehavior, - renderthread::ColorMode colorMode, uint32_t extraBuffers) override; + uint32_t extraBuffers) override; void onStop() override; bool isSurfaceReady() override; bool isContextReady() override; diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp index 6f4af3d26b3a..29b4dd7f32e7 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp @@ -44,6 +44,7 @@ namespace uirenderer { namespace skiapipeline { SkiaPipeline::SkiaPipeline(RenderThread& thread) : mRenderThread(thread) { + setSurfaceColorProperties(mColorMode); } SkiaPipeline::~SkiaPipeline() { @@ -584,6 +585,7 @@ void SkiaPipeline::dumpResourceCacheUsage() const { } void SkiaPipeline::setSurfaceColorProperties(ColorMode colorMode) { + mColorMode = colorMode; if (colorMode == ColorMode::SRGB) { mSurfaceColorType = SkColorType::kN32_SkColorType; mSurfaceColorSpace = SkColorSpace::MakeSRGB(); diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h index af8414de4bc1..8341164edc19 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaPipeline.h @@ -50,6 +50,7 @@ public: bool createOrUpdateLayer(RenderNode* node, const DamageAccumulator& damageAccumulator, ErrorHandler* errorHandler) override; + void setSurfaceColorProperties(renderthread::ColorMode colorMode) override; SkColorType getSurfaceColorType() const override { return mSurfaceColorType; } sk_sp<SkColorSpace> getSurfaceColorSpace() override { return mSurfaceColorSpace; } @@ -72,9 +73,10 @@ public: protected: void dumpResourceCacheUsage() const; - void setSurfaceColorProperties(renderthread::ColorMode colorMode); renderthread::RenderThread& mRenderThread; + + renderthread::ColorMode mColorMode = renderthread::ColorMode::SRGB; SkColorType mSurfaceColorType; sk_sp<SkColorSpace> mSurfaceColorSpace; diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp index ad7c70614239..535a19956e03 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp @@ -117,17 +117,16 @@ DeferredLayerUpdater* SkiaVulkanPipeline::createTextureLayer() { void SkiaVulkanPipeline::onStop() {} bool SkiaVulkanPipeline::setSurface(ANativeWindow* surface, SwapBehavior swapBehavior, - ColorMode colorMode, uint32_t extraBuffers) { + uint32_t extraBuffers) { if (mVkSurface) { mVkManager.destroySurface(mVkSurface); mVkSurface = nullptr; } - setSurfaceColorProperties(colorMode); if (surface) { mRenderThread.requireVkContext(); mVkSurface = - mVkManager.createSurface(surface, colorMode, mSurfaceColorSpace, mSurfaceColorType, + mVkManager.createSurface(surface, mColorMode, mSurfaceColorSpace, mSurfaceColorType, mRenderThread.getGrContext(), extraBuffers); } diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h index 31734783de7f..c8bf233d8e1c 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h @@ -43,7 +43,7 @@ public: FrameInfo* currentFrameInfo, bool* requireSwap) override; DeferredLayerUpdater* createTextureLayer() override; bool setSurface(ANativeWindow* surface, renderthread::SwapBehavior swapBehavior, - renderthread::ColorMode colorMode, uint32_t extraBuffers) override; + uint32_t extraBuffers) override; void onStop() override; bool isSurfaceReady() override; bool isContextReady() override; diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 0b5e005ea19f..91f9447a3d59 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -161,9 +161,8 @@ void CanvasContext::setSurface(sp<Surface>&& surface, bool enableTimeout) { mRenderAheadCapacity = mRenderAheadDepth; } - ColorMode colorMode = mWideColorGamut ? ColorMode::WideColorGamut : ColorMode::SRGB; bool hasSurface = mRenderPipeline->setSurface( - mNativeSurface ? mNativeSurface->getNativeWindow() : nullptr, mSwapBehavior, colorMode, + mNativeSurface ? mNativeSurface->getNativeWindow() : nullptr, mSwapBehavior, mRenderAheadCapacity); mFrameNumber = -1; @@ -174,7 +173,7 @@ void CanvasContext::setSurface(sp<Surface>&& surface, bool enableTimeout) { // Enable frame stats after the surface has been bound to the appropriate graphics API. // Order is important when new and old surfaces are the same, because old surface has // its frame stats disabled automatically. - mNativeSurface->enableFrameTimestamps(true); + native_window_enable_frame_timestamps(mNativeSurface->getNativeWindow(), true); } else { mRenderThread.removeFrameCallback(this); mGenerationID++; @@ -225,7 +224,8 @@ void CanvasContext::setOpaque(bool opaque) { } void CanvasContext::setWideGamut(bool wideGamut) { - mWideColorGamut = wideGamut; + ColorMode colorMode = wideGamut ? ColorMode::WideColorGamut : ColorMode::SRGB; + mRenderPipeline->setSurfaceColorProperties(colorMode); } bool CanvasContext::makeCurrent() { @@ -556,8 +556,9 @@ void CanvasContext::draw() { FrameInfo* forthBehind = mLast4FrameInfos.front().first; int64_t composedFrameId = mLast4FrameInfos.front().second; nsecs_t acquireTime = -1; - mNativeSurface->getFrameTimestamps(composedFrameId, nullptr, &acquireTime, nullptr, nullptr, - nullptr, nullptr, nullptr, nullptr, nullptr); + native_window_get_frame_timestamps(mNativeSurface->getNativeWindow(), composedFrameId, + nullptr, &acquireTime, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr); // Ignore default -1, NATIVE_WINDOW_TIMESTAMP_INVALID and NATIVE_WINDOW_TIMESTAMP_PENDING forthBehind->set(FrameInfoIndex::GpuCompleted) = acquireTime > 0 ? acquireTime : -1; mJankTracker.finishGpuDraw(*forthBehind); diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index 0967b20e44ee..629c741e8757 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -251,7 +251,6 @@ private: nsecs_t mLastDropVsync = 0; bool mOpaque; - bool mWideColorGamut = false; bool mUseForceDark = false; LightInfo mLightInfo; LightGeometry mLightGeometry = {{0, 0, 0}, 0}; diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h index ef0aa98d4220..ba0d64c5492d 100644 --- a/libs/hwui/renderthread/IRenderPipeline.h +++ b/libs/hwui/renderthread/IRenderPipeline.h @@ -66,7 +66,7 @@ public: virtual bool swapBuffers(const Frame& frame, bool drew, const SkRect& screenDirty, FrameInfo* currentFrameInfo, bool* requireSwap) = 0; virtual DeferredLayerUpdater* createTextureLayer() = 0; - virtual bool setSurface(ANativeWindow* window, SwapBehavior swapBehavior, ColorMode colorMode, + virtual bool setSurface(ANativeWindow* window, SwapBehavior swapBehavior, uint32_t extraBuffers) = 0; virtual void onStop() = 0; virtual bool isSurfaceReady() = 0; @@ -80,6 +80,8 @@ public: virtual bool pinImages(std::vector<SkImage*>& mutableImages) = 0; virtual bool pinImages(LsaVector<sk_sp<Bitmap>>& images) = 0; virtual void unpinImages() = 0; + + virtual void setSurfaceColorProperties(ColorMode colorMode) = 0; virtual SkColorType getSurfaceColorType() const = 0; virtual sk_sp<SkColorSpace> getSurfaceColorSpace() = 0; virtual GrSurfaceOrigin getSurfaceOrigin() = 0; diff --git a/libs/hwui/renderthread/ReliableSurface.h b/libs/hwui/renderthread/ReliableSurface.h index da5097ce33f0..e3cd8c019a23 100644 --- a/libs/hwui/renderthread/ReliableSurface.h +++ b/libs/hwui/renderthread/ReliableSurface.h @@ -49,21 +49,6 @@ public: return ret; } - status_t getFrameTimestamps(uint64_t frameNumber, - nsecs_t* outRequestedPresentTime, nsecs_t* outAcquireTime, - nsecs_t* outLatchTime, nsecs_t* outFirstRefreshStartTime, - nsecs_t* outLastRefreshStartTime, nsecs_t* outGlCompositionDoneTime, - nsecs_t* outDisplayPresentTime, nsecs_t* outDequeueReadyTime, - nsecs_t* outReleaseTime) { - return mSurface->getFrameTimestamps(frameNumber, outRequestedPresentTime, outAcquireTime, - outLatchTime, outFirstRefreshStartTime, outLastRefreshStartTime, - outGlCompositionDoneTime, outDisplayPresentTime, outDequeueReadyTime, outReleaseTime); - } - - void enableFrameTimestamps(bool enable) { - return mSurface->enableFrameTimestamps(enable); - } - private: sp<Surface> mSurface; diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp index 307d13606cb8..90bcd1c0e370 100644 --- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp +++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp @@ -398,7 +398,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, context_lost) { auto surface = context.surface(); auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread); EXPECT_FALSE(pipeline->isSurfaceReady()); - EXPECT_TRUE(pipeline->setSurface(surface.get(), SwapBehavior::kSwap_default, ColorMode::SRGB, 0)); + EXPECT_TRUE(pipeline->setSurface(surface.get(), SwapBehavior::kSwap_default, 0)); EXPECT_TRUE(pipeline->isSurfaceReady()); renderThread.destroyRenderingContext(); EXPECT_FALSE(pipeline->isSurfaceReady()); diff --git a/libs/incident/Android.bp b/libs/incident/Android.bp index 150f6dcde5d0..512b8c439dcf 100644 --- a/libs/incident/Android.bp +++ b/libs/incident/Android.bp @@ -12,8 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -cc_library_shared { - name: "libincident", + +cc_defaults { + name: "libincidentpriv_defaults", cflags: [ "-Wall", @@ -50,6 +51,70 @@ cc_library_shared { ":libincident_aidl", "src/IncidentReportArgs.cpp", ], +} + +cc_library_shared { + name: "libincidentpriv", + defaults: ["libincidentpriv_defaults"], + export_include_dirs: ["include_priv"], +} + +cc_library_shared { + name: "libincident", + + cflags: [ + "-Wall", + "-Werror", + "-Wno-missing-field-initializers", + "-Wno-unused-variable", + "-Wunused-parameter", + ], + + shared_libs: [ + "libbinder", + "liblog", + "libutils", + "libincidentpriv", + ], + + srcs: [ + "src/incident_report.cpp", + ], export_include_dirs: ["include"], + + stubs: { + symbol_file: "libincident.map.txt", + versions: [ + "30", + ], + }, } + +cc_test { + name: "libincident_test", + defaults: ["libincidentpriv_defaults"], + test_suites: ["device-tests"], + + include_dirs: [ + "frameworks/base/libs/incident/include", + "frameworks/base/libs/incident/include_priv", + ], + + srcs: [ + "tests/IncidentReportArgs_test.cpp", + "tests/IncidentReportRequest_test.cpp", + "tests/c_api_compile_test.c", + ], + + shared_libs: [ + "libincident", + ], + + static_libs: [ + "libgmock", + ], +} + + + diff --git a/libs/incident/include/incident/incident_report.h b/libs/incident/include/incident/incident_report.h new file mode 100644 index 000000000000..49fe5b9b73b4 --- /dev/null +++ b/libs/incident/include/incident/incident_report.h @@ -0,0 +1,192 @@ +/** + * Copyright (c) 2020, 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. + */ + +/** + * @file incident_report.h + */ + +#ifndef ANDROID_INCIDENT_INCIDENT_REPORT_H +#define ANDROID_INCIDENT_INCIDENT_REPORT_H + +#include <stdbool.h> + +#if __cplusplus +#include <set> +#include <string> +#include <vector> + +extern "C" { +#endif // __cplusplus + +struct AIncidentReportArgs; +/** + * Opaque class to represent the arguments to an incident report request. + * Incident reports contain debugging data about the device at runtime. + * For more information see the android.os.IncidentManager java class. + */ +typedef struct AIncidentReportArgs AIncidentReportArgs; + +// Privacy policy enum value, sync with frameworks/base/core/proto/android/privacy.proto, +// IncidentReportArgs.h and IncidentReportArgs.java. +enum { + /** + * Flag marking fields and incident reports than can be taken + * off the device only via adb. + */ + INCIDENT_REPORT_PRIVACY_POLICY_LOCAL = 0, + + /** + * Flag marking fields and incident reports than can be taken + * off the device with contemporary consent. + */ + INCIDENT_REPORT_PRIVACY_POLICY_EXPLICIT = 100, + + /** + * Flag marking fields and incident reports than can be taken + * off the device with prior consent. + */ + INCIDENT_REPORT_PRIVACY_POLICY_AUTOMATIC = 200, + + /** + * Flag to indicate that a given field has not been marked + * with a privacy policy. + */ + INCIDENT_REPORT_PRIVACY_POLICY_UNSET = 255 +}; + +/** + * Allocate and initialize an AIncidentReportArgs object. + */ +AIncidentReportArgs* AIncidentReportArgs_init(); + +/** + * Duplicate an existing AIncidentReportArgs object. + */ +AIncidentReportArgs* AIncidentReportArgs_clone(AIncidentReportArgs* that); + +/** + * Clean up and delete an AIncidentReportArgs object. + */ +void AIncidentReportArgs_delete(AIncidentReportArgs* args); + +/** + * Set this incident report to include all sections. + */ +void AIncidentReportArgs_setAll(AIncidentReportArgs* args, bool all); + +/** + * Set this incident report privacy policy spec. + */ +void AIncidentReportArgs_setPrivacyPolicy(AIncidentReportArgs* args, int privacyPolicy); + +/** + * Add this section to the incident report. The section IDs are the field numbers + * from the android.os.IncidentProto protobuf message. + */ +void AIncidentReportArgs_addSection(AIncidentReportArgs* args, int section); + +/** + * Set the apk package name that will be sent a broadcast when the incident + * report completes. Must be called in conjunction with AIncidentReportArgs_setReceiverClass. + */ +void AIncidentReportArgs_setReceiverPackage(AIncidentReportArgs* args, char const* pkg); + +/** + * Set the fully qualified class name of the java BroadcastReceiver class that will be + * sent a broadcast when the report completes. Must be called in conjunction with + * AIncidentReportArgs_setReceiverPackage. + */ +void AIncidentReportArgs_setReceiverClass(AIncidentReportArgs* args, char const* cls); + +/** + * Add protobuf data as a header to the incident report. The buffer should be a serialized + * android.os.IncidentHeaderProto object. + */ +void AIncidentReportArgs_addHeader(AIncidentReportArgs* args, uint8_t const* buf, size_t size); + +/** + * Initiate taking the report described in the args object. Returns 0 on success, + * and non-zero otherwise. + */ +int AIncidentReportArgs_takeReport(AIncidentReportArgs* args); + +#if __cplusplus +} // extern "C" + +namespace android { +namespace os { + +class IncidentReportRequest { +public: + inline IncidentReportRequest() { + mImpl = AIncidentReportArgs_init(); + } + + inline IncidentReportRequest(const IncidentReportRequest& that) { + mImpl = AIncidentReportArgs_clone(that.mImpl); + } + + inline ~IncidentReportRequest() { + AIncidentReportArgs_delete(mImpl); + } + + inline AIncidentReportArgs* getImpl() { + return mImpl; + } + + inline void setAll(bool all) { + AIncidentReportArgs_setAll(mImpl, all); + } + + inline void setPrivacyPolicy(int privacyPolicy) { + AIncidentReportArgs_setPrivacyPolicy(mImpl, privacyPolicy); + } + + inline void addSection(int section) { + AIncidentReportArgs_addSection(mImpl, section); + } + + inline void setReceiverPackage(const std::string& pkg) { + AIncidentReportArgs_setReceiverPackage(mImpl, pkg.c_str()); + }; + + inline void setReceiverClass(const std::string& cls) { + AIncidentReportArgs_setReceiverClass(mImpl, cls.c_str()); + }; + + inline void addHeader(const std::vector<uint8_t>& headerProto) { + AIncidentReportArgs_addHeader(mImpl, headerProto.data(), headerProto.size()); + }; + + inline void addHeader(const uint8_t* buf, size_t size) { + AIncidentReportArgs_addHeader(mImpl, buf, size); + }; + + // returns a status_t + inline int takeReport() { + return AIncidentReportArgs_takeReport(mImpl); + } + +private: + AIncidentReportArgs* mImpl; +}; + +} // namespace os +} // namespace android + +#endif // __cplusplus + +#endif // ANDROID_INCIDENT_INCIDENT_REPORT_H diff --git a/libs/incident/include/android/os/IncidentReportArgs.h b/libs/incident/include_priv/android/os/IncidentReportArgs.h index 94b4ad6eae31..0e6159032e45 100644 --- a/libs/incident/include/android/os/IncidentReportArgs.h +++ b/libs/incident/include_priv/android/os/IncidentReportArgs.h @@ -14,9 +14,10 @@ * limitations under the License. */ -#ifndef ANDROID_OS_DUMPSTATE_ARGS_H_ -#define ANDROID_OS_DUMPSTATE_ARGS_H_ +#ifndef ANDROID_OS_INCIDENT_REPORT_ARGS_H +#define ANDROID_OS_INCIDENT_REPORT_ARGS_H +#include <binder/IServiceManager.h> #include <binder/Parcel.h> #include <binder/Parcelable.h> #include <utils/String16.h> @@ -29,7 +30,8 @@ namespace os { using namespace std; -// DESTINATION enum value, sync with frameworks/base/core/proto/android/privacy.proto +// DESTINATION enum value, sync with frameworks/base/core/proto/android/privacy.proto, +// incident/incident_report.h and IncidentReportArgs.java const uint8_t PRIVACY_POLICY_LOCAL = 0; const uint8_t PRIVACY_POLICY_EXPLICIT = 100; const uint8_t PRIVACY_POLICY_AUTOMATIC = 200; @@ -74,4 +76,4 @@ private: } } -#endif // ANDROID_OS_DUMPSTATE_ARGS_H_ +#endif // ANDROID_OS_INCIDENT_REPORT_ARGS_H diff --git a/libs/incident/libincident.map.txt b/libs/incident/libincident.map.txt new file mode 100644 index 000000000000..f157763f1a03 --- /dev/null +++ b/libs/incident/libincident.map.txt @@ -0,0 +1,15 @@ +LIBINCIDENT { + global: + AIncidentReportArgs_init; # apex # introduced=30 + AIncidentReportArgs_clone; # apex # introduced=30 + AIncidentReportArgs_delete; # apex # introduced=30 + AIncidentReportArgs_setAll; # apex # introduced=30 + AIncidentReportArgs_setPrivacyPolicy; # apex # introduced=30 + AIncidentReportArgs_addSection; # apex # introduced=30 + AIncidentReportArgs_setReceiverPackage; # apex # introduced=30 + AIncidentReportArgs_setReceiverClass; # apex # introduced=30 + AIncidentReportArgs_addHeader; # apex # introduced=30 + AIncidentReportArgs_takeReport; # apex # introduced=30 + local: + *; +}; diff --git a/libs/incident/src/incident_report.cpp b/libs/incident/src/incident_report.cpp new file mode 100644 index 000000000000..7897ddf6d251 --- /dev/null +++ b/libs/incident/src/incident_report.cpp @@ -0,0 +1,83 @@ +/** + * Copyright (c) 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "libincident" + +#include <incident/incident_report.h> + +#include <android/os/IIncidentManager.h> +#include <android/os/IncidentReportArgs.h> +#include <binder/IServiceManager.h> +#include <binder/Status.h> +#include <log/log.h> + +using android::sp; +using android::binder::Status; +using android::os::IncidentReportArgs; +using android::os::IIncidentManager; +using std::string; +using std::vector; + +AIncidentReportArgs* AIncidentReportArgs_init() { + return reinterpret_cast<AIncidentReportArgs*>(new IncidentReportArgs()); +} + +AIncidentReportArgs* AIncidentReportArgs_clone(AIncidentReportArgs* that) { + return reinterpret_cast<AIncidentReportArgs*>( + new IncidentReportArgs(*reinterpret_cast<IncidentReportArgs*>(that))); +} + +void AIncidentReportArgs_delete(AIncidentReportArgs* args) { + delete reinterpret_cast<IncidentReportArgs*>(args); +} + +void AIncidentReportArgs_setAll(AIncidentReportArgs* args, bool all) { + reinterpret_cast<IncidentReportArgs*>(args)->setAll(all); +} + +void AIncidentReportArgs_setPrivacyPolicy(AIncidentReportArgs* args, int privacyPolicy) { + reinterpret_cast<IncidentReportArgs*>(args)->setPrivacyPolicy(privacyPolicy); +} + +void AIncidentReportArgs_addSection(AIncidentReportArgs* args, int section) { + reinterpret_cast<IncidentReportArgs*>(args)->addSection(section); +} + +void AIncidentReportArgs_setReceiverPackage(AIncidentReportArgs* args, char const* pkg) { + reinterpret_cast<IncidentReportArgs*>(args)->setReceiverPkg(string(pkg)); +} + +void AIncidentReportArgs_setReceiverClass(AIncidentReportArgs* args, char const* cls) { + reinterpret_cast<IncidentReportArgs*>(args)->setReceiverCls(string(cls)); +} + +void AIncidentReportArgs_addHeader(AIncidentReportArgs* args, uint8_t const* buf, size_t size) { + vector<uint8_t> vec(buf, buf+size); + reinterpret_cast<IncidentReportArgs*>(args)->addHeader(vec); +} + +int AIncidentReportArgs_takeReport(AIncidentReportArgs* argp) { + IncidentReportArgs* args = reinterpret_cast<IncidentReportArgs*>(argp); + + sp<IIncidentManager> service = android::interface_cast<IIncidentManager>( + android::defaultServiceManager()->getService(android::String16("incident"))); + if (service == nullptr) { + ALOGW("Failed to fetch incident service."); + return false; + } + Status s = service->reportIncident(*args); + return s.transactionError(); +} diff --git a/cmds/statsd/tests/external/IncidentReportArgs_test.cpp b/libs/incident/tests/IncidentReportArgs_test.cpp index 38bc19452afa..224b343c554a 100644 --- a/cmds/statsd/tests/external/IncidentReportArgs_test.cpp +++ b/libs/incident/tests/IncidentReportArgs_test.cpp @@ -20,6 +20,8 @@ namespace android { namespace os { namespace statsd { +// Checks that all of the inline methods on IncidentReportRequest and the real C functions +// result in a working IncidentReportArgs. TEST(IncidentReportArgsTest, testSerialization) { IncidentReportArgs args; args.setAll(0); diff --git a/libs/incident/tests/IncidentReportRequest_test.cpp b/libs/incident/tests/IncidentReportRequest_test.cpp new file mode 100644 index 000000000000..6d218b6682a3 --- /dev/null +++ b/libs/incident/tests/IncidentReportRequest_test.cpp @@ -0,0 +1,65 @@ +// Copyright (C) 2018 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include <android/os/IncidentReportArgs.h> +#include <incident/incident_report.h> + +#include <gtest/gtest.h> + +namespace android { +namespace os { +namespace statsd { + +TEST(IncidentReportRequestTest, testWrite) { + IncidentReportRequest request; + request.setAll(0); + request.addSection(1000); + request.addSection(1001); + + vector<uint8_t> header1; + header1.push_back(0x1); + header1.push_back(0x2); + vector<uint8_t> header2; + header1.push_back(0x22); + header1.push_back(0x33); + + request.addHeader(header1); + request.addHeader(header2); + + request.setPrivacyPolicy(1); + + request.setReceiverPackage("com.android.os"); + request.setReceiverClass("com.android.os.Receiver"); + + IncidentReportArgs* args = reinterpret_cast<IncidentReportArgs*>(request.getImpl()); + + EXPECT_EQ(0, args->all()); + set<int> sections; + sections.insert(1000); + sections.insert(1001); + EXPECT_EQ(sections, args->sections()); + EXPECT_EQ(1, args->getPrivacyPolicy()); + + EXPECT_EQ(string("com.android.os"), args->receiverPkg()); + EXPECT_EQ(string("com.android.os.Receiver"), args->receiverCls()); + + vector<vector<uint8_t>> headers; + headers.push_back(header1); + headers.push_back(header2); + EXPECT_EQ(headers, args->headers()); +} + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/libs/incident/tests/c_api_compile_test.c b/libs/incident/tests/c_api_compile_test.c new file mode 100644 index 000000000000..e1620dfe3280 --- /dev/null +++ b/libs/incident/tests/c_api_compile_test.c @@ -0,0 +1,11 @@ +#include <stdio.h> +#include <incident/incident_report.h> + +/* + * This file ensures that incident/incident_report.h actually compiles with C, + * since there is no other place in the tree that actually uses it from C. + */ +int not_called() { + return 0; +} + diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index 197787e5b6e6..1c10edb11293 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -33,6 +33,9 @@ import android.annotation.SystemService; import android.annotation.TestApi; import android.app.AlarmManager; import android.app.PendingIntent; +import android.compat.Compatibility; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledAfter; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.pm.PackageManager; @@ -50,7 +53,6 @@ import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; import android.util.ArrayMap; -import android.util.Log; import com.android.internal.annotations.GuardedBy; import com.android.internal.location.ProviderProperties; @@ -82,6 +84,36 @@ public class LocationManager { private static final String TAG = "LocationManager"; /** + * For apps targeting Android K and above, supplied {@link PendingIntent}s must be targeted to a + * specific package. + * + * @hide + */ + @ChangeId + @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.JELLY_BEAN) + public static final long TARGETED_PENDING_INTENT = 148963590L; + + /** + * For apps targeting Android K and above, incomplete locations may not be passed to + * {@link #setTestProviderLocation}. + * + * @hide + */ + @ChangeId + @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.JELLY_BEAN) + private static final long INCOMPLETE_LOCATION = 148964793L; + + /** + * For apps targeting Android S and above, all {@link GpsStatus} API usage must be replaced with + * {@link GnssStatus} APIs. + * + * @hide + */ + @ChangeId + @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R) + private static final long GPS_STATUS_USAGE = 144027538L; + + /** * Name of the network location provider. * * <p>This provider determines location based on nearby of cell tower and WiFi access points. @@ -771,7 +803,6 @@ public class LocationManager { public void requestSingleUpdate(@NonNull String provider, @NonNull PendingIntent pendingIntent) { Preconditions.checkArgument(provider != null, "invalid null provider"); - checkPendingIntent(pendingIntent); LocationRequest request = LocationRequest.createFromDeprecatedProvider( provider, 0, 0, true); @@ -800,7 +831,6 @@ public class LocationManager { public void requestSingleUpdate(@NonNull Criteria criteria, @NonNull PendingIntent pendingIntent) { Preconditions.checkArgument(criteria != null, "invalid null criteria"); - checkPendingIntent(pendingIntent); LocationRequest request = LocationRequest.createFromDeprecatedCriteria( criteria, 0, 0, true); @@ -1021,7 +1051,6 @@ public class LocationManager { public void requestLocationUpdates(@NonNull String provider, long minTimeMs, float minDistanceM, @NonNull PendingIntent pendingIntent) { Preconditions.checkArgument(provider != null, "invalid null provider"); - checkPendingIntent(pendingIntent); LocationRequest request = LocationRequest.createFromDeprecatedProvider( provider, minTimeMs, minDistanceM, false); @@ -1048,7 +1077,6 @@ public class LocationManager { public void requestLocationUpdates(long minTimeMs, float minDistanceM, @NonNull Criteria criteria, @NonNull PendingIntent pendingIntent) { Preconditions.checkArgument(criteria != null, "invalid null criteria"); - checkPendingIntent(pendingIntent); LocationRequest request = LocationRequest.createFromDeprecatedCriteria( criteria, minTimeMs, minDistanceM, false); @@ -1164,9 +1192,9 @@ public class LocationManager { @NonNull PendingIntent pendingIntent) { Preconditions.checkArgument(locationRequest != null, "invalid null location request"); Preconditions.checkArgument(pendingIntent != null, "invalid null pending intent"); - if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.JELLY_BEAN) { + if (Compatibility.isChangeEnabled(TARGETED_PENDING_INTENT)) { Preconditions.checkArgument(pendingIntent.isTargetedToPackage(), - "pending intent must be targeted to package"); + "pending intent must be targeted to a package"); } try { @@ -1198,15 +1226,9 @@ public class LocationManager { */ @RequiresPermission(allOf = {LOCATION_HARDWARE, ACCESS_FINE_LOCATION}) public boolean injectLocation(@NonNull Location location) { - if (location == null) { - IllegalArgumentException e = new IllegalArgumentException("invalid null location"); - if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R) { - throw e; - } else { - Log.w(TAG, e); - return false; - } - } + Preconditions.checkArgument(location != null, "invalid null location"); + Preconditions.checkArgument(location.isComplete(), + "incomplete location object, missing timestamp or accuracy?"); try { return mService.injectLocation(location); @@ -1487,15 +1509,11 @@ public class LocationManager { Preconditions.checkArgument(provider != null, "invalid null provider"); Preconditions.checkArgument(location != null, "invalid null location"); - if (!location.isComplete()) { - IllegalArgumentException e = new IllegalArgumentException( - "Incomplete location object, missing timestamp or accuracy? " + location); - if (mContext.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.JELLY_BEAN) { - Log.w(TAG, e); - location.makeComplete(); - } else { - throw e; - } + if (Compatibility.isChangeEnabled(INCOMPLETE_LOCATION)) { + Preconditions.checkArgument(location.isComplete(), + "incomplete location object, missing timestamp or accuracy?"); + } else { + location.makeComplete(); } try { @@ -1629,7 +1647,11 @@ public class LocationManager { @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION}) public void addProximityAlert(double latitude, double longitude, float radius, long expiration, @NonNull PendingIntent intent) { - checkPendingIntent(intent); + Preconditions.checkArgument(intent != null, "invalid null pending intent"); + if (Compatibility.isChangeEnabled(TARGETED_PENDING_INTENT)) { + Preconditions.checkArgument(intent.isTargetedToPackage(), + "pending intent must be targeted to a package"); + } if (expiration < 0) expiration = Long.MAX_VALUE; Geofence fence = Geofence.createCircle(latitude, longitude, radius); @@ -1659,7 +1681,11 @@ public class LocationManager { * permission is not present */ public void removeProximityAlert(@NonNull PendingIntent intent) { - checkPendingIntent(intent); + Preconditions.checkArgument(intent != null, "invalid null pending intent"); + if (Compatibility.isChangeEnabled(TARGETED_PENDING_INTENT)) { + Preconditions.checkArgument(intent.isTargetedToPackage(), + "pending intent must be targeted to a package"); + } try { mService.removeGeofence(null, intent, mContext.getPackageName()); @@ -1709,8 +1735,13 @@ public class LocationManager { @NonNull LocationRequest request, @NonNull Geofence fence, @NonNull PendingIntent intent) { - checkPendingIntent(intent); + Preconditions.checkArgument(request != null, "invalid null location request"); Preconditions.checkArgument(fence != null, "invalid null geofence"); + Preconditions.checkArgument(intent != null, "invalid null pending intent"); + if (Compatibility.isChangeEnabled(TARGETED_PENDING_INTENT)) { + Preconditions.checkArgument(intent.isTargetedToPackage(), + "pending intent must be targeted to a package"); + } try { mService.requestGeofence(request, fence, intent, mContext.getPackageName(), @@ -1737,8 +1768,12 @@ public class LocationManager { * @hide */ public void removeGeofence(@NonNull Geofence fence, @NonNull PendingIntent intent) { - checkPendingIntent(intent); Preconditions.checkArgument(fence != null, "invalid null geofence"); + Preconditions.checkArgument(intent != null, "invalid null pending intent"); + if (Compatibility.isChangeEnabled(TARGETED_PENDING_INTENT)) { + Preconditions.checkArgument(intent.isTargetedToPackage(), + "pending intent must be targeted to a package"); + } try { mService.removeGeofence(fence, intent, mContext.getPackageName()); @@ -1759,7 +1794,11 @@ public class LocationManager { * @hide */ public void removeAllGeofences(@NonNull PendingIntent intent) { - checkPendingIntent(intent); + Preconditions.checkArgument(intent != null, "invalid null pending intent"); + if (Compatibility.isChangeEnabled(TARGETED_PENDING_INTENT)) { + Preconditions.checkArgument(intent.isTargetedToPackage(), + "pending intent must be targeted to a package"); + } try { mService.removeGeofence(null, intent, mContext.getPackageName()); @@ -1833,14 +1872,15 @@ public class LocationManager { * @param status object containing GPS status details, or null. * @return status object containing updated GPS status. * - * @deprecated GpsStatus APIs are deprecated, use {@link GnssStatus} APIs instead. + * @deprecated GpsStatus APIs are deprecated, use {@link GnssStatus} APIs instead. No longer + * supported in apps targeting S and above. */ @Deprecated @RequiresPermission(ACCESS_FINE_LOCATION) public @Nullable GpsStatus getGpsStatus(@Nullable GpsStatus status) { - if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.R) { + if (Compatibility.isChangeEnabled(GPS_STATUS_USAGE)) { throw new UnsupportedOperationException( - "GpsStatus APIs not supported in S and above, use GnssStatus APIs instead"); + "GpsStatus APIs not supported, please use GnssStatus APIs instead"); } GnssStatus gnssStatus = mGnssStatusListenerManager.getGnssStatus(); @@ -1863,17 +1903,14 @@ public class LocationManager { * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present * * @deprecated use {@link #registerGnssStatusCallback(GnssStatus.Callback)} instead. No longer - * supported in apps targeting R and above. + * supported in apps targeting S and above. */ @Deprecated @RequiresPermission(ACCESS_FINE_LOCATION) public boolean addGpsStatusListener(GpsStatus.Listener listener) { - UnsupportedOperationException ex = new UnsupportedOperationException( - "GpsStatus APIs not supported in S and above, use GnssStatus APIs instead"); - if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.R) { - throw ex; - } else { - Log.w(TAG, ex); + if (Compatibility.isChangeEnabled(GPS_STATUS_USAGE)) { + throw new UnsupportedOperationException( + "GpsStatus APIs not supported, please use GnssStatus APIs instead"); } try { @@ -1889,16 +1926,13 @@ public class LocationManager { * @param listener GPS status listener object to remove * * @deprecated use {@link #unregisterGnssStatusCallback(GnssStatus.Callback)} instead. No longer - * supported in apps targeting R and above. + * supported in apps targeting S and above. */ @Deprecated public void removeGpsStatusListener(GpsStatus.Listener listener) { - UnsupportedOperationException ex = new UnsupportedOperationException( - "GpsStatus APIs not supported in S and above, use GnssStatus APIs instead"); - if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.R) { - throw ex; - } else { - Log.w(TAG, ex); + if (Compatibility.isChangeEnabled(GPS_STATUS_USAGE)) { + throw new UnsupportedOperationException( + "GpsStatus APIs not supported, please use GnssStatus APIs instead"); } try { @@ -2397,19 +2431,6 @@ public class LocationManager { } } - private void checkPendingIntent(PendingIntent pendingIntent) { - Preconditions.checkArgument(pendingIntent != null, "invalid null pending intent"); - if (!pendingIntent.isTargetedToPackage()) { - IllegalArgumentException e = new IllegalArgumentException( - "invalid pending intent - must be targeted to package"); - if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.JELLY_BEAN) { - throw e; - } else { - Log.w(TAG, e); - } - } - } - private static class GetCurrentLocationTransport extends ILocationListener.Stub implements AlarmManager.OnAlarmListener { diff --git a/media/java/android/media/IMediaRoute2Provider.aidl b/media/java/android/media/IMediaRoute2Provider.aidl index 194669c19018..9131f3bc960d 100644 --- a/media/java/android/media/IMediaRoute2Provider.aidl +++ b/media/java/android/media/IMediaRoute2Provider.aidl @@ -37,5 +37,4 @@ oneway interface IMediaRoute2Provider { void notifyControlRequestSent(String id, in Intent request); void requestSetVolume(String id, int volume); - void requestUpdateVolume(String id, int delta); } diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl index dac0fba876be..6fef46889742 100644 --- a/media/java/android/media/IMediaRouterService.aidl +++ b/media/java/android/media/IMediaRouterService.aidl @@ -52,7 +52,6 @@ interface IMediaRouterService { void sendControlRequest(IMediaRouter2Client client, in MediaRoute2Info route, in Intent request); void requestSetVolume2(IMediaRouter2Client client, in MediaRoute2Info route, int volume); - void requestUpdateVolume2(IMediaRouter2Client client, in MediaRoute2Info route, int direction); void requestCreateSession(IMediaRouter2Client client, in MediaRoute2Info route, int requestId, in @nullable Bundle sessionHints); @@ -70,8 +69,6 @@ interface IMediaRouterService { void requestSetVolume2Manager(IMediaRouter2Manager manager, in MediaRoute2Info route, int volume); - void requestUpdateVolume2Manager(IMediaRouter2Manager manager, - in MediaRoute2Info route, int direction); List<RoutingSessionInfo> getActiveSessions(IMediaRouter2Manager manager); void selectClientRoute(IMediaRouter2Manager manager, diff --git a/media/java/android/media/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java index d0b5d486d19c..20a59bba54c6 100644 --- a/media/java/android/media/MediaRoute2ProviderService.java +++ b/media/java/android/media/MediaRoute2ProviderService.java @@ -46,13 +46,20 @@ import java.util.concurrent.atomic.AtomicBoolean; /** * Base class for media route provider services. * <p> + * Media route provider services are used to publish {@link MediaRoute2Info media routes} such as + * speakers, TVs, etc. The routes are published by calling {@link #notifyRoutes(Collection)}. + * Media apps which use {@link MediaRouter2} can request to play their media on the routes. + * </p><p> + * When {@link MediaRouter2 media router} wants to play media on a route, + * {@link #onCreateSession(String, String, long, Bundle)} will be called to handle the request. + * A session can be considered as a group of currently selected routes for each connection. + * Create and manage the sessions by yourself, and notify the {@link RoutingSessionInfo + * session infos} when there are any changes. + * </p><p> * The system media router service will bind to media route provider services when a * {@link RouteDiscoveryPreference discovery preference} is registered via - * a {@link MediaRouter2 media router} by an application. - * </p><p> - * To implement your own media route provider service, extend this class and - * override {@link #onDiscoveryPreferenceChanged(RouteDiscoveryPreference)} to publish - * {@link MediaRoute2Info routes}. + * a {@link MediaRouter2 media router} by an application. See + * {@link #onDiscoveryPreferenceChanged(RouteDiscoveryPreference)} for the details. * </p> */ public abstract class MediaRoute2ProviderService extends Service { @@ -118,22 +125,15 @@ public abstract class MediaRoute2ProviderService extends Service { public abstract void onControlRequest(@NonNull String routeId, @NonNull Intent request); /** - * Called when requestSetVolume is called on a route of the provider + * Called when requestSetVolume is called on a route of the provider. * * @param routeId the id of the route * @param volume the target volume + * @see MediaRoute2Info#getVolumeMax() */ public abstract void onSetVolume(@NonNull String routeId, int volume); /** - * Called when requestUpdateVolume is called on a route of the provider - * - * @param routeId id of the route - * @param delta the delta to add to the current volume - */ - public abstract void onUpdateVolume(@NonNull String routeId, int delta); - - /** * Gets information of the session with the given id. * * @param sessionId id of the session @@ -520,14 +520,5 @@ public abstract class MediaRoute2ProviderService extends Service { mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onSetVolume, MediaRoute2ProviderService.this, routeId, volume)); } - - @Override - public void requestUpdateVolume(String routeId, int delta) { - if (!checkCallerisSystem()) { - return; - } - mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onUpdateVolume, - MediaRoute2ProviderService.this, routeId, delta)); - } } } diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java index 515cabe5a5c9..d7b74df0a5e6 100644 --- a/media/java/android/media/MediaRouter2.java +++ b/media/java/android/media/MediaRouter2.java @@ -462,31 +462,6 @@ public class MediaRouter2 { } } - /** - * Requests an incremental volume update for the route asynchronously. - * <p> - * It may have no effect if the route is currently not selected. - * </p> - * - * @param delta The delta to add to the current volume. - * @hide - */ - public void requestUpdateVolume(@NonNull MediaRoute2Info route, int delta) { - Objects.requireNonNull(route, "route must not be null"); - - Client2 client; - synchronized (sRouterLock) { - client = mClient; - } - if (client != null) { - try { - mMediaRouterService.requestUpdateVolume2(client, route, delta); - } catch (RemoteException ex) { - Log.e(TAG, "Unable to send control request.", ex); - } - } - } - void addRoutesOnHandler(List<MediaRoute2Info> routes) { // TODO: When onRoutesAdded is first called, // 1) clear mRoutes before adding the routes diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java index 662eeb1418f1..b1f930d0f2cc 100644 --- a/media/java/android/media/MediaRouter2Manager.java +++ b/media/java/android/media/MediaRouter2Manager.java @@ -294,30 +294,6 @@ public class MediaRouter2Manager { } } - /** - * Requests an incremental volume update for the route asynchronously. - * <p> - * It may have no effect if the route is currently not selected. - * </p> - * - * @param delta The delta to add to the current volume. - */ - public void requestUpdateVolume(@NonNull MediaRoute2Info route, int delta) { - Objects.requireNonNull(route, "route must not be null"); - - Client client; - synchronized (sLock) { - client = mClient; - } - if (client != null) { - try { - mMediaRouterService.requestUpdateVolume2Manager(client, route, delta); - } catch (RemoteException ex) { - Log.e(TAG, "Unable to send control request.", ex); - } - } - } - void addRoutesOnHandler(List<MediaRoute2Info> routes) { synchronized (mRoutesLock) { for (MediaRoute2Info route : routes) { diff --git a/media/java/android/media/soundtrigger/SoundTriggerDetector.java b/media/java/android/media/soundtrigger/SoundTriggerDetector.java index 118f65c214e1..08953392ca18 100644 --- a/media/java/android/media/soundtrigger/SoundTriggerDetector.java +++ b/media/java/android/media/soundtrigger/SoundTriggerDetector.java @@ -113,7 +113,7 @@ public final class SoundTriggerDetector { * This capability may or may not be supported by the system, and support can be queried * by calling {@link SoundTriggerManager#getModuleProperties()} and checking * {@link ModuleProperties#audioCapabilities}. The corresponding capabilities field for - * this flag is {@link SoundTrigger.ModuleProperties#CAPABILITY_ECHO_CANCELLATION}. + * this flag is {@link SoundTrigger.ModuleProperties#AUDIO_CAPABILITY_ECHO_CANCELLATION}. * If this flag is passed without the audio capability supported, there will be no audio effect * applied. */ @@ -125,8 +125,9 @@ public final class SoundTriggerDetector { * This capability may or may not be supported by the system, and support can be queried * by calling {@link SoundTriggerManager#getModuleProperties()} and checking * {@link ModuleProperties#audioCapabilities}. The corresponding capabilities field for - * this flag is {@link SoundTrigger.ModuleProperties#CAPABILITY_NOISE_SUPPRESSION}. If this flag - * is passed without the audio capability supported, there will be no audio effect applied. + * this flag is {@link SoundTrigger.ModuleProperties#AUDIO_CAPABILITY_NOISE_SUPPRESSION}. + * If this flag is passed without the audio capability supported, there will be no audio effect + * applied. */ public static final int RECOGNITION_FLAG_ENABLE_AUDIO_NOISE_SUPPRESSION = 0x8; @@ -296,10 +297,10 @@ public final class SoundTriggerDetector { int audioCapabilities = 0; if ((recognitionFlags & RECOGNITION_FLAG_ENABLE_AUDIO_ECHO_CANCELLATION) != 0) { - audioCapabilities |= SoundTrigger.ModuleProperties.CAPABILITY_ECHO_CANCELLATION; + audioCapabilities |= SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_ECHO_CANCELLATION; } if ((recognitionFlags & RECOGNITION_FLAG_ENABLE_AUDIO_NOISE_SUPPRESSION) != 0) { - audioCapabilities |= SoundTrigger.ModuleProperties.CAPABILITY_NOISE_SUPPRESSION; + audioCapabilities |= SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_NOISE_SUPPRESSION; } int status; diff --git a/media/java/android/media/soundtrigger/SoundTriggerManager.java b/media/java/android/media/soundtrigger/SoundTriggerManager.java index dd4dac223a86..6a8483c58276 100644 --- a/media/java/android/media/soundtrigger/SoundTriggerManager.java +++ b/media/java/android/media/soundtrigger/SoundTriggerManager.java @@ -173,8 +173,13 @@ public final class SoundTriggerManager { } /** - * Factory constructor to create a SoundModel instance for use with methods in this - * class. + * Factory constructor to a voice model to be used with {@link SoundTriggerManager} + * + * @param modelUuid Unique identifier associated with the model. + * @param vendorUuid Unique identifier associated the calling vendor. + * @param data Model's data. + * @param version Version identifier for the model. + * @return Voice model */ @NonNull public static Model create(@NonNull UUID modelUuid, @NonNull UUID vendorUuid, @@ -186,8 +191,12 @@ public final class SoundTriggerManager { } /** - * Factory constructor to create a SoundModel instance for use with methods in this - * class. + * Factory constructor to a voice model to be used with {@link SoundTriggerManager} + * + * @param modelUuid Unique identifier associated with the model. + * @param vendorUuid Unique identifier associated the calling vendor. + * @param data Model's data. + * @return Voice model */ @NonNull public static Model create(@NonNull UUID modelUuid, @NonNull UUID vendorUuid, @@ -195,20 +204,40 @@ public final class SoundTriggerManager { return create(modelUuid, vendorUuid, data, -1); } + /** + * Get the model's unique identifier + * + * @return UUID associated with the model + */ @NonNull public UUID getModelUuid() { return mGenericSoundModel.uuid; } + /** + * Get the model's vendor identifier + * + * @return UUID associated with the vendor of the model + */ @NonNull public UUID getVendorUuid() { return mGenericSoundModel.vendorUuid; } + /** + * Get the model's version + * + * @return Version associated with the model + */ public int getVersion() { return mGenericSoundModel.version; } + /** + * Get the underlying model data + * + * @return Backing data of the model + */ @Nullable public byte[] getModelData() { return mGenericSoundModel.data; diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java index 5e012447e9dd..3561f8393eea 100644 --- a/media/java/android/media/tv/tuner/Tuner.java +++ b/media/java/android/media/tv/tuner/Tuner.java @@ -74,6 +74,8 @@ public class Tuner implements AutoCloseable { private List<Integer> mFrontendIds; private Frontend mFrontend; private EventHandler mHandler; + @Nullable + private FrontendInfo mFrontendInfo; private List<Integer> mLnbIds; private Lnb mLnb; @@ -97,6 +99,7 @@ public class Tuner implements AutoCloseable { public Tuner(@NonNull Context context, @NonNull String tvInputSessionId, @TvInputService.PriorityHintUseCaseType int useCase, @Nullable OnResourceLostListener listener) { + nativeSetup(); mContext = context; } @@ -185,7 +188,7 @@ public class Tuner implements AutoCloseable { /** * Listener for resource lost. * - * <p>Resource is reclaimed and tuner instance is forced to close. + * <p>Insufficient resources are reclaimed by higher priority clients. */ public interface OnResourceLostListener { /** @@ -292,6 +295,7 @@ public class Tuner implements AutoCloseable { @Result public int tune(@NonNull FrontendSettings settings) { TunerUtils.checkTunerPermission(mContext); + mFrontendInfo = null; return nativeTune(settings.getType(), settings); } @@ -333,6 +337,7 @@ public class Tuner implements AutoCloseable { } mScanCallback = scanCallback; mScanCallbackExecutor = executor; + mFrontendInfo = null; return nativeScan(settings.getType(), settings, scanType); } @@ -468,7 +473,10 @@ public class Tuner implements AutoCloseable { if (mFrontend == null) { throw new IllegalStateException("frontend is not initialized"); } - return nativeGetFrontendInfo(mFrontend.mId); + if (mFrontendInfo == null) { + mFrontendInfo = nativeGetFrontendInfo(mFrontend.mId); + } + return mFrontendInfo; } /** diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp index 9b37f955fa17..71ba59c8a707 100644 --- a/media/jni/android_media_MediaCodec.cpp +++ b/media/jni/android_media_MediaCodec.cpp @@ -724,18 +724,21 @@ status_t JMediaCodec::getOutputFrame( } if (buffer->size() > 0) { - // asC2Buffer clears internal reference, so set the reference again. std::shared_ptr<C2Buffer> c2Buffer = buffer->asC2Buffer(); - buffer->copy(c2Buffer); if (c2Buffer) { + // asC2Buffer clears internal reference, so set the reference again. + buffer->copy(c2Buffer); switch (c2Buffer->data().type()) { case C2BufferData::LINEAR: { std::unique_ptr<JMediaCodecLinearBlock> context{new JMediaCodecLinearBlock}; context->mBuffer = c2Buffer; ScopedLocalRef<jobject> linearBlock{env, env->NewObject( gLinearBlockInfo.clazz, gLinearBlockInfo.ctorId)}; - env->SetLongField( - linearBlock.get(), gLinearBlockInfo.contextId, (jlong)context.release()); + env->CallVoidMethod( + linearBlock.get(), + gLinearBlockInfo.setInternalStateId, + (jlong)context.release(), + true); env->SetObjectField(frame, gFields.outputFrameLinearBlockID, linearBlock.get()); break; } @@ -744,8 +747,11 @@ status_t JMediaCodec::getOutputFrame( context->mBuffer = c2Buffer; ScopedLocalRef<jobject> graphicBlock{env, env->NewObject( gGraphicBlockInfo.clazz, gGraphicBlockInfo.ctorId)}; - env->SetLongField( - graphicBlock.get(), gGraphicBlockInfo.contextId, (jlong)context.release()); + env->CallVoidMethod( + graphicBlock.get(), + gGraphicBlockInfo.setInternalStateId, + (jlong)context.release(), + true); env->SetObjectField(frame, gFields.outputFrameGraphicBlockID, graphicBlock.get()); break; } @@ -761,16 +767,22 @@ status_t JMediaCodec::getOutputFrame( context->mLegacyBuffer = buffer; ScopedLocalRef<jobject> linearBlock{env, env->NewObject( gLinearBlockInfo.clazz, gLinearBlockInfo.ctorId)}; - env->SetLongField( - linearBlock.get(), gLinearBlockInfo.contextId, (jlong)context.release()); + env->CallVoidMethod( + linearBlock.get(), + gLinearBlockInfo.setInternalStateId, + (jlong)context.release(), + true); env->SetObjectField(frame, gFields.outputFrameLinearBlockID, linearBlock.get()); } else { std::unique_ptr<JMediaCodecGraphicBlock> context{new JMediaCodecGraphicBlock}; context->mLegacyBuffer = buffer; ScopedLocalRef<jobject> graphicBlock{env, env->NewObject( gGraphicBlockInfo.clazz, gGraphicBlockInfo.ctorId)}; - env->SetLongField( - graphicBlock.get(), gGraphicBlockInfo.contextId, (jlong)context.release()); + env->CallVoidMethod( + graphicBlock.get(), + gGraphicBlockInfo.setInternalStateId, + (jlong)context.release(), + true); env->SetObjectField(frame, gFields.outputFrameGraphicBlockID, graphicBlock.get()); } } diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp index 81a85c9f08d6..08c3f988e85e 100644 --- a/media/jni/android_media_tv_Tuner.cpp +++ b/media/jni/android_media_tv_Tuner.cpp @@ -322,6 +322,179 @@ jobject JTuner::openFrontendById(int id) { (jint) jId); } +jobject JTuner::getAnalogFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities caps) { + jclass clazz = env->FindClass("android/media/tv/tuner/frontend/AnalogFrontendCapabilities"); + jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(II)V"); + + jint typeCap = caps.analogCaps().typeCap; + jint sifStandardCap = caps.analogCaps().sifStandardCap; + return env->NewObject(clazz, capsInit, typeCap, sifStandardCap); +} + +jobject JTuner::getAtsc3FrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities caps) { + jclass clazz = env->FindClass("android/media/tv/tuner/frontend/Atsc3FrontendCapabilities"); + jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(IIIIII)V"); + + jint bandwidthCap = caps.atsc3Caps().bandwidthCap; + jint modulationCap = caps.atsc3Caps().modulationCap; + jint timeInterleaveModeCap = caps.atsc3Caps().timeInterleaveModeCap; + jint codeRateCap = caps.atsc3Caps().codeRateCap; + jint fecCap = caps.atsc3Caps().fecCap; + jint demodOutputFormatCap = caps.atsc3Caps().demodOutputFormatCap; + + return env->NewObject(clazz, capsInit, bandwidthCap, modulationCap, timeInterleaveModeCap, + codeRateCap, fecCap, demodOutputFormatCap); +} + +jobject JTuner::getAtscFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities caps) { + jclass clazz = env->FindClass("android/media/tv/tuner/frontend/AtscFrontendCapabilities"); + jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(I)V"); + + jint modulationCap = caps.atscCaps().modulationCap; + + return env->NewObject(clazz, capsInit, modulationCap); +} + +jobject JTuner::getDvbcFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities caps) { + jclass clazz = env->FindClass("android/media/tv/tuner/frontend/DvbcFrontendCapabilities"); + jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(III)V"); + + jint modulationCap = caps.dvbcCaps().modulationCap; + jint fecCap = caps.dvbcCaps().fecCap; + jint annexCap = caps.dvbcCaps().annexCap; + + return env->NewObject(clazz, capsInit, modulationCap, fecCap, annexCap); +} + +jobject JTuner::getDvbsFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities caps) { + jclass clazz = env->FindClass("android/media/tv/tuner/frontend/DvbsFrontendCapabilities"); + jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(IJI)V"); + + jint modulationCap = caps.dvbsCaps().modulationCap; + jlong innerfecCap = caps.dvbsCaps().innerfecCap; + jint standard = caps.dvbsCaps().standard; + + return env->NewObject(clazz, capsInit, modulationCap, innerfecCap, standard); +} + +jobject JTuner::getDvbtFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities caps) { + jclass clazz = env->FindClass("android/media/tv/tuner/frontend/DvbtFrontendCapabilities"); + jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(IIIIIIZZ)V"); + + jint transmissionModeCap = caps.dvbtCaps().transmissionModeCap; + jint bandwidthCap = caps.dvbtCaps().bandwidthCap; + jint constellationCap = caps.dvbtCaps().constellationCap; + jint coderateCap = caps.dvbtCaps().coderateCap; + jint hierarchyCap = caps.dvbtCaps().hierarchyCap; + jint guardIntervalCap = caps.dvbtCaps().guardIntervalCap; + jboolean isT2Supported = caps.dvbtCaps().isT2Supported; + jboolean isMisoSupported = caps.dvbtCaps().isMisoSupported; + + return env->NewObject(clazz, capsInit, transmissionModeCap, bandwidthCap, constellationCap, + coderateCap, hierarchyCap, guardIntervalCap, isT2Supported, isMisoSupported); +} + +jobject JTuner::getIsdbs3FrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities caps) { + jclass clazz = env->FindClass("android/media/tv/tuner/frontend/Isdbs3FrontendCapabilities"); + jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(II)V"); + + jint modulationCap = caps.isdbs3Caps().modulationCap; + jint coderateCap = caps.isdbs3Caps().coderateCap; + + return env->NewObject(clazz, capsInit, modulationCap, coderateCap); +} + +jobject JTuner::getIsdbsFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities caps) { + jclass clazz = env->FindClass("android/media/tv/tuner/frontend/IsdbsFrontendCapabilities"); + jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(II)V"); + + jint modulationCap = caps.isdbsCaps().modulationCap; + jint coderateCap = caps.isdbsCaps().coderateCap; + + return env->NewObject(clazz, capsInit, modulationCap, coderateCap); +} + +jobject JTuner::getIsdbtFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities caps) { + jclass clazz = env->FindClass("android/media/tv/tuner/frontend/IsdbtFrontendCapabilities"); + jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(IIIII)V"); + + jint modeCap = caps.isdbtCaps().modeCap; + jint bandwidthCap = caps.isdbtCaps().bandwidthCap; + jint modulationCap = caps.isdbtCaps().modulationCap; + jint coderateCap = caps.isdbtCaps().coderateCap; + jint guardIntervalCap = caps.isdbtCaps().guardIntervalCap; + + return env->NewObject(clazz, capsInit, modeCap, bandwidthCap, modulationCap, coderateCap, + guardIntervalCap); +} + +jobject JTuner::getFrontendInfo(int id) { + FrontendInfo feInfo; + Result res; + mTuner->getFrontendInfo(id, [&](Result r, const FrontendInfo& info) { + feInfo = info; + res = r; + }); + if (res != Result::SUCCESS) { + return NULL; + } + + JNIEnv *env = AndroidRuntime::getJNIEnv(); + jclass clazz = env->FindClass("android/media/tv/tuner/frontend/FrontendInfo"); + jmethodID infoInit = env->GetMethodID(clazz, "<init>", + "(IIIIIIII[ILandroid/media/tv/tuner/frontend/FrontendCapabilities;)V"); + + jint type = (jint) feInfo.type; + jint minFrequency = feInfo.minFrequency; + jint maxFrequency = feInfo.maxFrequency; + jint minSymbolRate = feInfo.minSymbolRate; + jint maxSymbolRate = feInfo.maxSymbolRate; + jint acquireRange = feInfo.acquireRange; + jint exclusiveGroupId = feInfo.exclusiveGroupId; + jintArray statusCaps = env->NewIntArray(feInfo.statusCaps.size()); + env->SetIntArrayRegion( + statusCaps, 0, feInfo.statusCaps.size(), + reinterpret_cast<jint*>(&feInfo.statusCaps[0])); + FrontendInfo::FrontendCapabilities caps = feInfo.frontendCaps; + + jobject jcaps = NULL; + switch(feInfo.type) { + case FrontendType::ANALOG: + jcaps = getAnalogFrontendCaps(env, caps); + break; + case FrontendType::ATSC3: + jcaps = getAtsc3FrontendCaps(env, caps); + break; + case FrontendType::ATSC: + jcaps = getAtscFrontendCaps(env, caps); + break; + case FrontendType::DVBC: + jcaps = getDvbcFrontendCaps(env, caps); + break; + case FrontendType::DVBS: + jcaps = getDvbsFrontendCaps(env, caps); + break; + case FrontendType::DVBT: + jcaps = getDvbtFrontendCaps(env, caps); + break; + case FrontendType::ISDBS: + jcaps = getIsdbsFrontendCaps(env, caps); + break; + case FrontendType::ISDBS3: + jcaps = getIsdbs3FrontendCaps(env, caps); + break; + case FrontendType::ISDBT: + jcaps = getIsdbtFrontendCaps(env, caps); + break; + default: + break; + } + + return env->NewObject( + clazz, infoInit, (jint) id, type, minFrequency, maxFrequency, minSymbolRate, + maxSymbolRate, acquireRange, exclusiveGroupId, statusCaps, jcaps); +} + jobject JTuner::getLnbIds() { ALOGD("JTuner::getLnbIds()"); mTuner->getLnbIds([&](Result, const hidl_vec<FrontendId>& lnbIds) { @@ -1162,8 +1335,9 @@ static int android_media_tv_Tuner_disconnect_cicam(JNIEnv*, jobject) { return 0; } -static jobject android_media_tv_Tuner_get_frontend_info(JNIEnv*, jobject, jint) { - return NULL; +static jobject android_media_tv_Tuner_get_frontend_info(JNIEnv *env, jobject thiz, jint id) { + sp<JTuner> tuner = getTuner(env, thiz); + return tuner->getFrontendInfo(id); } static jobject android_media_tv_Tuner_get_lnb_ids(JNIEnv *env, jobject thiz) { diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h index 978d9c6e1059..cfe99b399979 100644 --- a/media/jni/android_media_tv_Tuner.h +++ b/media/jni/android_media_tv_Tuner.h @@ -39,6 +39,7 @@ using ::android::hardware::tv::tuner::V1_0::DemuxPid; using ::android::hardware::tv::tuner::V1_0::DvrType; using ::android::hardware::tv::tuner::V1_0::FrontendEventType; using ::android::hardware::tv::tuner::V1_0::FrontendId; +using ::android::hardware::tv::tuner::V1_0::FrontendInfo; using ::android::hardware::tv::tuner::V1_0::FrontendScanMessage; using ::android::hardware::tv::tuner::V1_0::FrontendScanMessageType; using ::android::hardware::tv::tuner::V1_0::FrontendScanType; @@ -132,6 +133,7 @@ struct JTuner : public RefBase { sp<ITuner> getTunerService(); jobject getFrontendIds(); jobject openFrontendById(int id); + jobject getFrontendInfo(int id); int tune(const FrontendSettings& settings); int stopTune(); int scan(const FrontendSettings& settings, FrontendScanType scanType); @@ -158,6 +160,15 @@ private: sp<ILnb> mLnb; sp<IDemux> mDemux; int mDemuxId; + static jobject getAnalogFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities caps); + static jobject getAtsc3FrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities caps); + static jobject getAtscFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities caps); + static jobject getDvbcFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities caps); + static jobject getDvbsFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities caps); + static jobject getDvbtFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities caps); + static jobject getIsdbs3FrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities caps); + static jobject getIsdbsFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities caps); + static jobject getIsdbtFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities caps); }; } // namespace android diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java index 4316e42bb0e1..f10e5ebb5d3e 100644 --- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java +++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java @@ -289,12 +289,13 @@ public class MediaRouterManagerTest { MediaRoute2Info volRoute = routes.get(ROUTE_ID_VARIABLE_VOLUME); int originalVolume = volRoute.getVolume(); - int deltaVolume = (originalVolume == volRoute.getVolumeMax() ? -1 : 1); + int targetVolume = originalVolume == volRoute.getVolumeMax() + ? originalVolume - 1 : originalVolume + 1; awaitOnRouteChangedManager( - () -> mManager.requestUpdateVolume(volRoute, deltaVolume), + () -> mManager.requestSetVolume(volRoute, targetVolume), ROUTE_ID_VARIABLE_VOLUME, - (route -> route.getVolume() == originalVolume + deltaVolume)); + (route -> route.getVolume() == targetVolume)); awaitOnRouteChangedManager( () -> mManager.requestSetVolume(volRoute, originalVolume), diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java index f1a08f29e0cc..1a866cafff90 100644 --- a/media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java +++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java @@ -154,20 +154,6 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService } @Override - public void onUpdateVolume(String routeId, int delta) { - MediaRoute2Info route = mRoutes.get(routeId); - if (route == null) { - return; - } - int volume = route.getVolume() + delta; - volume = Math.min(volume, Math.max(0, route.getVolumeMax())); - mRoutes.put(routeId, new MediaRoute2Info.Builder(route) - .setVolume(volume) - .build()); - publishRoutes(); - } - - @Override public void onCreateSession(String packageName, String routeId, long requestId, @Nullable Bundle sessionHints) { MediaRoute2Info route = mRoutes.get(routeId); diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp index 392c9f6404ba..ba793e83f1fb 100644 --- a/native/android/surface_control.cpp +++ b/native/android/surface_control.cpp @@ -294,7 +294,7 @@ void ASurfaceTransaction_setOnComplete(ASurfaceTransaction* aSurfaceTransaction, auto& aSurfaceControlStats = aSurfaceTransactionStats.aSurfaceControlStats; - for (const auto& [surfaceControl, acquireTime, previousReleaseFence, transformHint] : surfaceControlStats) { + for (const auto& [surfaceControl, latchTime, acquireTime, presentFence, previousReleaseFence, transformHint, frameEvents] : surfaceControlStats) { ASurfaceControl* aSurfaceControl = reinterpret_cast<ASurfaceControl*>(surfaceControl.get()); aSurfaceControlStats[aSurfaceControl].acquireTime = acquireTime; aSurfaceControlStats[aSurfaceControl].previousReleaseFence = previousReleaseFence; diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp index 9c8345dafbc8..62124934f416 100644 --- a/packages/SettingsLib/Android.bp +++ b/packages/SettingsLib/Android.bp @@ -10,6 +10,7 @@ android_library { "androidx.appcompat_appcompat", "androidx.lifecycle_lifecycle-runtime", "androidx.mediarouter_mediarouter-nodeps", + "iconloader", "SettingsLibHelpUtils", "SettingsLibRestrictedLockUtils", diff --git a/packages/SettingsLib/res/values/config.xml b/packages/SettingsLib/res/values/config.xml new file mode 100644 index 000000000000..332d6c7bc0fa --- /dev/null +++ b/packages/SettingsLib/res/values/config.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. + --> +<!-- These resources are around just to allow their values to be customized --> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- Threshold in micro watts below which a charger is rated as "slow"; 1A @ 5V --> + <integer name="config_chargingSlowlyThreshold">5000000</integer> + + <!-- Threshold in micro watts above which a charger is rated as "fast"; 1.5A @ 5V --> + <integer name="config_chargingFastThreshold">7500000</integer> +</resources>
\ No newline at end of file diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index dd21e5c07b13..2e7a3c0eb263 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -1034,8 +1034,10 @@ <string name="battery_info_status_unknown">Unknown</string> <!-- [CHAR_LIMIT=20] Battery use screen. Battery status shown in chart label when charging from an unknown source. --> <string name="battery_info_status_charging">Charging</string> - <!-- [CHAR_LIMIT=20] Battery use screen with lower case. Battery status shown in chart label when charging from an unknown source. --> - <string name="battery_info_status_charging_lower">charging</string> + <!-- [CHAR_LIMIT=20] Battery use screen. Battery status shown in chart label when charging speed is fast. --> + <string name="battery_info_status_charging_fast">Charging rapidly</string> + <!-- [CHAR_LIMIT=20] Battery use screen. Battery status shown in chart label when charging speed is slow. --> + <string name="battery_info_status_charging_slow">Charging slowly</string> <!-- Battery Info screen. Value for a status item. Used for diagnostic info screens, precise translation isn't needed --> <string name="battery_info_status_discharging">Not charging</string> <!-- Battery Info screen. Value for a status item. Used for diagnostic info screens, precise translation isn't needed --> diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java index de523d9f9bc8..f4857932064f 100644 --- a/packages/SettingsLib/src/com/android/settingslib/Utils.java +++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java @@ -3,6 +3,7 @@ package com.android.settingslib; import android.annotation.ColorInt; import android.content.Context; import android.content.Intent; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; @@ -13,6 +14,7 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Color; +import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.location.LocationManager; import android.media.AudioManager; @@ -27,9 +29,13 @@ import android.telephony.AccessNetworkConstants; import android.telephony.NetworkRegistrationInfo; import android.telephony.ServiceState; +import androidx.annotation.NonNull; + import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.UserIcons; +import com.android.launcher3.icons.IconFactory; import com.android.settingslib.drawable.UserIconDrawable; +import com.android.settingslib.fuelgauge.BatteryStatus; import java.text.NumberFormat; @@ -117,7 +123,7 @@ public class Utils { public static Drawable getUserIcon(Context context, UserManager um, UserInfo user) { final int iconSize = UserIconDrawable.getSizeForList(context); if (user.isManagedProfile()) { - Drawable drawable = UserIconDrawable.getManagedUserDrawable(context); + Drawable drawable = UserIconDrawable.getManagedUserDrawable(context); drawable.setBounds(0, 0, iconSize, iconSize); return drawable; } @@ -159,20 +165,43 @@ public class Utils { return (level * 100) / scale; } - public static String getBatteryStatus(Resources res, Intent batteryChangedIntent) { - int status = batteryChangedIntent.getIntExtra(BatteryManager.EXTRA_STATUS, + /** + * Get battery status string + * + * @param context the context + * @param batteryChangedIntent battery broadcast intent received from {@link + * Intent.ACTION_BATTERY_CHANGED}. + * @return battery status string + */ + public static String getBatteryStatus(Context context, Intent batteryChangedIntent) { + final int status = batteryChangedIntent.getIntExtra(BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_UNKNOWN); - String statusString; - if (status == BatteryManager.BATTERY_STATUS_CHARGING) { - statusString = res.getString(R.string.battery_info_status_charging); - } else if (status == BatteryManager.BATTERY_STATUS_DISCHARGING) { - statusString = res.getString(R.string.battery_info_status_discharging); - } else if (status == BatteryManager.BATTERY_STATUS_NOT_CHARGING) { - statusString = res.getString(R.string.battery_info_status_not_charging); - } else if (status == BatteryManager.BATTERY_STATUS_FULL) { + final Resources res = context.getResources(); + + String statusString = res.getString(R.string.battery_info_status_unknown); + final BatteryStatus batteryStatus = new BatteryStatus(batteryChangedIntent); + + if (batteryStatus.isCharged()) { statusString = res.getString(R.string.battery_info_status_full); } else { - statusString = res.getString(R.string.battery_info_status_unknown); + if (status == BatteryManager.BATTERY_STATUS_CHARGING) { + switch (batteryStatus.getChargingSpeed(context)) { + case BatteryStatus.CHARGING_FAST: + statusString = res.getString(R.string.battery_info_status_charging_fast); + break; + case BatteryStatus.CHARGING_SLOWLY: + statusString = res.getString(R.string.battery_info_status_charging_slow); + break; + default: + statusString = res.getString(R.string.battery_info_status_charging); + break; + } + + } else if (status == BatteryManager.BATTERY_STATUS_DISCHARGING) { + statusString = res.getString(R.string.battery_info_status_discharging); + } else if (status == BatteryManager.BATTERY_STATUS_NOT_CHARGING) { + statusString = res.getString(R.string.battery_info_status_not_charging); + } } return statusString; @@ -206,7 +235,7 @@ public class Utils { /** * This method computes disabled color from normal color * - * @param context + * @param context the context * @param inputColor normal color. * @return disabled color. */ @@ -424,6 +453,19 @@ public class Utils { return state; } + /** + * Get the {@link Drawable} that represents the app icon + */ + public static @NonNull Drawable getBadgedIcon( + @NonNull Context context, @NonNull ApplicationInfo appInfo) { + final UserHandle user = UserHandle.getUserHandleForUid(appInfo.uid); + try (IconFactory iconFactory = IconFactory.obtain(context)) { + final Bitmap iconBmp = iconFactory.createBadgedIconBitmap( + appInfo.loadUnbadgedIcon(context.getPackageManager()), user, false).icon; + return new BitmapDrawable(context.getResources(), iconBmp); + } + } + private static boolean isNotInIwlan(ServiceState serviceState) { final NetworkRegistrationInfo networkRegWlan = serviceState.getNetworkRegistrationInfo( NetworkRegistrationInfo.DOMAIN_PS, diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java index 19c666459723..af728887c917 100644 --- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java +++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java @@ -59,6 +59,7 @@ import androidx.lifecycle.OnLifecycleEvent; import com.android.internal.R; import com.android.internal.util.ArrayUtils; +import com.android.settingslib.Utils; import java.io.File; import java.io.IOException; @@ -495,7 +496,7 @@ public class ApplicationsState { return; } synchronized (entry) { - entry.ensureIconLocked(mContext, mDrawableFactory); + entry.ensureIconLocked(mContext); } } @@ -1216,7 +1217,7 @@ public class ApplicationsState { AppEntry entry = mAppEntries.get(i); if (entry.icon == null || !entry.mounted) { synchronized (entry) { - if (entry.ensureIconLocked(mContext, mDrawableFactory)) { + if (entry.ensureIconLocked(mContext)) { if (!mRunning) { mRunning = true; Message m = mMainHandler.obtainMessage( @@ -1587,10 +1588,10 @@ public class ApplicationsState { } } - boolean ensureIconLocked(Context context, IconDrawableFactory drawableFactory) { + boolean ensureIconLocked(Context context) { if (this.icon == null) { if (this.apkFile.exists()) { - this.icon = drawableFactory.getBadgedIcon(info); + this.icon = Utils.getBadgedIcon(context, info); return true; } else { this.mounted = false; @@ -1601,7 +1602,7 @@ public class ApplicationsState { // its icon. if (this.apkFile.exists()) { this.mounted = true; - this.icon = drawableFactory.getBadgedIcon(info); + this.icon = Utils.getBadgedIcon(context, info); return true; } } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java index ddb7341b7366..1ebe91736ba1 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java @@ -153,21 +153,6 @@ public class A2dpProfile implements LocalBluetoothProfile { return mService.getDevicesMatchingConnectionStates(states); } - public boolean connect(BluetoothDevice device) { - if (mService == null) { - return false; - } - return mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); - } - - public boolean disconnect(BluetoothDevice device) { - if (mService == null) { - return false; - } - - return mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); - } - public int getConnectionStatus(BluetoothDevice device) { if (mService == null) { return BluetoothProfile.STATE_DISCONNECTED; @@ -187,31 +172,37 @@ public class A2dpProfile implements LocalBluetoothProfile { return mService.getActiveDevice(); } - public boolean isPreferred(BluetoothDevice device) { + @Override + public boolean isEnabled(BluetoothDevice device) { if (mService == null) { return false; } return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN; } - public int getPreferred(BluetoothDevice device) { + @Override + public int getConnectionPolicy(BluetoothDevice device) { if (mService == null) { return CONNECTION_POLICY_FORBIDDEN; } return mService.getConnectionPolicy(device); } - public void setPreferred(BluetoothDevice device, boolean preferred) { + @Override + public boolean setEnabled(BluetoothDevice device, boolean enabled) { + boolean isEnabled = false; if (mService == null) { - return; + return false; } - if (preferred) { + if (enabled) { if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) { - mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); + isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); } } else { - mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); + isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); } + + return isEnabled; } boolean isA2dpPlaying() { if (mService == null) return false; diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java index 8ca5a74652dc..c7a5bd86c1cd 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java @@ -115,21 +115,6 @@ final class A2dpSinkProfile implements LocalBluetoothProfile { BluetoothProfile.STATE_DISCONNECTING}); } - public boolean connect(BluetoothDevice device) { - if (mService == null) { - return false; - } - return mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); - } - - public boolean disconnect(BluetoothDevice device) { - if (mService == null) { - return false; - } - - return mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); - } - public int getConnectionStatus(BluetoothDevice device) { if (mService == null) { return BluetoothProfile.STATE_DISCONNECTED; @@ -137,31 +122,37 @@ final class A2dpSinkProfile implements LocalBluetoothProfile { return mService.getConnectionState(device); } - public boolean isPreferred(BluetoothDevice device) { + @Override + public boolean isEnabled(BluetoothDevice device) { if (mService == null) { return false; } return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN; } - public int getPreferred(BluetoothDevice device) { + @Override + public int getConnectionPolicy(BluetoothDevice device) { if (mService == null) { return CONNECTION_POLICY_FORBIDDEN; } return mService.getConnectionPolicy(device); } - public void setPreferred(BluetoothDevice device, boolean preferred) { + @Override + public boolean setEnabled(BluetoothDevice device, boolean enabled) { + boolean isEnabled = false; if (mService == null) { - return; + return false; } - if (preferred) { + if (enabled) { if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) { - mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); + isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); } } else { - mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); + isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); } + + return isEnabled; } boolean isAudioPlaying() { diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java index 50d3a5daeb84..3aa35cb48c38 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java @@ -195,7 +195,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> if (newProfileState == BluetoothProfile.STATE_CONNECTED) { if (profile instanceof MapProfile) { - profile.setPreferred(mDevice, true); + profile.setEnabled(mDevice, true); } if (!mProfiles.contains(profile)) { mRemovedProfiles.remove(profile); @@ -208,7 +208,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> } } else if (profile instanceof MapProfile && newProfileState == BluetoothProfile.STATE_DISCONNECTED) { - profile.setPreferred(mDevice, false); + profile.setEnabled(mDevice, false); } else if (mLocalNapRoleConnected && profile instanceof PanProfile && ((PanProfile) profile).isLocalRoleNap(mDevice) && newProfileState == BluetoothProfile.STATE_DISCONNECTED) { @@ -250,12 +250,12 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> PbapServerProfile PbapProfile = mProfileManager.getPbapProfile(); if (PbapProfile != null && isConnectedProfile(PbapProfile)) { - PbapProfile.disconnect(mDevice); + PbapProfile.setEnabled(mDevice, false); } } public void disconnect(LocalBluetoothProfile profile) { - if (profile.disconnect(mDevice)) { + if (profile.setEnabled(mDevice, false)) { if (BluetoothUtils.D) { Log.d(TAG, "Command sent successfully:DISCONNECT " + describe(profile)); } @@ -342,7 +342,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> if (!ensurePaired()) { return; } - if (profile.connect(mDevice)) { + if (profile.setEnabled(mDevice, true)) { if (BluetoothUtils.D) { Log.d(TAG, "Command sent successfully:CONNECT " + describe(profile)); } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java index 218d0b2dc2c0..9dfc4d986745 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java @@ -114,21 +114,6 @@ public class HeadsetProfile implements LocalBluetoothProfile { return true; } - public boolean connect(BluetoothDevice device) { - if (mService == null) { - return false; - } - return mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); - } - - public boolean disconnect(BluetoothDevice device) { - if (mService == null) { - return false; - } - - return mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); - } - public int getConnectionStatus(BluetoothDevice device) { if (mService == null) { return BluetoothProfile.STATE_DISCONNECTED; @@ -164,31 +149,37 @@ public class HeadsetProfile implements LocalBluetoothProfile { return mService.getAudioState(device); } - public boolean isPreferred(BluetoothDevice device) { + @Override + public boolean isEnabled(BluetoothDevice device) { if (mService == null) { return false; } return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN; } - public int getPreferred(BluetoothDevice device) { + @Override + public int getConnectionPolicy(BluetoothDevice device) { if (mService == null) { return CONNECTION_POLICY_FORBIDDEN; } return mService.getConnectionPolicy(device); } - public void setPreferred(BluetoothDevice device, boolean preferred) { + @Override + public boolean setEnabled(BluetoothDevice device, boolean enabled) { + boolean isEnabled = false; if (mService == null) { - return; + return false; } - if (preferred) { + if (enabled) { if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) { - mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); + isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); } } else { - mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); + isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); } + + return isEnabled; } public List<BluetoothDevice> getConnectedDevices() { diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java index b82fb37a770f..a3b68b4b90b3 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java @@ -151,21 +151,6 @@ public class HearingAidProfile implements LocalBluetoothProfile { return mService.getDevicesMatchingConnectionStates(states); } - public boolean connect(BluetoothDevice device) { - if (mService == null) { - return false; - } - return mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); - } - - public boolean disconnect(BluetoothDevice device) { - if (mService == null) { - return false; - } - - return mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); - } - public int getConnectionStatus(BluetoothDevice device) { if (mService == null) { return BluetoothProfile.STATE_DISCONNECTED; @@ -185,31 +170,37 @@ public class HearingAidProfile implements LocalBluetoothProfile { return mService.getActiveDevices(); } - public boolean isPreferred(BluetoothDevice device) { + @Override + public boolean isEnabled(BluetoothDevice device) { if (mService == null) { return false; } return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN; } - public int getPreferred(BluetoothDevice device) { + @Override + public int getConnectionPolicy(BluetoothDevice device) { if (mService == null) { return CONNECTION_POLICY_FORBIDDEN; } return mService.getConnectionPolicy(device); } - public void setPreferred(BluetoothDevice device, boolean preferred) { + @Override + public boolean setEnabled(BluetoothDevice device, boolean enabled) { + boolean isEnabled = false; if (mService == null) { - return; + return false; } - if (preferred) { + if (enabled) { if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) { - mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); + isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); } } else { - mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); + isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); } + + return isEnabled; } public void setVolume(int volume) { diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java index 678f2e37c6bf..66225a2bffca 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java @@ -125,23 +125,6 @@ final class HfpClientProfile implements LocalBluetoothProfile { } @Override - public boolean connect(BluetoothDevice device) { - if (mService == null) { - return false; - } - return mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); - } - - @Override - public boolean disconnect(BluetoothDevice device) { - if (mService == null) { - return false; - } - - return mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); - } - - @Override public int getConnectionStatus(BluetoothDevice device) { if (mService == null) { return BluetoothProfile.STATE_DISCONNECTED; @@ -150,7 +133,7 @@ final class HfpClientProfile implements LocalBluetoothProfile { } @Override - public boolean isPreferred(BluetoothDevice device) { + public boolean isEnabled(BluetoothDevice device) { if (mService == null) { return false; } @@ -158,7 +141,7 @@ final class HfpClientProfile implements LocalBluetoothProfile { } @Override - public int getPreferred(BluetoothDevice device) { + public int getConnectionPolicy(BluetoothDevice device) { if (mService == null) { return CONNECTION_POLICY_FORBIDDEN; } @@ -166,17 +149,20 @@ final class HfpClientProfile implements LocalBluetoothProfile { } @Override - public void setPreferred(BluetoothDevice device, boolean preferred) { + public boolean setEnabled(BluetoothDevice device, boolean enabled) { + boolean isEnabled = false; if (mService == null) { - return; + return false; } - if (preferred) { + if (enabled) { if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) { - mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); + isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); } } else { - mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); + isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); } + + return isEnabled; } @Override diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java index 35600b538d4d..8a2c4f8a1230 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidDeviceProfile.java @@ -16,6 +16,8 @@ package com.android.settingslib.bluetooth; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; @@ -102,20 +104,6 @@ public class HidDeviceProfile implements LocalBluetoothProfile { } @Override - public boolean connect(BluetoothDevice device) { - // Don't invoke method in service because settings is not allowed to connect this profile. - return false; - } - - @Override - public boolean disconnect(BluetoothDevice device) { - if (mService == null) { - return false; - } - return mService.disconnect(device); - } - - @Override public int getConnectionStatus(BluetoothDevice device) { if (mService == null) { return BluetoothProfile.STATE_DISCONNECTED; @@ -124,21 +112,24 @@ public class HidDeviceProfile implements LocalBluetoothProfile { } @Override - public boolean isPreferred(BluetoothDevice device) { + public boolean isEnabled(BluetoothDevice device) { return getConnectionStatus(device) != BluetoothProfile.STATE_DISCONNECTED; } @Override - public int getPreferred(BluetoothDevice device) { + public int getConnectionPolicy(BluetoothDevice device) { return PREFERRED_VALUE; } @Override - public void setPreferred(BluetoothDevice device, boolean preferred) { + public boolean setEnabled(BluetoothDevice device, boolean enabled) { + boolean isEnabled = false; // if set preferred to false, then disconnect to the current device - if (!preferred) { - mService.disconnect(device); + if (!enabled) { + isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); } + + return isEnabled; } @Override diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java index 588083e73481..3c24b4a095b9 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java @@ -101,20 +101,6 @@ public class HidProfile implements LocalBluetoothProfile { return true; } - public boolean connect(BluetoothDevice device) { - if (mService == null) { - return false; - } - return mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); - } - - public boolean disconnect(BluetoothDevice device) { - if (mService == null) { - return false; - } - return mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); - } - public int getConnectionStatus(BluetoothDevice device) { if (mService == null) { return BluetoothProfile.STATE_DISCONNECTED; @@ -122,29 +108,37 @@ public class HidProfile implements LocalBluetoothProfile { return mService.getConnectionState(device); } - public boolean isPreferred(BluetoothDevice device) { + @Override + public boolean isEnabled(BluetoothDevice device) { if (mService == null) { return false; } return mService.getConnectionPolicy(device) != CONNECTION_POLICY_FORBIDDEN; } - public int getPreferred(BluetoothDevice device) { + @Override + public int getConnectionPolicy(BluetoothDevice device) { if (mService == null) { return CONNECTION_POLICY_FORBIDDEN; } return mService.getConnectionPolicy(device); } - public void setPreferred(BluetoothDevice device, boolean preferred) { - if (mService == null) return; - if (preferred) { + @Override + public boolean setEnabled(BluetoothDevice device, boolean enabled) { + boolean isEnabled = false; + if (mService == null) { + return false; + } + if (enabled) { if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) { - mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); + isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); } } else { - mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); + isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); } + + return isEnabled; } public String toString() { diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfile.java index 4b0ca7434f9a..f609e4311082 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfile.java @@ -35,17 +35,26 @@ public interface LocalBluetoothProfile { */ boolean isAutoConnectable(); - boolean connect(BluetoothDevice device); - - boolean disconnect(BluetoothDevice device); - int getConnectionStatus(BluetoothDevice device); - boolean isPreferred(BluetoothDevice device); + /** + * Return {@code true} if the profile is enabled, otherwise return {@code false}. + * @param device the device to query for enable status + */ + boolean isEnabled(BluetoothDevice device); - int getPreferred(BluetoothDevice device); + /** + * Get the connection policy of the profile. + * @param device the device to query for enable status + */ + int getConnectionPolicy(BluetoothDevice device); - void setPreferred(BluetoothDevice device, boolean preferred); + /** + * Enable the profile if {@code enabled} is {@code true}, otherwise disable profile. + * @param device the device to set profile status + * @param enabled {@code true} for enable profile, otherwise disable profile. + */ + boolean setEnabled(BluetoothDevice device, boolean enabled); boolean isProfileReady(); diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java index ae2acbea8e4d..c72efb7eec83 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java @@ -528,14 +528,14 @@ public class LocalBluetoothProfileManager { (mMapProfile.getConnectionStatus(device) == BluetoothProfile.STATE_CONNECTED)) { profiles.add(mMapProfile); removedProfiles.remove(mMapProfile); - mMapProfile.setPreferred(device, true); + mMapProfile.setEnabled(device, true); } if ((mPbapProfile != null) && (mPbapProfile.getConnectionStatus(device) == BluetoothProfile.STATE_CONNECTED)) { profiles.add(mPbapProfile); removedProfiles.remove(mPbapProfile); - mPbapProfile.setPreferred(device, true); + mPbapProfile.setEnabled(device, true); } if (mMapClientProfile != null) { diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java index 7d121aaa1ad1..19cb2f59f321 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java @@ -114,21 +114,6 @@ public final class MapClientProfile implements LocalBluetoothProfile { return true; } - public boolean connect(BluetoothDevice device) { - if (mService == null) { - return false; - } - return mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); - } - - public boolean disconnect(BluetoothDevice device) { - if (mService == null) { - return false; - } - - return mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); - } - public int getConnectionStatus(BluetoothDevice device) { if (mService == null) { return BluetoothProfile.STATE_DISCONNECTED; @@ -136,31 +121,37 @@ public final class MapClientProfile implements LocalBluetoothProfile { return mService.getConnectionState(device); } - public boolean isPreferred(BluetoothDevice device) { + @Override + public boolean isEnabled(BluetoothDevice device) { if (mService == null) { return false; } return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN; } - public int getPreferred(BluetoothDevice device) { + @Override + public int getConnectionPolicy(BluetoothDevice device) { if (mService == null) { return CONNECTION_POLICY_FORBIDDEN; } return mService.getConnectionPolicy(device); } - public void setPreferred(BluetoothDevice device, boolean preferred) { + @Override + public boolean setEnabled(BluetoothDevice device, boolean enabled) { + boolean isEnabled = false; if (mService == null) { - return; + return false; } - if (preferred) { + if (enabled) { if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) { - mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); + isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); } } else { - mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); + isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); } + + return isEnabled; } public List<BluetoothDevice> getConnectedDevices() { diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java index a96a4e73feea..75c1926683ef 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java @@ -16,6 +16,7 @@ package com.android.settingslib.bluetooth; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED; import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; import android.bluetooth.BluetoothAdapter; @@ -112,19 +113,6 @@ public class MapProfile implements LocalBluetoothProfile { return true; } - public boolean connect(BluetoothDevice device) { - Log.d(TAG, "connect() - should not get called"); - return false; // MAP never connects out - } - - public boolean disconnect(BluetoothDevice device) { - if (mService == null) { - return false; - } - - return mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); - } - public int getConnectionStatus(BluetoothDevice device) { if (mService == null) { return BluetoothProfile.STATE_DISCONNECTED; @@ -132,31 +120,37 @@ public class MapProfile implements LocalBluetoothProfile { return mService.getConnectionState(device); } - public boolean isPreferred(BluetoothDevice device) { + @Override + public boolean isEnabled(BluetoothDevice device) { if (mService == null) { return false; } return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN; } - public int getPreferred(BluetoothDevice device) { + @Override + public int getConnectionPolicy(BluetoothDevice device) { if (mService == null) { return CONNECTION_POLICY_FORBIDDEN; } return mService.getConnectionPolicy(device); } - public void setPreferred(BluetoothDevice device, boolean preferred) { + @Override + public boolean setEnabled(BluetoothDevice device, boolean enabled) { + boolean isEnabled = false; if (mService == null) { - return; + return false; } - if (preferred) { - if (mService.getConnectionPolicy(device) < BluetoothProfile.CONNECTION_POLICY_ALLOWED) { - mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); + if (enabled) { + if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) { + isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); } } else { - mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); + isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); } + + return isEnabled; } public List<BluetoothDevice> getConnectedDevices() { diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/OppProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/OppProfile.java index 8e3f3edcef10..5a6e6e8b830d 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/OppProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/OppProfile.java @@ -40,27 +40,23 @@ final class OppProfile implements LocalBluetoothProfile { return false; } - public boolean connect(BluetoothDevice device) { - return false; - } - - public boolean disconnect(BluetoothDevice device) { - return false; - } - public int getConnectionStatus(BluetoothDevice device) { return BluetoothProfile.STATE_DISCONNECTED; // Settings app doesn't handle OPP } - public boolean isPreferred(BluetoothDevice device) { + @Override + public boolean isEnabled(BluetoothDevice device) { return false; } - public int getPreferred(BluetoothDevice device) { + @Override + public int getConnectionPolicy(BluetoothDevice device) { return BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; // Settings app doesn't handle OPP } - public void setPreferred(BluetoothDevice device, boolean preferred) { + @Override + public boolean setEnabled(BluetoothDevice device, boolean enabled) { + return false; } public boolean isProfileReady() { diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java index 6638592e8be5..767df352b70f 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java @@ -16,6 +16,9 @@ package com.android.settingslib.bluetooth; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; + import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; @@ -83,22 +86,6 @@ public class PanProfile implements LocalBluetoothProfile { return false; } - public boolean connect(BluetoothDevice device) { - if (mService == null) return false; - List<BluetoothDevice> sinks = mService.getConnectedDevices(); - if (sinks != null) { - for (BluetoothDevice sink : sinks) { - mService.disconnect(sink); - } - } - return mService.connect(device); - } - - public boolean disconnect(BluetoothDevice device) { - if (mService == null) return false; - return mService.disconnect(device); - } - public int getConnectionStatus(BluetoothDevice device) { if (mService == null) { return BluetoothProfile.STATE_DISCONNECTED; @@ -106,16 +93,36 @@ public class PanProfile implements LocalBluetoothProfile { return mService.getConnectionState(device); } - public boolean isPreferred(BluetoothDevice device) { + @Override + public boolean isEnabled(BluetoothDevice device) { return true; } - public int getPreferred(BluetoothDevice device) { + @Override + public int getConnectionPolicy(BluetoothDevice device) { return -1; } - public void setPreferred(BluetoothDevice device, boolean preferred) { - // ignore: isPreferred is always true for PAN + @Override + public boolean setEnabled(BluetoothDevice device, boolean enabled) { + boolean isEnabled = false; + if (mService == null) { + return false; + } + + if (enabled) { + final List<BluetoothDevice> sinks = mService.getConnectedDevices(); + if (sinks != null) { + for (BluetoothDevice sink : sinks) { + mService.setConnectionPolicy(sink, CONNECTION_POLICY_FORBIDDEN); + } + } + isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); + } else { + isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); + } + + return isEnabled; } public String toString() { diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java index 56267fc596cf..0d11293a01b7 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java @@ -126,23 +126,6 @@ public final class PbapClientProfile implements LocalBluetoothProfile { BluetoothProfile.STATE_DISCONNECTING}); } - public boolean connect(BluetoothDevice device) { - Log.d(TAG,"PBAPClientProfile got connect request"); - if (mService == null) { - return false; - } - Log.d(TAG,"PBAPClientProfile attempting to connect to " + device.getAddress()); - return mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); - } - - public boolean disconnect(BluetoothDevice device) { - Log.d(TAG,"PBAPClientProfile got disconnect request"); - if (mService == null) { - return false; - } - return mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); - } - public int getConnectionStatus(BluetoothDevice device) { if (mService == null) { return BluetoothProfile.STATE_DISCONNECTED; @@ -150,31 +133,37 @@ public final class PbapClientProfile implements LocalBluetoothProfile { return mService.getConnectionState(device); } - public boolean isPreferred(BluetoothDevice device) { + @Override + public boolean isEnabled(BluetoothDevice device) { if (mService == null) { return false; } return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN; } - public int getPreferred(BluetoothDevice device) { + @Override + public int getConnectionPolicy(BluetoothDevice device) { if (mService == null) { return CONNECTION_POLICY_FORBIDDEN; } return mService.getConnectionPolicy(device); } - public void setPreferred(BluetoothDevice device, boolean preferred) { + @Override + public boolean setEnabled(BluetoothDevice device, boolean enabled) { + boolean isEnabled = false; if (mService == null) { - return; + return false; } - if (preferred) { + if (enabled) { if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) { - mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); + isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); } } else { - mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); + isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); } + + return isEnabled; } public String toString() { diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java index f7c0bf5c8c9d..9e2e4a14124a 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java @@ -91,34 +91,33 @@ public class PbapServerProfile implements LocalBluetoothProfile { return false; } - public boolean connect(BluetoothDevice device) { - /*Can't connect from server */ - return false; - - } - - public boolean disconnect(BluetoothDevice device) { - if (mService == null) { - return false; - } - return mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); - } - public int getConnectionStatus(BluetoothDevice device) { if (mService == null) return BluetoothProfile.STATE_DISCONNECTED; return mService.getConnectionState(device); } - public boolean isPreferred(BluetoothDevice device) { + @Override + public boolean isEnabled(BluetoothDevice device) { return false; } - public int getPreferred(BluetoothDevice device) { + @Override + public int getConnectionPolicy(BluetoothDevice device) { return -1; } - public void setPreferred(BluetoothDevice device, boolean preferred) { - // ignore: isPreferred is always true for PBAP + @Override + public boolean setEnabled(BluetoothDevice device, boolean enabled) { + boolean isEnabled = false; + if (mService == null) { + return false; + } + + if (!enabled) { + isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); + } + + return isEnabled; } public String toString() { diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java index 3022c5b566eb..104f1d738000 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java @@ -111,21 +111,6 @@ final class SapProfile implements LocalBluetoothProfile { return true; } - public boolean connect(BluetoothDevice device) { - if (mService == null) { - return false; - } - return mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); - } - - public boolean disconnect(BluetoothDevice device) { - if (mService == null) { - return false; - } - - return mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); - } - public int getConnectionStatus(BluetoothDevice device) { if (mService == null) { return BluetoothProfile.STATE_DISCONNECTED; @@ -133,31 +118,37 @@ final class SapProfile implements LocalBluetoothProfile { return mService.getConnectionState(device); } - public boolean isPreferred(BluetoothDevice device) { + @Override + public boolean isEnabled(BluetoothDevice device) { if (mService == null) { return false; } return mService.getConnectionPolicy(device) > CONNECTION_POLICY_FORBIDDEN; } - public int getPreferred(BluetoothDevice device) { + @Override + public int getConnectionPolicy(BluetoothDevice device) { if (mService == null) { return CONNECTION_POLICY_FORBIDDEN; } return mService.getConnectionPolicy(device); } - public void setPreferred(BluetoothDevice device, boolean preferred) { + @Override + public boolean setEnabled(BluetoothDevice device, boolean enabled) { + boolean isEnabled = false; if (mService == null) { - return; + return false; } - if (preferred) { + if (enabled) { if (mService.getConnectionPolicy(device) < CONNECTION_POLICY_ALLOWED) { - mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); + isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); } } else { - mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); + isEnabled = mService.setConnectionPolicy(device, CONNECTION_POLICY_FORBIDDEN); } + + return isEnabled; } public List<BluetoothDevice> getConnectedDevices() { diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java new file mode 100644 index 000000000000..bc40903d88e4 --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.fuelgauge; + +import static android.os.BatteryManager.BATTERY_HEALTH_UNKNOWN; +import static android.os.BatteryManager.BATTERY_STATUS_FULL; +import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN; +import static android.os.BatteryManager.EXTRA_HEALTH; +import static android.os.BatteryManager.EXTRA_LEVEL; +import static android.os.BatteryManager.EXTRA_MAX_CHARGING_CURRENT; +import static android.os.BatteryManager.EXTRA_MAX_CHARGING_VOLTAGE; +import static android.os.BatteryManager.EXTRA_PLUGGED; +import static android.os.BatteryManager.EXTRA_STATUS; + +import android.content.Context; +import android.content.Intent; +import android.os.BatteryManager; + +import com.android.settingslib.R; + +/** + * Stores and computes some battery information. + */ +public class BatteryStatus { + private static final int LOW_BATTERY_THRESHOLD = 20; + private static final int DEFAULT_CHARGING_VOLTAGE_MICRO_VOLT = 5000000; + + public static final int CHARGING_UNKNOWN = -1; + public static final int CHARGING_SLOWLY = 0; + public static final int CHARGING_REGULAR = 1; + public static final int CHARGING_FAST = 2; + + public final int status; + public final int level; + public final int plugged; + public final int health; + public final int maxChargingWattage; + + public BatteryStatus(int status, int level, int plugged, int health, + int maxChargingWattage) { + this.status = status; + this.level = level; + this.plugged = plugged; + this.health = health; + this.maxChargingWattage = maxChargingWattage; + } + + public BatteryStatus(Intent batteryChangedIntent) { + status = batteryChangedIntent.getIntExtra(EXTRA_STATUS, BATTERY_STATUS_UNKNOWN); + plugged = batteryChangedIntent.getIntExtra(EXTRA_PLUGGED, 0); + level = batteryChangedIntent.getIntExtra(EXTRA_LEVEL, 0); + health = batteryChangedIntent.getIntExtra(EXTRA_HEALTH, BATTERY_HEALTH_UNKNOWN); + + final int maxChargingMicroAmp = batteryChangedIntent.getIntExtra(EXTRA_MAX_CHARGING_CURRENT, + -1); + int maxChargingMicroVolt = batteryChangedIntent.getIntExtra(EXTRA_MAX_CHARGING_VOLTAGE, -1); + + if (maxChargingMicroVolt <= 0) { + maxChargingMicroVolt = DEFAULT_CHARGING_VOLTAGE_MICRO_VOLT; + } + if (maxChargingMicroAmp > 0) { + // Calculating muW = muA * muV / (10^6 mu^2 / mu); splitting up the divisor + // to maintain precision equally on both factors. + maxChargingWattage = (maxChargingMicroAmp / 1000) + * (maxChargingMicroVolt / 1000); + } else { + maxChargingWattage = -1; + } + } + + /** + * Determine whether the device is plugged in (USB, power, or wireless). + * + * @return true if the device is plugged in. + */ + public boolean isPluggedIn() { + return plugged == BatteryManager.BATTERY_PLUGGED_AC + || plugged == BatteryManager.BATTERY_PLUGGED_USB + || plugged == BatteryManager.BATTERY_PLUGGED_WIRELESS; + } + + /** + * Determine whether the device is plugged in (USB, power). + * + * @return true if the device is plugged in wired (as opposed to wireless) + */ + public boolean isPluggedInWired() { + return plugged == BatteryManager.BATTERY_PLUGGED_AC + || plugged == BatteryManager.BATTERY_PLUGGED_USB; + } + + /** + * Whether or not the device is charged. Note that some devices never return 100% for + * battery level, so this allows either battery level or status to determine if the + * battery is charged. + * + * @return true if the device is charged + */ + public boolean isCharged() { + return status == BATTERY_STATUS_FULL || level >= 100; + } + + /** + * Whether battery is low and needs to be charged. + * + * @return true if battery is low + */ + public boolean isBatteryLow() { + return level < LOW_BATTERY_THRESHOLD; + } + + /** + * Return current chargin speed is fast, slow or normal. + * + * @return the charing speed + */ + public final int getChargingSpeed(Context context) { + final int slowThreshold = context.getResources().getInteger( + R.integer.config_chargingSlowlyThreshold); + final int fastThreshold = context.getResources().getInteger( + R.integer.config_chargingFastThreshold); + return maxChargingWattage <= 0 ? CHARGING_UNKNOWN : + maxChargingWattage < slowThreshold ? CHARGING_SLOWLY : + maxChargingWattage > fastThreshold ? CHARGING_FAST : + CHARGING_REGULAR; + } + + @Override + public String toString() { + return "BatteryStatus{status=" + status + ",level=" + level + ",plugged=" + plugged + + ",health=" + health + ",maxChargingWattage=" + maxChargingWattage + "}"; + } +} diff --git a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaManager.java index 12d054e307ba..3a807c9b9b77 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaManager.java @@ -108,9 +108,9 @@ public class BluetoothMediaManager extends MediaManager implements BluetoothCall Log.d(TAG, "addConnectableA2dpDevices() device : " + cachedDevice.getName() + ", is connected : " + cachedDevice.isConnected() - + ", is preferred : " + a2dpProfile.isPreferred(device)); + + ", is enabled : " + a2dpProfile.isEnabled(device)); - if (a2dpProfile.isPreferred(device) + if (a2dpProfile.isEnabled(device) && BluetoothDevice.BOND_BONDED == cachedDevice.getBondState()) { addMediaDevice(cachedDevice); } @@ -143,9 +143,9 @@ public class BluetoothMediaManager extends MediaManager implements BluetoothCall Log.d(TAG, "addConnectableHearingAidDevices() device : " + cachedDevice.getName() + ", is connected : " + cachedDevice.isConnected() - + ", is preferred : " + hapProfile.isPreferred(device)); + + ", is enabled : " + hapProfile.isEnabled(device)); - if (hapProfile.isPreferred(device) + if (hapProfile.isEnabled(device) && BluetoothDevice.BOND_BONDED == cachedDevice.getBondState()) { addMediaDevice(cachedDevice); } diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java index d287f95e504a..ba741396a6bf 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java @@ -16,9 +16,12 @@ package com.android.settingslib.media; import android.content.Context; +import android.content.pm.PackageManager; import android.graphics.drawable.Drawable; import android.media.MediaRoute2Info; import android.media.MediaRouter2Manager; +import android.text.TextUtils; +import android.util.Log; import com.android.settingslib.R; import com.android.settingslib.bluetooth.BluetoothUtils; @@ -89,6 +92,31 @@ public class InfoMediaDevice extends MediaDevice { } @Override + public String getClientPackageName() { + return mRouteInfo.getClientPackageName(); + } + + @Override + public String getClientAppLabel() { + final String packageName = mRouteInfo.getClientPackageName(); + if (TextUtils.isEmpty(packageName)) { + Log.d(TAG, "Client package name is empty"); + return mContext.getResources().getString(R.string.unknown); + } + try { + final PackageManager packageManager = mContext.getPackageManager(); + final String appLabel = packageManager.getApplicationLabel( + packageManager.getApplicationInfo(packageName, 0)).toString(); + if (!TextUtils.isEmpty(appLabel)) { + return appLabel; + } + } catch (PackageManager.NameNotFoundException e) { + Log.e(TAG, "unable to find " + packageName); + } + return mContext.getResources().getString(R.string.unknown); + } + + @Override public void disconnect() { //TODO(b/144535188): disconnected last select device } diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java index 50196d2b2994..96d72e755026 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java @@ -235,6 +235,22 @@ public class LocalMediaManager implements BluetoothCallback { return mCurrentConnectedDevice; } + /** + * Find the active MediaDevice. + * + * @param type the media device type. + * @return MediaDevice list + */ + public List<MediaDevice> getActiveMediaDevice(@MediaDevice.MediaDeviceType int type) { + final List<MediaDevice> devices = new ArrayList<>(); + for (MediaDevice device : mMediaDevices) { + if (type == device.mType && device.getClientPackageName() != null) { + devices.add(device); + } + } + return devices; + } + private MediaDevice updateCurrentConnectedDevice() { for (MediaDevice device : mMediaDevices) { if (device instanceof BluetoothMediaDevice) { diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java index 44e70f436963..bfb79c05a432 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java @@ -16,6 +16,9 @@ package com.android.settingslib.wifi; +import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLED; +import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_PERMANENTLY_DISABLED; + import android.annotation.IntDef; import android.annotation.MainThread; import android.annotation.Nullable; @@ -1144,11 +1147,15 @@ public class AccessPoint implements Comparable<AccessPoint> { mInfo != null ? mInfo.getRequestingPackageName() : null)); } else { // not active if (mConfig != null && mConfig.hasNoInternetAccess()) { - int messageID = mConfig.getNetworkSelectionStatus().isNetworkPermanentlyDisabled() + int messageID = + mConfig.getNetworkSelectionStatus().getNetworkSelectionStatus() + == NETWORK_SELECTION_PERMANENTLY_DISABLED ? R.string.wifi_no_internet_no_reconnect : R.string.wifi_no_internet; summary.append(mContext.getString(messageID)); - } else if (mConfig != null && !mConfig.getNetworkSelectionStatus().isNetworkEnabled()) { + } else if (mConfig != null + && (mConfig.getNetworkSelectionStatus().getNetworkSelectionStatus() + != NETWORK_SELECTION_ENABLED)) { WifiConfiguration.NetworkSelectionStatus networkStatus = mConfig.getNetworkSelectionStatus(); switch (networkStatus.getNetworkSelectionDisableReason()) { diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java index d4e0510e15a7..d23364952357 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java @@ -16,9 +16,13 @@ package com.android.settingslib.wifi; +import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLED; +import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.getMaxNetworkSelectionDisableReason; + import android.content.Context; import android.net.wifi.ScanResult; import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiConfiguration.NetworkSelectionStatus; import android.net.wifi.WifiInfo; import android.os.SystemClock; @@ -41,7 +45,9 @@ public class WifiUtils { summary.append(" f=" + Integer.toString(info.getFrequency())); } summary.append(" " + getVisibilityStatus(accessPoint)); - if (config != null && !config.getNetworkSelectionStatus().isNetworkEnabled()) { + if (config != null + && (config.getNetworkSelectionStatus().getNetworkSelectionStatus() + != NETWORK_SELECTION_ENABLED)) { summary.append(" (" + config.getNetworkSelectionStatus().getNetworkStatusString()); if (config.getNetworkSelectionStatus().getDisableTime() > 0) { long now = System.currentTimeMillis(); @@ -58,15 +64,13 @@ public class WifiUtils { } if (config != null) { - WifiConfiguration.NetworkSelectionStatus networkStatus = - config.getNetworkSelectionStatus(); - for (int index = WifiConfiguration.NetworkSelectionStatus.DISABLED_NONE; - index < WifiConfiguration.NetworkSelectionStatus - .NETWORK_SELECTION_DISABLED_MAX; index++) { - if (networkStatus.getDisableReasonCounter(index) != 0) { - summary.append(" " + WifiConfiguration.NetworkSelectionStatus - .getNetworkDisableReasonString(index) + "=" - + networkStatus.getDisableReasonCounter(index)); + NetworkSelectionStatus networkStatus = config.getNetworkSelectionStatus(); + for (int reason = 0; reason <= getMaxNetworkSelectionDisableReason(); reason++) { + if (networkStatus.getDisableReasonCounter(reason) != 0) { + summary.append(" ") + .append(NetworkSelectionStatus.getNetworkDisableReasonString(reason)) + .append("=") + .append(networkStatus.getDisableReasonCounter(reason)); } } } diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java index 42f3cbb04cf8..bcabec858487 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java @@ -465,6 +465,8 @@ public class AccessPointTest { WifiConfiguration.NetworkSelectionStatus status = mock(WifiConfiguration.NetworkSelectionStatus.class); when(configuration.getNetworkSelectionStatus()).thenReturn(status); + when(status.getNetworkSelectionStatus()).thenReturn( + WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_TEMPORARY_DISABLED); when(status.getNetworkSelectionDisableReason()).thenReturn( WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WRONG_PASSWORD); AccessPoint ap = new AccessPoint(mContext, configuration); @@ -1370,13 +1372,13 @@ public class AccessPointTest { public void testOsuAccessPointSummary_showsProvisioningUpdates() { OsuProvider provider = createOsuProvider(); Context spyContext = spy(new ContextWrapper(mContext)); - AccessPoint osuAccessPoint = new AccessPoint(spyContext, provider, - mScanResults); + when(spyContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mMockWifiManager); Map<OsuProvider, PasspointConfiguration> osuProviderConfigMap = new HashMap<>(); osuProviderConfigMap.put(provider, null); - when(spyContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mMockWifiManager); when(mMockWifiManager.getMatchingPasspointConfigsForOsuProviders( Collections.singleton(provider))).thenReturn(osuProviderConfigMap); + AccessPoint osuAccessPoint = new AccessPoint(spyContext, provider, + mScanResults); osuAccessPoint.setListener(mMockAccessPointListener); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java index 11829451f640..6307caf5e02b 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java @@ -31,6 +31,7 @@ import android.content.Intent; import android.content.res.Resources; import android.location.LocationManager; import android.media.AudioManager; +import android.os.BatteryManager; import android.os.SystemProperties; import android.os.UserHandle; import android.provider.Settings; @@ -122,12 +123,12 @@ public class UtilsTest { public void testGetDefaultStorageManagerDaysToRetain_storageManagerDaysToRetainUsesResources() { Resources resources = mock(Resources.class); when(resources.getInteger( - eq( - com.android - .internal - .R - .integer - .config_storageManagerDaystoRetainDefault))) + eq( + com.android + .internal + .R + .integer + .config_storageManagerDaystoRetainDefault))) .thenReturn(60); assertThat(Utils.getDefaultStorageManagerDaysToRetain(resources)).isEqualTo(60); } @@ -147,7 +148,8 @@ public class UtilsTest { private static Map<String, Integer> map = new HashMap<>(); @Implementation - public static boolean putIntForUser(ContentResolver cr, String name, int value, int userHandle) { + public static boolean putIntForUser(ContentResolver cr, String name, int value, + int userHandle) { map.put(name, value); return true; } @@ -312,4 +314,33 @@ public class UtilsTest { assertThat(Utils.getCombinedServiceState(mServiceState)).isEqualTo( ServiceState.STATE_OUT_OF_SERVICE); } + + @Test + public void getBatteryStatus_statusIsFull_returnFullString() { + final Intent intent = new Intent().putExtra(BatteryManager.EXTRA_LEVEL, 100); + final Resources resources = mContext.getResources(); + + assertThat(Utils.getBatteryStatus(mContext, intent)).isEqualTo( + resources.getString(R.string.battery_info_status_full)); + } + + @Test + public void getBatteryStatus_batteryLevelIs100_returnFullString() { + final Intent intent = new Intent().putExtra(BatteryManager.EXTRA_STATUS, + BatteryManager.BATTERY_STATUS_FULL); + final Resources resources = mContext.getResources(); + + assertThat(Utils.getBatteryStatus(mContext, intent)).isEqualTo( + resources.getString(R.string.battery_info_status_full)); + } + + @Test + public void getBatteryStatus_batteryLevel99_returnChargingString() { + final Intent intent = new Intent().putExtra(BatteryManager.EXTRA_STATUS, + BatteryManager.BATTERY_STATUS_CHARGING); + final Resources resources = mContext.getResources(); + + assertThat(Utils.getBatteryStatus(mContext, intent)).isEqualTo( + resources.getString(R.string.battery_info_status_charging)); + } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpSinkProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpSinkProfileTest.java index ccb6646cf683..9bb2f22ddbcf 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpSinkProfileTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpSinkProfileTest.java @@ -16,12 +16,8 @@ package com.android.settingslib.bluetooth; -import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED; -import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; - import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.bluetooth.BluetoothA2dpSink; @@ -68,18 +64,6 @@ public class A2dpSinkProfileTest { } @Test - public void connect_shouldConnectBluetoothA2dpSink() { - mProfile.connect(mBluetoothDevice); - verify(mService).setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_ALLOWED); - } - - @Test - public void disconnect_shouldDisconnectBluetoothA2dpSink() { - mProfile.disconnect(mBluetoothDevice); - verify(mService).setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_FORBIDDEN); - } - - @Test public void getConnectionStatus_shouldReturnConnectionState() { when(mService.getConnectionState(mBluetoothDevice)). thenReturn(BluetoothProfile.STATE_CONNECTED); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HfpClientProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HfpClientProfileTest.java index 91807609df1a..d121e0b2d2fb 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HfpClientProfileTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HfpClientProfileTest.java @@ -16,12 +16,8 @@ package com.android.settingslib.bluetooth; -import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED; -import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; - import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.bluetooth.BluetoothAdapter; @@ -68,18 +64,6 @@ public class HfpClientProfileTest { } @Test - public void connect_shouldConnectBluetoothHeadsetClient() { - mProfile.connect(mBluetoothDevice); - verify(mService).setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_ALLOWED); - } - - @Test - public void disconnect_shouldDisconnectBluetoothHeadsetClient() { - mProfile.disconnect(mBluetoothDevice); - verify(mService).setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_FORBIDDEN); - } - - @Test public void getConnectionStatus_shouldReturnConnectionState() { when(mService.getConnectionState(mBluetoothDevice)). thenReturn(BluetoothProfile.STATE_CONNECTED); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HidDeviceProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HidDeviceProfileTest.java index f38af70c7498..3665d9c10165 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HidDeviceProfileTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HidDeviceProfileTest.java @@ -18,7 +18,6 @@ package com.android.settingslib.bluetooth; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.bluetooth.BluetoothAdapter; @@ -65,17 +64,6 @@ public class HidDeviceProfileTest { } @Test - public void connect_shouldReturnFalse() { - assertThat(mProfile.connect(mBluetoothDevice)).isFalse(); - } - - @Test - public void disconnect_shouldDisconnectBluetoothHidDevice() { - mProfile.disconnect(mBluetoothDevice); - verify(mService).disconnect(mBluetoothDevice); - } - - @Test public void getConnectionStatus_shouldReturnConnectionState() { when(mService.getConnectionState(mBluetoothDevice)). thenReturn(BluetoothProfile.STATE_CONNECTED); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java index 1425c381256b..25031a62294c 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/MapClientProfileTest.java @@ -16,12 +16,8 @@ package com.android.settingslib.bluetooth; -import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED; -import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; - import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.bluetooth.BluetoothAdapter; @@ -68,18 +64,6 @@ public class MapClientProfileTest { } @Test - public void connect_shouldConnectBluetoothMapClient() { - mProfile.connect(mBluetoothDevice); - verify(mService).setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_ALLOWED); - } - - @Test - public void disconnect_shouldDisconnectBluetoothMapClient() { - mProfile.disconnect(mBluetoothDevice); - verify(mService).setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_FORBIDDEN); - } - - @Test public void getConnectionStatus_shouldReturnConnectionState() { when(mService.getConnectionState(mBluetoothDevice)). thenReturn(BluetoothProfile.STATE_CONNECTED); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java index 15f560bef73e..4305a3bc25a3 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/PbapClientProfileTest.java @@ -16,12 +16,8 @@ package com.android.settingslib.bluetooth; -import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED; -import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; - import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.bluetooth.BluetoothAdapter; @@ -68,18 +64,6 @@ public class PbapClientProfileTest { } @Test - public void connect_shouldConnectBluetoothPbapClient() { - mProfile.connect(mBluetoothDevice); - verify(mService).setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_ALLOWED); - } - - @Test - public void disconnect_shouldDisconnectBluetoothPbapClient() { - mProfile.disconnect(mBluetoothDevice); - verify(mService).setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_FORBIDDEN); - } - - @Test public void getConnectionStatus_shouldReturnConnectionState() { when(mService.getConnectionState(mBluetoothDevice)). thenReturn(BluetoothProfile.STATE_CONNECTED); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java index 4f978a822890..e460eaf16bbf 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java @@ -16,12 +16,8 @@ package com.android.settingslib.bluetooth; -import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED; -import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; - import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.bluetooth.BluetoothAdapter; @@ -67,18 +63,6 @@ public class SapProfileTest { } @Test - public void connect_shouldConnectBluetoothSap() { - mProfile.connect(mBluetoothDevice); - verify(mService).setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_ALLOWED); - } - - @Test - public void disconnect_shouldDisconnectBluetoothSap() { - mProfile.disconnect(mBluetoothDevice); - verify(mService).setConnectionPolicy(mBluetoothDevice, CONNECTION_POLICY_FORBIDDEN); - } - - @Test public void getConnectionStatus_shouldReturnConnectionState() { when(mService.getConnectionState(mBluetoothDevice)). thenReturn(BluetoothProfile.STATE_CONNECTED); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaManagerTest.java index f27cef9620c5..0ee5ea8a2eed 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaManagerTest.java @@ -96,7 +96,7 @@ public class BluetoothMediaManagerTest { when(mA2dpProfile.getConnectableDevices()).thenReturn(devices); when(mCachedDeviceManager.findDevice(bluetoothDevice)).thenReturn(cachedDevice); when(cachedDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED); - when(mA2dpProfile.isPreferred(bluetoothDevice)).thenReturn(true); + when(mA2dpProfile.isEnabled(bluetoothDevice)).thenReturn(true); assertThat(mMediaManager.mMediaDevices).isEmpty(); mMediaManager.startScan(); @@ -113,7 +113,7 @@ public class BluetoothMediaManagerTest { when(mA2dpProfile.getConnectableDevices()).thenReturn(devices); when(mCachedDeviceManager.findDevice(bluetoothDevice)).thenReturn(cachedDevice); when(cachedDevice.getBondState()).thenReturn(BluetoothDevice.BOND_NONE); - when(mA2dpProfile.isPreferred(bluetoothDevice)).thenReturn(true); + when(mA2dpProfile.isEnabled(bluetoothDevice)).thenReturn(true); assertThat(mMediaManager.mMediaDevices).isEmpty(); mMediaManager.startScan(); @@ -141,7 +141,7 @@ public class BluetoothMediaManagerTest { when(mHapProfile.getConnectableDevices()).thenReturn(devices); when(mCachedDeviceManager.findDevice(bluetoothDevice)).thenReturn(cachedDevice); when(cachedDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED); - when(mHapProfile.isPreferred(bluetoothDevice)).thenReturn(true); + when(mHapProfile.isEnabled(bluetoothDevice)).thenReturn(true); assertThat(mMediaManager.mMediaDevices).isEmpty(); mMediaManager.startScan(); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java index c9db0d13a7e7..2e304dcd58aa 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java @@ -22,6 +22,9 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageStats; import android.media.MediaRoute2Info; import android.media.MediaRouter2Manager; @@ -34,11 +37,14 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +import org.robolectric.Shadows; +import org.robolectric.shadows.ShadowPackageManager; @RunWith(RobolectricTestRunner.class) public class InfoMediaDeviceTest { private static final String TEST_PACKAGE_NAME = "com.test.packagename"; + private static final String TEST_PACKAGE_NAME2 = "com.test.packagename2"; private static final String TEST_ID = "test_id"; private static final String TEST_NAME = "test_name"; @@ -50,11 +56,24 @@ public class InfoMediaDeviceTest { private Context mContext; private InfoMediaDevice mInfoMediaDevice; + private ShadowPackageManager mShadowPackageManager; + private ApplicationInfo mAppInfo; + private PackageInfo mPackageInfo; + private PackageStats mPackageStats; @Before public void setUp() { MockitoAnnotations.initMocks(this); mContext = RuntimeEnvironment.application; + mShadowPackageManager = Shadows.shadowOf(mContext.getPackageManager()); + mAppInfo = new ApplicationInfo(); + mAppInfo.flags = ApplicationInfo.FLAG_INSTALLED; + mAppInfo.packageName = TEST_PACKAGE_NAME; + mAppInfo.name = TEST_NAME; + mPackageInfo = new PackageInfo(); + mPackageInfo.packageName = TEST_PACKAGE_NAME; + mPackageInfo.applicationInfo = mAppInfo; + mPackageStats = new PackageStats(TEST_PACKAGE_NAME); mInfoMediaDevice = new InfoMediaDevice(mContext, mRouterManager, mRouteInfo, TEST_PACKAGE_NAME); @@ -95,4 +114,32 @@ public class InfoMediaDeviceTest { verify(mRouterManager).selectRoute(TEST_PACKAGE_NAME, mRouteInfo); } + + @Test + public void getClientPackageName_returnPackageName() { + when(mRouteInfo.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME); + + assertThat(mInfoMediaDevice.getClientPackageName()).isEqualTo(TEST_PACKAGE_NAME); + } + + @Test + public void getClientAppLabel_matchedPackageName_returnLabel() { + when(mRouteInfo.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME); + + assertThat(mInfoMediaDevice.getClientAppLabel()).isEqualTo( + mContext.getResources().getString(R.string.unknown)); + + mShadowPackageManager.addPackage(mPackageInfo, mPackageStats); + + assertThat(mInfoMediaDevice.getClientAppLabel()).isEqualTo(TEST_NAME); + } + + @Test + public void getClientAppLabel_noMatchedPackageName_returnDefault() { + mShadowPackageManager.addPackage(mPackageInfo, mPackageStats); + when(mRouteInfo.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME2); + + assertThat(mInfoMediaDevice.getClientAppLabel()).isEqualTo( + mContext.getResources().getString(R.string.unknown)); + } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java index c780a64c2fb4..15aaa8219269 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java @@ -384,4 +384,38 @@ public class LocalMediaManagerTest { verify(mCallback).onDeviceAttributesChanged(); } + + @Test + public void getActiveMediaDevice_checkList() { + final List<MediaDevice> devices = new ArrayList<>(); + final MediaDevice device1 = mock(MediaDevice.class); + final MediaDevice device2 = mock(MediaDevice.class); + final MediaDevice device3 = mock(MediaDevice.class); + device1.mType = MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE; + device2.mType = MediaDevice.MediaDeviceType.TYPE_CAST_DEVICE; + device3.mType = MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE; + when(device1.getClientPackageName()).thenReturn(TEST_DEVICE_ID_1); + when(device2.getClientPackageName()).thenReturn(TEST_DEVICE_ID_2); + when(device3.getClientPackageName()).thenReturn(TEST_DEVICE_ID_3); + when(device1.getId()).thenReturn(TEST_DEVICE_ID_1); + when(device2.getId()).thenReturn(TEST_DEVICE_ID_2); + when(device3.getId()).thenReturn(TEST_DEVICE_ID_3); + devices.add(device1); + devices.add(device2); + devices.add(device3); + mLocalMediaManager.registerCallback(mCallback); + mLocalMediaManager.mMediaDeviceCallback.onDeviceListAdded(devices); + + List<MediaDevice> activeDevices = mLocalMediaManager.getActiveMediaDevice( + MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE); + assertThat(activeDevices).containsExactly(device1); + + activeDevices = mLocalMediaManager.getActiveMediaDevice( + MediaDevice.MediaDeviceType.TYPE_CAST_DEVICE); + assertThat(activeDevices).containsExactly(device2); + + activeDevices = mLocalMediaManager.getActiveMediaDevice( + MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE); + assertThat(activeDevices).containsExactly(device3); + } } diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java index d67a9bc97cb8..8ff595b3bc53 100644 --- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java +++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java @@ -33,7 +33,6 @@ import android.os.UserHandle; import android.provider.Settings; import android.util.Log; -import org.junit.Ignore; import org.junit.Test; import java.util.concurrent.atomic.AtomicBoolean; @@ -692,125 +691,4 @@ public class SettingsProviderTest extends BaseSettingsProviderTest { cursor.close(); } } - - @Test - @Ignore("b/140250974") - public void testLocationModeChanges_viaFrontEndApi() throws Exception { - setStringViaFrontEndApiSetting( - SETTING_TYPE_SECURE, - Settings.Secure.LOCATION_MODE, - String.valueOf(Settings.Secure.LOCATION_MODE_OFF), - UserHandle.USER_SYSTEM); - assertEquals( - "Wrong location providers", - "", - getStringViaFrontEndApiSetting( - SETTING_TYPE_SECURE, - Settings.Secure.LOCATION_PROVIDERS_ALLOWED, - UserHandle.USER_SYSTEM)); - - setStringViaFrontEndApiSetting( - SETTING_TYPE_SECURE, - Settings.Secure.LOCATION_MODE, - String.valueOf(Settings.Secure.LOCATION_MODE_BATTERY_SAVING), - UserHandle.USER_SYSTEM); - assertEquals( - "Wrong location providers", - "network", - getStringViaFrontEndApiSetting( - SETTING_TYPE_SECURE, - Settings.Secure.LOCATION_PROVIDERS_ALLOWED, - UserHandle.USER_SYSTEM)); - - setStringViaFrontEndApiSetting( - SETTING_TYPE_SECURE, - Settings.Secure.LOCATION_MODE, - String.valueOf(Settings.Secure.LOCATION_MODE_HIGH_ACCURACY), - UserHandle.USER_SYSTEM); - assertEquals( - "Wrong location providers", - "gps,network", - getStringViaFrontEndApiSetting( - SETTING_TYPE_SECURE, - Settings.Secure.LOCATION_PROVIDERS_ALLOWED, - UserHandle.USER_SYSTEM)); - } - - @Test - @Ignore("b/140250974") - public void testLocationProvidersAllowed_disableProviders() throws Exception { - setStringViaFrontEndApiSetting( - SETTING_TYPE_SECURE, - Settings.Secure.LOCATION_MODE, - String.valueOf(Settings.Secure.LOCATION_MODE_HIGH_ACCURACY), - UserHandle.USER_SYSTEM); - - // Disable providers that were enabled - updateStringViaProviderApiSetting( - SETTING_TYPE_SECURE, - Settings.Secure.LOCATION_PROVIDERS_ALLOWED, - "-gps,-network"); - assertEquals( - "Wrong location providers", - "", - queryStringViaProviderApi( - SETTING_TYPE_SECURE, Settings.Secure.LOCATION_PROVIDERS_ALLOWED)); - - // Disable a provider that was not enabled - updateStringViaProviderApiSetting( - SETTING_TYPE_SECURE, - Settings.Secure.LOCATION_PROVIDERS_ALLOWED, - "-test"); - assertEquals( - "Wrong location providers", - "", - queryStringViaProviderApi( - SETTING_TYPE_SECURE, Settings.Secure.LOCATION_PROVIDERS_ALLOWED)); - } - - @Test - @Ignore("b/140250974") - public void testLocationProvidersAllowed_enableAndDisable() throws Exception { - setStringViaFrontEndApiSetting( - SETTING_TYPE_SECURE, - Settings.Secure.LOCATION_MODE, - String.valueOf(Settings.Secure.LOCATION_MODE_OFF), - UserHandle.USER_SYSTEM); - - updateStringViaProviderApiSetting( - SETTING_TYPE_SECURE, - Settings.Secure.LOCATION_PROVIDERS_ALLOWED, - "+gps,+network,+test"); - updateStringViaProviderApiSetting( - SETTING_TYPE_SECURE, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "-test"); - - assertEquals( - "Wrong location providers", - "gps,network", - queryStringViaProviderApi( - SETTING_TYPE_SECURE, Settings.Secure.LOCATION_PROVIDERS_ALLOWED)); - } - - @Test - @Ignore("b/140250974") - public void testLocationProvidersAllowedLocked_invalidInput() throws Exception { - setStringViaFrontEndApiSetting( - SETTING_TYPE_SECURE, - Settings.Secure.LOCATION_MODE, - String.valueOf(Settings.Secure.LOCATION_MODE_OFF), - UserHandle.USER_SYSTEM); - - // update providers with a invalid string - updateStringViaProviderApiSetting( - SETTING_TYPE_SECURE, - Settings.Secure.LOCATION_PROVIDERS_ALLOWED, - "+gps, invalid-string"); - - // Verifies providers list does not change - assertEquals( - "Wrong location providers", - "", - queryStringViaProviderApi( - SETTING_TYPE_SECURE, Settings.Secure.LOCATION_PROVIDERS_ALLOWED)); - } } diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 6a89b71be897..1d679c7bcbdd 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -223,6 +223,11 @@ <!-- permissions required for CTS test - PhoneStateListenerTest --> <uses-permission android:name="android.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH" /> + <!-- Permissions required for ganting and logging --> + <uses-permission android:name="android.permission.LOG_COMPAT_CHANGE"/> + <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG"/> + <uses-permission android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG"/> + <!-- Permission required for CTS test - UiModeManagerTest --> <uses-permission android:name="android.permission.ENTER_CAR_MODE_PRIORITIZED"/> diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 139a8c3f7411..1fe967b4750d 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -645,7 +645,6 @@ <activity android:name=".controls.management.ControlsProviderSelectorActivity" android:label="Controls Providers" android:theme="@style/Theme.ControlsManagement" - android:exported="true" android:showForAllUsers="true" android:excludeFromRecents="true" android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden" diff --git a/packages/SystemUI/res-keyguard/values/config.xml b/packages/SystemUI/res-keyguard/values/config.xml index bde6ed531353..8d9d6ee68c67 100644 --- a/packages/SystemUI/res-keyguard/values/config.xml +++ b/packages/SystemUI/res-keyguard/values/config.xml @@ -22,10 +22,4 @@ <!-- Allow the menu hard key to be disabled in LockScreen on some devices [DO NOT TRANSLATE] --> <bool name="config_disableMenuKeyInLockScreen">false</bool> - - <!-- Threshold in micro watts below which a charger is rated as "slow"; 1A @ 5V --> - <integer name="config_chargingSlowlyThreshold">5000000</integer> - - <!-- Threshold in micro watts above which a charger is rated as "fast"; 1.5A @ 5V --> - <integer name="config_chargingFastThreshold">7500000</integer> </resources> diff --git a/packages/SystemUI/res/drawable/ic_device_unknown_gm2_24px.xml b/packages/SystemUI/res/drawable/ic_device_unknown_gm2_24px.xml new file mode 100644 index 000000000000..24e063506250 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_device_unknown_gm2_24px.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:fillColor="#FF000000" + android:pathData="M12,2l-5.5,9h11L12,2zM12,5.84L13.93,9h-3.87L12,5.84zM17.5,13c-2.49,0 -4.5,2.01 -4.5,4.5s2.01,4.5 4.5,4.5 4.5,-2.01 4.5,-4.5 -2.01,-4.5 -4.5,-4.5zM17.5,20c-1.38,0 -2.5,-1.12 -2.5,-2.5s1.12,-2.5 2.5,-2.5 2.5,1.12 2.5,2.5 -1.12,2.5 -2.5,2.5zM3,21.5h8v-8L3,13.5v8zM5,15.5h4v4L5,19.5v-4z"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_touch.xml b/packages/SystemUI/res/drawable/ic_touch.xml new file mode 100644 index 000000000000..4f6698de5251 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_touch.xml @@ -0,0 +1,26 @@ +<!-- +Copyright (C) 2020 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. +--> +<!-- maybe need android:fillType="evenOdd" --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:fillColor="#FF000000" + android:pathData="M9,7.5V11.24C7.79,10.43 7,9.06 7,7.5C7,5.01 9.01,3 11.5,3C13.99,3 16,5.01 16,7.5C16,9.06 15.21,10.43 14,11.24V7.5C14,6.12 12.88,5 11.5,5C10.12,5 9,6.12 9,7.5ZM14.3,13.61L18.84,15.87C19.37,16.09 19.75,16.63 19.75,17.25C19.75,17.31 19.74,17.38 19.73,17.45L18.98,22.72C18.87,23.45 18.29,24 17.54,24H10.75C10.34,24 9.96,23.83 9.69,23.56L4.75,18.62L5.54,17.82C5.74,17.62 6.02,17.49 6.33,17.49C6.39,17.49 6.4411,17.4989 6.4922,17.5078C6.5178,17.5122 6.5433,17.5167 6.57,17.52L10,18.24V7.5C10,6.67 10.67,6 11.5,6C12.33,6 13,6.67 13,7.5V13.5H13.76C13.95,13.5 14.13,13.54 14.3,13.61Z" + /> +</vector> diff --git a/packages/SystemUI/res/layout/auth_credential_password_view.xml b/packages/SystemUI/res/layout/auth_credential_password_view.xml index a1c593fa455c..b14bc7de06c4 100644 --- a/packages/SystemUI/res/layout/auth_credential_password_view.xml +++ b/packages/SystemUI/res/layout/auth_credential_password_view.xml @@ -73,6 +73,7 @@ android:layout_width="208dp" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" + android:minHeight="48dp" android:gravity="center" android:inputType="textPassword" android:maxLength="500" diff --git a/packages/SystemUI/res/layout/screen_record_dialog.xml b/packages/SystemUI/res/layout/screen_record_dialog.xml new file mode 100644 index 000000000000..df576d83323f --- /dev/null +++ b/packages/SystemUI/res/layout/screen_record_dialog.xml @@ -0,0 +1,142 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:background="@drawable/rounded_bg_full"> + + <!-- Header --> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" + android:gravity="center" + android:padding="@dimen/screenrecord_dialog_padding"> + <ImageView + android:layout_width="@dimen/screenrecord_logo_size" + android:layout_height="@dimen/screenrecord_logo_size" + android:src="@drawable/ic_screenrecord" + android:tint="@color/GM2_red_500" + android:layout_marginBottom="@dimen/screenrecord_dialog_padding"/> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="?android:attr/textAppearanceLarge" + android:text="@string/screenrecord_start_label"/> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/screenrecord_description" + android:textAppearance="?android:attr/textAppearanceSmall" + android:paddingTop="@dimen/screenrecord_dialog_padding" + android:paddingBottom="@dimen/screenrecord_dialog_padding"/> + + <!-- Options --> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + <ImageView + android:layout_width="@dimen/screenrecord_logo_size" + android:layout_height="@dimen/screenrecord_logo_size" + android:src="@drawable/ic_mic_26dp" + android:tint="@color/GM2_grey_700" + android:layout_gravity="center" + android:layout_weight="0" + android:layout_marginRight="@dimen/screenrecord_dialog_padding"/> + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="vertical" + android:layout_weight="1"> + <TextView + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_gravity="center_vertical" + android:text="@string/screenrecord_audio_label" + android:textColor="?android:attr/textColorPrimary" + android:textAppearance="?android:attr/textAppearanceMedium"/> + <TextView + android:layout_width="match_parent" + android:layout_height="match_parent" + android:id="@+id/audio_type" + android:text="@string/screenrecord_mic_label" + android:textAppearance="?android:attr/textAppearanceSmall"/> + </LinearLayout> + <Switch + android:layout_width="wrap_content" + android:layout_height="48dp" + android:layout_weight="0" + android:id="@+id/screenrecord_audio_switch"/> + </LinearLayout> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + <ImageView + android:layout_width="@dimen/screenrecord_logo_size" + android:layout_height="@dimen/screenrecord_logo_size" + android:src="@drawable/ic_touch" + android:tint="@color/GM2_grey_700" + android:layout_gravity="center" + android:layout_marginRight="@dimen/screenrecord_dialog_padding"/> + <Switch + android:layout_width="match_parent" + android:layout_height="48dp" + android:id="@+id/screenrecord_taps_switch" + android:text="@string/screenrecord_taps_label" + android:textColor="?android:attr/textColorPrimary" + android:textAppearance="?android:attr/textAppearanceMedium"/> + </LinearLayout> + </LinearLayout> + + <!-- hr --> + <View + android:layout_width="match_parent" + android:layout_height="1dp" + android:background="@color/GM2_grey_300"/> + + <!-- Buttons --> + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:padding="@dimen/screenrecord_dialog_padding"> + <Button + android:id="@+id/button_cancel" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_weight="0" + android:layout_gravity="start" + android:text="@string/cancel" + style="@android:style/Widget.DeviceDefault.Button.Borderless.Colored"/> + <Space + android:layout_width="0dp" + android:layout_height="match_parent" + android:layout_weight="1"/> + <Button + android:id="@+id/button_start" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_weight="0" + android:layout_gravity="end" + android:text="@string/screenrecord_start" + style="@android:style/Widget.DeviceDefault.Button.Colored"/> + </LinearLayout> +</LinearLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 9c997e88c0c2..c4fa4e5d4ef3 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -1090,7 +1090,7 @@ <!-- Blur radius on status bar window and power menu --> <dimen name="min_window_blur_radius">1px</dimen> - <dimen name="max_window_blur_radius">100px</dimen> + <dimen name="max_window_blur_radius">250px</dimen> <!-- How much into a DisplayCutout's bounds we can go, on each side --> <dimen name="display_cutout_margin_consumption">0px</dimen> @@ -1209,5 +1209,7 @@ <dimen name="controls_card_margin">2dp</dimen> - + <!-- Screen Record --> + <dimen name="screenrecord_dialog_padding">18dp</dimen> + <dimen name="screenrecord_logo_size">24dp</dimen> </resources> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 07a926fa75d3..b85b51e4f2cc 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -217,15 +217,31 @@ your organization</string> <!-- Notification title displayed for screen recording [CHAR LIMIT=50]--> - <string name="screenrecord_name">Screen Recording</string> + <string name="screenrecord_name">Screen Recorder</string> <!-- Description of the screen recording notification channel [CHAR LIMIT=NONE]--> <string name="screenrecord_channel_description">Ongoing notification for a screen record session</string> - <!-- Label for the button to begin screen recording [CHAR LIMIT=NONE]--> - <string name="screenrecord_start_label">Start Recording</string> - <!-- Label for the checkbox to enable microphone input during screen recording [CHAR LIMIT=NONE]--> - <string name="screenrecord_mic_label">Record voiceover</string> + <!-- Title for the screen prompting the user to begin recording their screen [CHAR LIMIT=NONE]--> + <string name="screenrecord_start_label">Start Recording?</string> + <!-- Message reminding the user that sensitive information may be captured during a screen recording [CHAR_LIMIT=NONE]--> + <string name="screenrecord_description">While recording, Android System can capture any sensitive information that\u2019s visible on your screen or played on your device. This includes passwords, payment info, photos, messages, and audio.</string> + <!-- Label for a switch to enable recording audio [CHAR LIMIT=NONE]--> + <string name="screenrecord_audio_label">Record audio</string> + <!-- Label for the option to record audio from the device [CHAR LIMIT=NONE]--> + <string name="screenrecord_device_audio_label">Device audio</string> + <!-- Description of what audio may be captured from the device [CHAR LIMIT=NONE]--> + <string name="screenrecord_device_audio_description">Sound from your device, like music, calls, and ringtones</string> + <!-- Label for the option to enable microphone input during screen recording [CHAR LIMIT=NONE]--> + <string name="screenrecord_mic_label">Microphone</string> + <!-- Label for an option to record audio from both device and microphone [CHAR LIMIT=NONE]--> + <string name="screenrecord_device_audio_and_mic_label">Device audio and microphone</string> + <!-- Button to start a screen recording [CHAR LIMIT=50]--> + <string name="screenrecord_start">Start</string> + <!-- Notification text displayed when we are recording the screen [CHAR LIMIT=100]--> + <string name="screenrecord_ongoing_screen_only">Recording screen</string> + <!-- Notification text displayed when we are recording both the screen and audio [CHAR LIMIT=100]--> + <string name="screenrecord_ongoing_screen_and_audio">Recording screen and audio</string> <!-- Label for the checkbox to enable showing location of touches during screen recording [CHAR LIMIT=NONE]--> - <string name="screenrecord_taps_label">Show taps</string> + <string name="screenrecord_taps_label">Show touches on screen</string> <!-- Label for notification that the user can tap to stop and save the screen recording [CHAR LIMIT=NONE] --> <string name="screenrecord_stop_text">Tap to stop</string> <!-- Label for notification action to stop and save the screen recording [CHAR LIMIT=35] --> @@ -250,6 +266,8 @@ <string name="screenrecord_delete_error">Error deleting screen recording</string> <!-- A toast message shown when the screen recording cannot be started due to insufficient permissions [CHAR LIMIT=NONE] --> <string name="screenrecord_permission_error">Failed to get permissions</string> + <!-- A toast message shown when the screen recording cannot be started due to a generic error [CHAR LIMIT=NONE] --> + <string name="screenrecord_start_error">Error starting screen recording</string> <!-- Title for the USB function chooser in UsbPreferenceActivity. [CHAR LIMIT=30] --> <string name="usb_preference_title">USB file transfer options</string> diff --git a/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java b/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java index b2423b9bf252..85724a969fed 100644 --- a/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java +++ b/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java @@ -27,7 +27,7 @@ import android.os.IBinder; import android.os.RemoteException; import android.os.UserHandle; import android.util.Log; -import android.view.SurfaceControl; +import android.view.SurfaceControlViewHost; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.ViewGroup; @@ -47,7 +47,6 @@ public class AdminSecondaryLockScreenController { private Handler mHandler; private IKeyguardClient mClient; private KeyguardSecurityCallback mKeyguardCallback; - private SurfaceControl.Transaction mTransaction; private final ServiceConnection mConnection = new ServiceConnection() { @Override @@ -84,13 +83,13 @@ public class AdminSecondaryLockScreenController { } @Override - public void onSurfaceControlCreated(@Nullable SurfaceControl remoteSurfaceControl) { + public void onRemoteContentReady( + @Nullable SurfaceControlViewHost.SurfacePackage surfacePackage) { if (mHandler != null) { mHandler.removeCallbacksAndMessages(null); } - if (remoteSurfaceControl != null) { - mTransaction.reparent(remoteSurfaceControl, mView.getSurfaceControl()) - .apply(); + if (surfacePackage != null) { + mView.setChildSurfacePackage(surfacePackage); } else { dismiss(KeyguardUpdateMonitor.getCurrentUser()); } @@ -138,11 +137,10 @@ public class AdminSecondaryLockScreenController { public AdminSecondaryLockScreenController(Context context, ViewGroup parent, KeyguardUpdateMonitor updateMonitor, KeyguardSecurityCallback callback, - Handler handler, SurfaceControl.Transaction transaction) { + Handler handler) { mContext = context; mHandler = handler; mParent = parent; - mTransaction = transaction; mUpdateMonitor = updateMonitor; mKeyguardCallback = callback; mView = new AdminSecurityView(mContext, mSurfaceHolderCallback); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java index 29c67ae1b4a6..ba8a1a945a77 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java @@ -39,7 +39,6 @@ import android.util.Slog; import android.util.TypedValue; import android.view.LayoutInflater; import android.view.MotionEvent; -import android.view.SurfaceControl; import android.view.VelocityTracker; import android.view.View; import android.view.ViewConfiguration; @@ -149,8 +148,7 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe mViewConfiguration = ViewConfiguration.get(context); mKeyguardStateController = Dependency.get(KeyguardStateController.class); mSecondaryLockScreenController = new AdminSecondaryLockScreenController(context, this, - mUpdateMonitor, mCallback, new Handler(Looper.myLooper()), - new SurfaceControl.Transaction()); + mUpdateMonitor, mCallback, new Handler(Looper.myLooper())); } public void setSecurityCallback(SecurityCallback callback) { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 6a04583b70ba..e5f6d3c90eb7 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -21,15 +21,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.Intent.ACTION_USER_REMOVED; import static android.content.Intent.ACTION_USER_STOPPED; import static android.content.Intent.ACTION_USER_UNLOCKED; -import static android.os.BatteryManager.BATTERY_HEALTH_UNKNOWN; -import static android.os.BatteryManager.BATTERY_STATUS_FULL; import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN; -import static android.os.BatteryManager.EXTRA_HEALTH; -import static android.os.BatteryManager.EXTRA_LEVEL; -import static android.os.BatteryManager.EXTRA_MAX_CHARGING_CURRENT; -import static android.os.BatteryManager.EXTRA_MAX_CHARGING_VOLTAGE; -import static android.os.BatteryManager.EXTRA_PLUGGED; -import static android.os.BatteryManager.EXTRA_STATUS; import static android.telephony.PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE; import static android.telephony.TelephonyManager.MODEM_COUNT_DUAL_MODEM; @@ -67,7 +59,6 @@ import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback; import android.hardware.fingerprint.FingerprintManager.AuthenticationResult; import android.media.AudioManager; -import android.os.BatteryManager; import android.os.CancellationSignal; import android.os.Handler; import android.os.IRemoteCallback; @@ -94,6 +85,7 @@ import android.util.SparseBooleanArray; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.widget.LockPatternUtils; import com.android.settingslib.WirelessUtils; +import com.android.settingslib.fuelgauge.BatteryStatus; import com.android.systemui.DejankUtils; import com.android.systemui.DumpController; import com.android.systemui.Dumpable; @@ -1059,29 +1051,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab MSG_TIMEZONE_UPDATE, intent.getStringExtra("time-zone")); mHandler.sendMessage(msg); } else if (Intent.ACTION_BATTERY_CHANGED.equals(action)) { - final int status = intent.getIntExtra(EXTRA_STATUS, BATTERY_STATUS_UNKNOWN); - final int plugged = intent.getIntExtra(EXTRA_PLUGGED, 0); - final int level = intent.getIntExtra(EXTRA_LEVEL, 0); - final int health = intent.getIntExtra(EXTRA_HEALTH, BATTERY_HEALTH_UNKNOWN); - final int maxChargingMicroAmp = intent.getIntExtra(EXTRA_MAX_CHARGING_CURRENT, -1); - int maxChargingMicroVolt = intent.getIntExtra(EXTRA_MAX_CHARGING_VOLTAGE, -1); - final int maxChargingMicroWatt; - - if (maxChargingMicroVolt <= 0) { - maxChargingMicroVolt = DEFAULT_CHARGING_VOLTAGE_MICRO_VOLT; - } - if (maxChargingMicroAmp > 0) { - // Calculating muW = muA * muV / (10^6 mu^2 / mu); splitting up the divisor - // to maintain precision equally on both factors. - maxChargingMicroWatt = (maxChargingMicroAmp / 1000) - * (maxChargingMicroVolt / 1000); - } else { - maxChargingMicroWatt = -1; - } final Message msg = mHandler.obtainMessage( - MSG_BATTERY_UPDATE, new BatteryStatus(status, level, plugged, health, - maxChargingMicroWatt)); + MSG_BATTERY_UPDATE, new BatteryStatus(intent)); mHandler.sendMessage(msg); } else if (Intent.ACTION_SIM_STATE_CHANGED.equals(action)) { SimData args = SimData.fromIntent(intent); @@ -1323,82 +1295,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } } - public static class BatteryStatus { - public static final int CHARGING_UNKNOWN = -1; - public static final int CHARGING_SLOWLY = 0; - public static final int CHARGING_REGULAR = 1; - public static final int CHARGING_FAST = 2; - - public final int status; - public final int level; - public final int plugged; - public final int health; - public final int maxChargingWattage; - - public BatteryStatus(int status, int level, int plugged, int health, - int maxChargingWattage) { - this.status = status; - this.level = level; - this.plugged = plugged; - this.health = health; - this.maxChargingWattage = maxChargingWattage; - } - - /** - * Determine whether the device is plugged in (USB, power, or wireless). - * - * @return true if the device is plugged in. - */ - public boolean isPluggedIn() { - return plugged == BatteryManager.BATTERY_PLUGGED_AC - || plugged == BatteryManager.BATTERY_PLUGGED_USB - || plugged == BatteryManager.BATTERY_PLUGGED_WIRELESS; - } - - /** - * Determine whether the device is plugged in (USB, power). - * - * @return true if the device is plugged in wired (as opposed to wireless) - */ - public boolean isPluggedInWired() { - return plugged == BatteryManager.BATTERY_PLUGGED_AC - || plugged == BatteryManager.BATTERY_PLUGGED_USB; - } - - /** - * Whether or not the device is charged. Note that some devices never return 100% for - * battery level, so this allows either battery level or status to determine if the - * battery is charged. - * - * @return true if the device is charged - */ - public boolean isCharged() { - return status == BATTERY_STATUS_FULL || level >= 100; - } - - /** - * Whether battery is low and needs to be charged. - * - * @return true if battery is low - */ - public boolean isBatteryLow() { - return level < LOW_BATTERY_THRESHOLD; - } - - public final int getChargingSpeed(int slowThreshold, int fastThreshold) { - return maxChargingWattage <= 0 ? CHARGING_UNKNOWN : - maxChargingWattage < slowThreshold ? CHARGING_SLOWLY : - maxChargingWattage > fastThreshold ? CHARGING_FAST : - CHARGING_REGULAR; - } - - @Override - public String toString() { - return "BatteryStatus{status=" + status + ",level=" + level + ",plugged=" + plugged - + ",health=" + health + ",maxChargingWattage=" + maxChargingWattage + "}"; - } - } - public static class StrongAuthTracker extends LockPatternUtils.StrongAuthTracker { private final Consumer<Integer> mStrongAuthRequiredChangedCallback; @@ -2640,9 +2536,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab */ private boolean refreshSimState(int subId, int slotId) { final TelephonyManager tele = - (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); + (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); int state = (tele != null) ? - tele.getSimState(slotId) : TelephonyManager.SIM_STATE_UNKNOWN; + tele.getSimState(slotId) : TelephonyManager.SIM_STATE_UNKNOWN; SimData data = mSimDatas.get(subId); final boolean changed; if (data == null) { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java index 8e87b7ad45b9..49f72a925a0e 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java @@ -23,6 +23,7 @@ import android.os.SystemClock; import android.telephony.TelephonyManager; import android.view.WindowManagerPolicyConstants; +import com.android.settingslib.fuelgauge.BatteryStatus; import com.android.systemui.statusbar.KeyguardIndicationController; import java.util.TimeZone; @@ -42,7 +43,7 @@ public class KeyguardUpdateMonitorCallback { * * @param status current battery status */ - public void onRefreshBatteryInfo(KeyguardUpdateMonitor.BatteryStatus status) { } + public void onRefreshBatteryInfo(BatteryStatus status) { } /** * Called once per minute or when the time changes. diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java index 5c65977c8929..8a492a83b3df 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java @@ -87,7 +87,7 @@ public class AuthContainerView extends LinearLayout @VisibleForTesting @Nullable AuthBiometricView mBiometricView; @VisibleForTesting @Nullable AuthCredentialView mCredentialView; - private final ImageView mBackgroundView; + @VisibleForTesting final ImageView mBackgroundView; @VisibleForTesting final ScrollView mBiometricScrollView; private final View mPanelView; @@ -333,6 +333,12 @@ public class AuthContainerView extends LinearLayout throw new IllegalStateException("Unknown credential type: " + credentialType); } + // The background is used for detecting taps / cancelling authentication. Since the + // credential view is full-screen and should not be canceled from background taps, + // disable it. + mBackgroundView.setOnClickListener(null); + mBackgroundView.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO); + mCredentialView.setContainerView(this); mCredentialView.setEffectiveUserId(mEffectiveUserId); mCredentialView.setCredentialType(credentialType); diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index 7c07c9d81000..2f1e4b41bd2d 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -970,7 +970,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi * The cancellation of summaries with children associated with bubbles are also handled in this * method. User-cancelled summaries are tracked by {@link BubbleData#addSummaryToSuppress}. * - * @return true if we want to intercept the dismissal of the entry, else false + * @return true if we want to intercept the dismissal of the entry, else false. */ public boolean shouldInterceptDismissal(NotificationEntry entry, int dismissReason) { if (entry == null) { @@ -1010,7 +1010,8 @@ public class BubbleController implements ConfigurationController.ConfigurationLi // The bubble notification sticks around in the data as long as the bubble is // not dismissed and the app hasn't cancelled the notification. Bubble bubble = mBubbleData.getBubbleWithKey(key); - boolean bubbleExtended = entry != null && entry.isBubble() && userRemovedNotif; + boolean bubbleExtended = entry != null && entry.isBubble() + && (userRemovedNotif || isUserCreatedBubble(bubble.getKey())); if (bubbleExtended) { bubble.setSuppressNotification(true); bubble.setShowDot(false /* show */, true /* animate */); @@ -1019,8 +1020,7 @@ public class BubbleController implements ConfigurationController.ConfigurationLi + ".shouldInterceptDismissal"); } return true; - } else if (!userRemovedNotif && entry != null - && !isUserCreatedBubble(bubble.getKey())) { + } else if (!userRemovedNotif && entry != null) { // This wasn't a user removal so we should remove the bubble as well mBubbleData.notificationEntryRemoved(entry, DISMISS_NOTIF_CANCEL); return false; diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java index 6062a3d45be0..20d19ece575c 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java @@ -533,10 +533,6 @@ public class BubbleStackView extends FrameLayout { mBubbleContainer.addView(mOverflowBtn, 0, new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT)); - mOverflowBtn.setOnClickListener(v -> { - setSelectedBubble(null); - }); - TypedArray ta = mContext.obtainStyledAttributes( new int[]{android.R.attr.colorBackgroundFloating}); int bgColor = ta.getColor(0, Color.WHITE /* default */); @@ -856,6 +852,10 @@ public class BubbleStackView extends FrameLayout { updateBubbleZOrdersAndDotPosition(false /* animate */); } + void showOverflow() { + setSelectedBubble(null); + } + /** * Changes the currently selected bubble. If the stack is already expanded, the newly selected * bubble will be shown immediately. This does not change the expanded state or change the @@ -950,6 +950,10 @@ public class BubbleStackView extends FrameLayout { } if (mIsExpanded) { if (isIntersecting(mBubbleContainer, x, y)) { + if (BubbleExperimentConfig.allowBubbleOverflow(mContext) + && isIntersecting(mOverflowBtn, x, y)) { + return mOverflowBtn; + } // Could be tapping or dragging a bubble while expanded for (int i = 0; i < getBubbleCount(); i++) { BadgedImageView view = (BadgedImageView) mBubbleContainer.getChildAt(i); diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java index fdeaf1f016c3..5a9d44b6da2c 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java @@ -24,6 +24,7 @@ import android.view.View; import android.view.ViewConfiguration; import com.android.systemui.Dependency; +import com.android.systemui.R; /** * Handles interpreting touches on a {@link BubbleStackView}. This includes expanding, collapsing, @@ -109,6 +110,10 @@ class BubbleTouchHandler implements View.OnTouchListener { if (!(mTouchedView instanceof BadgedImageView) && !(mTouchedView instanceof BubbleStackView) && !(mTouchedView instanceof BubbleFlyoutView)) { + + if (mTouchedView.getId() == R.id.bubble_overflow_button) { + mStack.showOverflow(); + } // Not touching anything touchable, but we shouldn't collapse (e.g. touching edge // of expanded view). mStack.hideBubbleMenu(); diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt index a6f1d84877c5..7de1557ebc65 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt @@ -19,9 +19,12 @@ package com.android.systemui.controls.controller import android.app.PendingIntent import android.content.BroadcastReceiver import android.content.ComponentName +import android.content.ContentResolver import android.content.Context import android.content.Intent import android.content.IntentFilter +import android.database.ContentObserver +import android.net.Uri import android.os.Environment import android.os.UserHandle import android.provider.Settings @@ -30,6 +33,7 @@ import android.service.controls.actions.ControlAction import android.util.ArrayMap import android.util.Log import com.android.internal.annotations.GuardedBy +import com.android.internal.annotations.VisibleForTesting import com.android.systemui.DumpController import com.android.systemui.Dumpable import com.android.systemui.broadcast.BroadcastDispatcher @@ -53,15 +57,16 @@ class ControlsControllerImpl @Inject constructor ( private val uiController: ControlsUiController, private val bindingController: ControlsBindingController, private val listingController: ControlsListingController, - broadcastDispatcher: BroadcastDispatcher, + private val broadcastDispatcher: BroadcastDispatcher, optionalWrapper: Optional<ControlsFavoritePersistenceWrapper>, dumpController: DumpController ) : Dumpable, ControlsController { companion object { private const val TAG = "ControlsControllerImpl" - const val CONTROLS_AVAILABLE = "systemui.controls_available" - const val USER_CHANGE_RETRY_DELAY = 500L // ms + internal const val CONTROLS_AVAILABLE = "systemui.controls_available" + internal val URI = Settings.Secure.getUriFor(CONTROLS_AVAILABLE) + private const val USER_CHANGE_RETRY_DELAY = 500L // ms } // Map of map: ComponentName -> (String -> ControlInfo). @@ -69,9 +74,11 @@ class ControlsControllerImpl @Inject constructor ( @GuardedBy("currentFavorites") private val currentFavorites = ArrayMap<ComponentName, MutableMap<String, ControlInfo>>() - private var userChanging = true - override var available = Settings.Secure.getInt( - context.contentResolver, CONTROLS_AVAILABLE, 0) != 0 + private var userChanging: Boolean = true + + private val contentResolver: ContentResolver + get() = context.contentResolver + override var available = Settings.Secure.getInt(contentResolver, CONTROLS_AVAILABLE, 0) != 0 private set private var currentUser = context.user @@ -95,8 +102,8 @@ class ControlsControllerImpl @Inject constructor ( val fileName = Environment.buildPath( userContext.filesDir, ControlsFavoritePersistenceWrapper.FILE_NAME) persistenceWrapper.changeFile(fileName) - available = Settings.Secure.getIntForUser( - context.contentResolver, CONTROLS_AVAILABLE, 0) != 0 + available = Settings.Secure.getIntForUser(contentResolver, CONTROLS_AVAILABLE, + /* default */ 0, newUser.identifier) != 0 synchronized(currentFavorites) { currentFavorites.clear() } @@ -123,6 +130,25 @@ class ControlsControllerImpl @Inject constructor ( } } + @VisibleForTesting + internal val settingObserver = object : ContentObserver(null) { + override fun onChange(selfChange: Boolean, uri: Uri, userId: Int) { + // Do not listen to changes in the middle of user change, those will be read by the + // user-switch receiver. + if (userChanging || userId != currentUserId) { + return + } + available = Settings.Secure.getIntForUser(contentResolver, CONTROLS_AVAILABLE, + /* default */ 0, currentUserId) != 0 + synchronized(currentFavorites) { + currentFavorites.clear() + } + if (available) { + loadFavorites() + } + } + } + init { dumpController.registerDumpable(this) if (available) { @@ -135,6 +161,7 @@ class ControlsControllerImpl @Inject constructor ( executor, UserHandle.ALL ) + contentResolver.registerContentObserver(URI, false, settingObserver, UserHandle.USER_ALL) } private fun confirmAvailability(): Boolean { diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt index fad2d94d6cf3..88b19b58a453 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt @@ -34,14 +34,17 @@ import android.widget.ImageView import android.widget.TextView import com.android.systemui.controls.controller.ControlsController +import com.android.systemui.util.concurrency.DelayableExecutor import com.android.systemui.R const val MIN_LEVEL = 0 const val MAX_LEVEL = 10000 +private const val UPDATE_DELAY_IN_MILLIS = 2000L class ControlViewHolder( val layout: ViewGroup, - val controlsController: ControlsController + val controlsController: ControlsController, + val uiExecutor: DelayableExecutor ) { val icon: ImageView = layout.requireViewById(R.id.icon) val status: TextView = layout.requireViewById(R.id.status) @@ -52,6 +55,7 @@ class ControlViewHolder( val clipLayer: ClipDrawable val gd: GradientDrawable lateinit var cws: ControlWithState + var cancelUpdate: Runnable? = null init { val ld = layout.getBackground() as LayerDrawable @@ -63,6 +67,8 @@ class ControlViewHolder( fun bindData(cws: ControlWithState) { this.cws = cws + cancelUpdate?.run() + val (status, template) = cws.control?.let { title.setText(it.getTitle()) subtitle.setText(it.getSubtitle()) @@ -86,6 +92,27 @@ class ControlViewHolder( findBehavior(status, template).apply(this, cws) } + fun actionResponse(@ControlAction.ResponseResult response: Int) { + val text = when (response) { + ControlAction.RESPONSE_OK -> "Success" + ControlAction.RESPONSE_FAIL -> "Error" + else -> "" + } + + if (!text.isEmpty()) { + val previousText = status.getText() + val previousTextExtra = statusExtra.getText() + + cancelUpdate = uiExecutor.executeDelayed({ + status.setText(previousText) + statusExtra.setText(previousTextExtra) + }, UPDATE_DELAY_IN_MILLIS) + + status.setText(text) + statusExtra.setText("") + } + } + fun action(action: ControlAction) { controlsController.action(cws.ci, action) } diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt index b07a75d5e757..d70c86fc3266 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt @@ -22,6 +22,8 @@ import android.service.controls.actions.ControlAction import android.view.ViewGroup interface ControlsUiController { + val available: Boolean + fun show(parent: ViewGroup) fun hide() fun onRefreshState(componentName: ComponentName, controls: List<Control>) diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt index a777faf57fce..ed521e3be535 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt @@ -38,10 +38,10 @@ import com.android.systemui.controls.controller.ControlInfo import com.android.systemui.controls.management.ControlsProviderSelectorActivity import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.R +import com.android.systemui.util.concurrency.DelayableExecutor import dagger.Lazy -import java.util.concurrent.Executor import javax.inject.Inject import javax.inject.Singleton @@ -104,18 +104,23 @@ class TokenProviderConnection(val cc: ControlsController, val context: Context) } } +private data class ControlKey(val componentName: ComponentName, val controlId: String) + @Singleton class ControlsUiControllerImpl @Inject constructor ( val controlsController: Lazy<ControlsController>, val context: Context, - @Main val uiExecutor: Executor + @Main val uiExecutor: DelayableExecutor ) : ControlsUiController { private lateinit var controlInfos: List<ControlInfo> - private val controlsById = mutableMapOf<Pair<ComponentName, String>, ControlWithState>() - private val controlViewsById = mutableMapOf<String, ControlViewHolder>() + private val controlsById = mutableMapOf<ControlKey, ControlWithState>() + private val controlViewsById = mutableMapOf<ControlKey, ControlViewHolder>() private lateinit var parent: ViewGroup + override val available: Boolean + get() = controlsController.get().available + override fun show(parent: ViewGroup) { Log.d(TAG, "show()") @@ -125,7 +130,7 @@ class ControlsUiControllerImpl @Inject constructor ( controlInfos.map { ControlWithState(it, null) - }.associateByTo(controlsById) { Pair(it.ci.component, it.ci.controlId) } + }.associateByTo(controlsById) { ControlKey(it.ci.component, it.ci.controlId) } if (controlInfos.isEmpty()) { showInitialSetupView() @@ -178,9 +183,10 @@ class ControlsUiControllerImpl @Inject constructor ( val item = inflater.inflate( R.layout.controls_base_item, lastRow, false) as ViewGroup lastRow.addView(item) - val cvh = ControlViewHolder(item, controlsController.get()) - cvh.bindData(controlsById.get(Pair(it.component, it.controlId))!!) - controlViewsById.put(it.controlId, cvh) + val cvh = ControlViewHolder(item, controlsController.get(), uiExecutor) + val key = ControlKey(it.component, it.controlId) + cvh.bindData(controlsById.getValue(key)) + controlViewsById.put(key, cvh) } if ((controlInfos.size % 2) == 1) { @@ -205,21 +211,24 @@ class ControlsUiControllerImpl @Inject constructor ( override fun onRefreshState(componentName: ComponentName, controls: List<Control>) { Log.d(TAG, "onRefreshState()") controls.forEach { c -> - controlsById.get(Pair(componentName, c.getControlId()))?.let { + controlsById.get(ControlKey(componentName, c.getControlId()))?.let { Log.d(TAG, "onRefreshState() for id: " + c.getControlId()) val cws = ControlWithState(it.ci, c) - controlsById.put(Pair(componentName, c.getControlId()), cws) + val key = ControlKey(componentName, c.getControlId()) + controlsById.put(key, cws) uiExecutor.execute { - controlViewsById.get(c.getControlId())?.bindData(cws) + controlViewsById.get(key)?.bindData(cws) } } } } override fun onActionResponse(componentName: ComponentName, controlId: String, response: Int) { - Log.d(TAG, "onActionResponse()") - TODO("not implemented") + val key = ControlKey(componentName, controlId) + uiExecutor.execute { + controlViewsById.get(key)?.actionResponse(response) + } } private fun createRow(inflater: LayoutInflater, parent: ViewGroup): ViewGroup { diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt index 093c99f57a9a..da52c6f8ee21 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt @@ -108,10 +108,18 @@ private val deviceIconMap = mapOf<Int, IconState>( DeviceTypes.TYPE_OUTLET to IconState( R.drawable.ic_power_off_gm2_24px, R.drawable.ic_power_gm2_24px + ), + DeviceTypes.TYPE_VACUUM to IconState( + R.drawable.ic_vacuum_gm2_24px, + R.drawable.ic_vacuum_gm2_24px + ), + DeviceTypes.TYPE_MOP to IconState( + R.drawable.ic_vacuum_gm2_24px, + R.drawable.ic_vacuum_gm2_24px ) ).withDefault { IconState( - R.drawable.ic_light_off_gm2_24px, - R.drawable.ic_lightbulb_outline_gm2_24px + R.drawable.ic_device_unknown_gm2_24px, + R.drawable.ic_device_unknown_gm2_24px ) } diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java index 45c07a3e4693..b3fc027d1ac7 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java @@ -1725,7 +1725,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, if (!(mBackgroundDrawable instanceof ScrimDrawable)) { return; } - ((ScrimDrawable) mBackgroundDrawable).setColor(Color.BLACK, animate); + ((ScrimDrawable) mBackgroundDrawable).setColor(colors.supportsDarkText() ? Color.WHITE + : Color.BLACK, animate); View decorView = getWindow().getDecorView(); if (colors.supportsDarkText()) { decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR | @@ -1899,9 +1900,7 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener, } private boolean shouldShowControls() { - return isCurrentUserOwner() - && !mKeyguardManager.isDeviceLocked() - && Settings.Secure.getInt(mContext.getContentResolver(), - "systemui.controls_available", 0) == 1; + return !mKeyguardManager.isDeviceLocked() + && mControlsUiController.getAvailable(); } } diff --git a/packages/SystemUI/src/com/android/systemui/log/Event.java b/packages/SystemUI/src/com/android/systemui/log/Event.java deleted file mode 100644 index 7bc1abfbb0d8..000000000000 --- a/packages/SystemUI/src/com/android/systemui/log/Event.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * 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.log; - -import android.annotation.IntDef; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * Stores information about an event that occurred in SystemUI to be used for debugging and triage. - * Every event has a time stamp, log level and message. - * Events are stored in {@link SysuiLog} and can be printed in a dumpsys. - */ -public class Event { - public static final int UNINITIALIZED = -1; - - @IntDef({ERROR, WARN, INFO, DEBUG, VERBOSE}) - @Retention(RetentionPolicy.SOURCE) - public @interface Level {} - public static final int VERBOSE = 2; - public static final int DEBUG = 3; - public static final int INFO = 4; - public static final int WARN = 5; - public static final int ERROR = 6; - public static final @Level int DEFAULT_LOG_LEVEL = DEBUG; - - private long mTimestamp; - private @Level int mLogLevel = DEFAULT_LOG_LEVEL; - private String mMessage = ""; - - /** - * initialize an event with a message - */ - public Event init(String message) { - init(DEFAULT_LOG_LEVEL, message); - return this; - } - - /** - * initialize an event with a logLevel and message - */ - public Event init(@Level int logLevel, String message) { - mTimestamp = System.currentTimeMillis(); - mLogLevel = logLevel; - mMessage = message; - return this; - } - - public String getMessage() { - return mMessage; - } - - public long getTimestamp() { - return mTimestamp; - } - - public @Level int getLogLevel() { - return mLogLevel; - } - - /** - * Recycle this event - */ - void recycle() { - mTimestamp = -1; - mLogLevel = DEFAULT_LOG_LEVEL; - mMessage = ""; - } -} diff --git a/packages/SystemUI/src/com/android/systemui/log/RichEvent.java b/packages/SystemUI/src/com/android/systemui/log/RichEvent.java deleted file mode 100644 index 470f2b0d1b98..000000000000 --- a/packages/SystemUI/src/com/android/systemui/log/RichEvent.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * 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.log; - -/** - * Stores information about an event that occurred in SystemUI to be used for debugging and triage. - * Every rich event has a time stamp, event type, and log level, with the option to provide the - * reason this event was triggered. - * Events are stored in {@link SysuiLog} and can be printed in a dumpsys. - */ -public abstract class RichEvent extends Event { - private int mType; - - /** - * Initializes a rich event that includes an event type that matches with an index in the array - * getEventLabels(). - */ - public RichEvent init(@Event.Level int logLevel, int type, String reason) { - final int numEvents = getEventLabels().length; - if (type < 0 || type >= numEvents) { - throw new IllegalArgumentException("Unsupported event type. Events only supported" - + " from 0 to " + (numEvents - 1) + ", but given type=" + type); - } - mType = type; - super.init(logLevel, getEventLabels()[mType] + " " + reason); - return this; - } - - /** - * Returns an array of the event labels. The index represents the event type and the - * corresponding String stored at that index is the user-readable representation of that event. - * @return array of user readable events, where the index represents its event type constant - */ - public abstract String[] getEventLabels(); - - @Override - public void recycle() { - super.recycle(); - mType = -1; - } - - public int getType() { - return mType; - } - - /** - * Builder to build a RichEvent. - * @param <B> Log specific builder that is extending this builder - * @param <E> Type of event we'll be building - */ - public abstract static class Builder<B extends Builder<B, E>, E extends RichEvent> { - public static final int UNINITIALIZED = -1; - - public final SysuiLog mLog; - private B mBuilder = getBuilder(); - protected int mType; - protected String mReason; - protected @Level int mLogLevel; - - public Builder(SysuiLog sysuiLog) { - mLog = sysuiLog; - reset(); - } - - /** - * Reset this builder's parameters so it can be reused to build another RichEvent. - */ - public void reset() { - mType = UNINITIALIZED; - mReason = null; - mLogLevel = VERBOSE; - } - - /** - * Get the log-specific builder. - */ - public abstract B getBuilder(); - - /** - * Build the log-specific event given an event to populate. - */ - public abstract E build(E e); - - /** - * Optional - set the log level. Defaults to DEBUG. - */ - public B setLogLevel(@Level int logLevel) { - mLogLevel = logLevel; - return mBuilder; - } - - /** - * Required - set the event type. These events must correspond with the events from - * getEventLabels(). - */ - public B setType(int type) { - mType = type; - return mBuilder; - } - - /** - * Optional - set the reason why this event was triggered. - */ - public B setReason(String reason) { - mReason = reason; - return mBuilder; - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/log/SysuiLog.java b/packages/SystemUI/src/com/android/systemui/log/SysuiLog.java deleted file mode 100644 index 9ee3e6765e4a..000000000000 --- a/packages/SystemUI/src/com/android/systemui/log/SysuiLog.java +++ /dev/null @@ -1,180 +0,0 @@ -/* - * 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.log; - -import android.os.Build; -import android.os.SystemProperties; -import android.util.Log; - -import com.android.internal.annotations.GuardedBy; -import com.android.internal.annotations.VisibleForTesting; -import com.android.systemui.DumpController; -import com.android.systemui.Dumpable; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.text.SimpleDateFormat; -import java.util.ArrayDeque; -import java.util.Locale; - -/** - * Thread-safe logger in SystemUI which prints logs to logcat and stores logs to be - * printed by the DumpController. This is an alternative to printing directly - * to avoid logs being deleted by chatty. The number of logs retained is varied based on - * whether the build is {@link Build.IS_DEBUGGABLE}. - * - * To manually view the logs via adb: - * adb shell dumpsys activity service com.android.systemui/.SystemUIService \ - * dependency DumpController <SysuiLogId> - * - * Logs can be disabled by setting the following SystemProperty and then restarting the device: - * adb shell setprop persist.sysui.log.enabled.<id> true/false && adb reboot - * - * @param <E> Type of event we'll be logging - */ -public class SysuiLog<E extends Event> implements Dumpable { - public static final SimpleDateFormat DATE_FORMAT = - new SimpleDateFormat("MM-dd HH:mm:ss.S", Locale.US); - - protected final Object mDataLock = new Object(); - private final String mId; - private final int mMaxLogs; - protected boolean mEnabled; - protected boolean mLogToLogcatEnabled; - - @VisibleForTesting protected ArrayDeque<E> mTimeline; - - /** - * Creates a SysuiLog - * @param dumpController where to register this logger's dumpsys - * @param id user-readable tag for this logger - * @param maxDebugLogs maximum number of logs to retain when {@link sDebuggable} is true - * @param maxLogs maximum number of logs to retain when {@link sDebuggable} is false - */ - public SysuiLog(DumpController dumpController, String id, int maxDebugLogs, int maxLogs) { - this(dumpController, id, sDebuggable ? maxDebugLogs : maxLogs, - SystemProperties.getBoolean(SYSPROP_ENABLED_PREFIX + id, DEFAULT_ENABLED), - SystemProperties.getBoolean(SYSPROP_LOGCAT_ENABLED_PREFIX + id, - DEFAULT_LOGCAT_ENABLED)); - } - - @VisibleForTesting - protected SysuiLog(DumpController dumpController, String id, int maxLogs, boolean enabled, - boolean logcatEnabled) { - mId = id; - mMaxLogs = maxLogs; - mEnabled = enabled; - mLogToLogcatEnabled = logcatEnabled; - mTimeline = mEnabled ? new ArrayDeque<>(mMaxLogs) : null; - dumpController.registerDumpable(mId, this); - } - - /** - * Logs an event to the timeline which can be printed by the dumpsys. - * May also log to logcat if enabled. - * @return the last event that was discarded from the Timeline (can be recycled) - */ - public E log(E event) { - if (!mEnabled) { - return null; - } - - E recycledEvent = null; - synchronized (mDataLock) { - if (mTimeline.size() >= mMaxLogs) { - recycledEvent = mTimeline.removeFirst(); - } - - mTimeline.add(event); - } - - if (mLogToLogcatEnabled) { - final String strEvent = eventToString(event); - switch (event.getLogLevel()) { - case Event.VERBOSE: - Log.v(mId, strEvent); - break; - case Event.DEBUG: - Log.d(mId, strEvent); - break; - case Event.ERROR: - Log.e(mId, strEvent); - break; - case Event.INFO: - Log.i(mId, strEvent); - break; - case Event.WARN: - Log.w(mId, strEvent); - break; - } - } - - if (recycledEvent != null) { - recycledEvent.recycle(); - } - - return recycledEvent; - } - - /** - * @return user-readable string of the given event with timestamp - */ - private String eventToTimestampedString(Event event) { - StringBuilder sb = new StringBuilder(); - sb.append(SysuiLog.DATE_FORMAT.format(event.getTimestamp())); - sb.append(" "); - sb.append(event.getMessage()); - return sb.toString(); - } - - /** - * @return user-readable string of the given event without a timestamp - */ - public String eventToString(Event event) { - return event.getMessage(); - } - - @GuardedBy("mDataLock") - private void dumpTimelineLocked(PrintWriter pw) { - pw.println("\tTimeline:"); - - for (Event event : mTimeline) { - pw.println("\t" + eventToTimestampedString(event)); - } - } - - @Override - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - pw.println(mId + ":"); - - if (mEnabled) { - synchronized (mDataLock) { - dumpTimelineLocked(pw); - } - } else { - pw.print(" - Logging disabled."); - } - } - - private static boolean sDebuggable = Build.IS_DEBUGGABLE; - private static final String SYSPROP_ENABLED_PREFIX = "persist.sysui.log.enabled."; - private static final String SYSPROP_LOGCAT_ENABLED_PREFIX = "persist.sysui.log.enabled.logcat."; - private static final boolean DEFAULT_ENABLED = sDebuggable; - private static final boolean DEFAULT_LOGCAT_ENABLED = false; - private static final int DEFAULT_MAX_DEBUG_LOGS = 100; - private static final int DEFAULT_MAX_LOGS = 50; -} diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java index eba2e7874574..3ae627d27def 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java @@ -19,10 +19,6 @@ package com.android.systemui.pip.phone; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; -import android.animation.AnimationHandler; -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.TimeAnimator; import android.annotation.Nullable; import android.app.ActivityManager.StackInfo; import android.app.IActivityManager; @@ -36,6 +32,7 @@ import android.os.Handler; import android.os.Message; import android.os.RemoteException; import android.util.Log; +import android.view.Choreographer; import androidx.dynamicanimation.animation.SpringForce; @@ -88,9 +85,11 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call /** PIP's current bounds on the screen. */ private final Rect mBounds = new Rect(); + private final SfVsyncFrameCallbackProvider mSfVsyncFrameProvider = + new SfVsyncFrameCallbackProvider(); + /** - * Bounds that are animated using the physics animator. PIP is moved to these bounds whenever - * the {@link #mVsyncTimeAnimator} ticks. + * Bounds that are animated using the physics animator. */ private final Rect mAnimatedBounds = new Rect(); @@ -100,12 +99,16 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call private PhysicsAnimator<Rect> mAnimatedBoundsPhysicsAnimator = PhysicsAnimator.getInstance( mAnimatedBounds); + /** Callback that re-sizes PIP to the animated bounds. */ + private final Choreographer.FrameCallback mResizePipVsyncCallback = + l -> resizePipUnchecked(mAnimatedBounds); + /** - * Time animator whose frame timing comes from the SurfaceFlinger vsync frame provider. At each - * frame, PIP is moved to {@link #mAnimatedBounds}, which are animated asynchronously using - * physics animations. + * Update listener that posts a vsync frame callback to resize PIP to {@link #mAnimatedBounds}. */ - private TimeAnimator mVsyncTimeAnimator; + private final PhysicsAnimator.UpdateListener<Rect> mResizePipVsyncUpdateListener = + (target, values) -> + mSfVsyncFrameProvider.postFrameCallback(mResizePipVsyncCallback); /** FlingConfig instances provided to PhysicsAnimator for fling gestures. */ private PhysicsAnimator.FlingConfig mFlingConfigX; @@ -126,39 +129,7 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call mMenuController = menuController; mSnapAlgorithm = snapAlgorithm; mFlingAnimationUtils = flingAnimationUtils; - final AnimationHandler vsyncFrameCallbackProvider = new AnimationHandler(); - vsyncFrameCallbackProvider.setProvider(new SfVsyncFrameCallbackProvider()); - onConfigurationChanged(); - - // Construct a time animator that uses the vsync frame provider. Physics animations can't - // use custom frame providers, since they rely on constant time between frames to run the - // physics simulations. To work around this, we physically-animate a second set of bounds, - // and apply those animating bounds to the PIP in-sync via this TimeAnimator. - mVsyncTimeAnimator = new TimeAnimator() { - @Override - public AnimationHandler getAnimationHandler() { - return vsyncFrameCallbackProvider; - } - }; - - // When the time animator ticks, move PIP to the animated bounds. - mVsyncTimeAnimator.setTimeListener( - (animation, totalTime, deltaTime) -> - resizePipUnchecked(mAnimatedBounds)); - - // Add a listener for cancel/end events that moves PIP to the final animated bounds. - mVsyncTimeAnimator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationCancel(Animator animation) { - resizePipUnchecked(mAnimatedBounds); - } - - @Override - public void onAnimationEnd(Animator animation) { - resizePipUnchecked(mAnimatedBounds); - } - }); } /** @@ -429,7 +400,6 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call */ private void cancelAnimations() { mAnimatedBoundsPhysicsAnimator.cancel(); - mVsyncTimeAnimator.cancel(); } /** @@ -457,10 +427,8 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call cancelAnimations(); mAnimatedBoundsPhysicsAnimator - .withEndActions( - mVsyncTimeAnimator::cancel) + .addUpdateListener(mResizePipVsyncUpdateListener) .start(); - mVsyncTimeAnimator.start(); } /** diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java index 0134aa3a15df..5de6d1c42b4f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java @@ -169,7 +169,7 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, if (DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext)) { v.setText(mContext.getString( com.android.internal.R.string.bugreport_status, - Build.VERSION.RELEASE, + Build.VERSION.RELEASE_OR_CODENAME, Build.ID)); v.setVisibility(View.VISIBLE); } else { diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java b/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java index 9e3e94ce4186..6c697184f082 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java @@ -36,6 +36,7 @@ import android.media.MediaMetadata; import android.media.session.MediaController; import android.media.session.MediaSession; import android.media.session.PlaybackState; +import android.os.Handler; import android.util.Log; import android.view.KeyEvent; import android.view.LayoutInflater; @@ -370,6 +371,13 @@ public class QSMediaPlayer { if (mSeamless == null) { return; } + Handler handler = mSeamless.getHandler(); + handler.post(() -> { + updateChipInternal(device); + }); + } + + private void updateChipInternal(MediaDevice device) { ColorStateList fgTintList = ColorStateList.valueOf(mForegroundColor); // Update the outline color diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java index b091ad8c0db8..626f298055a0 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java @@ -16,7 +16,6 @@ package com.android.systemui.screenrecord; -import android.app.Activity; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; @@ -26,16 +25,21 @@ import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; import android.content.Intent; +import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.drawable.Icon; import android.hardware.display.DisplayManager; import android.hardware.display.VirtualDisplay; import android.media.MediaRecorder; +import android.media.projection.IMediaProjection; +import android.media.projection.IMediaProjectionManager; import android.media.projection.MediaProjection; import android.media.projection.MediaProjectionManager; import android.net.Uri; import android.os.Bundle; import android.os.IBinder; +import android.os.RemoteException; +import android.os.ServiceManager; import android.provider.MediaStore; import android.provider.Settings; import android.util.DisplayMetrics; @@ -83,7 +87,6 @@ public class RecordingService extends Service { private static final int AUDIO_SAMPLE_RATE = 44100; private final RecordingController mController; - private MediaProjectionManager mMediaProjectionManager; private MediaProjection mMediaProjection; private Surface mInputSurface; private VirtualDisplay mVirtualDisplay; @@ -134,13 +137,30 @@ public class RecordingService extends Service { switch (action) { case ACTION_START: - int resultCode = intent.getIntExtra(EXTRA_RESULT_CODE, Activity.RESULT_CANCELED); mUseAudio = intent.getBooleanExtra(EXTRA_USE_AUDIO, false); mShowTaps = intent.getBooleanExtra(EXTRA_SHOW_TAPS, false); - Intent data = intent.getParcelableExtra(EXTRA_DATA); - if (data != null) { - mMediaProjection = mMediaProjectionManager.getMediaProjection(resultCode, data); + try { + IBinder b = ServiceManager.getService(MEDIA_PROJECTION_SERVICE); + IMediaProjectionManager mediaService = + IMediaProjectionManager.Stub.asInterface(b); + IMediaProjection proj = mediaService.createProjection(getUserId(), + getPackageName(), + MediaProjectionManager.TYPE_SCREEN_CAPTURE, false); + IBinder projection = proj.asBinder(); + if (projection == null) { + Log.e(TAG, "Projection was null"); + Toast.makeText(this, R.string.screenrecord_start_error, Toast.LENGTH_LONG) + .show(); + return Service.START_NOT_STICKY; + } + mMediaProjection = new MediaProjection(getApplicationContext(), + IMediaProjection.Stub.asInterface(projection)); startRecording(); + } catch (RemoteException e) { + e.printStackTrace(); + Toast.makeText(this, R.string.screenrecord_start_error, Toast.LENGTH_LONG) + .show(); + return Service.START_NOT_STICKY; } break; @@ -195,9 +215,6 @@ public class RecordingService extends Service { @Override public void onCreate() { super.onCreate(); - - mMediaProjectionManager = - (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE); } /** @@ -269,6 +286,7 @@ public class RecordingService extends Service { } private void createRecordingNotification() { + Resources res = getResources(); NotificationChannel channel = new NotificationChannel( CHANNEL_ID, getString(R.string.screenrecord_name), @@ -281,11 +299,15 @@ public class RecordingService extends Service { Bundle extras = new Bundle(); extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, - getResources().getString(R.string.screenrecord_name)); + res.getString(R.string.screenrecord_name)); + + String notificationTitle = mUseAudio + ? res.getString(R.string.screenrecord_ongoing_screen_and_audio) + : res.getString(R.string.screenrecord_ongoing_screen_only); mRecordingNotificationBuilder = new Notification.Builder(this, CHANNEL_ID) .setSmallIcon(R.drawable.ic_screenrecord) - .setContentTitle(getResources().getString(R.string.screenrecord_name)) + .setContentTitle(notificationTitle) .setContentText(getResources().getString(R.string.screenrecord_stop_text)) .setUsesChronometer(true) .setColorized(true) @@ -332,8 +354,7 @@ public class RecordingService extends Service { Notification.Builder builder = new Notification.Builder(this, CHANNEL_ID) .setSmallIcon(R.drawable.ic_screenrecord) - .setContentTitle(getResources().getString(R.string.screenrecord_name)) - .setContentText(getResources().getString(R.string.screenrecord_save_message)) + .setContentTitle(getResources().getString(R.string.screenrecord_save_message)) .setContentIntent(PendingIntent.getActivity( this, REQUEST_CODE, diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java index 8324986123bd..566f12b42795 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java @@ -16,15 +16,14 @@ package com.android.systemui.screenrecord; -import android.Manifest; import android.app.Activity; import android.app.PendingIntent; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.media.projection.MediaProjectionManager; import android.os.Bundle; -import android.widget.Toast; +import android.view.Gravity; +import android.view.ViewGroup; +import android.view.Window; +import android.widget.Button; +import android.widget.Switch; import com.android.systemui.R; @@ -34,15 +33,11 @@ import javax.inject.Inject; * Activity to select screen recording options */ public class ScreenRecordDialog extends Activity { - private static final int REQUEST_CODE_VIDEO_ONLY = 200; - private static final int REQUEST_CODE_VIDEO_TAPS = 201; - private static final int REQUEST_CODE_PERMISSIONS = 299; - private static final int REQUEST_CODE_VIDEO_AUDIO = 300; - private static final int REQUEST_CODE_VIDEO_AUDIO_TAPS = 301; - private static final int REQUEST_CODE_PERMISSIONS_AUDIO = 399; private static final long DELAY_MS = 3000; private final RecordingController mController; + private Switch mAudioSwitch; + private Switch mTapsSwitch; @Inject public ScreenRecordDialog(RecordingController controller) { @@ -52,81 +47,42 @@ public class ScreenRecordDialog extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - requestScreenCapture(); - } - private void requestScreenCapture() { - MediaProjectionManager mediaProjectionManager = (MediaProjectionManager) getSystemService( - Context.MEDIA_PROJECTION_SERVICE); - Intent permissionIntent = mediaProjectionManager.createScreenCaptureIntent(); + Window window = getWindow(); + // Inflate the decor view, so the attributes below are not overwritten by the theme. + window.getDecorView(); + window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); + window.setGravity(Gravity.TOP); + + setContentView(R.layout.screen_record_dialog); + + Button cancelBtn = findViewById(R.id.button_cancel); + cancelBtn.setOnClickListener(v -> { + finish(); + }); - // TODO get saved settings - boolean useAudio = false; - boolean showTaps = false; - if (useAudio) { - startActivityForResult(permissionIntent, - showTaps ? REQUEST_CODE_VIDEO_AUDIO_TAPS : REQUEST_CODE_VIDEO_AUDIO); - } else { - startActivityForResult(permissionIntent, - showTaps ? REQUEST_CODE_VIDEO_TAPS : REQUEST_CODE_VIDEO_ONLY); - } + Button startBtn = findViewById(R.id.button_start); + startBtn.setOnClickListener(v -> { + requestScreenCapture(); + finish(); + }); + + mAudioSwitch = findViewById(R.id.screenrecord_audio_switch); + mTapsSwitch = findViewById(R.id.screenrecord_taps_switch); } - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - boolean showTaps = (requestCode == REQUEST_CODE_VIDEO_TAPS - || requestCode == REQUEST_CODE_VIDEO_AUDIO_TAPS); - boolean useAudio = (requestCode == REQUEST_CODE_VIDEO_AUDIO - || requestCode == REQUEST_CODE_VIDEO_AUDIO_TAPS); - switch (requestCode) { - case REQUEST_CODE_VIDEO_TAPS: - case REQUEST_CODE_VIDEO_AUDIO_TAPS: - case REQUEST_CODE_VIDEO_ONLY: - case REQUEST_CODE_VIDEO_AUDIO: - if (resultCode == RESULT_OK) { - PendingIntent startIntent = PendingIntent.getForegroundService( - this, RecordingService.REQUEST_CODE, RecordingService.getStartIntent( - ScreenRecordDialog.this, resultCode, data, useAudio, - showTaps), - PendingIntent.FLAG_UPDATE_CURRENT - ); - PendingIntent stopIntent = PendingIntent.getService( - this, RecordingService.REQUEST_CODE, - RecordingService.getStopIntent(this), - PendingIntent.FLAG_UPDATE_CURRENT); - mController.startCountdown(DELAY_MS, startIntent, stopIntent); - } else { - Toast.makeText(this, - getResources().getString(R.string.screenrecord_permission_error), - Toast.LENGTH_SHORT).show(); - } - finish(); - break; - case REQUEST_CODE_PERMISSIONS: - int permission = checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE); - if (permission != PackageManager.PERMISSION_GRANTED) { - Toast.makeText(this, - getResources().getString(R.string.screenrecord_permission_error), - Toast.LENGTH_SHORT).show(); - finish(); - } else { - requestScreenCapture(); - } - break; - case REQUEST_CODE_PERMISSIONS_AUDIO: - int videoPermission = checkSelfPermission( - Manifest.permission.WRITE_EXTERNAL_STORAGE); - int audioPermission = checkSelfPermission(Manifest.permission.RECORD_AUDIO); - if (videoPermission != PackageManager.PERMISSION_GRANTED - || audioPermission != PackageManager.PERMISSION_GRANTED) { - Toast.makeText(this, - getResources().getString(R.string.screenrecord_permission_error), - Toast.LENGTH_SHORT).show(); - finish(); - } else { - requestScreenCapture(); - } - break; - } + private void requestScreenCapture() { + boolean useAudio = mAudioSwitch.isChecked(); + boolean showTaps = mTapsSwitch.isChecked(); + PendingIntent startIntent = PendingIntent.getForegroundService(this, + RecordingService.REQUEST_CODE, + RecordingService.getStartIntent( + ScreenRecordDialog.this, RESULT_OK, null, useAudio, showTaps), + PendingIntent.FLAG_UPDATE_CURRENT); + PendingIntent stopIntent = PendingIntent.getService(this, + RecordingService.REQUEST_CODE, + RecordingService.getStopIntent(this), + PendingIntent.FLAG_UPDATE_CURRENT); + mController.startCountdown(DELAY_MS, startIntent, stopIntent); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index 7ad07c266cc3..7d3d4061014b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -20,7 +20,6 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.content.Context; import android.content.res.ColorStateList; -import android.content.res.Resources; import android.graphics.Color; import android.hardware.biometrics.BiometricSourceType; import android.hardware.face.FaceManager; @@ -46,6 +45,7 @@ import com.android.internal.widget.ViewClippingUtil; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.settingslib.Utils; +import com.android.settingslib.fuelgauge.BatteryStatus; import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; @@ -97,8 +97,6 @@ public class KeyguardIndicationController implements StateListener, private final LockPatternUtils mLockPatternUtils; private final DockManager mDockManager; - private final int mSlowThreshold; - private final int mFastThreshold; private final LockIcon mLockIcon; private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger(); @@ -178,10 +176,6 @@ public class KeyguardIndicationController implements StateListener, mWakeLock = new SettableWakeLock(wakeLock, TAG); mLockPatternUtils = lockPatternUtils; - Resources res = context.getResources(); - mSlowThreshold = res.getInteger(R.integer.config_chargingSlowlyThreshold); - mFastThreshold = res.getInteger(R.integer.config_chargingFastThreshold); - mUserManager = context.getSystemService(UserManager.class); mBatteryInfo = iBatteryStats; @@ -484,12 +478,12 @@ public class KeyguardIndicationController implements StateListener, int chargingId; if (mPowerPluggedInWired) { switch (mChargingSpeed) { - case KeyguardUpdateMonitor.BatteryStatus.CHARGING_FAST: + case BatteryStatus.CHARGING_FAST: chargingId = hasChargingTime ? R.string.keyguard_indication_charging_time_fast : R.string.keyguard_plugged_in_charging_fast; break; - case KeyguardUpdateMonitor.BatteryStatus.CHARGING_SLOWLY: + case BatteryStatus.CHARGING_SLOWLY: chargingId = hasChargingTime ? R.string.keyguard_indication_charging_time_slowly : R.string.keyguard_plugged_in_charging_slowly; @@ -620,7 +614,7 @@ public class KeyguardIndicationController implements StateListener, public static final int HIDE_DELAY_MS = 5000; @Override - public void onRefreshBatteryInfo(KeyguardUpdateMonitor.BatteryStatus status) { + public void onRefreshBatteryInfo(BatteryStatus status) { boolean isChargingOrFull = status.status == BatteryManager.BATTERY_STATUS_CHARGING || status.status == BatteryManager.BATTERY_STATUS_FULL; boolean wasPluggedIn = mPowerPluggedIn; @@ -628,7 +622,7 @@ public class KeyguardIndicationController implements StateListener, mPowerPluggedIn = status.isPluggedIn() && isChargingOrFull; mPowerCharged = status.isCharged(); mChargingWattage = status.maxChargingWattage; - mChargingSpeed = status.getChargingSpeed(mSlowThreshold, mFastThreshold); + mChargingSpeed = status.getChargingSpeed(mContext); mBatteryLevel = status.level; try { mChargingTimeRemaining = mPowerPluggedIn diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java index 61915ad92d87..916da6eca0c9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java @@ -47,8 +47,6 @@ import com.android.systemui.statusbar.notification.collection.NotificationRankin import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder; import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; -import com.android.systemui.statusbar.notification.logging.NotifEvent; -import com.android.systemui.statusbar.notification.logging.NotifLog; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.phone.NotificationGroupManager; @@ -129,6 +127,8 @@ public class NotificationEntryManager implements private final Map<NotificationEntry, NotificationLifetimeExtender> mRetainedNotifications = new ArrayMap<>(); + private final NotificationEntryManagerLogger mLogger; + // Lazily retrieved dependencies private final Lazy<NotificationRowBinder> mNotificationRowBinderLazy; private final Lazy<NotificationRemoteInputManager> mRemoteInputManagerLazy; @@ -143,7 +143,6 @@ public class NotificationEntryManager implements private NotificationPresenter mPresenter; private RankingMap mLatestRankingMap; - private NotifLog mNotifLog; @VisibleForTesting final ArrayList<NotificationLifetimeExtender> mNotificationLifetimeExtenders @@ -184,7 +183,7 @@ public class NotificationEntryManager implements @Inject public NotificationEntryManager( - NotifLog notifLog, + NotificationEntryManagerLogger logger, NotificationGroupManager groupManager, NotificationRankingManager rankingManager, KeyguardEnvironment keyguardEnvironment, @@ -193,7 +192,7 @@ public class NotificationEntryManager implements Lazy<NotificationRemoteInputManager> notificationRemoteInputManagerLazy, LeakDetector leakDetector, ForegroundServiceDismissalFeatureController fgsFeatureController) { - mNotifLog = notifLog; + mLogger = logger; mGroupManager = groupManager; mRankingManager = rankingManager; mKeyguardEnvironment = keyguardEnvironment; @@ -291,13 +290,12 @@ public class NotificationEntryManager implements NotificationEntry entry = mPendingNotifications.get(key); entry.abortTask(); mPendingNotifications.remove(key); - mNotifLog.log(NotifEvent.INFLATION_ABORTED, entry, "PendingNotification aborted" - + " reason=" + reason); + mLogger.logInflationAborted(key, "pending", reason); } NotificationEntry addedEntry = getActiveNotificationUnfiltered(key); if (addedEntry != null) { addedEntry.abortTask(); - mNotifLog.log(NotifEvent.INFLATION_ABORTED, addedEntry.getKey() + " " + reason); + mLogger.logInflationAborted(key, "active", reason); } } @@ -328,9 +326,9 @@ public class NotificationEntryManager implements // the list, otherwise we might get leaks. if (!entry.isRowRemoved()) { boolean isNew = getActiveNotificationUnfiltered(entry.getKey()) == null; + mLogger.logNotifInflated(entry.getKey(), isNew); if (isNew) { for (NotificationEntryListener listener : mNotificationEntryListeners) { - mNotifLog.log(NotifEvent.INFLATED, entry); listener.onEntryInflated(entry); } addActiveNotification(entry); @@ -340,7 +338,6 @@ public class NotificationEntryManager implements } } else { for (NotificationEntryListener listener : mNotificationEntryListeners) { - mNotifLog.log(NotifEvent.INFLATED, entry); listener.onEntryReinflated(entry); } } @@ -422,7 +419,7 @@ public class NotificationEntryManager implements for (NotificationRemoveInterceptor interceptor : mRemoveInterceptors) { if (interceptor.onNotificationRemoveRequested(key, entry, reason)) { // Remove intercepted; log and skip - mNotifLog.log(NotifEvent.REMOVE_INTERCEPTED); + mLogger.logRemovalIntercepted(key); return; } } @@ -437,10 +434,7 @@ public class NotificationEntryManager implements if (extender.shouldExtendLifetimeForPendingNotification(pendingEntry)) { extendLifetime(pendingEntry, extender); lifetimeExtended = true; - mNotifLog.log( - NotifEvent.LIFETIME_EXTENDED, - pendingEntry.getSbn(), - "pendingEntry extendedBy=" + extender.toString()); + mLogger.logLifetimeExtended(key, extender.getClass().getName(), "pending"); } } } @@ -460,10 +454,7 @@ public class NotificationEntryManager implements mLatestRankingMap = ranking; extendLifetime(entry, extender); lifetimeExtended = true; - mNotifLog.log( - NotifEvent.LIFETIME_EXTENDED, - entry.getSbn(), - "entry extendedBy=" + extender.toString()); + mLogger.logLifetimeExtended(key, extender.getClass().getName(), "active"); break; } } @@ -486,8 +477,7 @@ public class NotificationEntryManager implements mLeakDetector.trackGarbage(entry); removedByUser |= entryDismissed; - mNotifLog.log(NotifEvent.NOTIF_REMOVED, entry.getSbn(), - "removedByUser=" + removedByUser); + mLogger.logNotifRemoved(entry.getKey(), removedByUser); for (NotificationEntryListener listener : mNotificationEntryListeners) { listener.onEntryRemoved(entry, visibility, removedByUser); } @@ -576,7 +566,7 @@ public class NotificationEntryManager implements abortExistingInflation(key, "addNotification"); mPendingNotifications.put(key, entry); - mNotifLog.log(NotifEvent.NOTIF_ADDED, entry); + mLogger.logNotifAdded(entry.getKey()); for (NotificationEntryListener listener : mNotificationEntryListeners) { listener.onPendingEntryAdded(entry); } @@ -613,7 +603,7 @@ public class NotificationEntryManager implements entry.setSbn(notification); mGroupManager.onEntryUpdated(entry, oldSbn); - mNotifLog.log(NotifEvent.NOTIF_UPDATED, entry); + mLogger.logNotifUpdated(entry.getKey()); for (NotificationEntryListener listener : mNotificationEntryListeners) { listener.onPreEntryUpdated(entry); } @@ -808,7 +798,7 @@ public class NotificationEntryManager implements //TODO: Get rid of this in favor of NotificationUpdateHandler#updateNotificationRanking /** * @param rankingMap the {@link RankingMap} to apply to the current notification list - * @param reason the reason for calling this method, for {@link NotifLog} + * @param reason the reason for calling this method, which will be logged */ public void updateRanking(RankingMap rankingMap, String reason) { updateRankingAndSort(rankingMap, reason); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManagerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManagerLogger.kt new file mode 100644 index 000000000000..4382ab50390a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManagerLogger.kt @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification + +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel.DEBUG +import com.android.systemui.log.LogLevel.INFO +import com.android.systemui.log.dagger.NotificationLog +import javax.inject.Inject + +/** Logger for [NotificationEntryManager]. */ +class NotificationEntryManagerLogger @Inject constructor( + @NotificationLog private val buffer: LogBuffer +) { + fun logNotifAdded(key: String) { + buffer.log(TAG, INFO, { + str1 = key + }, { + "NOTIF ADDED $str1" + }) + } + + fun logNotifUpdated(key: String) { + buffer.log(TAG, INFO, { + str1 = key + }, { + "NOTIF UPDATED $str1" + }) + } + + fun logInflationAborted(key: String, status: String, reason: String) { + buffer.log(TAG, DEBUG, { + str1 = key + str2 = status + str3 = reason + }, { + "NOTIF INFLATION ABORTED $str1 notifStatus=$str2 reason=$str3" + }) + } + + fun logNotifInflated(key: String, isNew: Boolean) { + buffer.log(TAG, DEBUG, { + str1 = key + bool1 = isNew + }, { + "NOTIF INFLATED $str1 isNew=$bool1}" + }) + } + + fun logRemovalIntercepted(key: String) { + buffer.log(TAG, INFO, { + str1 = key + }, { + "NOTIF REMOVE INTERCEPTED for $str1" + }) + } + + fun logLifetimeExtended(key: String, extenderName: String, status: String) { + buffer.log(TAG, INFO, { + str1 = key + str2 = extenderName + str3 = status + }, { + "NOTIF LIFETIME EXTENDED $str1 extender=$str2 status=$str3" + }) + } + + fun logNotifRemoved(key: String, removedByUser: Boolean) { + buffer.log(TAG, INFO, { + str1 = key + bool1 = removedByUser + }, { + "NOTIF REMOVED $str1 removedByUser=$bool1" + }) + } + + fun logFilterAndSort(reason: String) { + buffer.log(TAG, INFO, { + str1 = reason + }, { + "FILTER AND SORT reason=$str1" + }) + } +} + +private const val TAG = "NotificationEntryMgr"
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt index 1eeeab3e93cb..2981252f148c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt @@ -22,11 +22,10 @@ import android.service.notification.NotificationListenerService.Ranking import android.service.notification.NotificationListenerService.RankingMap import android.service.notification.StatusBarNotification import com.android.systemui.statusbar.NotificationMediaManager +import com.android.systemui.statusbar.notification.NotificationEntryManagerLogger import com.android.systemui.statusbar.notification.NotificationFilter import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider -import com.android.systemui.statusbar.notification.logging.NotifEvent -import com.android.systemui.statusbar.notification.logging.NotifLog import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_PEOPLE @@ -53,7 +52,7 @@ open class NotificationRankingManager @Inject constructor( private val groupManager: NotificationGroupManager, private val headsUpManager: HeadsUpManager, private val notifFilter: NotificationFilter, - private val notifLog: NotifLog, + private val logger: NotificationEntryManagerLogger, sectionsFeatureManager: NotificationSectionsFeatureManager, private val peopleNotificationIdentifier: PeopleNotificationIdentifier, private val highPriorityProvider: HighPriorityProvider @@ -134,7 +133,7 @@ open class NotificationRankingManager @Inject constructor( entries: Sequence<NotificationEntry>, reason: String ): Sequence<NotificationEntry> { - notifLog.log(NotifEvent.FILTER_AND_SORT, reason) + logger.logFilterAndSort(reason) return entries.filter { !notifFilter.shouldFilterOut(it) } .sortedWith(rankingComparator) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java index 41314b86695a..1e5946a85cfa 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java @@ -22,8 +22,6 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; -import com.android.systemui.statusbar.notification.logging.NotifEvent; -import com.android.systemui.statusbar.notification.logging.NotifLog; import java.util.ArrayList; import java.util.List; @@ -42,13 +40,15 @@ import javax.inject.Singleton; public class PreparationCoordinator implements Coordinator { private static final String TAG = "PreparationCoordinator"; - private final NotifLog mNotifLog; + private final PreparationCoordinatorLogger mLogger; private final NotifInflater mNotifInflater; private final List<NotificationEntry> mPendingNotifications = new ArrayList<>(); @Inject - public PreparationCoordinator(NotifLog notifLog, NotifInflaterImpl notifInflater) { - mNotifLog = notifLog; + public PreparationCoordinator( + PreparationCoordinatorLogger logger, + NotifInflaterImpl notifInflater) { + mLogger = logger; mNotifInflater = notifInflater; mNotifInflater.setInflationCallback(mInflationCallback); } @@ -106,7 +106,7 @@ public class PreparationCoordinator implements Coordinator { new NotifInflater.InflationCallback() { @Override public void onInflationFinished(NotificationEntry entry) { - mNotifLog.log(NotifEvent.INFLATED, entry); + mLogger.logNotifInflated(entry.getKey()); mPendingNotifications.remove(entry); mNotifInflatingFilter.invalidateList(); } @@ -123,7 +123,7 @@ public class PreparationCoordinator implements Coordinator { } private void abortInflation(NotificationEntry entry, String reason) { - mNotifLog.log(NotifEvent.INFLATION_ABORTED, reason); + mLogger.logInflationAborted(entry.getKey(), reason); entry.abortTask(); mPendingNotifications.remove(entry); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorLogger.kt new file mode 100644 index 000000000000..75e7bc9b79a2 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorLogger.kt @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.collection.coordinator + +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel +import com.android.systemui.log.dagger.NotificationLog +import javax.inject.Inject + +class PreparationCoordinatorLogger @Inject constructor( + @NotificationLog private val buffer: LogBuffer +) { + fun logNotifInflated(key: String) { + buffer.log(TAG, LogLevel.DEBUG, { + str1 = key + }, { + "NOTIF INFLATED $str1" + }) + } + + fun logInflationAborted(key: String, reason: String) { + buffer.log(TAG, LogLevel.DEBUG, { + str1 = key + str2 = reason + }, { + "NOTIF INFLATION ABORTED $str1 reason=$str2" + }) + } +} + +private const val TAG = "PreparationCoordinator"
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifEvent.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifEvent.java deleted file mode 100644 index 9adceb78c249..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifEvent.java +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.statusbar.notification.logging; - -import android.annotation.IntDef; -import android.service.notification.NotificationListenerService; -import android.service.notification.StatusBarNotification; - -import com.android.systemui.log.RichEvent; -import com.android.systemui.statusbar.notification.NotificationEntryManager; -import com.android.systemui.statusbar.notification.collection.ShadeListBuilder; -import com.android.systemui.statusbar.notification.collection.coalescer.GroupCoalescer; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * An event related to notifications. {@link NotifLog} stores and prints these events for debugging - * and triaging purposes. We do not store a copy of the status bar notification nor ranking - * here to mitigate memory usage. - */ -public class NotifEvent extends RichEvent { - /** - * Initializes a rich event that includes an event type that matches with an index in the array - * getEventLabels(). - */ - public NotifEvent init(@EventType int type, StatusBarNotification sbn, - NotificationListenerService.Ranking ranking, String reason) { - StringBuilder extraInfo = new StringBuilder(reason); - if (sbn != null) { - extraInfo.append(" " + sbn.getKey()); - } - - if (ranking != null) { - extraInfo.append(" Ranking="); - extraInfo.append(ranking.getRank()); - } - super.init(INFO, type, extraInfo.toString()); - return this; - } - - /** - * Event labels for ListBuilderEvents - * Index corresponds to an # in {@link EventType} - */ - @Override - public String[] getEventLabels() { - assert (TOTAL_EVENT_LABELS - == (TOTAL_NEM_EVENT_TYPES - + TOTAL_LIST_BUILDER_EVENT_TYPES - + TOTAL_COALESCER_EVENT_TYPES)); - return EVENT_LABELS; - } - - /** - * @return if this event occurred in {@link ShadeListBuilder} - */ - static boolean isListBuilderEvent(@EventType int type) { - return isBetweenInclusive(type, 0, TOTAL_LIST_BUILDER_EVENT_TYPES); - } - - /** - * @return if this event occurred in {@link NotificationEntryManager} - */ - static boolean isNemEvent(@EventType int type) { - return isBetweenInclusive(type, TOTAL_LIST_BUILDER_EVENT_TYPES, - TOTAL_LIST_BUILDER_EVENT_TYPES + TOTAL_NEM_EVENT_TYPES); - } - - private static boolean isBetweenInclusive(int x, int a, int b) { - return x >= a && x <= b; - } - - @IntDef({ - // NotifListBuilder events: - WARN, - ON_BUILD_LIST, - START_BUILD_LIST, - DISPATCH_FINAL_LIST, - LIST_BUILD_COMPLETE, - PRE_GROUP_FILTER_INVALIDATED, - PROMOTER_INVALIDATED, - SECTION_INVALIDATED, - COMPARATOR_INVALIDATED, - PARENT_CHANGED, - FILTER_CHANGED, - PROMOTER_CHANGED, - PRE_RENDER_FILTER_INVALIDATED, - - // NotificationEntryManager events: - NOTIF_ADDED, - NOTIF_REMOVED, - NOTIF_UPDATED, - FILTER, - SORT, - FILTER_AND_SORT, - NOTIF_VISIBILITY_CHANGED, - LIFETIME_EXTENDED, - REMOVE_INTERCEPTED, - INFLATION_ABORTED, - INFLATED, - - // GroupCoalescer - COALESCED_EVENT, - EARLY_BATCH_EMIT, - EMIT_EVENT_BATCH - }) - @Retention(RetentionPolicy.SOURCE) - public @interface EventType {} - - private static final String[] EVENT_LABELS = - new String[]{ - // NotifListBuilder labels: - "Warning", - "OnBuildList", - "StartBuildList", - "DispatchFinalList", - "ListBuildComplete", - "FilterInvalidated", - "PromoterInvalidated", - "SectionInvalidated", - "ComparatorInvalidated", - "ParentChanged", - "FilterChanged", - "PromoterChanged", - "FinalFilterInvalidated", - "SectionerChanged", - - // NEM event labels: - "NotifAdded", - "NotifRemoved", - "NotifUpdated", - "Filter", - "Sort", - "FilterAndSort", - "NotifVisibilityChanged", - "LifetimeExtended", - "RemoveIntercepted", - "InflationAborted", - "Inflated", - - // GroupCoalescer labels: - "CoalescedEvent", - "EarlyBatchEmit", - "EmitEventBatch", - "BatchMaxTimeout" - }; - - private static final int TOTAL_EVENT_LABELS = EVENT_LABELS.length; - - /** - * Events related to {@link ShadeListBuilder} - */ - public static final int WARN = 0; - public static final int ON_BUILD_LIST = 1; - public static final int START_BUILD_LIST = 2; - public static final int DISPATCH_FINAL_LIST = 3; - public static final int LIST_BUILD_COMPLETE = 4; - public static final int PRE_GROUP_FILTER_INVALIDATED = 5; - public static final int PROMOTER_INVALIDATED = 6; - public static final int SECTION_INVALIDATED = 7; - public static final int COMPARATOR_INVALIDATED = 8; - public static final int PARENT_CHANGED = 9; - public static final int FILTER_CHANGED = 10; - public static final int PROMOTER_CHANGED = 11; - public static final int PRE_RENDER_FILTER_INVALIDATED = 12; - public static final int SECTION_CHANGED = 13; - private static final int TOTAL_LIST_BUILDER_EVENT_TYPES = 14; - - /** - * Events related to {@link NotificationEntryManager} - */ - private static final int NEM_EVENT_START_INDEX = TOTAL_LIST_BUILDER_EVENT_TYPES; - public static final int NOTIF_ADDED = NEM_EVENT_START_INDEX; - public static final int NOTIF_REMOVED = NEM_EVENT_START_INDEX + 1; - public static final int NOTIF_UPDATED = NEM_EVENT_START_INDEX + 2; - public static final int FILTER = NEM_EVENT_START_INDEX + 3; - public static final int SORT = NEM_EVENT_START_INDEX + 4; - public static final int FILTER_AND_SORT = NEM_EVENT_START_INDEX + 5; - public static final int NOTIF_VISIBILITY_CHANGED = NEM_EVENT_START_INDEX + 6; - public static final int LIFETIME_EXTENDED = NEM_EVENT_START_INDEX + 7; - // unable to remove notif - removal intercepted by {@link NotificationRemoveInterceptor} - public static final int REMOVE_INTERCEPTED = NEM_EVENT_START_INDEX + 8; - public static final int INFLATION_ABORTED = NEM_EVENT_START_INDEX + 9; - public static final int INFLATED = NEM_EVENT_START_INDEX + 10; - private static final int TOTAL_NEM_EVENT_TYPES = 11; - - /** - * Events related to {@link GroupCoalescer} - */ - private static final int COALESCER_EVENT_START_INDEX = NEM_EVENT_START_INDEX - + TOTAL_NEM_EVENT_TYPES; - public static final int COALESCED_EVENT = COALESCER_EVENT_START_INDEX; - public static final int EARLY_BATCH_EMIT = COALESCER_EVENT_START_INDEX + 1; - public static final int EMIT_EVENT_BATCH = COALESCER_EVENT_START_INDEX + 2; - public static final int BATCH_MAX_TIMEOUT = COALESCER_EVENT_START_INDEX + 3; - private static final int TOTAL_COALESCER_EVENT_TYPES = 3; -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifLog.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifLog.java deleted file mode 100644 index 299d628d0fd2..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotifLog.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.statusbar.notification.logging; - -import android.os.SystemProperties; -import android.service.notification.NotificationListenerService.Ranking; -import android.service.notification.StatusBarNotification; - -import com.android.systemui.DumpController; -import com.android.systemui.log.SysuiLog; -import com.android.systemui.statusbar.notification.collection.NotificationEntry; - -import javax.inject.Inject; -import javax.inject.Singleton; - -/** - * Logs systemui notification events for debugging and triaging purposes. Logs are dumped in - * bugreports or on demand: - * adb shell dumpsys activity service com.android.systemui/.SystemUIService \ - * dependency DumpController NotifLog - */ -@Singleton -public class NotifLog extends SysuiLog<NotifEvent> { - private static final String TAG = "NotifLog"; - private static final boolean SHOW_NEM_LOGS = - SystemProperties.getBoolean("persist.sysui.log.notif.nem", true); - private static final boolean SHOW_LIST_BUILDER_LOGS = - SystemProperties.getBoolean("persist.sysui.log.notif.listbuilder", true); - - private static final int MAX_DOZE_DEBUG_LOGS = 400; - private static final int MAX_DOZE_LOGS = 50; - - private NotifEvent mRecycledEvent; - - @Inject - public NotifLog(DumpController dumpController) { - super(dumpController, TAG, MAX_DOZE_DEBUG_LOGS, MAX_DOZE_LOGS); - } - - /** - * Logs a {@link NotifEvent} with a notification, ranking and message. - * Uses the last recycled event if available. - * @return true if successfully logged, else false - */ - public void log(@NotifEvent.EventType int eventType, - StatusBarNotification sbn, Ranking ranking, String msg) { - if (!mEnabled - || (NotifEvent.isListBuilderEvent(eventType) && !SHOW_LIST_BUILDER_LOGS) - || (NotifEvent.isNemEvent(eventType) && !SHOW_NEM_LOGS)) { - return; - } - - if (mRecycledEvent != null) { - mRecycledEvent = log(mRecycledEvent.init(eventType, sbn, ranking, msg)); - } else { - mRecycledEvent = log(new NotifEvent().init(eventType, sbn, ranking, msg)); - } - } - - /** - * Logs a {@link NotifEvent} with no extra information aside from the event type - */ - public void log(@NotifEvent.EventType int eventType) { - log(eventType, null, null, ""); - } - - /** - * Logs a {@link NotifEvent} with a message - */ - public void log(@NotifEvent.EventType int eventType, String msg) { - log(eventType, null, null, msg); - } - - /** - * Logs a {@link NotifEvent} with a entry - */ - public void log(@NotifEvent.EventType int eventType, NotificationEntry entry) { - log(eventType, entry.getSbn(), entry.getRanking(), ""); - } - - /** - * Logs a {@link NotifEvent} with a NotificationEntry and message - */ - public void log(@NotifEvent.EventType int eventType, NotificationEntry entry, String msg) { - log(eventType, entry.getSbn(), entry.getRanking(), msg); - } - - /** - * Logs a {@link NotifEvent} with a notification and message - */ - public void log(@NotifEvent.EventType int eventType, StatusBarNotification sbn, String msg) { - log(eventType, sbn, null, msg); - } - - /** - * Logs a {@link NotifEvent} with a ranking and message - */ - public void log(@NotifEvent.EventType int eventType, Ranking ranking, String msg) { - log(eventType, null, ranking, msg); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java index 2907cd41a0db..8dfcb0ac215d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java @@ -134,6 +134,8 @@ public class UserSwitcherController implements Dumpable { mBroadcastDispatcher.registerReceiver( mReceiver, filter, null /* handler */, UserHandle.SYSTEM); + mSimpleUserSwitcher = shouldUseSimpleUserSwitcher(); + mSecondaryUserServiceIntent = new Intent(context, SystemUISecondaryUserService.class); filter = new IntentFilter(); @@ -258,22 +260,20 @@ public class UserSwitcherController implements Dumpable { && mUserManager.canAddMoreUsers(); boolean createIsRestricted = !addUsersWhenLocked; - if (!mSimpleUserSwitcher) { - if (guestRecord == null) { - if (canCreateGuest) { - guestRecord = new UserRecord(null /* info */, null /* picture */, - true /* isGuest */, false /* isCurrent */, - false /* isAddUser */, createIsRestricted, canSwitchUsers); - checkIfAddUserDisallowedByAdminOnly(guestRecord); - records.add(guestRecord); - } - } else { - int index = guestRecord.isCurrent ? 0 : records.size(); - records.add(index, guestRecord); + if (guestRecord == null) { + if (canCreateGuest) { + guestRecord = new UserRecord(null /* info */, null /* picture */, + true /* isGuest */, false /* isCurrent */, + false /* isAddUser */, createIsRestricted, canSwitchUsers); + checkIfAddUserDisallowedByAdminOnly(guestRecord); + records.add(guestRecord); } + } else { + int index = guestRecord.isCurrent ? 0 : records.size(); + records.add(index, guestRecord); } - if (!mSimpleUserSwitcher && canCreateUser) { + if (canCreateUser) { UserRecord addUserRecord = new UserRecord(null /* info */, null /* picture */, false /* isGuest */, false /* isCurrent */, true /* isAddUser */, createIsRestricted, canSwitchUsers); @@ -562,8 +562,7 @@ public class UserSwitcherController implements Dumpable { private final ContentObserver mSettingsObserver = new ContentObserver(new Handler()) { public void onChange(boolean selfChange) { - mSimpleUserSwitcher = Settings.Global.getInt(mContext.getContentResolver(), - SIMPLE_USER_SWITCHER_GLOBAL_SETTING, 0) != 0; + mSimpleUserSwitcher = shouldUseSimpleUserSwitcher(); mAddUsersWhenLocked = Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.ADD_USERS_WHEN_LOCKED, 0) != 0; refreshUsers(UserHandle.USER_NULL); @@ -579,6 +578,7 @@ public class UserSwitcherController implements Dumpable { final UserRecord u = mUsers.get(i); pw.print(" "); pw.println(u.toString()); } + pw.println("mSimpleUserSwitcher=" + mSimpleUserSwitcher); } public String getCurrentUserName(Context context) { @@ -717,6 +717,13 @@ public class UserSwitcherController implements Dumpable { } } + private boolean shouldUseSimpleUserSwitcher() { + int defaultSimpleUserSwitcher = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_expandLockScreenUserSwitcher) ? 1 : 0; + return Settings.Global.getInt(mContext.getContentResolver(), + SIMPLE_USER_SWITCHER_GLOBAL_SETTING, defaultSimpleUserSwitcher) != 0; + } + public void startActivity(Intent intent) { mActivityStarter.startActivity(intent, true); } diff --git a/packages/SystemUI/src/com/android/systemui/tuner/DemoModeFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/DemoModeFragment.java index a60ca6201419..49ada1a5e41e 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/DemoModeFragment.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/DemoModeFragment.java @@ -158,7 +158,7 @@ public class DemoModeFragment extends PreferenceFragment implements OnPreference String demoTime = "1010"; // 10:10, a classic choice of horologists try { - String[] versionParts = android.os.Build.VERSION.RELEASE.split("\\."); + String[] versionParts = android.os.Build.VERSION.RELEASE_OR_CODENAME.split("\\."); int majorVersion = Integer.valueOf(versionParts[0]); demoTime = String.format("%02d00", majorVersion % 24); } catch (IllegalArgumentException ex) { diff --git a/packages/SystemUI/src/com/android/systemui/util/FloatingContentCoordinator.kt b/packages/SystemUI/src/com/android/systemui/util/FloatingContentCoordinator.kt new file mode 100644 index 000000000000..70bcc21426b1 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/FloatingContentCoordinator.kt @@ -0,0 +1,320 @@ +package com.android.systemui.util + +import android.graphics.Rect +import android.util.Log +import com.android.systemui.util.FloatingContentCoordinator.FloatingContent +import java.util.HashMap +import javax.inject.Inject +import javax.inject.Singleton + +/** Tag for debug logging. */ +private const val TAG = "FloatingCoordinator" + +/** + * Coordinates the positions and movement of floating content, such as PIP and Bubbles, to ensure + * that they don't overlap. If content does overlap due to content appearing or moving, the + * coordinator will ask content to move to resolve the conflict. + * + * After implementing [FloatingContent], content should call [onContentAdded] to begin coordination. + * Subsequently, call [onContentMoved] whenever the content moves, and the coordinator will move + * other content out of the way. [onContentRemoved] should be called when the content is removed or + * no longer visible. + */ +@Singleton +class FloatingContentCoordinator @Inject constructor() { + + /** + * Represents a piece of floating content, such as PIP or the Bubbles stack. Provides methods + * that allow the [FloatingContentCoordinator] to determine the current location of the content, + * as well as the ability to ask it to move out of the way of other content. + * + * The default implementation of [calculateNewBoundsOnOverlap] moves the content up or down, + * depending on the position of the conflicting content. You can override this method if you + * want your own custom conflict resolution logic. + */ + interface FloatingContent { + + /** + * Return the bounds claimed by this content. This should include the bounds occupied by the + * content itself, as well as any padding, if desired. The coordinator will ensure that no + * other content is located within these bounds. + * + * If the content is animating, this method should return the bounds to which the content is + * animating. If that animation is cancelled, or updated, be sure that your implementation + * of this method returns the appropriate bounds, and call [onContentMoved] so that the + * coordinator moves other content out of the way. + */ + fun getFloatingBoundsOnScreen(): Rect + + /** + * Return the area within which this floating content is allowed to move. When resolving + * conflicts, the coordinator will never ask your content to move to a position where any + * part of the content would be out of these bounds. + */ + fun getAllowedFloatingBoundsRegion(): Rect + + /** + * Called when the coordinator needs this content to move to the given bounds. It's up to + * you how to do that. + * + * Note that if you start an animation to these bounds, [getFloatingBoundsOnScreen] should + * return the destination bounds, not the in-progress animated bounds. This is so the + * coordinator knows where floating content is going to be and can resolve conflicts + * accordingly. + */ + fun moveToBounds(bounds: Rect) + + /** + * Called by the coordinator when it needs to find a new home for this floating content, + * because a new or moving piece of content is now overlapping with it. + * + * [findAreaForContentVertically] and [findAreaForContentAboveOrBelow] are helpful utility + * functions that will find new bounds for your content automatically. Unless you require + * specific conflict resolution logic, these should be sufficient. By default, this method + * delegates to [findAreaForContentVertically]. + * + * @param overlappingContentBounds The bounds of the other piece of content, which + * necessitated this content's relocation. Your new position must not overlap with these + * bounds. + * @param otherContentBounds The bounds of any other pieces of floating content. Your new + * position must not overlap with any of these either. These bounds are guaranteed to be + * non-overlapping. + * @return The new bounds for this content. + */ + @JvmDefault + fun calculateNewBoundsOnOverlap( + overlappingContentBounds: Rect, + otherContentBounds: List<Rect> + ): Rect { + return findAreaForContentVertically( + getFloatingBoundsOnScreen(), + overlappingContentBounds, + otherContentBounds, + getAllowedFloatingBoundsRegion()) + } + } + + /** The bounds of all pieces of floating content added to the coordinator. */ + private val allContentBounds: MutableMap<FloatingContent, Rect> = HashMap() + + /** + * Makes the coordinator aware of a new piece of floating content, and moves any existing + * content out of the way, if necessary. + * + * If you don't want your new content to move existing content, use [getOccupiedBounds] to find + * an unoccupied area, and move the content there before calling this method. + */ + fun onContentAdded(newContent: FloatingContent) { + updateContentBounds() + allContentBounds[newContent] = newContent.getFloatingBoundsOnScreen() + maybeMoveConflictingContent(newContent) + } + + /** + * Called to notify the coordinator that a piece of floating content has moved (or is animating) + * to a new position, and that any conflicting floating content should be moved out of the way. + * + * The coordinator will call [FloatingContent.getFloatingBoundsOnScreen] to find the new bounds + * for the moving content. If you're animating the content, be sure that your implementation of + * getFloatingBoundsOnScreen returns the bounds to which it's animating, not the content's + * current bounds. + * + * If the animation moving this content is cancelled or updated, you'll need to call this method + * again, to ensure that content is moved out of the way of the latest bounds. + * + * @param content The content that has moved. + */ + @JvmOverloads + fun onContentMoved(content: FloatingContent) { + if (!allContentBounds.containsKey(content)) { + Log.wtf(TAG, "Received onContentMoved call before onContentAdded! " + + "This should never happen.") + return + } + + updateContentBounds() + maybeMoveConflictingContent(content) + } + + /** + * Called to notify the coordinator that a piece of floating content has been removed or is no + * longer visible. + */ + fun onContentRemoved(removedContent: FloatingContent) { + allContentBounds.remove(removedContent) + } + + /** + * Returns a set of Rects that represent the bounds of all of the floating content on the + * screen. + * + * [onContentAdded] will move existing content out of the way if the added content intersects + * existing content. That's fine - but if your specific starting position is not important, you + * can use this function to find unoccupied space for your content before calling + * [onContentAdded], so that moving existing content isn't necessary. + */ + fun getOccupiedBounds(): Collection<Rect> { + return allContentBounds.values + } + + /** + * Identifies any pieces of content that are now overlapping with the given content, and asks + * them to move out of the way. + */ + private fun maybeMoveConflictingContent(fromContent: FloatingContent) { + val conflictingNewBounds = allContentBounds[fromContent]!! + allContentBounds + // Filter to content that intersects with the new bounds. That's content that needs + // to move. + .filter { (content, bounds) -> + content != fromContent && Rect.intersects(conflictingNewBounds, bounds) } + // Tell that content to get out of the way, and save the bounds it says it's moving + // (or animating) to. + .forEach { (content, bounds) -> + content.moveToBounds( + content.calculateNewBoundsOnOverlap( + conflictingNewBounds, + // Pass all of the content bounds except the bounds of the + // content we're asking to move, and the conflicting new bounds + // (since those are passed separately). + otherContentBounds = allContentBounds.values + .minus(bounds) + .minus(conflictingNewBounds))) + allContentBounds[content] = content.getFloatingBoundsOnScreen() + } + } + + /** + * Update [allContentBounds] by calling [FloatingContent.getFloatingBoundsOnScreen] for all + * content and saving the result. + */ + private fun updateContentBounds() { + allContentBounds.keys.forEach { allContentBounds[it] = it.getFloatingBoundsOnScreen() } + } + + companion object { + /** + * Finds new bounds for the given content, either above or below its current position. The + * new bounds won't intersect with the newly overlapping rect or the exclusion rects, and + * will be within the allowed bounds unless no possible position exists. + * + * You can use this method to help find a new position for your content when the coordinator + * calls [FloatingContent.moveToAreaExcluding]. + * + * @param contentRect The bounds of the content for which we're finding a new home. + * @param newlyOverlappingRect The bounds of the content that forced this relocation by + * intersecting with the content we now need to move. If the overlapping content is + * overlapping the top half of this content, we'll try to move this content downward if + * possible (since the other content is 'pushing' it down), and vice versa. + * @param exclusionRects Any other areas that we need to avoid when finding a new home for + * the content. These areas must be non-overlapping with each other. + * @param allowedBounds The area within which we're allowed to find new bounds for the + * content. + * @return New bounds for the content that don't intersect the exclusion rects or the + * newly overlapping rect, and that is within bounds unless no possible in-bounds position + * exists. + */ + @JvmStatic + fun findAreaForContentVertically( + contentRect: Rect, + newlyOverlappingRect: Rect, + exclusionRects: Collection<Rect>, + allowedBounds: Rect + ): Rect { + // If the newly overlapping Rect's center is above the content's center, we'll prefer to + // find a space for this content that is below the overlapping content, since it's + // 'pushing' it down. This may not be possible due to to screen bounds, in which case + // we'll find space in the other direction. + val overlappingContentPushingDown = + newlyOverlappingRect.centerY() < contentRect.centerY() + + // Filter to exclusion rects that are above or below the content that we're finding a + // place for. Then, split into two lists - rects above the content, and rects below it. + var (rectsToAvoidAbove, rectsToAvoidBelow) = exclusionRects + .filter { rectToAvoid -> rectsIntersectVertically(rectToAvoid, contentRect) } + .partition { rectToAvoid -> rectToAvoid.top < contentRect.top } + + // Lazily calculate the closest possible new tops for the content, above and below its + // current location. + val newContentBoundsAbove by lazy { findAreaForContentAboveOrBelow( + contentRect, + exclusionRects = rectsToAvoidAbove.plus(newlyOverlappingRect), + findAbove = true) } + val newContentBoundsBelow by lazy { findAreaForContentAboveOrBelow( + contentRect, + exclusionRects = rectsToAvoidBelow.plus(newlyOverlappingRect), + findAbove = false) } + + val positionAboveInBounds by lazy { allowedBounds.contains(newContentBoundsAbove) } + val positionBelowInBounds by lazy { allowedBounds.contains(newContentBoundsBelow) } + + // Use the 'below' position if the content is being overlapped from the top, unless it's + // out of bounds. Also use it if the content is being overlapped from the bottom, but + // the 'above' position is out of bounds. Otherwise, use the 'above' position. + val usePositionBelow = + overlappingContentPushingDown && positionBelowInBounds || + !overlappingContentPushingDown && !positionAboveInBounds + + // Return the content rect, but offset to reflect the new position. + return if (usePositionBelow) newContentBoundsBelow else newContentBoundsAbove + } + + /** + * Finds a new position for the given content, either above or below its current position + * depending on whether [findAbove] is true or false, respectively. This new position will + * not intersect with any of the [exclusionRects]. + * + * This method is useful as a helper method for implementing your own conflict resolution + * logic. Otherwise, you'd want to use [findAreaForContentVertically], which takes screen + * bounds and conflicting bounds' location into account when deciding whether to move to new + * bounds above or below the current bounds. + * + * @param contentRect The content we're finding an area for. + * @param exclusionRects The areas we need to avoid when finding a new area for the content. + * These areas must be non-overlapping with each other. + * @param findAbove Whether we are finding an area above the content's current position, + * rather than an area below it. + */ + fun findAreaForContentAboveOrBelow( + contentRect: Rect, + exclusionRects: Collection<Rect>, + findAbove: Boolean + ): Rect { + // Sort the rects, since we want to move the content as little as possible. We'll + // start with the rects closest to the content and move outward. If we're finding an + // area above the content, that means we sort in reverse order to search the rects + // from highest to lowest y-value. + val sortedExclusionRects = + exclusionRects.sortedBy { if (findAbove) -it.top else it.top } + + val proposedNewBounds = Rect(contentRect) + for (exclusionRect in sortedExclusionRects) { + // If the proposed new bounds don't intersect with this exclusion rect, that + // means there's room for the content here. We know this because the rects are + // sorted and non-overlapping, so any subsequent exclusion rects would be higher + // (or lower) than this one and can't possibly intersect if this one doesn't. + if (!Rect.intersects(proposedNewBounds, exclusionRect)) { + break + } else { + // Otherwise, we need to keep searching for new bounds. If we're finding an + // area above, propose new bounds that place the content just above the + // exclusion rect. If we're finding an area below, propose new bounds that + // place the content just below the exclusion rect. + val verticalOffset = + if (findAbove) -contentRect.height() else exclusionRect.height() + proposedNewBounds.offsetTo( + proposedNewBounds.left, + exclusionRect.top + verticalOffset) + } + } + + return proposedNewBounds + } + + /** Returns whether or not the two Rects share any of the same space on the X axis. */ + private fun rectsIntersectVertically(r1: Rect, r2: Rect): Boolean { + return (r1.left >= r2.left && r1.left <= r2.right) || + (r1.right <= r2.right && r1.right >= r2.left) + } + } +}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java index 1954b3936376..0e9a245d5be6 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java @@ -39,7 +39,7 @@ import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; import android.testing.ViewUtils; -import android.view.SurfaceControl; +import android.view.SurfaceControlViewHost; import android.view.SurfaceView; import android.view.ViewGroup; import android.widget.FrameLayout; @@ -54,7 +54,6 @@ import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import org.mockito.Spy; @RunWithLooper @RunWith(AndroidTestingRunner.class) @@ -77,8 +76,8 @@ public class AdminSecondaryLockScreenControllerTest extends SysuiTestCase { private KeyguardSecurityCallback mKeyguardCallback; @Mock private KeyguardUpdateMonitor mUpdateMonitor; - @Spy - private StubTransaction mTransaction; + @Mock + private SurfaceControlViewHost.SurfacePackage mSurfacePackage; @Before public void setUp() { @@ -97,21 +96,20 @@ public class AdminSecondaryLockScreenControllerTest extends SysuiTestCase { when(mKeyguardClient.asBinder()).thenReturn(mKeyguardClient); mTestController = new AdminSecondaryLockScreenController( - mContext, mParent, mUpdateMonitor, mKeyguardCallback, mHandler, mTransaction); + mContext, mParent, mUpdateMonitor, mKeyguardCallback, mHandler); } @Test public void testShow() throws Exception { doAnswer(invocation -> { IKeyguardCallback callback = (IKeyguardCallback) invocation.getArguments()[1]; - callback.onSurfaceControlCreated(new SurfaceControl()); + callback.onRemoteContentReady(mSurfacePackage); return null; }).when(mKeyguardClient).onSurfaceReady(any(), any(IKeyguardCallback.class)); mTestController.show(mServiceIntent); verifySurfaceReady(); - verify(mTransaction).reparent(any(), any()); assertThat(mContext.isBound(mComponentName)).isTrue(); } @@ -133,7 +131,7 @@ public class AdminSecondaryLockScreenControllerTest extends SysuiTestCase { // Show the view first, then hide. doAnswer(invocation -> { IKeyguardCallback callback = (IKeyguardCallback) invocation.getArguments()[1]; - callback.onSurfaceControlCreated(new SurfaceControl()); + callback.onRemoteContentReady(mSurfacePackage); return null; }).when(mKeyguardClient).onSurfaceReady(any(), any(IKeyguardCallback.class)); @@ -189,19 +187,4 @@ public class AdminSecondaryLockScreenControllerTest extends SysuiTestCase { verify(mKeyguardCallback).dismiss(true, TARGET_USER_ID); assertThat(mContext.isBound(mComponentName)).isFalse(); } - - /** - * Stubbed {@link SurfaceControl.Transaction} class that can be used when unit testing to - * avoid calls to native code. - */ - private class StubTransaction extends SurfaceControl.Transaction { - @Override - public void apply() { - } - - @Override - public SurfaceControl.Transaction reparent(SurfaceControl sc, SurfaceControl newParent) { - return this; - } - } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java index 486aac894d9b..c6c7b87da544 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.java @@ -178,6 +178,20 @@ public class AuthContainerViewTest extends SysuiTestCase { } @Test + public void testCredentialUI_disablesClickingOnBackground() { + // In the credential view, clicking on the background (to cancel authentication) is not + // valid. Thus, the listener should be null, and it should not be in the accessibility + // hierarchy. + initializeContainer(Authenticators.DEVICE_CREDENTIAL); + + mAuthContainer.onAttachedToWindowInternal(); + + verify(mAuthContainer.mBackgroundView).setOnClickListener(eq(null)); + verify(mAuthContainer.mBackgroundView).setImportantForAccessibility( + eq(View.IMPORTANT_FOR_ACCESSIBILITY_NO)); + } + + @Test public void testLayoutParams_hasSecureWindowFlag() { final IBinder windowToken = mock(IBinder.class); final WindowManager.LayoutParams layoutParams = diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt index 897091f69f36..e3bcdc8b0b60 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt @@ -32,12 +32,13 @@ import androidx.test.filters.SmallTest import com.android.systemui.DumpController import com.android.systemui.SysuiTestCase import com.android.systemui.broadcast.BroadcastDispatcher -import com.android.systemui.controls.management.ControlsListingController import com.android.systemui.controls.ControlStatus +import com.android.systemui.controls.management.ControlsListingController import com.android.systemui.controls.ui.ControlsUiController import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.time.FakeSystemClock import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test @@ -82,7 +83,7 @@ class ControlsControllerImplTest : SysuiTestCase() { private lateinit var broadcastReceiverCaptor: ArgumentCaptor<BroadcastReceiver> private lateinit var delayableExecutor: FakeExecutor - private lateinit var controller: ControlsController + private lateinit var controller: ControlsControllerImpl companion object { fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture() @@ -416,5 +417,70 @@ class ControlsControllerImplTest : SysuiTestCase() { verify(listingController).changeUser(UserHandle.of(otherUser)) assertTrue(controller.getFavoriteControls().isEmpty()) assertEquals(otherUser, controller.currentUserId) + assertTrue(controller.available) + } + + @Test + fun testDisableFeature_notAvailable() { + Settings.Secure.putIntForUser(mContext.contentResolver, + ControlsControllerImpl.CONTROLS_AVAILABLE, 0, user) + controller.settingObserver.onChange(false, ControlsControllerImpl.URI, 0) + assertFalse(controller.available) + } + + @Test + fun testDisableFeature_clearFavorites() { + controller.changeFavoriteStatus(TEST_CONTROL_INFO, true) + assertFalse(controller.getFavoriteControls().isEmpty()) + + Settings.Secure.putIntForUser(mContext.contentResolver, + ControlsControllerImpl.CONTROLS_AVAILABLE, 0, user) + controller.settingObserver.onChange(false, ControlsControllerImpl.URI, user) + assertTrue(controller.getFavoriteControls().isEmpty()) + } + + @Test + fun testDisableFeature_noChangeForNotCurrentUser() { + controller.changeFavoriteStatus(TEST_CONTROL_INFO, true) + Settings.Secure.putIntForUser(mContext.contentResolver, + ControlsControllerImpl.CONTROLS_AVAILABLE, 0, otherUser) + controller.settingObserver.onChange(false, ControlsControllerImpl.URI, otherUser) + + assertTrue(controller.available) + assertFalse(controller.getFavoriteControls().isEmpty()) + } + + @Test + fun testCorrectUserSettingOnUserChange() { + Settings.Secure.putIntForUser(mContext.contentResolver, + ControlsControllerImpl.CONTROLS_AVAILABLE, 0, otherUser) + + val intent = Intent(Intent.ACTION_USER_SWITCHED).apply { + putExtra(Intent.EXTRA_USER_HANDLE, otherUser) + } + val pendingResult = mock(BroadcastReceiver.PendingResult::class.java) + `when`(pendingResult.sendingUserId).thenReturn(otherUser) + broadcastReceiverCaptor.value.pendingResult = pendingResult + + broadcastReceiverCaptor.value.onReceive(mContext, intent) + + assertFalse(controller.available) + } + + @Test + fun testCountFavoritesForComponent_singleComponent() { + controller.changeFavoriteStatus(TEST_CONTROL_INFO, true) + + assertEquals(1, controller.countFavoritesForComponent(TEST_COMPONENT)) + assertEquals(0, controller.countFavoritesForComponent(TEST_COMPONENT_2)) + } + + @Test + fun testCountFavoritesForComponent_multipleComponents() { + controller.changeFavoriteStatus(TEST_CONTROL_INFO, true) + controller.changeFavoriteStatus(TEST_CONTROL_INFO_2, true) + + assertEquals(1, controller.countFavoritesForComponent(TEST_COMPONENT)) + assertEquals(1, controller.countFavoritesForComponent(TEST_COMPONENT_2)) } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/RichEventTest.java b/packages/SystemUI/tests/src/com/android/systemui/log/RichEventTest.java deleted file mode 100644 index 4a90bb91ca37..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/log/RichEventTest.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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.log; - -import static junit.framework.Assert.assertEquals; - -import android.testing.AndroidTestingRunner; - -import androidx.test.filters.SmallTest; - -import com.android.systemui.SysuiTestCase; - -import junit.framework.Assert; - -import org.junit.Test; -import org.junit.runner.RunWith; - -@SmallTest -@RunWith(AndroidTestingRunner.class) -public class RichEventTest extends SysuiTestCase { - - private static final int TOTAL_EVENT_TYPES = 1; - - @Test - public void testCreateRichEvent_invalidType() { - try { - // indexing for events starts at 0, so TOTAL_EVENT_TYPES is an invalid type - new TestableRichEvent(Event.DEBUG, TOTAL_EVENT_TYPES, "msg"); - } catch (IllegalArgumentException e) { - // expected - return; - } - - Assert.fail("Expected an invalidArgumentException since the event type was invalid."); - } - - @Test - public void testCreateRichEvent() { - final int eventType = 0; - RichEvent e = new TestableRichEvent(Event.DEBUG, eventType, "msg"); - assertEquals(e.getType(), eventType); - } - - class TestableRichEvent extends RichEvent { - TestableRichEvent(int logLevel, int type, String reason) { - init(logLevel, type, reason); - } - - @Override - public String[] getEventLabels() { - return new String[]{"ACTION_NAME"}; - } - } - -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/SysuiLogTest.java b/packages/SystemUI/tests/src/com/android/systemui/log/SysuiLogTest.java deleted file mode 100644 index e7b317e882ef..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/log/SysuiLogTest.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * 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.log; - -import static junit.framework.Assert.assertEquals; - -import android.testing.AndroidTestingRunner; - -import androidx.test.filters.SmallTest; - -import com.android.systemui.DumpController; -import com.android.systemui.SysuiTestCase; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -@SmallTest -@RunWith(AndroidTestingRunner.class) -public class SysuiLogTest extends SysuiTestCase { - private static final String TEST_ID = "TestLogger"; - private static final String TEST_MSG = "msg"; - private static final int MAX_LOGS = 5; - - @Mock - private DumpController mDumpController; - private SysuiLog<Event> mSysuiLog; - - @Before - public void setup() { - MockitoAnnotations.initMocks(this); - } - - @Test - public void testLogDisabled_noLogsWritten() { - mSysuiLog = new TestSysuiLog(mDumpController, TEST_ID, MAX_LOGS, false); - assertEquals(null, mSysuiLog.mTimeline); - - mSysuiLog.log(createEvent(TEST_MSG)); - assertEquals(null, mSysuiLog.mTimeline); - } - - @Test - public void testLogEnabled_logWritten() { - mSysuiLog = new TestSysuiLog(mDumpController, TEST_ID, MAX_LOGS, true); - assertEquals(0, mSysuiLog.mTimeline.size()); - - mSysuiLog.log(createEvent(TEST_MSG)); - assertEquals(1, mSysuiLog.mTimeline.size()); - } - - @Test - public void testMaxLogs() { - mSysuiLog = new TestSysuiLog(mDumpController, TEST_ID, MAX_LOGS, true); - assertEquals(mSysuiLog.mTimeline.size(), 0); - - for (int i = 0; i < MAX_LOGS + 1; i++) { - mSysuiLog.log(createEvent(TEST_MSG + i)); - } - - assertEquals(MAX_LOGS, mSysuiLog.mTimeline.size()); - - // check the first message (msg0) was replaced with msg1: - assertEquals(TEST_MSG + "1", mSysuiLog.mTimeline.getFirst().getMessage()); - } - - @Test - public void testRecycleLogs() { - // GIVEN a SysuiLog with one log - mSysuiLog = new TestSysuiLog(mDumpController, TEST_ID, MAX_LOGS, true); - Event e = createEvent(TEST_MSG); // msg - mSysuiLog.log(e); // Logs: [msg] - - Event recycledEvent = null; - // WHEN we add MAX_LOGS after the first log - for (int i = 0; i < MAX_LOGS; i++) { - recycledEvent = mSysuiLog.log(createEvent(TEST_MSG + i)); - } - // Logs: [msg1, msg2, msg3, msg4] - - // THEN we see the recycledEvent is e - assertEquals(e, recycledEvent); - } - - private Event createEvent(String msg) { - return new Event().init(msg); - } - - public class TestSysuiLog extends SysuiLog<Event> { - protected TestSysuiLog(DumpController dumpController, String id, int maxLogs, - boolean enabled) { - super(dumpController, id, maxLogs, enabled, false); - } - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java index 0a3bc6def160..1d4b4be9e683 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java @@ -53,8 +53,8 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.app.IBatteryStats; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.keyguard.KeyguardUpdateMonitor.BatteryStatus; import com.android.settingslib.Utils; +import com.android.settingslib.fuelgauge.BatteryStatus; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.dock.DockManager; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java index b51581f544f5..07f6936ece07 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java @@ -79,7 +79,6 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntryB import com.android.systemui.statusbar.notification.collection.NotificationRankingManager; import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl; import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider; -import com.android.systemui.statusbar.notification.logging.NotifLog; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier; import com.android.systemui.statusbar.notification.row.ActivatableNotificationViewController; @@ -139,7 +138,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase { @Mock private NotificationRemoteInputManager mRemoteInputManager; @Mock private DeviceProvisionedController mDeviceProvisionedController; @Mock private RowInflaterTask mAsyncInflationTask; - @Mock private NotifLog mNotifLog; + @Mock private NotificationEntryManagerLogger mLogger; @Mock private FeatureFlags mFeatureFlags; @Mock private LeakDetector mLeakDetector; @Mock private ActivatableNotificationViewController mActivatableNotificationViewController; @@ -234,14 +233,14 @@ public class NotificationEntryManagerTest extends SysuiTestCase { when(mFeatureFlags.isNewNotifPipelineEnabled()).thenReturn(false); when(mFeatureFlags.isNewNotifPipelineRenderingEnabled()).thenReturn(false); mEntryManager = new TestableNotificationEntryManager( - mNotifLog, + mLogger, mGroupManager, new NotificationRankingManager( () -> mock(NotificationMediaManager.class), mGroupManager, mHeadsUpManager, mock(NotificationFilter.class), - mNotifLog, + mLogger, mock(NotificationSectionsFeatureManager.class), mock(PeopleNotificationIdentifier.class), mock(HighPriorityProvider.class)), diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/TestableNotificationEntryManager.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/TestableNotificationEntryManager.kt index a9f9db67ff0b..0e730e5c3ffb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/TestableNotificationEntryManager.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/TestableNotificationEntryManager.kt @@ -22,7 +22,6 @@ import com.android.systemui.statusbar.NotificationRemoteInputManager import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.NotificationRankingManager import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder -import com.android.systemui.statusbar.notification.logging.NotifLog import com.android.systemui.statusbar.notification.stack.NotificationListContainer import com.android.systemui.statusbar.phone.HeadsUpManagerPhone import com.android.systemui.statusbar.phone.NotificationGroupManager @@ -34,7 +33,7 @@ import java.util.concurrent.CountDownLatch * Enable some test capabilities for NEM without making everything public on the base class */ class TestableNotificationEntryManager( - log: NotifLog, + logger: NotificationEntryManagerLogger, gm: NotificationGroupManager, rm: NotificationRankingManager, ke: KeyguardEnvironment, @@ -43,7 +42,7 @@ class TestableNotificationEntryManager( notificationRemoteInputManagerLazy: dagger.Lazy<NotificationRemoteInputManager>, leakDetector: LeakDetector, fgsFeatureController: ForegroundServiceDismissalFeatureController -) : NotificationEntryManager(log, gm, rm, ke, ff, rb, +) : NotificationEntryManager(logger, gm, rm, ke, ff, rb, notificationRemoteInputManagerLazy, leakDetector, fgsFeatureController) { public var countDownLatch: CountDownLatch = CountDownLatch(1) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt index 7ab4846ea066..c6b496dd8215 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt @@ -27,10 +27,10 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking import com.android.systemui.statusbar.NotificationMediaManager +import com.android.systemui.statusbar.notification.NotificationEntryManagerLogger import com.android.systemui.statusbar.notification.NotificationFilter import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider -import com.android.systemui.statusbar.notification.logging.NotifLog import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_SILENT @@ -62,7 +62,7 @@ class NotificationRankingManagerTest : SysuiTestCase() { mock(NotificationGroupManager::class.java), mock(HeadsUpManager::class.java), mock(NotificationFilter::class.java), - mock(NotifLog::class.java), + mock(NotificationEntryManagerLogger::class.java), mock(NotificationSectionsFeatureManager::class.java), personNotificationIdentifier, HighPriorityProvider(personNotificationIdentifier) @@ -189,7 +189,7 @@ class NotificationRankingManagerTest : SysuiTestCase() { groupManager: NotificationGroupManager, headsUpManager: HeadsUpManager, filter: NotificationFilter, - notifLog: NotifLog, + logger: NotificationEntryManagerLogger, sectionsFeatureManager: NotificationSectionsFeatureManager, peopleNotificationIdentifier: PeopleNotificationIdentifier, highPriorityProvider: HighPriorityProvider @@ -198,7 +198,7 @@ class NotificationRankingManagerTest : SysuiTestCase() { groupManager, headsUpManager, filter, - notifLog, + logger, sectionsFeatureManager, peopleNotificationIdentifier, highPriorityProvider diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java index 70d76f0c3a52..b16e52ce7bd4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java @@ -65,6 +65,7 @@ import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.ForegroundServiceDismissalFeatureController; import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.notification.NotificationEntryManagerLogger; import com.android.systemui.statusbar.notification.NotificationFilter; import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager; import com.android.systemui.statusbar.notification.TestableNotificationEntryManager; @@ -74,7 +75,6 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntryB import com.android.systemui.statusbar.notification.collection.NotificationRankingManager; import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder; import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider; -import com.android.systemui.statusbar.notification.logging.NotifLog; import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.FooterView; @@ -163,14 +163,14 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { ArgumentCaptor<UserChangedListener> userChangedCaptor = ArgumentCaptor .forClass(UserChangedListener.class); mEntryManager = new TestableNotificationEntryManager( - mock(NotifLog.class), + mock(NotificationEntryManagerLogger.class), mock(NotificationGroupManager.class), new NotificationRankingManager( () -> mock(NotificationMediaManager.class), mGroupManager, mHeadsUpManager, mock(NotificationFilter.class), - mock(NotifLog.class), + mock(NotificationEntryManagerLogger.class), mock(NotificationSectionsFeatureManager.class), mock(PeopleNotificationIdentifier.class), mock(HighPriorityProvider.class) diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/FloatingContentCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/FloatingContentCoordinatorTest.kt new file mode 100644 index 000000000000..8eecde1f4f7c --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/util/FloatingContentCoordinatorTest.kt @@ -0,0 +1,218 @@ +package com.android.systemui.util + +import android.graphics.Rect +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import org.junit.After +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@TestableLooper.RunWithLooper +@RunWith(AndroidTestingRunner::class) +@SmallTest +class FloatingContentCoordinatorTest : SysuiTestCase() { + + private val screenBounds = Rect(0, 0, 1000, 1000) + + private val rect100px = Rect() + private val rect100pxFloating = FloatingRect(rect100px) + + private val rect200px = Rect() + private val rect200pxFloating = FloatingRect(rect200px) + + private val rect300px = Rect() + private val rect300pxFloating = FloatingRect(rect300px) + + private val floatingCoordinator = FloatingContentCoordinator() + + @Before + fun setup() { + rect100px.set(0, 0, 100, 100) + rect200px.set(0, 0, 200, 200) + rect300px.set(0, 0, 300, 300) + } + + @After + fun tearDown() { + // We need to remove this stuff since it's a singleton object and it'll be there for the + // next test. + floatingCoordinator.onContentRemoved(rect100pxFloating) + floatingCoordinator.onContentRemoved(rect200pxFloating) + floatingCoordinator.onContentRemoved(rect300pxFloating) + } + + @Test + fun testOnContentAdded() { + // Add rect1, and verify that the coordinator didn't move it. + floatingCoordinator.onContentAdded(rect100pxFloating) + assertEquals(rect100px.top, 0) + + // Add rect2, which intersects rect1. Verify that rect2 was not moved, since newly added + // content is allowed to remain where it is. rect1 should have been moved below rect2 + // since it was in the way. + floatingCoordinator.onContentAdded(rect200pxFloating) + assertEquals(rect200px.top, 0) + assertEquals(rect100px.top, 200) + + verifyRectSizes() + } + + @Test + fun testOnContentRemoved() { + // Add rect1, and remove it. Then add rect2. Since rect1 was removed before that, it should + // no longer be considered in the way, so it shouldn't move when rect2 is added. + floatingCoordinator.onContentAdded(rect100pxFloating) + floatingCoordinator.onContentRemoved(rect100pxFloating) + floatingCoordinator.onContentAdded(rect200pxFloating) + + assertEquals(rect100px.top, 0) + assertEquals(rect200px.top, 0) + + verifyRectSizes() + } + + @Test + fun testOnContentMoved_twoRects() { + // Add rect1, which is at y = 0. + floatingCoordinator.onContentAdded(rect100pxFloating) + + // Move rect2 down to 500px, where it won't conflict with rect1. + rect200px.offsetTo(0, 500) + floatingCoordinator.onContentAdded(rect200pxFloating) + + // Then, move it to 0px where it will absolutely conflict with rect1. + rect200px.offsetTo(0, 0) + floatingCoordinator.onContentMoved(rect200pxFloating) + + // The coordinator should have left rect2 alone, and moved rect1 below it. rect1 should now + // be at y = 200. + assertEquals(rect200px.top, 0) + assertEquals(rect100px.top, 200) + + verifyRectSizes() + + // Move rect2 to y = 275px. Since this puts it at the bottom half of rect1, it should push + // rect1 upward and leave rect2 alone. + rect200px.offsetTo(0, 275) + floatingCoordinator.onContentMoved(rect200pxFloating) + + assertEquals(rect200px.top, 275) + assertEquals(rect100px.top, 175) + + verifyRectSizes() + + // Move rect2 to y = 110px. This makes it intersect rect1 again, but above its center of + // mass. That means rect1 should be pushed downward. + rect200px.offsetTo(0, 110) + floatingCoordinator.onContentMoved(rect200pxFloating) + + assertEquals(rect200px.top, 110) + assertEquals(rect100px.top, 310) + + verifyRectSizes() + } + + @Test + fun testOnContentMoved_threeRects() { + floatingCoordinator.onContentAdded(rect100pxFloating) + + // Add rect2, which should displace rect1 to y = 200 + floatingCoordinator.onContentAdded(rect200pxFloating) + assertEquals(rect200px.top, 0) + assertEquals(rect100px.top, 200) + + // Add rect3, which should completely cover both rect1 and rect2. That should cause them to + // move away. The order in which they do so is non-deterministic, so just make sure none of + // the three Rects intersect. + floatingCoordinator.onContentAdded(rect300pxFloating) + + assertFalse(Rect.intersects(rect100px, rect200px)) + assertFalse(Rect.intersects(rect100px, rect300px)) + assertFalse(Rect.intersects(rect200px, rect300px)) + + // Move rect2 to intersect both rect1 and rect3. + rect200px.offsetTo(0, 150) + floatingCoordinator.onContentMoved(rect200pxFloating) + + assertFalse(Rect.intersects(rect100px, rect200px)) + assertFalse(Rect.intersects(rect100px, rect300px)) + assertFalse(Rect.intersects(rect200px, rect300px)) + } + + @Test + fun testOnContentMoved_respectsUpperBounds() { + // Add rect1, which is at y = 0. + floatingCoordinator.onContentAdded(rect100pxFloating) + + // Move rect2 down to 500px, where it won't conflict with rect1. + rect200px.offsetTo(0, 500) + floatingCoordinator.onContentAdded(rect200pxFloating) + + // Then, move it to 90px where it will conflict with rect1, but with a center of mass below + // that of rect1's. This would normally mean that rect1 moves upward. However, since it's at + // the top of the screen, it should go downward instead. + rect200px.offsetTo(0, 90) + floatingCoordinator.onContentMoved(rect200pxFloating) + + // rect2 should have been left alone, rect1 is now below rect2 at y = 290px even though it + // was intersected from below. + assertEquals(rect200px.top, 90) + assertEquals(rect100px.top, 290) + } + + @Test + fun testOnContentMoved_respectsLowerBounds() { + // Put rect1 at the bottom of the screen and add it. + rect100px.offsetTo(0, screenBounds.bottom - 100) + floatingCoordinator.onContentAdded(rect100pxFloating) + + // Put rect2 at the bottom as well. Since its center of mass is above rect1's, rect1 would + // normally move downward. Since it's at the bottom of the screen, it should go upward + // instead. + rect200px.offsetTo(0, 800) + floatingCoordinator.onContentAdded(rect200pxFloating) + + assertEquals(rect200px.top, 800) + assertEquals(rect100px.top, 700) + } + + /** + * Tests that the rect sizes didn't change when the coordinator manipulated them. This allows us + * to assert only the value of rect.top in tests, since if top, width, and height are correct, + * that means top/left/right/bottom are all correct. + */ + private fun verifyRectSizes() { + assertEquals(100, rect100px.width()) + assertEquals(200, rect200px.width()) + assertEquals(300, rect300px.width()) + + assertEquals(100, rect100px.height()) + assertEquals(200, rect200px.height()) + assertEquals(300, rect300px.height()) + } + + /** + * Helper class that uses [floatingCoordinator.findAreaForContentVertically] to move a + * Rect when needed. + */ + inner class FloatingRect( + private val underlyingRect: Rect + ) : FloatingContentCoordinator.FloatingContent { + override fun moveToBounds(bounds: Rect) { + underlyingRect.set(bounds) + } + + override fun getAllowedFloatingBoundsRegion(): Rect { + return screenBounds + } + + override fun getFloatingBoundsOnScreen(): Rect { + return underlyingRect + } + } +}
\ No newline at end of file diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java index 07abe1adeb52..39c402be84a3 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java @@ -272,13 +272,6 @@ public class Tethering { mStateReceiver = new StateReceiver(); - mNetdCallback = new NetdCallback(); - try { - mNetd.registerUnsolicitedEventListener(mNetdCallback); - } catch (RemoteException e) { - mLog.e("Unable to register netd UnsolicitedEventListener"); - } - final UserManager userManager = (UserManager) mContext.getSystemService( Context.USER_SERVICE); mTetheringRestriction = new UserRestrictionActionListener(userManager, this); @@ -287,6 +280,14 @@ public class Tethering { // Load tethering configuration. updateConfiguration(); + // NetdCallback should be registered after updateConfiguration() to ensure + // TetheringConfiguration is created. + mNetdCallback = new NetdCallback(); + try { + mNetd.registerUnsolicitedEventListener(mNetdCallback); + } catch (RemoteException e) { + mLog.e("Unable to register netd UnsolicitedEventListener"); + } startStateMachineUpdaters(mHandler); startTrackDefaultNetwork(); diff --git a/packages/overlays/Android.mk b/packages/overlays/Android.mk index fc7709c4a51e..dcdb80b497d0 100644 --- a/packages/overlays/Android.mk +++ b/packages/overlays/Android.mk @@ -28,6 +28,7 @@ LOCAL_REQUIRED_MODULES := \ DisplayCutoutEmulationDoubleOverlay \ DisplayCutoutEmulationHoleOverlay \ DisplayCutoutEmulationTallOverlay \ + DisplayCutoutEmulationWaterfallOverlay \ FontNotoSerifSourceOverlay \ IconPackCircularAndroidOverlay \ IconPackCircularLauncherOverlay \ diff --git a/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/Android.mk new file mode 100644 index 000000000000..b6b6dd1c25bc --- /dev/null +++ b/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/Android.mk @@ -0,0 +1,14 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_RRO_THEME := DisplayCutoutEmulationWaterfall + + +LOCAL_PRODUCT_MODULE := true + +LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res + +LOCAL_PACKAGE_NAME := DisplayCutoutEmulationWaterfallOverlay +LOCAL_SDK_VERSION := current + +include $(BUILD_RRO_PACKAGE) diff --git a/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/AndroidManifest.xml b/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/AndroidManifest.xml new file mode 100644 index 000000000000..2d5bb14f4dd3 --- /dev/null +++ b/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/AndroidManifest.xml @@ -0,0 +1,26 @@ +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.internal.display.cutout.emulation.waterfall" + android:versionCode="1" + android:versionName="1.0"> + <overlay android:targetPackage="android" + android:category="com.android.internal.display_cutout_emulation" + android:priority="1"/> + + <application android:label="@string/display_cutout_emulation_overlay" android:hasCode="false"/> +</manifest> diff --git a/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values-land/config.xml b/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values-land/config.xml new file mode 100644 index 000000000000..df2f3d19626e --- /dev/null +++ b/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values-land/config.xml @@ -0,0 +1,22 @@ +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<resources> + <!-- Can't link to other dimensions here, but this should be status_bar_height_landscape --> + <dimen name="quick_qs_offset_height">48dp</dimen> + <!-- Total height of QQS in landscape; quick_qs_offset_height + 128 --> + <dimen name="quick_qs_total_height">176dp</dimen> +</resources>
\ No newline at end of file diff --git a/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values/config.xml b/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values/config.xml new file mode 100644 index 000000000000..6f692c8021c0 --- /dev/null +++ b/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values/config.xml @@ -0,0 +1,35 @@ +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + + <!-- Height of the status bar in portrait. The height should be + Max((status bar content height + waterfall top size), top cutout size) --> + <dimen name="status_bar_height_portrait">28dp</dimen> + <!-- Max((28 + 20), 0) = 48 --> + <dimen name="status_bar_height_landscape">48dp</dimen> + <!-- Height of area above QQS where battery/time go (equal to status bar height if > 48dp) --> + <dimen name="quick_qs_offset_height">28dp</dimen> + <!-- Total height of QQS (quick_qs_offset_height + 128) --> + <dimen name="quick_qs_total_height">156dp</dimen> + + <dimen name="waterfall_display_left_edge_size">20dp</dimen> + <dimen name="waterfall_display_top_edge_size">0dp</dimen> + <dimen name="waterfall_display_right_edge_size">20dp</dimen> + <dimen name="waterfall_display_bottom_edge_size">0dp</dimen> +</resources> + + diff --git a/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values/strings.xml b/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values/strings.xml new file mode 100644 index 000000000000..ed073d0e244e --- /dev/null +++ b/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + + <string name="display_cutout_emulation_overlay">Waterfall cutout</string> + +</resources>
\ No newline at end of file diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java index a5877ccbde7c..565ee63d89ab 100644 --- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java @@ -16,6 +16,8 @@ package com.android.server.accessibility; +import static android.accessibilityservice.AccessibilityService.KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE_ID; +import static android.accessibilityservice.AccessibilityService.KEY_ACCESSIBILITY_SCREENSHOT_HARDWAREBUFFER; import static android.accessibilityservice.AccessibilityServiceInfo.DEFAULT; import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY; import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS; @@ -36,10 +38,11 @@ import android.content.Intent; import android.content.ServiceConnection; import android.content.pm.PackageManager; import android.content.pm.ParceledListSlice; -import android.graphics.Bitmap; +import android.graphics.GraphicBuffer; import android.graphics.Point; import android.graphics.Rect; import android.graphics.Region; +import android.hardware.HardwareBuffer; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManagerGlobal; import android.os.Binder; @@ -50,6 +53,7 @@ import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.PowerManager; +import android.os.RemoteCallback; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; @@ -71,6 +75,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.compat.IPlatformCompat; import com.android.internal.os.SomeArgs; import com.android.internal.util.DumpUtils; +import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.LocalServices; import com.android.server.accessibility.AccessibilityWindowManager.RemoteAccessibilityConnection; import com.android.server.wm.ActivityTaskManagerInternal; @@ -106,6 +111,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ private final PowerManager mPowerManager; private final IPlatformCompat mIPlatformCompat; + private final Handler mMainHandler; + // Handler for scheduling method invocations on the main thread. public final InvocationHandler mInvocationHandler; @@ -238,6 +245,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ mSecurityPolicy = securityPolicy; mSystemActionPerformer = systemActionPerfomer; mSystemSupport = systemSupport; + mMainHandler = mainHandler; mInvocationHandler = new InvocationHandler(mainHandler.getLooper()); mA11yWindowManager = a11yWindowManager; mDisplayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE); @@ -959,52 +967,72 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ mInvocationHandler.setSoftKeyboardCallbackEnabled(enabled); } - @Nullable @Override - public Bitmap takeScreenshot(int displayId) { + public void takeScreenshot(int displayId, RemoteCallback callback) { synchronized (mLock) { if (!hasRightsToCurrentUserLocked()) { - return null; + sendScreenshotResult(true, null, callback); + return; } if (!mSecurityPolicy.canTakeScreenshotLocked(this)) { - return null; + sendScreenshotResult(true, null, callback); + throw new SecurityException("Services don't have the capability of taking" + + " the screenshot."); } } if (!mSecurityPolicy.checkAccessibilityAccess(this)) { - return null; + sendScreenshotResult(true, null, callback); + return; } final Display display = DisplayManagerGlobal.getInstance() .getRealDisplay(displayId); if (display == null) { - return null; + sendScreenshotResult(true, null, callback); + return; } - final Point displaySize = new Point(); - display.getRealSize(displaySize); - final int rotation = display.getRotation(); - Bitmap screenShot = null; + sendScreenshotResult(false, display, callback); + } + private void sendScreenshotResult(boolean noResult, Display display, RemoteCallback callback) { + final boolean noScreenshot = noResult; final long identity = Binder.clearCallingIdentity(); try { - final Rect crop = new Rect(0, 0, displaySize.x, displaySize.y); - // TODO (b/145893483): calling new API with the display as a parameter - // when surface control supported. - screenShot = SurfaceControl.screenshot(crop, displaySize.x, displaySize.y, - rotation); - if (screenShot != null) { - // Optimization for telling the bitmap that all of the pixels are known to be - // opaque (false). This is meant as a drawing hint, as in some cases a bitmap - // that is known to be opaque can take a faster drawing case than one that may - // have non-opaque per-pixel alpha values. - screenShot.setHasAlpha(false); - } + mMainHandler.post(PooledLambda.obtainRunnable((nonArg) -> { + if (noScreenshot) { + callback.sendResult(null); + return; + } + final Point displaySize = new Point(); + // TODO (b/145893483): calling new API with the display as a parameter + // when surface control supported. + final IBinder token = SurfaceControl.getInternalDisplayToken(); + final Rect crop = new Rect(0, 0, displaySize.x, displaySize.y); + final int rotation = display.getRotation(); + display.getRealSize(displaySize); + + final SurfaceControl.ScreenshotGraphicBuffer screenshotBuffer = + SurfaceControl.screenshotToBufferWithSecureLayersUnsafe(token, crop, + displaySize.x, displaySize.y, false, + rotation); + final GraphicBuffer graphicBuffer = screenshotBuffer.getGraphicBuffer(); + final HardwareBuffer hardwareBuffer = + HardwareBuffer.createFromGraphicBuffer(graphicBuffer); + final int colorSpaceId = screenshotBuffer.getColorSpace().getId(); + + // Send back the result. + final Bundle payload = new Bundle(); + payload.putParcelable(KEY_ACCESSIBILITY_SCREENSHOT_HARDWAREBUFFER, + hardwareBuffer); + payload.putInt(KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE_ID, colorSpaceId); + callback.sendResult(payload); + }, null).recycleOnUse()); } finally { Binder.restoreCallingIdentity(identity); } - return screenShot; } @Override diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java index 25911a7ed0ea..edb4445151d5 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java @@ -16,8 +16,6 @@ package com.android.server.accessibility; -import static android.accessibilityservice.AccessibilityService.KEY_ACCESSIBILITY_SCREENSHOT; - import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; import android.Manifest; @@ -27,20 +25,16 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.ParceledListSlice; -import android.graphics.Bitmap; import android.os.Binder; -import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Process; -import android.os.RemoteCallback; import android.os.RemoteException; import android.os.UserHandle; import android.provider.Settings; import android.util.Slog; import android.view.Display; -import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.inputmethod.InputMethodManagerInternal; import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.WindowManagerInternal; @@ -393,15 +387,4 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect } } } - - @Override - public void takeScreenshotWithCallback(int displayId, RemoteCallback callback) { - mMainHandler.post(PooledLambda.obtainRunnable((nonArg) -> { - final Bitmap screenshot = super.takeScreenshot(displayId); - // Send back the result. - final Bundle payload = new Bundle(); - payload.putParcelable(KEY_ACCESSIBILITY_SCREENSHOT, screenshot); - callback.sendResult(payload); - }, null).recycleOnUse()); - } } diff --git a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java index 5d9af26a8339..d1c3a02c6761 100644 --- a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java +++ b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java @@ -328,6 +328,6 @@ class UiAutomationManager { public void onFingerprintGesture(int gesture) {} @Override - public void takeScreenshotWithCallback(int displayId, RemoteCallback callback) {} + public void takeScreenshot(int displayId, RemoteCallback callback) {} } } diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java index 5d170d34d77d..b74be7e3f345 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java @@ -30,6 +30,13 @@ import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_RIGHT; import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_UP; import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_TRIPLE_TAP; +import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_DOUBLE_TAP; +import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_SINGLE_TAP; +import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_SWIPE_DOWN; +import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_SWIPE_LEFT; +import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_SWIPE_RIGHT; +import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_SWIPE_UP; +import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_TRIPLE_TAP; import static android.accessibilityservice.AccessibilityService.GESTURE_DOUBLE_TAP; import static android.accessibilityservice.AccessibilityService.GESTURE_DOUBLE_TAP_AND_HOLD; import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_DOWN; @@ -133,6 +140,13 @@ class GestureManifold implements GestureMatcher.StateChangeListener { new MultiFingerMultiTap(mContext, 3, 2, GESTURE_3_FINGER_DOUBLE_TAP, this)); mMultiFingerGestures.add( new MultiFingerMultiTap(mContext, 3, 3, GESTURE_3_FINGER_TRIPLE_TAP, this)); + // Four-finger taps. + mMultiFingerGestures.add( + new MultiFingerMultiTap(mContext, 4, 1, GESTURE_4_FINGER_SINGLE_TAP, this)); + mMultiFingerGestures.add( + new MultiFingerMultiTap(mContext, 4, 2, GESTURE_4_FINGER_DOUBLE_TAP, this)); + mMultiFingerGestures.add( + new MultiFingerMultiTap(mContext, 4, 3, GESTURE_4_FINGER_TRIPLE_TAP, this)); // Two-finger swipes. mMultiFingerGestures.add( new MultiFingerSwipe(context, 2, DOWN, GESTURE_2_FINGER_SWIPE_DOWN, this)); @@ -151,6 +165,15 @@ class GestureManifold implements GestureMatcher.StateChangeListener { new MultiFingerSwipe(context, 3, RIGHT, GESTURE_3_FINGER_SWIPE_RIGHT, this)); mMultiFingerGestures.add( new MultiFingerSwipe(context, 3, UP, GESTURE_3_FINGER_SWIPE_UP, this)); + // Four-finger swipes. + mMultiFingerGestures.add( + new MultiFingerSwipe(context, 4, DOWN, GESTURE_4_FINGER_SWIPE_DOWN, this)); + mMultiFingerGestures.add( + new MultiFingerSwipe(context, 4, LEFT, GESTURE_4_FINGER_SWIPE_LEFT, this)); + mMultiFingerGestures.add( + new MultiFingerSwipe(context, 4, RIGHT, GESTURE_4_FINGER_SWIPE_RIGHT, this)); + mMultiFingerGestures.add( + new MultiFingerSwipe(context, 4, UP, GESTURE_4_FINGER_SWIPE_UP, this)); } /** diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java index 8249239e3602..a14584a63bb2 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java @@ -139,7 +139,7 @@ class MultiFingerSwipe extends GestureMatcher { final int actionIndex = getActionIndex(rawEvent); final int pointerId = rawEvent.getPointerId(actionIndex); int pointerIndex = rawEvent.getPointerCount() - 1; - if (pointerId < 0 || pointerId > rawEvent.getPointerCount() - 1) { + if (pointerId < 0) { // Nonsensical pointer id. cancelGesture(event, rawEvent, policyFlags); return; @@ -185,7 +185,7 @@ class MultiFingerSwipe extends GestureMatcher { } final int actionIndex = getActionIndex(rawEvent); final int pointerId = rawEvent.getPointerId(actionIndex); - if (pointerId < 0 || pointerId > rawEvent.getPointerCount() - 1) { + if (pointerId < 0) { // Nonsensical pointer id. cancelGesture(event, rawEvent, policyFlags); return; @@ -224,7 +224,7 @@ class MultiFingerSwipe extends GestureMatcher { mCurrentFingerCount -= 1; final int actionIndex = getActionIndex(event); final int pointerId = event.getPointerId(actionIndex); - if (pointerId < 0 || pointerId > rawEvent.getPointerCount() - 1) { + if (pointerId < 0) { // Nonsensical pointer id. cancelGesture(event, rawEvent, policyFlags); return; @@ -250,11 +250,29 @@ class MultiFingerSwipe extends GestureMatcher { @Override protected void onMove(MotionEvent event, MotionEvent rawEvent, int policyFlags) { - for (int pointerIndex = 0; pointerIndex < rawEvent.getPointerCount(); ++pointerIndex) { + for (int pointerIndex = 0; pointerIndex < mTargetFingerCount; ++pointerIndex) { + if (mPointerIds[pointerIndex] == INVALID_POINTER_ID) { + // Fingers have started to move before the required number of fingers are down. + // However, they can still move less than the touch slop and still be considered + // touching, not moving. + // So we just ignore fingers that haven't been assigned a pointer id and process + // those who have. + continue; + } if (DEBUG) { Slog.d(getGestureName(), "Processing move on finger " + pointerIndex); } int index = rawEvent.findPointerIndex(mPointerIds[pointerIndex]); + if (index < 0) { + // This finger is not present in this event. It could have gone up just before this + // movement. + if (DEBUG) { + Slog.d( + getGestureName(), + "Finger " + pointerIndex + " not found in this event. skipping."); + } + continue; + } final float x = rawEvent.getX(index); final float y = rawEvent.getY(index); if (x < 0f || y < 0f) { diff --git a/services/core/Android.bp b/services/core/Android.bp index a603fa975b74..f33237f490e4 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -115,8 +115,8 @@ java_library_static { "android.hardware.health-V2.0-java", "android.hardware.light-java", "android.hardware.weaver-V1.0-java", - "android.hardware.biometrics.face-V1.0-java", - "android.hardware.biometrics.fingerprint-V2.1-java", + "android.hardware.biometrics.face-V1.1-java", + "android.hardware.biometrics.fingerprint-V2.2-java", "android.hardware.oemlock-V1.0-java", "android.hardware.configstore-V1.0-java", "android.hardware.contexthub-V1.0-java", diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index ec3dbe9f850e..caacf13d7bc4 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -602,7 +602,7 @@ public class ConnectivityService extends IConnectivityManager.Stub private Set<String> mWolSupportedInterfaces; - private TelephonyManager mTelephonyManager; + private final TelephonyManager mTelephonyManager; private final AppOpsManager mAppOpsManager; private final LocationPermissionChecker mLocationPermissionChecker; @@ -961,6 +961,7 @@ public class ConnectivityService extends IConnectivityManager.Stub mDeps = Objects.requireNonNull(deps, "missing Dependencies"); mSystemProperties = mDeps.getSystemProperties(); mNetIdManager = mDeps.makeNetIdManager(); + mContext = Objects.requireNonNull(context, "missing Context"); mMetricsLog = logger; mDefaultRequest = createDefaultInternetRequestForTransport(-1, NetworkRequest.Type.REQUEST); @@ -989,7 +990,6 @@ public class ConnectivityService extends IConnectivityManager.Stub mLingerDelayMs = mSystemProperties.getInt(LINGER_DELAY_PROPERTY, DEFAULT_LINGER_DELAY_MS); - mContext = Objects.requireNonNull(context, "missing Context"); mNMS = Objects.requireNonNull(netManager, "missing INetworkManagementService"); mStatsService = Objects.requireNonNull(statsService, "missing INetworkStatsService"); mPolicyManager = Objects.requireNonNull(policyManager, "missing INetworkPolicyManager"); @@ -1169,6 +1169,7 @@ public class ConnectivityService extends IConnectivityManager.Stub int transportType, NetworkRequest.Type type) { final NetworkCapabilities netCap = new NetworkCapabilities(); netCap.addCapability(NET_CAPABILITY_INTERNET); + netCap.setRequestorUidAndPackageName(Process.myUid(), mContext.getPackageName()); if (transportType > -1) { netCap.addTransportType(transportType); } @@ -1699,10 +1700,12 @@ public class ConnectivityService extends IConnectivityManager.Stub return newLp; } - private void restrictRequestUidsForCaller(NetworkCapabilities nc) { + private void restrictRequestUidsForCallerAndSetRequestorInfo(NetworkCapabilities nc, + int callerUid, String callerPackageName) { if (!checkSettingsPermission()) { - nc.setSingleUid(Binder.getCallingUid()); + nc.setSingleUid(callerUid); } + nc.setRequestorUidAndPackageName(callerUid, callerPackageName); nc.setAdministratorUids(Collections.EMPTY_LIST); // Clear owner UID; this can never come from an app. @@ -5304,7 +5307,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // This checks that the passed capabilities either do not request a // specific SSID/SignalStrength, or the calling app has permission to do so. private void ensureSufficientPermissionsForRequest(NetworkCapabilities nc, - int callerPid, int callerUid) { + int callerPid, int callerUid, String callerPackageName) { if (null != nc.getSSID() && !checkSettingsPermission(callerPid, callerUid)) { throw new SecurityException("Insufficient permissions to request a specific SSID"); } @@ -5314,6 +5317,7 @@ public class ConnectivityService extends IConnectivityManager.Stub throw new SecurityException( "Insufficient permissions to request a specific signal strength"); } + mAppOpsManager.checkPackage(callerUid, callerPackageName); } private ArrayList<Integer> getSignalStrengthThresholds(NetworkAgentInfo nai) { @@ -5360,7 +5364,6 @@ public class ConnectivityService extends IConnectivityManager.Stub return; } MatchAllNetworkSpecifier.checkNotMatchAllNetworkSpecifier(ns); - ns.assertValidFromUid(Binder.getCallingUid()); } private void ensureValid(NetworkCapabilities nc) { @@ -5372,7 +5375,9 @@ public class ConnectivityService extends IConnectivityManager.Stub @Override public NetworkRequest requestNetwork(NetworkCapabilities networkCapabilities, - Messenger messenger, int timeoutMs, IBinder binder, int legacyType) { + Messenger messenger, int timeoutMs, IBinder binder, int legacyType, + @NonNull String callingPackageName) { + final int callingUid = Binder.getCallingUid(); final NetworkRequest.Type type = (networkCapabilities == null) ? NetworkRequest.Type.TRACK_DEFAULT : NetworkRequest.Type.REQUEST; @@ -5380,7 +5385,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // the default network request. This allows callers to keep track of // the system default network. if (type == NetworkRequest.Type.TRACK_DEFAULT) { - networkCapabilities = createDefaultNetworkCapabilitiesForUid(Binder.getCallingUid()); + networkCapabilities = createDefaultNetworkCapabilitiesForUid(callingUid); enforceAccessPermission(); } else { networkCapabilities = new NetworkCapabilities(networkCapabilities); @@ -5392,13 +5397,14 @@ public class ConnectivityService extends IConnectivityManager.Stub } ensureRequestableCapabilities(networkCapabilities); ensureSufficientPermissionsForRequest(networkCapabilities, - Binder.getCallingPid(), Binder.getCallingUid()); + Binder.getCallingPid(), callingUid, callingPackageName); // Set the UID range for this request to the single UID of the requester, or to an empty // set of UIDs if the caller has the appropriate permission and UIDs have not been set. // This will overwrite any allowed UIDs in the requested capabilities. Though there // are no visible methods to set the UIDs, an app could use reflection to try and get // networks for other apps so it's essential that the UIDs are overwritten. - restrictRequestUidsForCaller(networkCapabilities); + restrictRequestUidsForCallerAndSetRequestorInfo(networkCapabilities, + callingUid, callingPackageName); if (timeoutMs < 0) { throw new IllegalArgumentException("Bad timeout specified"); @@ -5473,16 +5479,18 @@ public class ConnectivityService extends IConnectivityManager.Stub @Override public NetworkRequest pendingRequestForNetwork(NetworkCapabilities networkCapabilities, - PendingIntent operation) { + PendingIntent operation, @NonNull String callingPackageName) { Objects.requireNonNull(operation, "PendingIntent cannot be null."); + final int callingUid = Binder.getCallingUid(); networkCapabilities = new NetworkCapabilities(networkCapabilities); enforceNetworkRequestPermissions(networkCapabilities); enforceMeteredApnPolicy(networkCapabilities); ensureRequestableCapabilities(networkCapabilities); ensureSufficientPermissionsForRequest(networkCapabilities, - Binder.getCallingPid(), Binder.getCallingUid()); + Binder.getCallingPid(), callingUid, callingPackageName); ensureValidNetworkSpecifier(networkCapabilities); - restrictRequestUidsForCaller(networkCapabilities); + restrictRequestUidsForCallerAndSetRequestorInfo(networkCapabilities, + callingUid, callingPackageName); NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, TYPE_NONE, nextNetworkRequestId(), NetworkRequest.Type.REQUEST); @@ -5530,15 +5538,16 @@ public class ConnectivityService extends IConnectivityManager.Stub @Override public NetworkRequest listenForNetwork(NetworkCapabilities networkCapabilities, - Messenger messenger, IBinder binder) { + Messenger messenger, IBinder binder, @NonNull String callingPackageName) { + final int callingUid = Binder.getCallingUid(); if (!hasWifiNetworkListenPermission(networkCapabilities)) { enforceAccessPermission(); } NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities); ensureSufficientPermissionsForRequest(networkCapabilities, - Binder.getCallingPid(), Binder.getCallingUid()); - restrictRequestUidsForCaller(nc); + Binder.getCallingPid(), callingUid, callingPackageName); + restrictRequestUidsForCallerAndSetRequestorInfo(nc, callingUid, callingPackageName); // Apps without the CHANGE_NETWORK_STATE permission can't use background networks, so // make all their listens include NET_CAPABILITY_FOREGROUND. That way, they will get // onLost and onAvailable callbacks when networks move in and out of the background. @@ -5558,17 +5567,17 @@ public class ConnectivityService extends IConnectivityManager.Stub @Override public void pendingListenForNetwork(NetworkCapabilities networkCapabilities, - PendingIntent operation) { + PendingIntent operation, @NonNull String callingPackageName) { Objects.requireNonNull(operation, "PendingIntent cannot be null."); + final int callingUid = Binder.getCallingUid(); if (!hasWifiNetworkListenPermission(networkCapabilities)) { enforceAccessPermission(); } ensureValid(networkCapabilities); ensureSufficientPermissionsForRequest(networkCapabilities, - Binder.getCallingPid(), Binder.getCallingUid()); - + Binder.getCallingPid(), callingUid, callingPackageName); final NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities); - restrictRequestUidsForCaller(nc); + restrictRequestUidsForCallerAndSetRequestorInfo(nc, callingUid, callingPackageName); NetworkRequest networkRequest = new NetworkRequest(nc, TYPE_NONE, nextNetworkRequestId(), NetworkRequest.Type.LISTEN); @@ -6581,6 +6590,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } private ArrayMap<NetworkRequestInfo, NetworkAgentInfo> computeRequestReassignmentForNetwork( + @NonNull final NetworkReassignment changes, @NonNull final NetworkAgentInfo newNetwork) { final int score = newNetwork.getCurrentScore(); final ArrayMap<NetworkRequestInfo, NetworkAgentInfo> reassignedRequests = new ArrayMap<>(); @@ -6591,7 +6601,10 @@ public class ConnectivityService extends IConnectivityManager.Stub // requests or not, and doesn't affect the network's score. if (nri.request.isListen()) continue; - final NetworkAgentInfo currentNetwork = nri.mSatisfier; + // The reassignment has been seeded with the initial assignment, therefore + // getReassignment can't be null and mNewNetwork is only null if there was no + // satisfier in the first place or there was an explicit reassignment to null. + final NetworkAgentInfo currentNetwork = changes.getReassignment(nri).mNewNetwork; final boolean satisfies = newNetwork.satisfies(nri.request); if (newNetwork == currentNetwork && satisfies) continue; @@ -6641,7 +6654,7 @@ public class ConnectivityService extends IConnectivityManager.Stub if (VDBG || DDBG) log("rematching " + newNetwork.name()); final ArrayMap<NetworkRequestInfo, NetworkAgentInfo> reassignedRequests = - computeRequestReassignmentForNetwork(newNetwork); + computeRequestReassignmentForNetwork(changes, newNetwork); // Find and migrate to this Network any NetworkRequests for // which this network is now the best. @@ -7855,12 +7868,13 @@ public class ConnectivityService extends IConnectivityManager.Stub throw new IllegalArgumentException("ConnectivityManager.TYPE_* are deprecated." + " Please use NetworkCapabilities instead."); } - mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackageName); + final int callingUid = Binder.getCallingUid(); + mAppOpsManager.checkPackage(callingUid, callingPackageName); // This NetworkCapabilities is only used for matching to Networks. Clear out its owner uid // and administrator uids to be safe. final NetworkCapabilities nc = new NetworkCapabilities(request.networkCapabilities); - restrictRequestUidsForCaller(nc); + restrictRequestUidsForCallerAndSetRequestorInfo(nc, callingUid, callingPackageName); final NetworkRequest requestWithId = new NetworkRequest( diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java index 50843b0999a1..63cddac0bfba 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -30,6 +30,7 @@ import static android.os.PowerManager.locationPowerSaveModeToString; import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.ActivityManager; import android.app.AppOpsManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; @@ -305,11 +306,7 @@ public class LocationManagerService extends ILocationManager.Stub { public void onOpChanged(int op, String packageName) { // onOpChanged invoked on ui thread, move to our thread to reduce risk // of blocking ui thread - mHandler.post(() -> { - synchronized (mLock) { - onAppOpChangedLocked(); - } - }); + mHandler.post(() -> onAppOpChanged(packageName)); } }); mPackageManager.addOnPermissionsChangeListener( @@ -392,13 +389,26 @@ public class LocationManagerService extends ILocationManager.Stub { } } - @GuardedBy("mLock") - private void onAppOpChangedLocked() { - for (Receiver receiver : mReceivers.values()) { - receiver.updateMonitoring(true); - } - for (LocationProviderManager manager : mProviderManagers) { - applyRequirementsLocked(manager); + private void onAppOpChanged(String packageName) { + synchronized (mLock) { + for (Receiver receiver : mReceivers.values()) { + if (receiver.mCallerIdentity.mPackageName.equals(packageName)) { + receiver.updateMonitoring(true); + } + } + + HashSet<String> affectedProviders = new HashSet<>(mRecordsByProvider.size()); + for (Entry<String, ArrayList<UpdateRecord>> entry : mRecordsByProvider.entrySet()) { + String provider = entry.getKey(); + for (UpdateRecord record : entry.getValue()) { + if (record.mReceiver.mCallerIdentity.mPackageName.equals(packageName)) { + affectedProviders.add(provider); + } + } + } + for (String provider : affectedProviders) { + applyRequirementsLocked(provider); + } } } @@ -2161,10 +2171,10 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public boolean injectLocation(Location location) { - mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE, - "Location Hardware permission not granted to inject location"); - mContext.enforceCallingPermission(ACCESS_FINE_LOCATION, - "Access Fine Location permission not granted to inject Location"); + mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE, null); + mContext.enforceCallingPermission(ACCESS_FINE_LOCATION, null); + + Preconditions.checkArgument(location.isComplete()); synchronized (mLock) { LocationProviderManager manager = getLocationProviderManager(location.getProvider()); @@ -2410,20 +2420,15 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public boolean isLocationEnabledForUser(int userId) { - if (UserHandle.getCallingUserId() != userId) { - mContext.enforceCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS, - null); - } - + userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), + userId, false, false, "isLocationEnabledForUser", null); return mSettingsHelper.isLocationEnabled(userId); } @Override public boolean isProviderEnabledForUser(String providerName, int userId) { - if (UserHandle.getCallingUserId() != userId) { - mContext.enforceCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS, - null); - } + userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), + userId, false, false, "isProviderEnabledForUser", null); // Fused provider is accessed indirectly via criteria rather than the provider-based APIs, // so we discourage its use @@ -2732,6 +2737,9 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public void setTestProviderLocation(String provider, Location location, String packageName) { + Preconditions.checkArgument(location.isComplete(), + "incomplete location object, missing timestamp or accuracy?"); + if (mAppOps.checkOp(AppOpsManager.OP_MOCK_LOCATION, Binder.getCallingUid(), packageName) != AppOpsManager.MODE_ALLOWED) { return; diff --git a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java index b19a37e0d840..f872c6bd1b8b 100644 --- a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java +++ b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java @@ -50,7 +50,7 @@ class ActivityManagerDebugConfig { static final boolean DEBUG_BROADCAST_LIGHT = DEBUG_BROADCAST || false; static final boolean DEBUG_BROADCAST_DEFERRAL = DEBUG_BROADCAST || false; static final boolean DEBUG_COMPACTION = DEBUG_ALL || false; - static final boolean DEBUG_FREEZER = DEBUG_ALL || false; + static final boolean DEBUG_FREEZER = DEBUG_ALL || true; static final boolean DEBUG_LRU = DEBUG_ALL || false; static final boolean DEBUG_MU = DEBUG_ALL || false; static final boolean DEBUG_NETWORK = DEBUG_ALL || false; diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 12b1cbf877a3..3ad96ea193bf 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -2617,7 +2617,7 @@ public class ActivityManagerService extends IActivityManager.Stub mProcessCpuThread.start(); mBatteryStatsService.publish(); - mAppOpsService.publish(mContext); + mAppOpsService.publish(); Slog.d("AppOps", "AppOpsService published"); LocalServices.addService(ActivityManagerInternal.class, mInternal); mActivityTaskManager.onActivityManagerInternalAdded(); @@ -19510,7 +19510,7 @@ public class ActivityManagerService extends IActivityManager.Stub } public AppOpsService getAppOpsService(File file, Handler handler) { - return new AppOpsService(file, handler); + return new AppOpsService(file, handler, getContext()); } public Handler getUiHandler(ActivityManagerService service) { diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java index 313c185a84c6..d047a3ca993c 100644 --- a/services/core/java/com/android/server/am/CachedAppOptimizer.java +++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java @@ -442,7 +442,7 @@ public final class CachedAppOptimizer { */ @GuardedBy("mPhenotypeFlagLock") private void updateUseFreezer() { - if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT, KEY_USE_FREEZER, DEFAULT_USE_FREEZER)) { mUseFreezer = isFreezerSupported(); } diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index a0589c53c366..06561f57f495 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -19,11 +19,15 @@ package com.android.server.appop; import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_CAMERA; import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION; import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_MICROPHONE; +import static android.app.AppOpsManager.CALL_BACK_ON_CHANGED_LISTENER_WITH_SWITCHED_OP_CHANGE; import static android.app.AppOpsManager.FILTER_BY_FEATURE_ID; import static android.app.AppOpsManager.FILTER_BY_OP_NAMES; import static android.app.AppOpsManager.FILTER_BY_PACKAGE_NAME; import static android.app.AppOpsManager.FILTER_BY_UID; import static android.app.AppOpsManager.HistoricalOpsRequestFilter; +import static android.app.AppOpsManager.KEY_BG_STATE_SETTLE_TIME; +import static android.app.AppOpsManager.KEY_FG_SERVICE_STATE_SETTLE_TIME; +import static android.app.AppOpsManager.KEY_TOP_STATE_SETTLE_TIME; import static android.app.AppOpsManager.MODE_ALLOWED; import static android.app.AppOpsManager.NoteOpEvent; import static android.app.AppOpsManager.OP_CAMERA; @@ -41,6 +45,7 @@ import static android.app.AppOpsManager.UID_STATE_FOREGROUND_SERVICE; import static android.app.AppOpsManager.UID_STATE_MAX_LAST_NON_RESTRICTED; import static android.app.AppOpsManager.UID_STATE_PERSISTENT; import static android.app.AppOpsManager.UID_STATE_TOP; +import static android.app.AppOpsManager.WATCH_FOREGROUND_CHANGES; import static android.app.AppOpsManager._NUM_OP; import static android.app.AppOpsManager.extractFlagsFromKey; import static android.app.AppOpsManager.extractUidStateFromKey; @@ -53,6 +58,10 @@ import static android.content.Intent.EXTRA_REPLACING; import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS; import static android.os.Process.STATSD_UID; +import static com.android.server.appop.AppOpsService.ModeCallback.ALL_OPS; + +import static java.lang.Long.max; + import android.Manifest; import android.annotation.IntRange; import android.annotation.NonNull; @@ -70,6 +79,7 @@ import android.app.AppOpsManager.OpFlags; import android.app.AppOpsManagerInternal; import android.app.AppOpsManagerInternal.CheckOpsDelegate; import android.app.AsyncNotedAppOp; +import android.compat.Compatibility; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; @@ -129,7 +139,6 @@ import com.android.internal.app.IAppOpsNotedCallback; import com.android.internal.app.IAppOpsService; import com.android.internal.os.Zygote; import com.android.internal.util.ArrayUtils; -import com.android.internal.util.CollectionUtils; import com.android.internal.util.DumpUtils; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.Preconditions; @@ -216,7 +225,7 @@ public class AppOpsService extends IAppOpsService.Stub { //TODO: remove this when development is done. private static final int TEMP_PROCESS_CAPABILITY_FOREGROUND_LOCATION = 1 << 31; - Context mContext; + final Context mContext; final AtomicFile mFile; final Handler mHandler; @@ -291,8 +300,11 @@ public class AppOpsService extends IAppOpsService.Stub { @GuardedBy("this") private CheckOpsDelegate mCheckOpsDelegate; - @GuardedBy("this") - private SparseArray<List<Integer>> mSwitchOpToOps; + /** + * Reverse lookup for {@link AppOpsManager#opToSwitch(int)}. Initialized once and never + * changed + */ + private final SparseArray<int[]> mSwitchedOps = new SparseArray<>(); private ActivityManagerInternal mActivityManagerInternal; @@ -343,30 +355,25 @@ public class AppOpsService extends IAppOpsService.Stub { */ @VisibleForTesting final class Constants extends ContentObserver { - // Key names stored in the settings value. - private static final String KEY_TOP_STATE_SETTLE_TIME = "top_state_settle_time"; - private static final String KEY_FG_SERVICE_STATE_SETTLE_TIME - = "fg_service_state_settle_time"; - private static final String KEY_BG_STATE_SETTLE_TIME = "bg_state_settle_time"; /** * How long we want for a drop in uid state from top to settle before applying it. * @see Settings.Global#APP_OPS_CONSTANTS - * @see #KEY_TOP_STATE_SETTLE_TIME + * @see AppOpsManager#KEY_TOP_STATE_SETTLE_TIME */ public long TOP_STATE_SETTLE_TIME; /** * How long we want for a drop in uid state from foreground to settle before applying it. * @see Settings.Global#APP_OPS_CONSTANTS - * @see #KEY_FG_SERVICE_STATE_SETTLE_TIME + * @see AppOpsManager#KEY_FG_SERVICE_STATE_SETTLE_TIME */ public long FG_SERVICE_STATE_SETTLE_TIME; /** * How long we want for a drop in uid state from background to settle before applying it. * @see Settings.Global#APP_OPS_CONSTANTS - * @see #KEY_BG_STATE_SETTLE_TIME + * @see AppOpsManager#KEY_BG_STATE_SETTLE_TIME */ public long BG_STATE_SETTLE_TIME; @@ -1191,17 +1198,22 @@ public class AppOpsService extends IAppOpsService.Stub { final AudioRestrictionManager mAudioRestrictionManager = new AudioRestrictionManager(); final class ModeCallback implements DeathRecipient { + /** If mWatchedOpCode==ALL_OPS notify for ops affected by the switch-op */ + public static final int ALL_OPS = -2; + final IAppOpsCallback mCallback; final int mWatchingUid; final int mFlags; + final int mWatchedOpCode; final int mCallingUid; final int mCallingPid; - ModeCallback(IAppOpsCallback callback, int watchingUid, int flags, int callingUid, - int callingPid) { + ModeCallback(IAppOpsCallback callback, int watchingUid, int flags, int watchedOp, + int callingUid, int callingPid) { mCallback = callback; mWatchingUid = watchingUid; mFlags = flags; + mWatchedOpCode = watchedOp; mCallingUid = callingUid; mCallingPid = callingPid; try { @@ -1224,6 +1236,10 @@ public class AppOpsService extends IAppOpsService.Stub { UserHandle.formatUid(sb, mWatchingUid); sb.append(" flags=0x"); sb.append(Integer.toHexString(mFlags)); + if (mWatchedOpCode != OP_NONE) { + sb.append(" op="); + sb.append(opToName(mWatchedOpCode)); + } sb.append(" from uid="); UserHandle.formatUid(sb, mCallingUid); sb.append(" pid="); @@ -1337,16 +1353,23 @@ public class AppOpsService extends IAppOpsService.Stub { featureOp.onClientDeath(clientId); } - public AppOpsService(File storagePath, Handler handler) { + public AppOpsService(File storagePath, Handler handler, Context context) { + mContext = context; + LockGuard.installLock(this, LockGuard.INDEX_APP_OPS); mFile = new AtomicFile(storagePath, "appops"); mHandler = handler; mConstants = new Constants(mHandler); readState(); + + for (int switchedCode = 0; switchedCode < _NUM_OP; switchedCode++) { + int switchCode = AppOpsManager.opToSwitch(switchedCode); + mSwitchedOps.put(switchCode, + ArrayUtils.appendInt(mSwitchedOps.get(switchCode), switchedCode)); + } } - public void publish(Context context) { - mContext = context; + public void publish() { ServiceManager.addService(Context.APP_OPS_SERVICE, asBinder()); LocalServices.addService(AppOpsManagerInternal.class, mAppOpsManagerInternal); } @@ -1616,6 +1639,19 @@ public class AppOpsService extends IAppOpsService.Stub { } } + /** + * Update the pending state for the uid + * + * @param currentTime The current elapsed real time + * @param uid The uid that has a pending state + */ + private void updatePendingState(long currentTime, int uid) { + synchronized (this) { + mLastRealtime = max(currentTime, mLastRealtime); + updatePendingStateIfNeededLocked(mUidStates.get(uid)); + } + } + public void updateUidProcState(int uid, int procState, @ActivityManager.ProcessCapability int capability) { synchronized (this) { @@ -1647,7 +1683,12 @@ public class AppOpsService extends IAppOpsService.Stub { } else { settleTime = mConstants.BG_STATE_SETTLE_TIME; } - uidState.pendingStateCommitTime = SystemClock.elapsedRealtime() + settleTime; + final long commitTime = SystemClock.elapsedRealtime() + settleTime; + uidState.pendingStateCommitTime = commitTime; + + mHandler.sendMessageDelayed( + PooledLambda.obtainMessage(AppOpsService::updatePendingState, this, + commitTime + 1, uid), settleTime + 1); } if (uidState.pkgOps != null) { @@ -2014,6 +2055,19 @@ public class AppOpsService extends IAppOpsService.Stub { uidState.evalForegroundOps(mOpModeWatchers); } + notifyOpChangedForAllPkgsInUid(code, uid, false, callbackToIgnore); + notifyOpChangedSync(code, uid, null, mode); + } + + /** + * Notify that an op changed for all packages in an uid. + * + * @param code The op that changed + * @param uid The uid the op was changed for + * @param onlyForeground Only notify watchers that watch for foreground changes + */ + private void notifyOpChangedForAllPkgsInUid(int code, int uid, boolean onlyForeground, + @Nullable IAppOpsCallback callbackToIgnore) { String[] uidPackageNames = getPackagesForUid(uid); ArrayMap<ModeCallback, ArraySet<String>> callbackSpecs = null; @@ -2023,6 +2077,10 @@ public class AppOpsService extends IAppOpsService.Stub { final int callbackCount = callbacks.size(); for (int i = 0; i < callbackCount; i++) { ModeCallback callback = callbacks.valueAt(i); + if (onlyForeground && (callback.mFlags & WATCH_FOREGROUND_CHANGES) == 0) { + continue; + } + ArraySet<String> changedPackages = new ArraySet<>(); Collections.addAll(changedPackages, uidPackageNames); if (callbackSpecs == null) { @@ -2041,6 +2099,10 @@ public class AppOpsService extends IAppOpsService.Stub { final int callbackCount = callbacks.size(); for (int i = 0; i < callbackCount; i++) { ModeCallback callback = callbacks.valueAt(i); + if (onlyForeground && (callback.mFlags & WATCH_FOREGROUND_CHANGES) == 0) { + continue; + } + ArraySet<String> changedPackages = callbackSpecs.get(callback); if (changedPackages == null) { changedPackages = new ArraySet<>(); @@ -2057,7 +2119,6 @@ public class AppOpsService extends IAppOpsService.Stub { } if (callbackSpecs == null) { - notifyOpChangedSync(code, uid, null, mode); return; } @@ -2079,23 +2140,24 @@ public class AppOpsService extends IAppOpsService.Stub { } } } - - notifyOpChangedSync(code, uid, null, mode); } private void updatePermissionRevokedCompat(int uid, int switchCode, int mode) { PackageManager packageManager = mContext.getPackageManager(); + if (packageManager == null) { + // This can only happen during early boot. At this time the permission state and appop + // state are in sync + return; + } + String[] packageNames = packageManager.getPackagesForUid(uid); if (ArrayUtils.isEmpty(packageNames)) { return; } String packageName = packageNames[0]; - List<Integer> ops = getSwitchOpToOps().get(switchCode); - int opsSize = CollectionUtils.size(ops); - for (int i = 0; i < opsSize; i++) { - int code = ops.get(i); - + int[] ops = mSwitchedOps.get(switchCode); + for (int code : ops) { String permissionName = AppOpsManager.opToPermission(code); if (permissionName == null) { continue; @@ -2173,25 +2235,6 @@ public class AppOpsService extends IAppOpsService.Stub { } } - @NonNull - private SparseArray<List<Integer>> getSwitchOpToOps() { - synchronized (this) { - if (mSwitchOpToOps == null) { - mSwitchOpToOps = new SparseArray<>(); - for (int op = 0; op < _NUM_OP; op++) { - int switchOp = AppOpsManager.opToSwitch(op); - List<Integer> ops = mSwitchOpToOps.get(switchOp); - if (ops == null) { - ops = new ArrayList<>(); - mSwitchOpToOps.put(switchOp, ops); - } - ops.add(op); - } - } - return mSwitchOpToOps; - } - } - private void notifyOpChangedSync(int code, int uid, @NonNull String packageName, int mode) { final StorageManagerInternal storageManagerInternal = LocalServices.getService(StorageManagerInternal.class); @@ -2292,16 +2335,29 @@ public class AppOpsService extends IAppOpsService.Stub { if (uid != UID_ANY && callback.mWatchingUid >= 0 && callback.mWatchingUid != uid) { return; } - // There are features watching for mode changes such as window manager - // and location manager which are in our process. The callbacks in these - // features may require permissions our remote caller does not have. - final long identity = Binder.clearCallingIdentity(); - try { - callback.mCallback.opChanged(code, uid, packageName); - } catch (RemoteException e) { - /* ignore */ - } finally { - Binder.restoreCallingIdentity(identity); + + // See CALL_BACK_ON_CHANGED_LISTENER_WITH_SWITCHED_OP_CHANGE + int[] switchedCodes; + if (callback.mWatchedOpCode == ALL_OPS) { + switchedCodes = mSwitchedOps.get(code); + } else if (callback.mWatchedOpCode == OP_NONE) { + switchedCodes = new int[]{code}; + } else { + switchedCodes = new int[]{callback.mWatchedOpCode}; + } + + for (int switchedCode : switchedCodes) { + // There are features watching for mode changes such as window manager + // and location manager which are in our process. The callbacks in these + // features may require permissions our remote caller does not have. + final long identity = Binder.clearCallingIdentity(); + try { + callback.mCallback.opChanged(switchedCode, uid, packageName); + } catch (RemoteException e) { + /* ignore */ + } finally { + Binder.restoreCallingIdentity(identity); + } } } @@ -2496,17 +2552,32 @@ public class AppOpsService extends IAppOpsService.Stub { return; } synchronized (this) { - op = (op != AppOpsManager.OP_NONE) ? AppOpsManager.opToSwitch(op) : op; + int switchOp = (op != AppOpsManager.OP_NONE) ? AppOpsManager.opToSwitch(op) : op; + + // See CALL_BACK_ON_CHANGED_LISTENER_WITH_SWITCHED_OP_CHANGE + int notifiedOps; + if (Compatibility.isChangeEnabled( + CALL_BACK_ON_CHANGED_LISTENER_WITH_SWITCHED_OP_CHANGE)) { + if (op == OP_NONE) { + notifiedOps = ALL_OPS; + } else { + notifiedOps = op; + } + } else { + notifiedOps = switchOp; + } + ModeCallback cb = mModeWatchers.get(callback.asBinder()); if (cb == null) { - cb = new ModeCallback(callback, watchedUid, flags, callingUid, callingPid); + cb = new ModeCallback(callback, watchedUid, flags, notifiedOps, callingUid, + callingPid); mModeWatchers.put(callback.asBinder(), cb); } - if (op != AppOpsManager.OP_NONE) { - ArraySet<ModeCallback> cbs = mOpModeWatchers.get(op); + if (switchOp != AppOpsManager.OP_NONE) { + ArraySet<ModeCallback> cbs = mOpModeWatchers.get(switchOp); if (cbs == null) { cbs = new ArraySet<>(); - mOpModeWatchers.put(op, cbs); + mOpModeWatchers.put(switchOp, cbs); } cbs.add(cb); } @@ -3325,6 +3396,18 @@ public class AppOpsService extends IAppOpsService.Stub { uidState = new UidState(uid); mUidStates.put(uid, uidState); } else { + updatePendingStateIfNeededLocked(uidState); + } + return uidState; + } + + /** + * Check if the pending state should be updated and do so if needed + * + * @param uidState The uidState that might have a pending state + */ + private void updatePendingStateIfNeededLocked(@NonNull UidState uidState) { + if (uidState != null) { if (uidState.pendingStateCommitTime != 0) { if (uidState.pendingStateCommitTime < mLastRealtime) { commitUidPendingStateLocked(uidState); @@ -3336,7 +3419,6 @@ public class AppOpsService extends IAppOpsService.Stub { } } } - return uidState; } private void commitUidPendingStateLocked(UidState uidState) { @@ -3356,24 +3438,28 @@ public class AppOpsService extends IAppOpsService.Stub { && uidState.appWidgetVisible == uidState.pendingAppWidgetVisible) { continue; } - final ArraySet<ModeCallback> callbacks = mOpModeWatchers.get(code); - if (callbacks != null) { - for (int cbi = callbacks.size() - 1; cbi >= 0; cbi--) { - final ModeCallback callback = callbacks.valueAt(cbi); - if ((callback.mFlags & AppOpsManager.WATCH_FOREGROUND_CHANGES) == 0 - || !callback.isWatchingUid(uidState.uid)) { - continue; - } - boolean doAllPackages = uidState.opModes != null - && uidState.opModes.indexOfKey(code) >= 0 - && uidState.opModes.get(code) == AppOpsManager.MODE_FOREGROUND; - if (uidState.pkgOps != null) { + + if (uidState.opModes != null + && uidState.opModes.indexOfKey(code) >= 0 + && uidState.opModes.get(code) == AppOpsManager.MODE_FOREGROUND) { + mHandler.sendMessage(PooledLambda.obtainMessage( + AppOpsService::notifyOpChangedForAllPkgsInUid, + this, code, uidState.uid, true, null)); + } else { + final ArraySet<ModeCallback> callbacks = mOpModeWatchers.get(code); + if (callbacks != null) { + for (int cbi = callbacks.size() - 1; cbi >= 0; cbi--) { + final ModeCallback callback = callbacks.valueAt(cbi); + if ((callback.mFlags & AppOpsManager.WATCH_FOREGROUND_CHANGES) == 0 + || !callback.isWatchingUid(uidState.uid)) { + continue; + } for (int pkgi = uidState.pkgOps.size() - 1; pkgi >= 0; pkgi--) { final Op op = uidState.pkgOps.valueAt(pkgi).get(code); if (op == null) { continue; } - if (doAllPackages || op.mode == AppOpsManager.MODE_FOREGROUND) { + if (op.mode == AppOpsManager.MODE_FOREGROUND) { mHandler.sendMessage(PooledLambda.obtainMessage( AppOpsService::notifyOpChanged, this, callback, code, uidState.uid, @@ -3798,11 +3884,7 @@ public class AppOpsService extends IAppOpsService.Stub { if (tagName.equals("op")) { final int code = Integer.parseInt(parser.getAttributeValue(null, "n")); final int mode = Integer.parseInt(parser.getAttributeValue(null, "m")); - UidState uidState = getUidStateLocked(uid, true); - if (uidState.opModes == null) { - uidState.opModes = new SparseIntArray(); - } - uidState.opModes.put(code, mode); + setUidMode(code, uid, mode); } else { Slog.w(TAG, "Unknown element under <uid-ops>: " + parser.getName()); diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java index 0d88388742d2..0f549842a763 100644 --- a/services/core/java/com/android/server/biometrics/AuthService.java +++ b/services/core/java/com/android/server/biometrics/AuthService.java @@ -99,6 +99,33 @@ public class AuthService extends SystemService { public String[] getConfiguration(Context context) { return context.getResources().getStringArray(R.array.config_biometric_sensors); } + + /** + * Allows us to mock FingerprintService for testing + */ + @VisibleForTesting + public IFingerprintService getFingerprintService() { + return IFingerprintService.Stub.asInterface( + ServiceManager.getService(Context.FINGERPRINT_SERVICE)); + } + + /** + * Allows us to mock FaceService for testing + */ + @VisibleForTesting + public IFaceService getFaceService() { + return IFaceService.Stub.asInterface( + ServiceManager.getService(Context.FACE_SERVICE)); + } + + /** + * Allows us to mock IrisService for testing + */ + @VisibleForTesting + public IIrisService getIrisService() { + return IIrisService.Stub.asInterface( + ServiceManager.getService(Context.IRIS_SERVICE)); + } } private final class AuthServiceImpl extends IAuthService.Stub { @@ -178,7 +205,6 @@ public class AuthService extends SystemService { mInjector = injector; mImpl = new AuthServiceImpl(); - final PackageManager pm = context.getPackageManager(); } private void registerAuthenticator(SensorConfig config) throws RemoteException { @@ -191,18 +217,36 @@ public class AuthService extends SystemService { switch (config.mModality) { case TYPE_FINGERPRINT: - authenticator = new FingerprintAuthenticator(IFingerprintService.Stub.asInterface( - ServiceManager.getService(Context.FINGERPRINT_SERVICE))); + final IFingerprintService fingerprintService = mInjector.getFingerprintService(); + if (fingerprintService == null) { + Slog.e(TAG, "Attempting to register with null FingerprintService. Please check" + + " your device configuration."); + return; + } + + authenticator = new FingerprintAuthenticator(fingerprintService); break; case TYPE_FACE: - authenticator = new FaceAuthenticator(IFaceService.Stub.asInterface( - ServiceManager.getService(Context.FACE_SERVICE))); + final IFaceService faceService = mInjector.getFaceService(); + if (faceService == null) { + Slog.e(TAG, "Attempting to register with null FaceService. Please check your" + + " device configuration."); + return; + } + + authenticator = new FaceAuthenticator(faceService); break; case TYPE_IRIS: - authenticator = new IrisAuthenticator(IIrisService.Stub.asInterface( - ServiceManager.getService(Context.IRIS_SERVICE))); + final IIrisService irisService = mInjector.getIrisService(); + if (irisService == null) { + Slog.e(TAG, "Attempting to register with null IrisService. Please check your" + + " device configuration."); + return; + } + + authenticator = new IrisAuthenticator(irisService); break; default: diff --git a/services/core/java/com/android/server/biometrics/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/AuthenticationClient.java index 766e5c4d638f..7bbda9f3f732 100644 --- a/services/core/java/com/android/server/biometrics/AuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/AuthenticationClient.java @@ -20,11 +20,14 @@ import android.content.Context; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricsProtoEnums; +import android.hardware.biometrics.IBiometricNativeHandle; import android.os.IBinder; +import android.os.NativeHandle; import android.os.RemoteException; import android.security.KeyStore; import android.util.Slog; +import java.io.IOException; import java.util.ArrayList; /** @@ -41,6 +44,7 @@ public abstract class AuthenticationClient extends ClientMonitor { public static final int LOCKOUT_PERMANENT = 2; private final boolean mRequireConfirmation; + private final NativeHandle mWindowId; // We need to track this state since it's possible for applications to request for // authentication while the device is already locked out. In that case, the client is created @@ -69,11 +73,25 @@ public abstract class AuthenticationClient extends ClientMonitor { public AuthenticationClient(Context context, Constants constants, BiometricServiceBase.DaemonWrapper daemon, long halDeviceId, IBinder token, BiometricServiceBase.ServiceListener listener, int targetUserId, int groupId, long opId, - boolean restricted, String owner, int cookie, boolean requireConfirmation) { + boolean restricted, String owner, int cookie, boolean requireConfirmation, + IBiometricNativeHandle windowId) { super(context, constants, daemon, halDeviceId, token, listener, targetUserId, groupId, restricted, owner, cookie); mOpId = opId; mRequireConfirmation = requireConfirmation; + mWindowId = Utils.dupNativeHandle(windowId); + } + + @Override + public void destroy() { + if (mWindowId != null && mWindowId.getFileDescriptors() != null) { + try { + mWindowId.close(); + } catch (IOException e) { + Slog.e(getLogTag(), "Failed to close windowId NativeHandle: ", e); + } + } + super.destroy(); } protected long getStartTimeMs() { @@ -233,7 +251,7 @@ public abstract class AuthenticationClient extends ClientMonitor { onStart(); try { mStartTimeMs = System.currentTimeMillis(); - final int result = getDaemonWrapper().authenticate(mOpId, getGroupId()); + final int result = getDaemonWrapper().authenticate(mOpId, getGroupId(), mWindowId); if (result != 0) { Slog.w(getLogTag(), "startAuthentication failed, result=" + result); mMetricsLogger.histogram(mConstants.tagAuthStartError(), result); diff --git a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java index 687d9353ebd1..0e709944b03f 100644 --- a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java +++ b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java @@ -32,6 +32,7 @@ import android.content.pm.UserInfo; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricsProtoEnums; +import android.hardware.biometrics.IBiometricNativeHandle; import android.hardware.biometrics.IBiometricService; import android.hardware.biometrics.IBiometricServiceLockoutResetCallback; import android.hardware.biometrics.IBiometricServiceReceiverInternal; @@ -43,6 +44,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.IHwBinder; import android.os.IRemoteCallback; +import android.os.NativeHandle; import android.os.PowerManager; import android.os.Process; import android.os.RemoteException; @@ -220,9 +222,10 @@ public abstract class BiometricServiceBase extends SystemService public AuthenticationClientImpl(Context context, DaemonWrapper daemon, long halDeviceId, IBinder token, ServiceListener listener, int targetUserId, int groupId, long opId, - boolean restricted, String owner, int cookie, boolean requireConfirmation) { + boolean restricted, String owner, int cookie, boolean requireConfirmation, + IBiometricNativeHandle windowId) { super(context, getConstants(), daemon, halDeviceId, token, listener, targetUserId, - groupId, opId, restricted, owner, cookie, requireConfirmation); + groupId, opId, restricted, owner, cookie, requireConfirmation, windowId); } @Override @@ -283,10 +286,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, int timeoutSec) { + final int[] disabledFeatures, int timeoutSec, IBiometricNativeHandle windowId) { super(context, getConstants(), daemon, halDeviceId, token, listener, userId, groupId, cryptoToken, restricted, owner, getBiometricUtils(), - disabledFeatures, timeoutSec); + disabledFeatures, timeoutSec, windowId); } @Override @@ -472,12 +475,13 @@ public abstract class BiometricServiceBase extends SystemService */ protected interface DaemonWrapper { int ERROR_ESRCH = 3; // Likely HAL is dead. see errno.h. - int authenticate(long operationId, int groupId) throws RemoteException; + int authenticate(long operationId, int groupId, NativeHandle windowId) + throws RemoteException; int cancel() throws RemoteException; int remove(int groupId, int biometricId) throws RemoteException; int enumerate() throws RemoteException; int enroll(byte[] token, int groupId, int timeout, - ArrayList<Integer> disabledFeatures) throws RemoteException; + ArrayList<Integer> disabledFeatures, NativeHandle windowId) throws RemoteException; void resetLockout(byte[] token) throws RemoteException; } diff --git a/services/core/java/com/android/server/biometrics/EnrollClient.java b/services/core/java/com/android/server/biometrics/EnrollClient.java index 7ebb7c059b4c..684795ec66b5 100644 --- a/services/core/java/com/android/server/biometrics/EnrollClient.java +++ b/services/core/java/com/android/server/biometrics/EnrollClient.java @@ -20,10 +20,13 @@ import android.content.Context; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricsProtoEnums; +import android.hardware.biometrics.IBiometricNativeHandle; import android.os.IBinder; +import android.os.NativeHandle; import android.os.RemoteException; import android.util.Slog; +import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; @@ -35,6 +38,7 @@ public abstract class EnrollClient extends ClientMonitor { private final BiometricUtils mBiometricUtils; private final int[] mDisabledFeatures; private final int mTimeoutSec; + private final NativeHandle mWindowId; private long mEnrollmentStartTimeMs; @@ -44,13 +48,26 @@ 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, int timeoutSec) { + final int[] disabledFeatures, int timeoutSec, IBiometricNativeHandle windowId) { 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; + mWindowId = Utils.dupNativeHandle(windowId); + } + + @Override + public void destroy() { + if (mWindowId != null && mWindowId.getFileDescriptors() != null) { + try { + mWindowId.close(); + } catch (IOException e) { + Slog.e(getLogTag(), "Failed to close windowId NativeHandle: ", e); + } + } + super.destroy(); } @Override @@ -102,7 +119,7 @@ public abstract class EnrollClient extends ClientMonitor { } final int result = getDaemonWrapper().enroll(mCryptoToken, getGroupId(), mTimeoutSec, - disabledFeatures); + disabledFeatures, mWindowId); if (result != 0) { Slog.w(getLogTag(), "startEnroll failed, result=" + result); mMetricsLogger.histogram(mConstants.tagEnrollStartError(), result); diff --git a/services/core/java/com/android/server/biometrics/Utils.java b/services/core/java/com/android/server/biometrics/Utils.java index 389763b5377a..2d4ab6308a8b 100644 --- a/services/core/java/com/android/server/biometrics/Utils.java +++ b/services/core/java/com/android/server/biometrics/Utils.java @@ -23,12 +23,17 @@ import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricManager; import android.hardware.biometrics.BiometricPrompt; import android.hardware.biometrics.BiometricPrompt.AuthenticationResultType; +import android.hardware.biometrics.IBiometricNativeHandle; import android.os.Build; import android.os.Bundle; +import android.os.NativeHandle; import android.os.UserHandle; import android.provider.Settings; import android.util.Slog; +import java.io.FileDescriptor; +import java.io.IOException; + public class Utils { public static boolean isDebugEnabled(Context context, int targetUserId) { if (targetUserId == UserHandle.USER_NULL) { @@ -237,4 +242,31 @@ public class Utils { throw new IllegalArgumentException("Unsupported dismissal reason: " + reason); } } + + /** + * Converts an {@link IBiometricNativeHandle} to a {@link NativeHandle} by duplicating the + * the underlying file descriptors. + * + * Both the original and new handle must be closed after use. + * + * @param h {@link IBiometricNativeHandle} received as a binder call argument. Usually used to + * identify a WindowManager window. Can be null. + * @return A {@link NativeHandle} representation of {@code h}. Will be null if either {@code h} + * or its contents are null. + */ + public static NativeHandle dupNativeHandle(IBiometricNativeHandle h) { + NativeHandle handle = null; + if (h != null && h.fds != null && h.ints != null) { + FileDescriptor[] fds = new FileDescriptor[h.fds.length]; + for (int i = 0; i < h.fds.length; ++i) { + try { + fds[i] = h.fds[i].dup().getFileDescriptor(); + } catch (IOException e) { + return null; + } + } + handle = new NativeHandle(fds, h.ints, true /* own */); + } + return handle; + } } 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 b512475e7971..31c3d4d7b24e 100644 --- a/services/core/java/com/android/server/biometrics/face/FaceService.java +++ b/services/core/java/com/android/server/biometrics/face/FaceService.java @@ -34,6 +34,7 @@ import android.content.pm.UserInfo; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricsProtoEnums; +import android.hardware.biometrics.IBiometricNativeHandle; import android.hardware.biometrics.IBiometricServiceLockoutResetCallback; import android.hardware.biometrics.IBiometricServiceReceiverInternal; import android.hardware.biometrics.face.V1_0.IBiometricsFace; @@ -214,9 +215,10 @@ public class FaceService extends BiometricServiceBase { public FaceAuthClient(Context context, DaemonWrapper daemon, long halDeviceId, IBinder token, ServiceListener listener, int targetUserId, int groupId, long opId, - boolean restricted, String owner, int cookie, boolean requireConfirmation) { + boolean restricted, String owner, int cookie, boolean requireConfirmation, + IBiometricNativeHandle windowId) { super(context, daemon, halDeviceId, token, listener, targetUserId, groupId, opId, - restricted, owner, cookie, requireConfirmation); + restricted, owner, cookie, requireConfirmation, windowId); } @Override @@ -373,7 +375,7 @@ public class FaceService extends BiometricServiceBase { @Override // Binder call public void enroll(int userId, final IBinder token, final byte[] cryptoToken, final IFaceServiceReceiver receiver, final String opPackageName, - final int[] disabledFeatures) { + final int[] disabledFeatures, IBiometricNativeHandle windowId) { checkPermission(MANAGE_BIOMETRIC); updateActiveGroup(userId, opPackageName); @@ -384,7 +386,7 @@ public class FaceService extends BiometricServiceBase { final EnrollClientImpl client = new EnrollClientImpl(getContext(), mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver), mCurrentUserId, 0 /* groupId */, cryptoToken, restricted, opPackageName, disabledFeatures, - ENROLL_TIMEOUT_SEC) { + ENROLL_TIMEOUT_SEC, windowId) { @Override public int[] getAcquireIgnorelist() { @@ -411,6 +413,14 @@ public class FaceService extends BiometricServiceBase { } @Override // Binder call + public void enrollRemotely(int userId, final IBinder token, final byte[] cryptoToken, + final IFaceServiceReceiver receiver, final String opPackageName, + final int[] disabledFeatures) { + checkPermission(MANAGE_BIOMETRIC); + // TODO(b/145027036): Implement this. + } + + @Override // Binder call public void cancelEnrollment(final IBinder token) { checkPermission(MANAGE_BIOMETRIC); cancelEnrollmentInternal(token); @@ -426,7 +436,7 @@ public class FaceService extends BiometricServiceBase { final AuthenticationClientImpl client = new FaceAuthClient(getContext(), mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver), mCurrentUserId, 0 /* groupId */, opId, restricted, opPackageName, - 0 /* cookie */, false /* requireConfirmation */); + 0 /* cookie */, false /* requireConfirmation */, null /* windowId */); authenticateInternal(client, opId, opPackageName); } @@ -442,7 +452,7 @@ public class FaceService extends BiometricServiceBase { mDaemonWrapper, mHalDeviceId, token, new BiometricPromptServiceListenerImpl(wrapperReceiver), mCurrentUserId, 0 /* groupId */, opId, restricted, opPackageName, cookie, - requireConfirmation); + requireConfirmation, null /* windowId */); authenticateInternal(client, opId, opPackageName, callingUid, callingPid, callingUserId); } @@ -985,7 +995,8 @@ public class FaceService extends BiometricServiceBase { */ private final DaemonWrapper mDaemonWrapper = new DaemonWrapper() { @Override - public int authenticate(long operationId, int groupId) throws RemoteException { + public int authenticate(long operationId, int groupId, NativeHandle windowId) + throws RemoteException { IBiometricsFace daemon = getFaceDaemon(); if (daemon == null) { Slog.w(TAG, "authenticate(): no face HAL!"); @@ -1026,7 +1037,7 @@ public class FaceService extends BiometricServiceBase { @Override public int enroll(byte[] cryptoToken, int groupId, int timeout, - ArrayList<Integer> disabledFeatures) throws RemoteException { + ArrayList<Integer> disabledFeatures, NativeHandle windowId) throws RemoteException { IBiometricsFace daemon = getFaceDaemon(); if (daemon == null) { Slog.w(TAG, "enroll(): no face HAL!"); @@ -1036,7 +1047,17 @@ public class FaceService extends BiometricServiceBase { for (int i = 0; i < cryptoToken.length; i++) { token.add(cryptoToken[i]); } - return daemon.enroll(token, timeout, disabledFeatures); + android.hardware.biometrics.face.V1_1.IBiometricsFace daemon11 = + android.hardware.biometrics.face.V1_1.IBiometricsFace.castFrom( + daemon); + if (daemon11 != null) { + return daemon11.enroll_1_1(token, timeout, disabledFeatures, windowId); + } else if (windowId == null) { + return daemon.enroll(token, timeout, disabledFeatures); + } else { + Slog.e(TAG, "enroll(): windowId is only supported in @1.1 HAL"); + return ERROR_ESRCH; + } } @Override diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintAuthenticator.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintAuthenticator.java index 6150de151ccc..7a4e62e18474 100644 --- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintAuthenticator.java +++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintAuthenticator.java @@ -38,7 +38,7 @@ public final class FingerprintAuthenticator extends IBiometricAuthenticator.Stub String opPackageName, int cookie, int callingUid, int callingPid, int callingUserId) throws RemoteException { mFingerprintService.prepareForAuthentication(token, sessionId, userId, wrapperReceiver, - opPackageName, cookie, callingUid, callingPid, callingUserId); + opPackageName, cookie, callingUid, callingPid, callingUserId, null /* windowId */); } @Override 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 44797ad97b37..57d1867b3aca 100644 --- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java @@ -37,6 +37,7 @@ import android.content.pm.UserInfo; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricsProtoEnums; +import android.hardware.biometrics.IBiometricNativeHandle; import android.hardware.biometrics.IBiometricServiceLockoutResetCallback; import android.hardware.biometrics.IBiometricServiceReceiverInternal; import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint; @@ -50,6 +51,7 @@ import android.os.Binder; import android.os.Build; import android.os.Environment; import android.os.IBinder; +import android.os.NativeHandle; import android.os.RemoteException; import android.os.SELinux; import android.os.SystemClock; @@ -132,9 +134,9 @@ public class FingerprintService extends BiometricServiceBase { DaemonWrapper daemon, long halDeviceId, IBinder token, ServiceListener listener, int targetUserId, int groupId, long opId, boolean restricted, String owner, int cookie, - boolean requireConfirmation) { + boolean requireConfirmation, IBiometricNativeHandle windowId) { super(context, daemon, halDeviceId, token, listener, targetUserId, groupId, opId, - restricted, owner, cookie, requireConfirmation); + restricted, owner, cookie, requireConfirmation, windowId); } @Override @@ -198,7 +200,7 @@ public class FingerprintService extends BiometricServiceBase { @Override // Binder call public void enroll(final IBinder token, final byte[] cryptoToken, final int userId, final IFingerprintServiceReceiver receiver, final int flags, - final String opPackageName) { + final String opPackageName, IBiometricNativeHandle windowId) { checkPermission(MANAGE_FINGERPRINT); final boolean restricted = isRestricted(); @@ -206,7 +208,7 @@ public class FingerprintService extends BiometricServiceBase { final EnrollClientImpl client = new EnrollClientImpl(getContext(), mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver), mCurrentUserId, groupId, cryptoToken, restricted, opPackageName, new int[0] /* disabledFeatures */, - ENROLL_TIMEOUT_SEC) { + ENROLL_TIMEOUT_SEC, windowId) { @Override public boolean shouldVibrate() { return true; @@ -230,20 +232,22 @@ public class FingerprintService extends BiometricServiceBase { @Override // Binder call public void authenticate(final IBinder token, final long opId, final int groupId, final IFingerprintServiceReceiver receiver, final int flags, - final String opPackageName) { + final String opPackageName, IBiometricNativeHandle windowId) { updateActiveGroup(groupId, opPackageName); final boolean restricted = isRestricted(); final AuthenticationClientImpl client = new FingerprintAuthClient(getContext(), mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver), mCurrentUserId, groupId, opId, restricted, opPackageName, - 0 /* cookie */, false /* requireConfirmation */); + 0 /* cookie */, false /* requireConfirmation */, + windowId); authenticateInternal(client, opId, opPackageName); } @Override // Binder call public void prepareForAuthentication(IBinder token, long opId, int groupId, IBiometricServiceReceiverInternal wrapperReceiver, String opPackageName, - int cookie, int callingUid, int callingPid, int callingUserId) { + int cookie, int callingUid, int callingPid, int callingUserId, + IBiometricNativeHandle windowId) { checkPermission(MANAGE_BIOMETRIC); updateActiveGroup(groupId, opPackageName); final boolean restricted = true; // BiometricPrompt is always restricted @@ -251,7 +255,8 @@ public class FingerprintService extends BiometricServiceBase { mDaemonWrapper, mHalDeviceId, token, new BiometricPromptServiceListenerImpl(wrapperReceiver), mCurrentUserId, groupId, opId, restricted, opPackageName, cookie, - false /* requireConfirmation */); + false /* requireConfirmation */, + windowId); authenticateInternal(client, opId, opPackageName, callingUid, callingPid, callingUserId); } @@ -654,13 +659,24 @@ public class FingerprintService extends BiometricServiceBase { */ private final DaemonWrapper mDaemonWrapper = new DaemonWrapper() { @Override - public int authenticate(long operationId, int groupId) throws RemoteException { + public int authenticate(long operationId, int groupId, NativeHandle windowId) + throws RemoteException { IBiometricsFingerprint daemon = getFingerprintDaemon(); if (daemon == null) { Slog.w(TAG, "authenticate(): no fingerprint HAL!"); return ERROR_ESRCH; } - return daemon.authenticate(operationId, groupId); + android.hardware.biometrics.fingerprint.V2_2.IBiometricsFingerprint daemon22 = + android.hardware.biometrics.fingerprint.V2_2.IBiometricsFingerprint.castFrom( + daemon); + if (daemon22 != null) { + return daemon22.authenticate_2_2(operationId, groupId, windowId); + } else if (windowId == null) { + return daemon.authenticate(operationId, groupId); + } else { + Slog.e(TAG, "authenticate(): windowId is only supported in @2.2 HAL"); + return ERROR_ESRCH; + } } @Override @@ -695,13 +711,27 @@ public class FingerprintService extends BiometricServiceBase { @Override public int enroll(byte[] cryptoToken, int groupId, int timeout, - ArrayList<Integer> disabledFeatures) throws RemoteException { + ArrayList<Integer> disabledFeatures, NativeHandle windowId) throws RemoteException { IBiometricsFingerprint daemon = getFingerprintDaemon(); if (daemon == null) { Slog.w(TAG, "enroll(): no fingerprint HAL!"); return ERROR_ESRCH; } - return daemon.enroll(cryptoToken, groupId, timeout); + android.hardware.biometrics.fingerprint.V2_2.IBiometricsFingerprint daemon22 = + android.hardware.biometrics.fingerprint.V2_2.IBiometricsFingerprint.castFrom( + daemon); + if (daemon22 != null) { + ArrayList<Byte> cryptoTokenAsList = new ArrayList<>(cryptoToken.length); + for (byte b : cryptoToken) { + cryptoTokenAsList.add(b); + } + return daemon22.enroll_2_2(cryptoTokenAsList, groupId, timeout, windowId); + } else if (windowId == null) { + return daemon.enroll(cryptoToken, groupId, timeout); + } else { + Slog.e(TAG, "enroll(): windowId is only supported in @2.2 HAL"); + return ERROR_ESRCH; + } } @Override diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java index 2fc9d04623f8..af474304c76e 100644 --- a/services/core/java/com/android/server/compat/PlatformCompat.java +++ b/services/core/java/com/android/server/compat/PlatformCompat.java @@ -16,6 +16,12 @@ package com.android.server.compat; +import static android.Manifest.permission.LOG_COMPAT_CHANGE; +import static android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG; +import static android.Manifest.permission.READ_COMPAT_CHANGE_CONFIG; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; + +import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.IActivityManager; import android.content.Context; @@ -67,12 +73,14 @@ public class PlatformCompat extends IPlatformCompat.Stub { @Override public void reportChange(long changeId, ApplicationInfo appInfo) { + checkCompatChangeLogPermission(); reportChange(changeId, appInfo.uid, ChangeReporter.STATE_LOGGED); } @Override public void reportChangeByPackageName(long changeId, String packageName, int userId) { + checkCompatChangeLogPermission(); ApplicationInfo appInfo = getApplicationInfo(packageName, userId); if (appInfo == null) { return; @@ -82,11 +90,13 @@ public class PlatformCompat extends IPlatformCompat.Stub { @Override public void reportChangeByUid(long changeId, int uid) { + checkCompatChangeLogPermission(); reportChange(changeId, uid, ChangeReporter.STATE_LOGGED); } @Override public boolean isChangeEnabled(long changeId, ApplicationInfo appInfo) { + checkCompatChangeReadAndLogPermission(); if (mCompatConfig.isChangeEnabled(changeId, appInfo)) { reportChange(changeId, appInfo.uid, ChangeReporter.STATE_ENABLED); @@ -98,7 +108,9 @@ public class PlatformCompat extends IPlatformCompat.Stub { } @Override - public boolean isChangeEnabledByPackageName(long changeId, String packageName, int userId) { + public boolean isChangeEnabledByPackageName(long changeId, String packageName, + @UserIdInt int userId) { + checkCompatChangeReadAndLogPermission(); ApplicationInfo appInfo = getApplicationInfo(packageName, userId); if (appInfo == null) { return true; @@ -108,6 +120,7 @@ public class PlatformCompat extends IPlatformCompat.Stub { @Override public boolean isChangeEnabledByUid(long changeId, int uid) { + checkCompatChangeReadAndLogPermission(); String[] packages = mContext.getPackageManager().getPackagesForUid(uid); if (packages == null || packages.length == 0) { return true; @@ -140,6 +153,7 @@ public class PlatformCompat extends IPlatformCompat.Stub { @Override public void setOverrides(CompatibilityChangeConfig overrides, String packageName) throws RemoteException, SecurityException { + checkCompatChangeOverridePermission(); mCompatConfig.addOverrides(overrides, packageName); killPackage(packageName); } @@ -147,11 +161,13 @@ public class PlatformCompat extends IPlatformCompat.Stub { @Override public void setOverridesForTest(CompatibilityChangeConfig overrides, String packageName) throws RemoteException, SecurityException { + checkCompatChangeOverridePermission(); mCompatConfig.addOverrides(overrides, packageName); } @Override public void clearOverrides(String packageName) throws RemoteException, SecurityException { + checkCompatChangeOverridePermission(); mCompatConfig.removePackageOverrides(packageName); killPackage(packageName); } @@ -159,12 +175,14 @@ public class PlatformCompat extends IPlatformCompat.Stub { @Override public void clearOverridesForTest(String packageName) throws RemoteException, SecurityException { + checkCompatChangeOverridePermission(); mCompatConfig.removePackageOverrides(packageName); } @Override public boolean clearOverride(long changeId, String packageName) throws RemoteException, SecurityException { + checkCompatChangeOverridePermission(); boolean existed = mCompatConfig.removeOverride(changeId, packageName); killPackage(packageName); return existed; @@ -172,11 +190,13 @@ public class PlatformCompat extends IPlatformCompat.Stub { @Override public CompatibilityChangeConfig getAppConfig(ApplicationInfo appInfo) { + checkCompatChangeReadAndLogPermission(); return mCompatConfig.getAppConfig(appInfo); } @Override public CompatibilityChangeInfo[] listAllChanges() { + checkCompatChangeReadPermission(); return mCompatConfig.dumpChanges(); } @@ -215,6 +235,7 @@ public class PlatformCompat extends IPlatformCompat.Stub { @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + checkCompatChangeReadAndLogPermission(); if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, "platform_compat", pw)) return; mCompatConfig.dumpConfig(pw); } @@ -272,4 +293,30 @@ public class PlatformCompat extends IPlatformCompat.Stub { Binder.restoreCallingIdentity(identity); } } + + private void checkCompatChangeLogPermission() throws SecurityException { + if (mContext.checkCallingOrSelfPermission(LOG_COMPAT_CHANGE) + != PERMISSION_GRANTED) { + throw new SecurityException("Cannot log compat change usage"); + } + } + + private void checkCompatChangeReadPermission() throws SecurityException { + if (mContext.checkCallingOrSelfPermission(READ_COMPAT_CHANGE_CONFIG) + != PERMISSION_GRANTED) { + throw new SecurityException("Cannot read compat change"); + } + } + + private void checkCompatChangeOverridePermission() throws SecurityException { + if (mContext.checkCallingOrSelfPermission(OVERRIDE_COMPAT_CHANGE_CONFIG) + != PERMISSION_GRANTED) { + throw new SecurityException("Cannot override compat change"); + } + } + + private void checkCompatChangeReadAndLogPermission() throws SecurityException { + checkCompatChangeReadPermission(); + checkCompatChangeLogPermission(); + } } diff --git a/services/core/java/com/android/server/compat/PlatformCompatNative.java b/services/core/java/com/android/server/compat/PlatformCompatNative.java index 85dfbf411667..5d7af650db0b 100644 --- a/services/core/java/com/android/server/compat/PlatformCompatNative.java +++ b/services/core/java/com/android/server/compat/PlatformCompatNative.java @@ -16,6 +16,8 @@ package com.android.server.compat; +import android.annotation.UserIdInt; + import com.android.internal.compat.IPlatformCompatNative; /** @@ -39,7 +41,8 @@ public class PlatformCompatNative extends IPlatformCompatNative.Stub { } @Override - public boolean isChangeEnabledByPackageName(long changeId, String packageName, int userId) { + public boolean isChangeEnabledByPackageName(long changeId, String packageName, + @UserIdInt int userId) { return mPlatformCompat.isChangeEnabledByPackageName(changeId, packageName, userId); } diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 89af5b58ff72..cb88c4e8a739 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -49,6 +49,7 @@ import android.content.pm.UserInfo; import android.net.ConnectivityManager; import android.net.INetworkManagementEventObserver; import android.net.IpPrefix; +import android.net.IpSecManager; import android.net.LinkAddress; import android.net.LinkProperties; import android.net.LocalSocket; @@ -180,7 +181,10 @@ public class Vpn { private boolean mIsPackageTargetingAtLeastQ; private String mInterface; private Connection mConnection; - private LegacyVpnRunner mLegacyVpnRunner; + + /** Tracks the runners for all VPN types managed by the platform (eg. LegacyVpn, PlatformVpn) */ + private VpnRunner mVpnRunner; + private PendingIntent mStatusIntent; private volatile boolean mEnableTeardown = true; private final INetworkManagementService mNetd; @@ -762,7 +766,7 @@ public class Vpn { mNetworkCapabilities.setUids(null); } - // Revoke the connection or stop LegacyVpnRunner. + // Revoke the connection or stop the VpnRunner. if (mConnection != null) { try { mConnection.mService.transact(IBinder.LAST_CALL_TRANSACTION, @@ -772,9 +776,9 @@ public class Vpn { } mContext.unbindService(mConnection); mConnection = null; - } else if (mLegacyVpnRunner != null) { - mLegacyVpnRunner.exit(); - mLegacyVpnRunner = null; + } else if (mVpnRunner != null) { + mVpnRunner.exit(); + mVpnRunner = null; } try { @@ -1510,8 +1514,8 @@ public class Vpn { @Override public void interfaceStatusChanged(String interfaze, boolean up) { synchronized (Vpn.this) { - if (!up && mLegacyVpnRunner != null) { - mLegacyVpnRunner.check(interfaze); + if (!up && mVpnRunner != null && mVpnRunner instanceof LegacyVpnRunner) { + ((LegacyVpnRunner) mVpnRunner).exitIfOuterInterfaceIs(interfaze); } } } @@ -1528,9 +1532,10 @@ public class Vpn { mContext.unbindService(mConnection); mConnection = null; agentDisconnect(); - } else if (mLegacyVpnRunner != null) { - mLegacyVpnRunner.exit(); - mLegacyVpnRunner = null; + } else if (mVpnRunner != null) { + // agentDisconnect must be called from mVpnRunner.exit() + mVpnRunner.exit(); + mVpnRunner = null; } } } @@ -1913,23 +1918,40 @@ public class Vpn { private synchronized void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd, VpnProfile profile) { - stopLegacyVpnPrivileged(); + stopVpnRunnerPrivileged(); // Prepare for the new request. prepareInternal(VpnConfig.LEGACY_VPN); updateState(DetailedState.CONNECTING, "startLegacyVpn"); // Start a new LegacyVpnRunner and we are done! - mLegacyVpnRunner = new LegacyVpnRunner(config, racoon, mtpd, profile); - mLegacyVpnRunner.start(); + mVpnRunner = new LegacyVpnRunner(config, racoon, mtpd, profile); + mVpnRunner.start(); + } + + /** + * Checks if this the currently running VPN (if any) was started by the Settings app + * + * <p>This includes both Legacy VPNs and Platform VPNs. + */ + private boolean isSettingsVpnLocked() { + return mVpnRunner != null && VpnConfig.LEGACY_VPN.equals(mPackage); } - /** Stop legacy VPN. Permissions must be checked by callers. */ - public synchronized void stopLegacyVpnPrivileged() { - if (mLegacyVpnRunner != null) { - mLegacyVpnRunner.exit(); - mLegacyVpnRunner = null; + /** Stop VPN runner. Permissions must be checked by callers. */ + public synchronized void stopVpnRunnerPrivileged() { + if (!isSettingsVpnLocked()) { + return; + } + + final boolean isLegacyVpn = mVpnRunner instanceof LegacyVpnRunner; + mVpnRunner.exit(); + mVpnRunner = null; + + // LegacyVpn uses daemons that must be shut down before new ones are brought up. + // The same limitation does not apply to Platform VPNs. + if (isLegacyVpn) { synchronized (LegacyVpnRunner.TAG) { // wait for old thread to completely finish before spinning up // new instance, otherwise state updates can be out of order. @@ -1951,7 +1973,7 @@ public class Vpn { * Callers are responsible for checking permissions if needed. */ private synchronized LegacyVpnInfo getLegacyVpnInfoPrivileged() { - if (mLegacyVpnRunner == null) return null; + if (!isSettingsVpnLocked()) return null; final LegacyVpnInfo info = new LegacyVpnInfo(); info.key = mConfig.user; @@ -1962,14 +1984,53 @@ public class Vpn { return info; } - public VpnConfig getLegacyVpnConfig() { - if (mLegacyVpnRunner != null) { + public synchronized VpnConfig getLegacyVpnConfig() { + if (isSettingsVpnLocked()) { return mConfig; } else { return null; } } + /** This class represents the common interface for all VPN runners. */ + private abstract class VpnRunner extends Thread { + + protected VpnRunner(String name) { + super(name); + } + + public abstract void run(); + + protected abstract void exit(); + } + + private class IkeV2VpnRunner extends VpnRunner { + private static final String TAG = "IkeV2VpnRunner"; + + private final IpSecManager mIpSecManager; + private final VpnProfile mProfile; + + IkeV2VpnRunner(VpnProfile profile) { + super(TAG); + mProfile = profile; + + // TODO: move this to startVpnRunnerPrivileged() + mConfig = new VpnConfig(); + mIpSecManager = mContext.getSystemService(IpSecManager.class); + } + + @Override + public void run() { + // TODO: Build IKE config, start IKE session + } + + @Override + public void exit() { + // TODO: Teardown IKE session & any resources. + agentDisconnect(); + } + } + /** * Bringing up a VPN connection takes time, and that is all this thread * does. Here we have plenty of time. The only thing we need to take @@ -1977,7 +2038,7 @@ public class Vpn { * requests will pile up. This could be done in a Handler as a state * machine, but it is much easier to read in the current form. */ - private class LegacyVpnRunner extends Thread { + private class LegacyVpnRunner extends VpnRunner { private static final String TAG = "LegacyVpnRunner"; private final String[] mDaemons; @@ -2047,13 +2108,21 @@ public class Vpn { mContext.registerReceiver(mBroadcastReceiver, filter); } - public void check(String interfaze) { + /** + * Checks if the parameter matches the underlying interface + * + * <p>If the underlying interface is torn down, the LegacyVpnRunner also should be. It has + * no ability to migrate between interfaces (or Networks). + */ + public void exitIfOuterInterfaceIs(String interfaze) { if (interfaze.equals(mOuterInterface)) { Log.i(TAG, "Legacy VPN is going down with " + interfaze); exit(); } } + /** Tears down this LegacyVpn connection */ + @Override public void exit() { // We assume that everything is reset after stopping the daemons. interrupt(); diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java index 9754b6d4db02..0450647b9403 100644 --- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java +++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java @@ -39,13 +39,20 @@ import android.content.integrity.Rule; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; +import android.content.pm.PackageParser; +import android.content.pm.PackageUserState; import android.content.pm.ParceledListSlice; import android.content.pm.Signature; +import android.content.pm.SigningInfo; +import android.content.pm.parsing.ApkParseUtils; +import android.content.pm.parsing.PackageInfoUtils; +import android.content.pm.parsing.ParsedPackage; import android.net.Uri; import android.os.Binder; import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; +import android.os.UserHandle; import android.provider.Settings; import android.util.Slog; @@ -67,6 +74,7 @@ import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; @@ -121,11 +129,11 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { IntegrityFileManager.getInstance(), handlerThread.getThreadHandler(), Settings.Global.getInt( - context.getContentResolver(), - Settings.Global.INTEGRITY_CHECK_INCLUDES_RULE_PROVIDER, - 0) - == 1 - ); + context.getContentResolver(), + Settings.Global.INTEGRITY_CHECK_INCLUDES_RULE_PROVIDER, + 0) + == 1 + ); } @VisibleForTesting @@ -260,16 +268,25 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { return; } - String appCert = getCertificateFingerprint(packageInfo); + List<String> appCertificates = getCertificateFingerprint(packageInfo); + List<String> installerCertificates = + getInstallerCertificateFingerprint(installerPackageName); + + // TODO (b/148373316): Figure out what field contains which fields are populated for + // rotated and the multiple signers. Until then, return the first certificate. + String appCert = appCertificates.isEmpty() ? "" : appCertificates.get(0); + String installerCert = + installerCertificates.isEmpty() ? "" : installerCertificates.get(0); + + Slog.w(TAG, appCertificates.toString()); AppInstallMetadata.Builder builder = new AppInstallMetadata.Builder(); builder.setPackageName(getPackageNameNormalized(packageName)); - builder.setAppCertificate(appCert == null ? "" : appCert); + builder.setAppCertificate(appCert); builder.setVersionCode(intent.getLongExtra(EXTRA_LONG_VERSION_CODE, -1)); builder.setInstallerName(getPackageNameNormalized(installerPackageName)); - builder.setInstallerCertificate( - getInstallerCertificateFingerprint(installerPackageName)); + builder.setInstallerCertificate(installerCert); builder.setIsPreInstalled(isSystemApp(packageName)); AppInstallMetadata appInstallMetadata = builder.build(); @@ -320,7 +337,7 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { * Verify the UID and return the installer package name. * * @return the package name of the installer, or null if it cannot be determined or it is - * installed via adb. + * installed via adb. */ @Nullable private String getInstallerPackageName(Intent intent) { @@ -399,23 +416,27 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { } } - private String getCertificateFingerprint(@NonNull PackageInfo packageInfo) { - return getFingerprint(getSignature(packageInfo)); - } - - private String getInstallerCertificateFingerprint(String installer) { + private List<String> getInstallerCertificateFingerprint(String installer) { if (installer.equals(ADB_INSTALLER) || installer.equals(UNKNOWN_INSTALLER)) { - return INSTALLER_CERT_NOT_APPLICABLE; + return Collections.emptyList(); } try { PackageInfo installerInfo = mContext.getPackageManager() - .getPackageInfo(installer, PackageManager.GET_SIGNATURES); + .getPackageInfo(installer, PackageManager.GET_SIGNING_CERTIFICATES); return getCertificateFingerprint(installerInfo); } catch (PackageManager.NameNotFoundException e) { Slog.i(TAG, "Installer package " + installer + " not found."); - return ""; + return Collections.emptyList(); + } + } + + private List<String> getCertificateFingerprint(@NonNull PackageInfo packageInfo) { + ArrayList<String> certificateFingerprints = new ArrayList(); + for (Signature signature : getSignatures(packageInfo)) { + certificateFingerprints.add(getFingerprint(signature)); } + return certificateFingerprints; } /** Get the allowed installers and their associated certificate hashes from <meta-data> tag. */ @@ -445,12 +466,15 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { return packageCertMap; } - private static Signature getSignature(@NonNull PackageInfo packageInfo) { - if (packageInfo.signatures == null || packageInfo.signatures.length < 1) { + private static Signature[] getSignatures(@NonNull PackageInfo packageInfo) { + SigningInfo signingInfo = packageInfo.signingInfo; + + if (signingInfo == null || signingInfo.getApkContentsSigners().length < 1) { throw new IllegalArgumentException("Package signature not found in " + packageInfo); } - // Only the first element is guaranteed to be present. - return packageInfo.signatures[0]; + + // We are only interested in evaluating the active signatures. + return signingInfo.getApkContentsSigners(); } private static String getFingerprint(Signature cert) { @@ -489,20 +513,14 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { if (installationPath == null) { throw new IllegalArgumentException("Installation path is null, package not found"); } - PackageInfo packageInfo; + + PackageParser parser = new PackageParser(); try { - // The installation path will be a directory for a multi-apk install on L+ - if (installationPath.isDirectory()) { - packageInfo = getMultiApkInfo(installationPath); - } else { - packageInfo = - mContext.getPackageManager() - .getPackageArchiveInfo( - installationPath.getPath(), - PackageManager.GET_SIGNATURES - | PackageManager.GET_META_DATA); - } - return packageInfo; + ParsedPackage pkg = parser.parseParsedPackage(installationPath, 0, false); + int flags = PackageManager.GET_SIGNING_CERTIFICATES | PackageManager.GET_META_DATA; + ApkParseUtils.collectCertificates(pkg, false); + return PackageInfoUtils.generate(pkg, null, flags, 0, 0, null, new PackageUserState(), + UserHandle.getCallingUserId()); } catch (Exception e) { throw new IllegalArgumentException("Exception reading " + dataUri, e); } @@ -515,7 +533,8 @@ public class AppIntegrityManagerServiceImpl extends IAppIntegrityManager.Stub { mContext.getPackageManager() .getPackageArchiveInfo( baseFile.getAbsolutePath(), - PackageManager.GET_SIGNATURES | PackageManager.GET_META_DATA); + PackageManager.GET_SIGNING_CERTIFICATES + | PackageManager.GET_META_DATA); if (basePackageInfo == null) { for (File apkFile : multiApkDirectory.listFiles()) { diff --git a/services/core/java/com/android/server/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java index 7b17b4ed6219..83588846ade8 100644 --- a/services/core/java/com/android/server/media/MediaRoute2Provider.java +++ b/services/core/java/com/android/server/media/MediaRoute2Provider.java @@ -63,7 +63,6 @@ abstract class MediaRoute2Provider { public abstract void sendControlRequest(String routeId, Intent request); public abstract void requestSetVolume(String routeId, int volume); - public abstract void requestUpdateVolume(String routeId, int delta); @NonNull public String getUniqueId() { diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java index e22ea4681428..d08fb71d3dee 100644 --- a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java +++ b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java @@ -138,14 +138,6 @@ final class MediaRoute2ProviderProxy extends MediaRoute2Provider implements Serv } } - @Override - public void requestUpdateVolume(String routeId, int delta) { - if (mConnectionReady) { - mActiveConnection.requestUpdateVolume(routeId, delta); - updateBinding(); - } - } - public boolean hasComponentName(String packageName, String className) { return mComponentName.getPackageName().equals(packageName) && mComponentName.getClassName().equals(className); @@ -518,14 +510,6 @@ final class MediaRoute2ProviderProxy extends MediaRoute2Provider implements Serv } } - public void requestUpdateVolume(String routeId, int delta) { - try { - mProvider.requestUpdateVolume(routeId, delta); - } catch (RemoteException ex) { - Slog.e(TAG, "Failed to deliver request to request update volume.", ex); - } - } - @Override public void binderDied() { mHandler.post(() -> onConnectionDied(Connection.this)); diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java index 358cc29e3d64..b1133223e59a 100644 --- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java @@ -334,20 +334,6 @@ class MediaRouter2ServiceImpl { } } - public void requestUpdateVolume2(IMediaRouter2Client client, MediaRoute2Info route, int delta) { - Objects.requireNonNull(client, "client must not be null"); - Objects.requireNonNull(route, "route must not be null"); - - final long token = Binder.clearCallingIdentity(); - try { - synchronized (mLock) { - requestUpdateVolumeLocked(client, route, delta); - } - } finally { - Binder.restoreCallingIdentity(token); - } - } - public void requestCreateClientSession(IMediaRouter2Manager manager, String packageName, MediaRoute2Info route, int requestId) { final long token = Binder.clearCallingIdentity(); @@ -375,21 +361,6 @@ class MediaRouter2ServiceImpl { } } - public void requestUpdateVolume2Manager(IMediaRouter2Manager manager, - MediaRoute2Info route, int delta) { - Objects.requireNonNull(manager, "manager must not be null"); - Objects.requireNonNull(route, "route must not be null"); - - final long token = Binder.clearCallingIdentity(); - try { - synchronized (mLock) { - requestUpdateVolumeLocked(manager, route, delta); - } - } finally { - Binder.restoreCallingIdentity(token); - } - } - @NonNull public List<RoutingSessionInfo> getActiveSessions(IMediaRouter2Manager manager) { final long token = Binder.clearCallingIdentity(); @@ -628,18 +599,6 @@ class MediaRouter2ServiceImpl { } } - private void requestUpdateVolumeLocked(IMediaRouter2Client client, MediaRoute2Info route, - int delta) { - final IBinder binder = client.asBinder(); - Client2Record clientRecord = mAllClientRecords.get(binder); - - if (clientRecord != null) { - clientRecord.mUserRecord.mHandler.sendMessage( - obtainMessage(UserHandler::requestUpdateVolume, - clientRecord.mUserRecord.mHandler, route, delta)); - } - } - private void registerManagerLocked(IMediaRouter2Manager manager, int uid, int pid, String packageName, int userId, boolean trusted) { final IBinder binder = manager.asBinder(); @@ -713,18 +672,6 @@ class MediaRouter2ServiceImpl { } } - private void requestUpdateVolumeLocked(IMediaRouter2Manager manager, MediaRoute2Info route, - int delta) { - final IBinder binder = manager.asBinder(); - ManagerRecord managerRecord = mAllManagerRecords.get(binder); - - if (managerRecord != null) { - managerRecord.mUserRecord.mHandler.sendMessage( - obtainMessage(UserHandler::requestUpdateVolume, - managerRecord.mUserRecord.mHandler, route, delta)); - } - } - private List<RoutingSessionInfo> getActiveSessionsLocked(IMediaRouter2Manager manager) { final IBinder binder = manager.asBinder(); ManagerRecord managerRecord = mAllManagerRecords.get(binder); @@ -1459,13 +1406,6 @@ class MediaRouter2ServiceImpl { } } - private void requestUpdateVolume(MediaRoute2Info route, int delta) { - final MediaRoute2Provider provider = findProvider(route.getProviderId()); - if (provider != null) { - provider.requestUpdateVolume(route.getOriginalId(), delta); - } - } - private List<IMediaRouter2Client> getClients() { final List<IMediaRouter2Client> clients = new ArrayList<>(); MediaRouter2ServiceImpl service = mServiceRef.get(); diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java index e1d38039b955..57f0328f1c00 100644 --- a/services/core/java/com/android/server/media/MediaRouterService.java +++ b/services/core/java/com/android/server/media/MediaRouterService.java @@ -539,12 +539,6 @@ public final class MediaRouterService extends IMediaRouterService.Stub // Binder call @Override - public void requestUpdateVolume2(IMediaRouter2Client client, MediaRoute2Info route, int delta) { - mService2.requestUpdateVolume2(client, route, delta); - } - - // Binder call - @Override public void requestSetVolume2Manager(IMediaRouter2Manager manager, MediaRoute2Info route, int volume) { mService2.requestSetVolume2Manager(manager, route, volume); @@ -552,13 +546,6 @@ public final class MediaRouterService extends IMediaRouterService.Stub // Binder call @Override - public void requestUpdateVolume2Manager(IMediaRouter2Manager manager, - MediaRoute2Info route, int delta) { - mService2.requestUpdateVolume2Manager(manager, route, delta); - } - - // Binder call - @Override public List<RoutingSessionInfo> getActiveSessions(IMediaRouter2Manager manager) { return mService2.getActiveSessions(manager); } diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java index 798a781a9d6c..888f7ce74ba4 100644 --- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java +++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java @@ -153,11 +153,6 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { public void requestSetVolume(String routeId, int volume) { } - //TODO: implement method - @Override - public void requestUpdateVolume(String routeId, int delta) { - } - private void initializeDefaultRoute() { mDefaultRoute = new MediaRoute2Info.Builder( DEFAULT_ROUTE_ID, diff --git a/services/core/java/com/android/server/net/LockdownVpnTracker.java b/services/core/java/com/android/server/net/LockdownVpnTracker.java index ef8f6479cb3d..3cafafffc62a 100644 --- a/services/core/java/com/android/server/net/LockdownVpnTracker.java +++ b/services/core/java/com/android/server/net/LockdownVpnTracker.java @@ -138,7 +138,7 @@ public class LockdownVpnTracker { if (egressDisconnected || egressChanged) { mAcceptedEgressIface = null; - mVpn.stopLegacyVpnPrivileged(); + mVpn.stopVpnRunnerPrivileged(); } if (egressDisconnected) { hideNotification(); @@ -218,7 +218,7 @@ public class LockdownVpnTracker { mAcceptedEgressIface = null; mErrorCount = 0; - mVpn.stopLegacyVpnPrivileged(); + mVpn.stopVpnRunnerPrivileged(); mVpn.setLockdown(false); hideNotification(); diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index b52289e36b50..f07113591fa5 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -2681,18 +2681,24 @@ public class NotificationManagerService extends SystemService { @Override public void enqueueTextToast(String pkg, IBinder token, CharSequence text, int duration, int displayId, @Nullable ITransientNotificationCallback callback) { - enqueueToast(pkg, token, text, null, duration, displayId, callback); + enqueueToast(pkg, token, text, null, duration, displayId, callback, false); } @Override public void enqueueToast(String pkg, IBinder token, ITransientNotification callback, int duration, int displayId) { - enqueueToast(pkg, token, null, callback, duration, displayId, null); + enqueueToast(pkg, token, null, callback, duration, displayId, null, true); + } + + @Override + public void enqueueTextOrCustomToast(String pkg, IBinder token, + ITransientNotification callback, int duration, int displayId, boolean isCustom) { + enqueueToast(pkg, token, null, callback, duration, displayId, null, isCustom); } private void enqueueToast(String pkg, IBinder token, @Nullable CharSequence text, @Nullable ITransientNotification callback, int duration, int displayId, - @Nullable ITransientNotificationCallback textCallback) { + @Nullable ITransientNotificationCallback textCallback, boolean isCustom) { if (DBG) { Slog.i(TAG, "enqueueToast pkg=" + pkg + " token=" + token + " duration=" + duration + " displayId=" + displayId); @@ -2730,7 +2736,7 @@ public class NotificationManagerService extends SystemService { return; } - if (callback != null && !appIsForeground && !isSystemToast) { + if (callback != null && !appIsForeground && !isSystemToast && isCustom) { boolean block; long id = Binder.clearCallingIdentity(); try { diff --git a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java index 639cc70fa275..90fc59a893a6 100644 --- a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java +++ b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java @@ -38,6 +38,8 @@ import android.util.Log; import android.util.LruCache; import android.util.Slog; +import libcore.util.EmptyArray; + import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedList; @@ -301,7 +303,7 @@ public class ValidateNotificationPeople implements NotificationSignalExtractor { for (String person: second) { people.add(person); } - return (String[]) people.toArray(); + return people.toArray(EmptyArray.STRING); } @Nullable diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java index 40ea6cfb6d4a..b98bb0831b0e 100644 --- a/services/core/java/com/android/server/pm/Installer.java +++ b/services/core/java/com/android/server/pm/Installer.java @@ -234,12 +234,12 @@ public class Installer extends SystemService { } public void moveCompleteApp(String fromUuid, String toUuid, String packageName, - String dataAppName, int appId, String seInfo, int targetSdkVersion) - throws InstallerException { + String dataAppName, int appId, String seInfo, int targetSdkVersion, + String fromCodePath) throws InstallerException { if (!checkBeforeRemote()) return; try { mInstalld.moveCompleteApp(fromUuid, toUuid, packageName, dataAppName, appId, seInfo, - targetSdkVersion); + targetSdkVersion, fromCodePath); } catch (Exception e) { throw InstallerException.from(e); } diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java index 7bb782b9fadc..3e64e9828c3e 100644 --- a/services/core/java/com/android/server/pm/LauncherAppsService.java +++ b/services/core/java/com/android/server/pm/LauncherAppsService.java @@ -29,12 +29,14 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentSender; +import android.content.LocusId; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.ILauncherApps; import android.content.pm.IOnAppsChangedListener; import android.content.pm.IPackageInstallerCallback; import android.content.pm.IPackageManager; +import android.content.pm.IShortcutChangeCallback; import android.content.pm.LauncherApps; import android.content.pm.LauncherApps.ShortcutQuery; import android.content.pm.PackageInfo; @@ -661,8 +663,8 @@ public class LauncherAppsService extends SystemService { @Override public ParceledListSlice getShortcuts(String callingPackage, long changedSince, - String packageName, List shortcutIds, ComponentName componentName, int flags, - UserHandle targetUser) { + String packageName, List shortcutIds, List<LocusId> locusIds, + ComponentName componentName, int flags, UserHandle targetUser) { ensureShortcutPermission(callingPackage); if (!canAccessProfile(targetUser.getIdentifier(), "Cannot get shortcuts")) { return new ParceledListSlice<>(Collections.EMPTY_LIST); @@ -671,16 +673,31 @@ public class LauncherAppsService extends SystemService { throw new IllegalArgumentException( "To query by shortcut ID, package name must also be set"); } + if (locusIds != null && packageName == null) { + throw new IllegalArgumentException( + "To query by locus ID, package name must also be set"); + } // TODO(b/29399275): Eclipse compiler requires explicit List<ShortcutInfo> cast below. return new ParceledListSlice<>((List<ShortcutInfo>) mShortcutServiceInternal.getShortcuts(getCallingUserId(), - callingPackage, changedSince, packageName, shortcutIds, + callingPackage, changedSince, packageName, shortcutIds, locusIds, componentName, flags, targetUser.getIdentifier(), injectBinderCallingPid(), injectBinderCallingUid())); } @Override + public void registerShortcutChangeCallback(String callingPackage, long changedSince, + String packageName, List shortcutIds, List<LocusId> locusIds, + ComponentName componentName, int flags, IShortcutChangeCallback callback, + int callbackId) { + } + + @Override + public void unregisterShortcutChangeCallback(String callingPackage, int callbackId) { + } + + @Override public void pinShortcuts(String callingPackage, String packageName, List<String> ids, UserHandle targetUser) { ensureShortcutPermission(callingPackage); @@ -1137,7 +1154,7 @@ public class LauncherAppsService extends SystemService { mShortcutServiceInternal.getShortcuts(launcherUserId, cookie.packageName, /* changedSince= */ 0, packageName, /* shortcutIds=*/ null, - /* component= */ null, + /* locusIds=*/ null, /* component= */ null, ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY | ShortcutQuery.FLAG_MATCH_ALL_KINDS_WITH_ALL_PINNED , userId, cookie.callingPid, cookie.callingUid); diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index bf7bebd10a13..0cf8b424b84d 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -129,6 +129,7 @@ import com.android.server.pm.dex.DexManager; import com.android.server.security.VerityUtils; import libcore.io.IoUtils; +import libcore.util.EmptyArray; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -212,7 +213,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { private static final String ATTR_SIGNATURE = "signature"; private static final String PROPERTY_NAME_INHERIT_NATIVE = "pi.inherit_native_on_dont_kill"; - private static final int[] EMPTY_CHILD_SESSION_ARRAY = {}; + private static final int[] EMPTY_CHILD_SESSION_ARRAY = EmptyArray.INT; + private static final FileInfo[] EMPTY_FILE_INFO_ARRAY = {}; private static final String SYSTEM_DATA_LOADER_PACKAGE = "android"; @@ -375,8 +377,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // TODO(b/146080380): merge file list with Callback installation. private IncrementalFileStorages mIncrementalFileStorages; - private static final String[] EMPTY_STRING_ARRAY = new String[]{}; - private static final FileFilter sAddedApkFilter = new FileFilter() { @Override public boolean accept(File file) { @@ -558,10 +558,22 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mStagedSessionErrorMessage = stagedSessionErrorMessage != null ? stagedSessionErrorMessage : ""; - if (isStreamingInstallation() - && this.params.dataLoaderParams.getComponentName().getPackageName() - == SYSTEM_DATA_LOADER_PACKAGE) { - assertShellOrSystemCalling("System data loaders"); + if (isDataLoaderInstallation()) { + if (isApexInstallation()) { + throw new IllegalArgumentException( + "DataLoader installation of APEX modules is not allowed."); + } + } + + if (isStreamingInstallation()) { + if (!isIncrementalInstallationAllowed(mPackageName)) { + throw new IllegalArgumentException( + "Incremental installation of this package is not allowed."); + } + if (this.params.dataLoaderParams.getComponentName().getPackageName() + == SYSTEM_DATA_LOADER_PACKAGE) { + assertShellOrSystemCalling("System data loaders"); + } } } @@ -719,7 +731,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { if (!isDataLoaderInstallation()) { String[] result = stageDir.list(); if (result == null) { - result = EMPTY_STRING_ARRAY; + result = EmptyArray.STRING; } return result; } @@ -1174,6 +1186,19 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } /** + * Checks if the package can be installed on IncFs. + */ + private static boolean isIncrementalInstallationAllowed(String packageName) { + final PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class); + final AndroidPackage existingPackage = pmi.getPackage(packageName); + if (existingPackage == null) { + return true; + } + + return !PackageManagerService.isSystemApp(existingPackage); + } + + /** * If this was not already called, the session will be sealed. * * This method may be called multiple times to update the status receiver validate caller @@ -1362,14 +1387,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { return false; } - final PackageInfo pkgInfo = mPm.getPackageInfo( - params.appPackageName, PackageManager.GET_SIGNATURES - | PackageManager.MATCH_STATIC_SHARED_LIBRARIES /*flags*/, userId); - if (isApexInstallation()) { validateApexInstallLocked(); } else { - validateApkInstallLocked(pkgInfo); + validateApkInstallLocked(); } } @@ -1786,8 +1807,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { * {@link PackageManagerService}. */ @GuardedBy("mLock") - private void validateApkInstallLocked(@Nullable PackageInfo pkgInfo) - throws PackageManagerException { + private void validateApkInstallLocked() throws PackageManagerException { ApkLite baseApk = null; mPackageName = null; mVersionCode = -1; @@ -1797,6 +1817,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mResolvedStagedFiles.clear(); mResolvedInheritedFiles.clear(); + final PackageInfo pkgInfo = mPm.getPackageInfo( + params.appPackageName, PackageManager.GET_SIGNATURES + | PackageManager.MATCH_STATIC_SHARED_LIBRARIES /*flags*/, userId); + // Partial installs must be consistent with existing install if (params.mode == SessionParams.MODE_INHERIT_EXISTING && (pkgInfo == null || pkgInfo.applicationInfo == null)) { @@ -3096,7 +3120,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } if (grantedRuntimePermissions.size() > 0) { - params.grantedRuntimePermissions = (String[]) grantedRuntimePermissions.toArray(); + params.grantedRuntimePermissions = + grantedRuntimePermissions.toArray(EmptyArray.STRING); } if (whitelistedRestrictedPermissions.size() > 0) { @@ -3115,7 +3140,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { FileInfo[] fileInfosArray = null; if (!files.isEmpty()) { - fileInfosArray = (FileInfo[]) files.toArray(); + fileInfosArray = files.toArray(EMPTY_FILE_INFO_ARRAY); } InstallSource installSource = InstallSource.create(installInitiatingPackageName, diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 8e5ff66a7561..c85859072d89 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -150,6 +150,7 @@ import android.content.pm.ApplicationInfo; import android.content.pm.AuxiliaryResolveInfo; import android.content.pm.ChangedPackages; import android.content.pm.ComponentInfo; +import android.content.pm.DataLoaderType; import android.content.pm.FallbackCategoryProvider; import android.content.pm.FeatureInfo; import android.content.pm.IDexModuleRegisterCallback; @@ -1658,7 +1659,8 @@ public class PackageManagerService extends IPackageManager.Stub handlePackagePostInstall(parentRes, grantPermissions, killApp, virtualPreload, grantedPermissions, whitelistedRestrictedPermissions, didRestore, - args.installSource.installerPackageName, args.observer); + args.installSource.installerPackageName, args.observer, + args.mDataLoaderType); // Handle the child packages final int childCount = (parentRes.addedChildPackages != null) @@ -1668,7 +1670,8 @@ public class PackageManagerService extends IPackageManager.Stub handlePackagePostInstall(childRes, grantPermissions, killApp, virtualPreload, grantedPermissions, whitelistedRestrictedPermissions, false /*didRestore*/, - args.installSource.installerPackageName, args.observer); + args.installSource.installerPackageName, args.observer, + args.mDataLoaderType); } // Log tracing if needed @@ -1995,7 +1998,7 @@ public class PackageManagerService extends IPackageManager.Stub boolean killApp, boolean virtualPreload, String[] grantedPermissions, List<String> whitelistedRestrictedPermissions, boolean launchedForRestore, String installerPackage, - IPackageInstallObserver2 installObserver) { + IPackageInstallObserver2 installObserver, int dataLoaderType) { final boolean succeeded = res.returnCode == PackageManager.INSTALL_SUCCEEDED; final boolean update = res.removedInfo != null && res.removedInfo.removedPackage != null; @@ -2096,11 +2099,14 @@ public class PackageManagerService extends IPackageManager.Stub if (update) { extras.putBoolean(Intent.EXTRA_REPLACING, true); } + extras.putInt(PackageInstaller.EXTRA_DATA_LOADER_TYPE, dataLoaderType); + // Send to all running apps. sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName, extras, 0 /*flags*/, null /*targetPackage*/, null /*finishedReceiver*/, updateUserIds, instantUserIds); if (installerPackageName != null) { + // Send to the installer, even if it's not running. sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName, extras, 0 /*flags*/, installerPackageName, null /*finishedReceiver*/, @@ -13978,9 +13984,11 @@ public class PackageManagerService extends IPackageManager.Stub final int appId; final String seinfo; final int targetSdkVersion; + final String fromCodePath; public MoveInfo(int moveId, String fromUuid, String toUuid, String packageName, - String dataAppName, int appId, String seinfo, int targetSdkVersion) { + String dataAppName, int appId, String seinfo, int targetSdkVersion, + String fromCodePath) { this.moveId = moveId; this.fromUuid = fromUuid; this.toUuid = toUuid; @@ -13989,6 +13997,7 @@ public class PackageManagerService extends IPackageManager.Stub this.appId = appId; this.seinfo = seinfo; this.targetSdkVersion = targetSdkVersion; + this.fromCodePath = fromCodePath; } } @@ -14114,13 +14123,14 @@ public class PackageManagerService extends IPackageManager.Stub MultiPackageInstallParams mParentInstallParams; final long requiredInstalledVersionCode; final boolean forceQueryableOverride; + final int mDataLoaderType; InstallParams(OriginInfo origin, MoveInfo move, IPackageInstallObserver2 observer, int installFlags, InstallSource installSource, String volumeUuid, VerificationInfo verificationInfo, UserHandle user, String packageAbiOverride, String[] grantedPermissions, List<String> whitelistedRestrictedPermissions, SigningDetails signingDetails, int installReason, - long requiredInstalledVersionCode) { + long requiredInstalledVersionCode, int dataLoaderType) { super(user); this.origin = origin; this.move = move; @@ -14136,40 +14146,42 @@ public class PackageManagerService extends IPackageManager.Stub this.installReason = installReason; this.requiredInstalledVersionCode = requiredInstalledVersionCode; this.forceQueryableOverride = false; + this.mDataLoaderType = dataLoaderType; } InstallParams(ActiveInstallSession activeInstallSession) { super(activeInstallSession.getUser()); + final PackageInstaller.SessionParams sessionParams = + activeInstallSession.getSessionParams(); if (DEBUG_INSTANT) { - if ((activeInstallSession.getSessionParams().installFlags + if ((sessionParams.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0) { Slog.d(TAG, "Ephemeral install of " + activeInstallSession.getPackageName()); } } verificationInfo = new VerificationInfo( - activeInstallSession.getSessionParams().originatingUri, - activeInstallSession.getSessionParams().referrerUri, - activeInstallSession.getSessionParams().originatingUid, + sessionParams.originatingUri, + sessionParams.referrerUri, + sessionParams.originatingUid, activeInstallSession.getInstallerUid()); origin = OriginInfo.fromStagedFile(activeInstallSession.getStagedDir()); move = null; installReason = fixUpInstallReason( activeInstallSession.getInstallSource().installerPackageName, activeInstallSession.getInstallerUid(), - activeInstallSession.getSessionParams().installReason); + sessionParams.installReason); observer = activeInstallSession.getObserver(); - installFlags = activeInstallSession.getSessionParams().installFlags; + installFlags = sessionParams.installFlags; installSource = activeInstallSession.getInstallSource(); - volumeUuid = activeInstallSession.getSessionParams().volumeUuid; - packageAbiOverride = activeInstallSession.getSessionParams().abiOverride; - grantedRuntimePermissions = activeInstallSession.getSessionParams() - .grantedRuntimePermissions; - whitelistedRestrictedPermissions = activeInstallSession.getSessionParams() - .whitelistedRestrictedPermissions; + volumeUuid = sessionParams.volumeUuid; + packageAbiOverride = sessionParams.abiOverride; + grantedRuntimePermissions = sessionParams.grantedRuntimePermissions; + whitelistedRestrictedPermissions = sessionParams.whitelistedRestrictedPermissions; signingDetails = activeInstallSession.getSigningDetails(); - requiredInstalledVersionCode = activeInstallSession.getSessionParams() - .requiredInstalledVersionCode; - forceQueryableOverride = activeInstallSession.getSessionParams().forceQueryableOverride; + requiredInstalledVersionCode = sessionParams.requiredInstalledVersionCode; + forceQueryableOverride = sessionParams.forceQueryableOverride; + mDataLoaderType = (sessionParams.dataLoaderParams != null) + ? sessionParams.dataLoaderParams.getType() : DataLoaderType.NONE; } @Override @@ -14772,6 +14784,7 @@ public class PackageManagerService extends IPackageManager.Stub final int installReason; final boolean forceQueryableOverride; @Nullable final MultiPackageInstallParams mMultiPackageInstallParams; + final int mDataLoaderType; // The list of instruction sets supported by this app. This is currently // only used during the rmdex() phase to clean up resources. We can get rid of this @@ -14785,7 +14798,7 @@ public class PackageManagerService extends IPackageManager.Stub List<String> whitelistedRestrictedPermissions, String traceMethod, int traceCookie, SigningDetails signingDetails, int installReason, boolean forceQueryableOverride, - MultiPackageInstallParams multiPackageInstallParams) { + MultiPackageInstallParams multiPackageInstallParams, int dataLoaderType) { this.origin = origin; this.move = move; this.installFlags = installFlags; @@ -14803,6 +14816,7 @@ public class PackageManagerService extends IPackageManager.Stub this.installReason = installReason; this.forceQueryableOverride = forceQueryableOverride; this.mMultiPackageInstallParams = multiPackageInstallParams; + this.mDataLoaderType = dataLoaderType; } /** New install */ @@ -14812,7 +14826,8 @@ public class PackageManagerService extends IPackageManager.Stub params.getUser(), null /*instructionSets*/, params.packageAbiOverride, params.grantedRuntimePermissions, params.whitelistedRestrictedPermissions, params.traceMethod, params.traceCookie, params.signingDetails, - params.installReason, params.forceQueryableOverride, params.mParentInstallParams); + params.installReason, params.forceQueryableOverride, + params.mParentInstallParams, params.mDataLoaderType); } abstract int copyApk(); @@ -14903,7 +14918,8 @@ public class PackageManagerService extends IPackageManager.Stub super(OriginInfo.fromNothing(), null, null, 0, InstallSource.EMPTY, null, null, instructionSets, null, null, null, null, 0, PackageParser.SigningDetails.UNKNOWN, - PackageManager.INSTALL_REASON_UNKNOWN, false, null /* parent */); + PackageManager.INSTALL_REASON_UNKNOWN, false, null /* parent */, + DataLoaderType.NONE); this.codeFile = (codePath != null) ? new File(codePath) : null; this.resourceFile = (resourcePath != null) ? new File(resourcePath) : null; } @@ -14983,9 +14999,7 @@ public class PackageManagerService extends IPackageManager.Stub try { makeDirRecursive(afterCodeFile.getParentFile(), 0775); if (onIncremental) { - // TODO(b/147371381): fix incremental installation - mIncrementalManager.rename(beforeCodeFile.getAbsolutePath(), - afterCodeFile.getAbsolutePath()); + mIncrementalManager.renameCodePath(beforeCodeFile, afterCodeFile); } else { Os.rename(beforeCodeFile.getAbsolutePath(), afterCodeFile.getAbsolutePath()); } @@ -15104,7 +15118,8 @@ public class PackageManagerService extends IPackageManager.Stub synchronized (mInstaller) { try { mInstaller.moveCompleteApp(move.fromUuid, move.toUuid, move.packageName, - move.dataAppName, move.appId, move.seinfo, move.targetSdkVersion); + move.dataAppName, move.appId, move.seinfo, move.targetSdkVersion, + move.fromCodePath); } catch (InstallerException e) { Slog.w(TAG, "Failed to move app", e); return PackageManager.INSTALL_FAILED_INTERNAL_ERROR; @@ -22054,6 +22069,7 @@ public class PackageManagerService extends IPackageManager.Stub final PackageFreezer freezer; final int[] installedUserIds; final boolean isCurrentLocationExternal; + final String fromCodePath; // reader synchronized (mLock) { @@ -22110,6 +22126,7 @@ public class PackageManagerService extends IPackageManager.Stub targetSdkVersion = pkg.getTargetSdkVersion(); freezer = freezePackage(packageName, "movePackageInternal"); installedUserIds = ps.queryInstalledUsers(mUserManager.getUserIds(), true); + fromCodePath = pkg.getCodePath(); } final Bundle extras = new Bundle(); @@ -22238,7 +22255,7 @@ public class PackageManagerService extends IPackageManager.Stub final String dataAppName = codeFile.getName(); move = new MoveInfo(moveId, currentVolumeUuid, volumeUuid, packageName, - dataAppName, appId, seinfo, targetSdkVersion); + dataAppName, appId, seinfo, targetSdkVersion, fromCodePath); } else { move = null; } @@ -22251,7 +22268,8 @@ public class PackageManagerService extends IPackageManager.Stub installSource, volumeUuid, null /*verificationInfo*/, user, packageAbiOverride, null /*grantedPermissions*/, null /*whitelistedRestrictedPermissions*/, PackageParser.SigningDetails.UNKNOWN, - PackageManager.INSTALL_REASON_UNKNOWN, PackageManager.VERSION_CODE_HIGHEST); + PackageManager.INSTALL_REASON_UNKNOWN, PackageManager.VERSION_CODE_HIGHEST, + DataLoaderType.NONE); params.setTraceMethod("movePackage").setTraceCookie(System.identityHashCode(params)); msg.obj = params; @@ -22757,6 +22775,11 @@ public class PackageManagerService extends IPackageManager.Stub private class PackageManagerNative extends IPackageManagerNative.Stub { @Override + public String[] getAllPackages() { + return PackageManagerService.this.getAllPackages().toArray(new String[0]); + } + + @Override public String[] getNamesForUids(int[] uids) throws RemoteException { final String[] results = PackageManagerService.this.getNamesForUids(uids); // massage results so they can be parsed by the native binder diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index c31aa246b25e..d16c0748ef0e 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -33,6 +33,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.IntentSender; import android.content.IntentSender.SendIntentException; +import android.content.LocusId; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; @@ -2589,7 +2590,7 @@ public class ShortcutService extends IShortcutService.Stub { public List<ShortcutInfo> getShortcuts(int launcherUserId, @NonNull String callingPackage, long changedSince, @Nullable String packageName, @Nullable List<String> shortcutIds, - @Nullable ComponentName componentName, + @Nullable List<LocusId> locusIds, @Nullable ComponentName componentName, int queryFlags, int userId, int callingPid, int callingUid) { final ArrayList<ShortcutInfo> ret = new ArrayList<>(); @@ -2610,15 +2611,16 @@ public class ShortcutService extends IShortcutService.Stub { if (packageName != null) { getShortcutsInnerLocked(launcherUserId, - callingPackage, packageName, shortcutIds, changedSince, + callingPackage, packageName, shortcutIds, locusIds, changedSince, componentName, queryFlags, userId, ret, cloneFlag, callingPid, callingUid); } else { final List<String> shortcutIdsF = shortcutIds; + final List<LocusId> locusIdsF = locusIds; getUserShortcutsLocked(userId).forAllPackages(p -> { getShortcutsInnerLocked(launcherUserId, - callingPackage, p.getPackageName(), shortcutIdsF, changedSince, - componentName, queryFlags, userId, ret, cloneFlag, + callingPackage, p.getPackageName(), shortcutIdsF, locusIdsF, + changedSince, componentName, queryFlags, userId, ret, cloneFlag, callingPid, callingUid); }); } @@ -2628,12 +2630,15 @@ public class ShortcutService extends IShortcutService.Stub { @GuardedBy("ShortcutService.this.mLock") private void getShortcutsInnerLocked(int launcherUserId, @NonNull String callingPackage, - @Nullable String packageName, @Nullable List<String> shortcutIds, long changedSince, + @Nullable String packageName, @Nullable List<String> shortcutIds, + @Nullable List<LocusId> locusIds, long changedSince, @Nullable ComponentName componentName, int queryFlags, int userId, ArrayList<ShortcutInfo> ret, int cloneFlag, int callingPid, int callingUid) { final ArraySet<String> ids = shortcutIds == null ? null : new ArraySet<>(shortcutIds); + final ArraySet<LocusId> locIds = locusIds == null ? null + : new ArraySet<>(locusIds); final ShortcutUser user = getUserShortcutsLocked(userId); final ShortcutPackage p = user.getPackageShortcutsIfExists(packageName); @@ -2660,6 +2665,9 @@ public class ShortcutService extends IShortcutService.Stub { if (ids != null && !ids.contains(si.getId())) { return false; } + if (locIds != null && !locIds.contains(si.getLocusId())) { + return false; + } if (componentName != null) { if (si.getActivity() != null && !si.getActivity().equals(componentName)) { diff --git a/services/core/java/com/android/server/policy/LegacyGlobalActions.java b/services/core/java/com/android/server/policy/LegacyGlobalActions.java index 6daf5162ebad..6eba59acbc94 100644 --- a/services/core/java/com/android/server/policy/LegacyGlobalActions.java +++ b/services/core/java/com/android/server/policy/LegacyGlobalActions.java @@ -402,7 +402,7 @@ class LegacyGlobalActions implements DialogInterface.OnDismissListener, DialogIn public String getStatus() { return mContext.getString( com.android.internal.R.string.bugreport_status, - Build.VERSION.RELEASE, + Build.VERSION.RELEASE_OR_CODENAME, Build.ID); } } diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java index 96f1219861ec..5c79f6e6391d 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -2480,7 +2480,7 @@ public class StatsPullAtomService extends SystemService { .writeString(Build.BRAND) .writeString(Build.PRODUCT) .writeString(Build.DEVICE) - .writeString(Build.VERSION.RELEASE) + .writeString(Build.VERSION.RELEASE_OR_CODENAME) .writeString(Build.ID) .writeString(Build.VERSION.INCREMENTAL) .writeString(Build.TYPE) diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorService.java b/services/core/java/com/android/server/timedetector/TimeDetectorService.java index 0bb0f94d1243..ed6424cb4a30 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorService.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorService.java @@ -21,7 +21,7 @@ import android.annotation.Nullable; import android.app.timedetector.ITimeDetectorService; import android.app.timedetector.ManualTimeSuggestion; import android.app.timedetector.NetworkTimeSuggestion; -import android.app.timedetector.PhoneTimeSuggestion; +import android.app.timedetector.TelephonyTimeSuggestion; import android.content.ContentResolver; import android.content.Context; import android.database.ContentObserver; @@ -94,11 +94,11 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub { } @Override - public void suggestPhoneTime(@NonNull PhoneTimeSuggestion timeSignal) { - enforceSuggestPhoneTimePermission(); + public void suggestTelephonyTime(@NonNull TelephonyTimeSuggestion timeSignal) { + enforceSuggestTelephonyTimePermission(); Objects.requireNonNull(timeSignal); - mHandler.post(() -> mTimeDetectorStrategy.suggestPhoneTime(timeSignal)); + mHandler.post(() -> mTimeDetectorStrategy.suggestTelephonyTime(timeSignal)); } @Override @@ -131,10 +131,10 @@ public final class TimeDetectorService extends ITimeDetectorService.Stub { mTimeDetectorStrategy.dump(pw, args); } - private void enforceSuggestPhoneTimePermission() { + private void enforceSuggestTelephonyTimePermission() { mContext.enforceCallingPermission( - android.Manifest.permission.SUGGEST_PHONE_TIME_AND_ZONE, - "suggest phone time and time zone"); + android.Manifest.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE, + "suggest telephony time and time zone"); } private void enforceSuggestManualTimePermission() { diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java index a7c3b4dad552..a5fba4e6ba49 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java @@ -20,7 +20,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.timedetector.ManualTimeSuggestion; import android.app.timedetector.NetworkTimeSuggestion; -import android.app.timedetector.PhoneTimeSuggestion; +import android.app.timedetector.TelephonyTimeSuggestion; import android.os.TimestampedValue; import java.io.PrintWriter; @@ -78,7 +78,7 @@ public interface TimeDetectorStrategy { void initialize(@NonNull Callback callback); /** Process the suggested time from telephony sources. */ - void suggestPhoneTime(@NonNull PhoneTimeSuggestion timeSuggestion); + void suggestTelephonyTime(@NonNull TelephonyTimeSuggestion timeSuggestion); /** Process the suggested manually entered time. */ void suggestManualTime(@NonNull ManualTimeSuggestion timeSuggestion); diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java index 19435ee16660..8c54fa95e04b 100644 --- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java +++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java @@ -22,7 +22,7 @@ import android.annotation.Nullable; import android.app.AlarmManager; import android.app.timedetector.ManualTimeSuggestion; import android.app.timedetector.NetworkTimeSuggestion; -import android.app.timedetector.PhoneTimeSuggestion; +import android.app.timedetector.TelephonyTimeSuggestion; import android.os.TimestampedValue; import android.util.LocalLog; import android.util.Slog; @@ -38,9 +38,9 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** - * An implementation of {@link TimeDetectorStrategy} that passes phone and manual suggestions to - * {@link AlarmManager}. When there are multiple phone sources, the one with the lowest ID is used - * unless the data becomes too stale. + * An implementation of {@link TimeDetectorStrategy} that passes telephony and manual suggestions to + * {@link AlarmManager}. When there are multiple telephony sources, the one with the lowest ID is + * used unless the data becomes too stale. * * <p>Most public methods are marked synchronized to ensure thread safety around internal state. */ @@ -50,23 +50,26 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { private static final String LOG_TAG = "SimpleTimeDetectorStrategy"; /** A score value used to indicate "no score", either due to validation failure or age. */ - private static final int PHONE_INVALID_SCORE = -1; - /** The number of buckets phone suggestions can be put in by age. */ - private static final int PHONE_BUCKET_COUNT = 24; + private static final int TELEPHONY_INVALID_SCORE = -1; + /** The number of buckets telephony suggestions can be put in by age. */ + private static final int TELEPHONY_BUCKET_COUNT = 24; /** Each bucket is this size. All buckets are equally sized. */ @VisibleForTesting - static final int PHONE_BUCKET_SIZE_MILLIS = 60 * 60 * 1000; - /** Phone and network suggestions older than this value are considered too old to be used. */ + static final int TELEPHONY_BUCKET_SIZE_MILLIS = 60 * 60 * 1000; + /** + * Telephony and network suggestions older than this value are considered too old to be used. + */ @VisibleForTesting - static final long MAX_UTC_TIME_AGE_MILLIS = PHONE_BUCKET_COUNT * PHONE_BUCKET_SIZE_MILLIS; + static final long MAX_UTC_TIME_AGE_MILLIS = + TELEPHONY_BUCKET_COUNT * TELEPHONY_BUCKET_SIZE_MILLIS; - @IntDef({ ORIGIN_PHONE, ORIGIN_MANUAL, ORIGIN_NETWORK }) + @IntDef({ ORIGIN_TELEPHONY, ORIGIN_MANUAL, ORIGIN_NETWORK }) @Retention(RetentionPolicy.SOURCE) public @interface Origin {} /** Used when a time value originated from a telephony signal. */ @Origin - private static final int ORIGIN_PHONE = 1; + private static final int ORIGIN_TELEPHONY = 1; /** Used when a time value originated from a user / manual settings. */ @Origin @@ -83,7 +86,9 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { */ private static final long SYSTEM_CLOCK_PARANOIA_THRESHOLD_MILLIS = 2 * 1000; - /** The number of previous phone suggestions to keep for each ID (for use during debugging). */ + /** + * The number of previous telephony suggestions to keep for each ID (for use during debugging). + */ private static final int KEEP_SUGGESTION_HISTORY_SIZE = 30; // A log for changes made to the system clock and why. @@ -106,7 +111,7 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { * stable. */ @GuardedBy("this") - private final ArrayMapWithHistory<Integer, PhoneTimeSuggestion> mSuggestionBySlotIndex = + private final ArrayMapWithHistory<Integer, TelephonyTimeSuggestion> mSuggestionBySlotIndex = new ArrayMapWithHistory<>(KEEP_SUGGESTION_HISTORY_SIZE); @GuardedBy("this") @@ -144,7 +149,7 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { } @Override - public synchronized void suggestPhoneTime(@NonNull PhoneTimeSuggestion timeSuggestion) { + public synchronized void suggestTelephonyTime(@NonNull TelephonyTimeSuggestion timeSuggestion) { // Empty time suggestion means that telephony network connectivity has been lost. // The passage of time is relentless, and we don't expect our users to use a time machine, // so we can continue relying on previous suggestions when we lose connectivity. This is @@ -157,13 +162,13 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { // Perform validation / input filtering and record the validated suggestion against the // slotIndex. - if (!validateAndStorePhoneSuggestion(timeSuggestion)) { + if (!validateAndStoreTelephonySuggestion(timeSuggestion)) { return; } // Now perform auto time detection. The new suggestion may be used to modify the system // clock. - String reason = "New phone time suggested. timeSuggestion=" + timeSuggestion; + String reason = "New telephony time suggested. timeSuggestion=" + timeSuggestion; doAutoTimeDetection(reason); } @@ -201,7 +206,7 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { mTimeChangesLog.dump(ipw); ipw.decreaseIndent(); // level 2 - ipw.println("Phone suggestion history:"); + ipw.println("Telephony suggestion history:"); ipw.increaseIndent(); // level 2 mSuggestionBySlotIndex.dump(ipw); ipw.decreaseIndent(); // level 2 @@ -216,7 +221,8 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { } @GuardedBy("this") - private boolean validateAndStorePhoneSuggestion(@NonNull PhoneTimeSuggestion suggestion) { + private boolean validateAndStoreTelephonySuggestion( + @NonNull TelephonyTimeSuggestion suggestion) { TimestampedValue<Long> newUtcTime = suggestion.getUtcTime(); if (!validateSuggestionTime(newUtcTime, suggestion)) { // There's probably nothing useful we can do: elsewhere we assume that reference @@ -225,7 +231,7 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { } int slotIndex = suggestion.getSlotIndex(); - PhoneTimeSuggestion previousSuggestion = mSuggestionBySlotIndex.get(slotIndex); + TelephonyTimeSuggestion previousSuggestion = mSuggestionBySlotIndex.get(slotIndex); if (previousSuggestion != null) { // We can log / discard suggestions with obvious issues with the reference time clock. if (previousSuggestion.getUtcTime() == null @@ -241,7 +247,7 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { newUtcTime, previousSuggestion.getUtcTime()); if (referenceTimeDifference < 0) { // The reference time is before the previously received suggestion. Ignore it. - Slog.w(LOG_TAG, "Out of order phone suggestion received." + Slog.w(LOG_TAG, "Out of order telephony suggestion received." + " referenceTimeDifference=" + referenceTimeDifference + " previousSuggestion=" + previousSuggestion + " suggestion=" + suggestion); @@ -282,18 +288,18 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { // Android devices currently prioritize any telephony over network signals. There are // carrier compliance tests that would need to be changed before we could ignore NITZ or - // prefer NTP generally. This check is cheap on devices without phone hardware. - PhoneTimeSuggestion bestPhoneSuggestion = findBestPhoneSuggestion(); - if (bestPhoneSuggestion != null) { - final TimestampedValue<Long> newUtcTime = bestPhoneSuggestion.getUtcTime(); - String cause = "Found good phone suggestion." - + ", bestPhoneSuggestion=" + bestPhoneSuggestion + // prefer NTP generally. This check is cheap on devices without telephony hardware. + TelephonyTimeSuggestion bestTelephonySuggestion = findBestTelephonySuggestion(); + if (bestTelephonySuggestion != null) { + final TimestampedValue<Long> newUtcTime = bestTelephonySuggestion.getUtcTime(); + String cause = "Found good telephony suggestion." + + ", bestTelephonySuggestion=" + bestTelephonySuggestion + ", detectionReason=" + detectionReason; - setSystemClockIfRequired(ORIGIN_PHONE, newUtcTime, cause); + setSystemClockIfRequired(ORIGIN_TELEPHONY, newUtcTime, cause); return; } - // There is no good phone suggestion, try network. + // There is no good telephony suggestion, try network. NetworkTimeSuggestion networkSuggestion = findLatestValidNetworkSuggestion(); if (networkSuggestion != null) { final TimestampedValue<Long> newUtcTime = networkSuggestion.getUtcTime(); @@ -305,18 +311,18 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { } if (DBG) { - Slog.d(LOG_TAG, "Could not determine time: No best phone or network suggestion." + Slog.d(LOG_TAG, "Could not determine time: No best telephony or network suggestion." + " detectionReason=" + detectionReason); } } @GuardedBy("this") @Nullable - private PhoneTimeSuggestion findBestPhoneSuggestion() { + private TelephonyTimeSuggestion findBestTelephonySuggestion() { long elapsedRealtimeMillis = mCallback.elapsedRealtimeMillis(); - // Phone time suggestions are assumed to be derived from NITZ or NITZ-like signals. These - // have a number of limitations: + // Telephony time suggestions are assumed to be derived from NITZ or NITZ-like signals. + // These have a number of limitations: // 1) No guarantee of accuracy ("accuracy of the time information is in the order of // minutes") [1] // 2) No guarantee of regular signals ("dependent on the handset crossing radio network @@ -335,8 +341,8 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { // For simplicity, we try to value recency, then consistency of slotIndex. // // The heuristic works as follows: - // Recency: The most recent suggestion from each phone is scored. The score is based on a - // discrete age bucket, i.e. so signals received around the same time will be in the same + // Recency: The most recent suggestion from each slotIndex is scored. The score is based on + // a discrete age bucket, i.e. so signals received around the same time will be in the same // bucket, thus applying a loose reference time ordering. The suggestion with the highest // score is used. // Consistency: If there a multiple suggestions with the same score, the suggestion with the @@ -345,11 +351,11 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { // In the trivial case with a single ID this will just mean that the latest received // suggestion is used. - PhoneTimeSuggestion bestSuggestion = null; - int bestScore = PHONE_INVALID_SCORE; + TelephonyTimeSuggestion bestSuggestion = null; + int bestScore = TELEPHONY_INVALID_SCORE; for (int i = 0; i < mSuggestionBySlotIndex.size(); i++) { Integer slotIndex = mSuggestionBySlotIndex.keyAt(i); - PhoneTimeSuggestion candidateSuggestion = mSuggestionBySlotIndex.valueAt(i); + TelephonyTimeSuggestion candidateSuggestion = mSuggestionBySlotIndex.valueAt(i); if (candidateSuggestion == null) { // Unexpected - null suggestions should never be stored. Slog.w(LOG_TAG, "Latest suggestion unexpectedly null for slotIndex." @@ -362,8 +368,9 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { continue; } - int candidateScore = scorePhoneSuggestion(elapsedRealtimeMillis, candidateSuggestion); - if (candidateScore == PHONE_INVALID_SCORE) { + int candidateScore = + scoreTelephonySuggestion(elapsedRealtimeMillis, candidateSuggestion); + if (candidateScore == TELEPHONY_INVALID_SCORE) { // Expected: This means the suggestion is obviously invalid or just too old. continue; } @@ -384,8 +391,8 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { return bestSuggestion; } - private static int scorePhoneSuggestion( - long elapsedRealtimeMillis, @NonNull PhoneTimeSuggestion timeSuggestion) { + private static int scoreTelephonySuggestion( + long elapsedRealtimeMillis, @NonNull TelephonyTimeSuggestion timeSuggestion) { // Validate first. TimestampedValue<Long> utcTime = timeSuggestion.getUtcTime(); @@ -393,21 +400,21 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { Slog.w(LOG_TAG, "Existing suggestion found to be invalid " + " elapsedRealtimeMillis=" + elapsedRealtimeMillis + ", timeSuggestion=" + timeSuggestion); - return PHONE_INVALID_SCORE; + return TELEPHONY_INVALID_SCORE; } // The score is based on the age since receipt. Suggestions are bucketed so two // suggestions in the same bucket from different slotIndexs are scored the same. long ageMillis = elapsedRealtimeMillis - utcTime.getReferenceTimeMillis(); - // Turn the age into a discrete value: 0 <= bucketIndex < PHONE_BUCKET_COUNT. - int bucketIndex = (int) (ageMillis / PHONE_BUCKET_SIZE_MILLIS); - if (bucketIndex >= PHONE_BUCKET_COUNT) { - return PHONE_INVALID_SCORE; + // Turn the age into a discrete value: 0 <= bucketIndex < TELEPHONY_BUCKET_COUNT. + int bucketIndex = (int) (ageMillis / TELEPHONY_BUCKET_SIZE_MILLIS); + if (bucketIndex >= TELEPHONY_BUCKET_COUNT) { + return TELEPHONY_INVALID_SCORE; } // We want the lowest bucket index to have the highest score. 0 > score >= BUCKET_COUNT. - return PHONE_BUCKET_COUNT - bucketIndex; + return TELEPHONY_BUCKET_COUNT - bucketIndex; } /** Returns the latest, valid, network suggestion. Returns {@code null} if there isn't one. */ @@ -537,13 +544,13 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { } /** - * Returns the current best phone suggestion. Not intended for general use: it is used during - * tests to check strategy behavior. + * Returns the current best telephony suggestion. Not intended for general use: it is used + * during tests to check strategy behavior. */ @VisibleForTesting @Nullable - public synchronized PhoneTimeSuggestion findBestPhoneSuggestionForTests() { - return findBestPhoneSuggestion(); + public synchronized TelephonyTimeSuggestion findBestTelephonySuggestionForTests() { + return findBestTelephonySuggestion(); } /** @@ -561,7 +568,7 @@ public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy { */ @VisibleForTesting @Nullable - public synchronized PhoneTimeSuggestion getLatestPhoneSuggestion(int slotIndex) { + public synchronized TelephonyTimeSuggestion getLatestTelephonySuggestion(int slotIndex) { return mSuggestionBySlotIndex.get(slotIndex); } diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java index 381ee101e125..57b6ec9062a8 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java @@ -20,7 +20,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.timezonedetector.ITimeZoneDetectorService; import android.app.timezonedetector.ManualTimeZoneSuggestion; -import android.app.timezonedetector.PhoneTimeZoneSuggestion; +import android.app.timezonedetector.TelephonyTimeZoneSuggestion; import android.content.ContentResolver; import android.content.Context; import android.database.ContentObserver; @@ -101,11 +101,11 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub } @Override - public void suggestPhoneTimeZone(@NonNull PhoneTimeZoneSuggestion timeZoneSuggestion) { - enforceSuggestPhoneTimeZonePermission(); + public void suggestTelephonyTimeZone(@NonNull TelephonyTimeZoneSuggestion timeZoneSuggestion) { + enforceSuggestTelephonyTimeZonePermission(); Objects.requireNonNull(timeZoneSuggestion); - mHandler.post(() -> mTimeZoneDetectorStrategy.suggestPhoneTimeZone(timeZoneSuggestion)); + mHandler.post(() -> mTimeZoneDetectorStrategy.suggestTelephonyTimeZone(timeZoneSuggestion)); } @Override @@ -122,10 +122,10 @@ public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub mHandler.post(mTimeZoneDetectorStrategy::handleAutoTimeZoneDetectionChanged); } - private void enforceSuggestPhoneTimeZonePermission() { + private void enforceSuggestTelephonyTimeZonePermission() { mContext.enforceCallingPermission( - android.Manifest.permission.SUGGEST_PHONE_TIME_AND_ZONE, - "suggest phone time and time zone"); + android.Manifest.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE, + "suggest telephony time and time zone"); } private void enforceSuggestManualTimeZonePermission() { diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java index 1d439e93a1f7..0eb27cc5cff0 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java @@ -17,7 +17,7 @@ package com.android.server.timezonedetector; import android.annotation.NonNull; import android.app.timezonedetector.ManualTimeZoneSuggestion; -import android.app.timezonedetector.PhoneTimeZoneSuggestion; +import android.app.timezonedetector.TelephonyTimeZoneSuggestion; import java.io.PrintWriter; @@ -38,13 +38,13 @@ public interface TimeZoneDetectorStrategy { /** * Suggests a time zone for the device, or withdraws a previous suggestion if - * {@link PhoneTimeZoneSuggestion#getZoneId()} is {@code null}. The suggestion is scoped to a - * specific {@link PhoneTimeZoneSuggestion#getSlotIndex() phone}. - * See {@link PhoneTimeZoneSuggestion} for an explanation of the metadata associated with a + * {@link TelephonyTimeZoneSuggestion#getZoneId()} is {@code null}. The suggestion is scoped to + * a specific {@link TelephonyTimeZoneSuggestion#getSlotIndex() slotIndex}. + * See {@link TelephonyTimeZoneSuggestion} for an explanation of the metadata associated with a * suggestion. The strategy uses suggestions to decide whether to modify the device's time zone * setting and what to set it to. */ - void suggestPhoneTimeZone(@NonNull PhoneTimeZoneSuggestion suggestion); + void suggestTelephonyTimeZone(@NonNull TelephonyTimeZoneSuggestion suggestion); /** * Called when there has been a change to the automatic time zone detection setting. diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java index f85f9fe998a5..652dbe153680 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java @@ -15,17 +15,17 @@ */ package com.android.server.timezonedetector; -import static android.app.timezonedetector.PhoneTimeZoneSuggestion.MATCH_TYPE_EMULATOR_ZONE_ID; -import static android.app.timezonedetector.PhoneTimeZoneSuggestion.MATCH_TYPE_TEST_NETWORK_OFFSET_ONLY; -import static android.app.timezonedetector.PhoneTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS; -import static android.app.timezonedetector.PhoneTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET; -import static android.app.timezonedetector.PhoneTimeZoneSuggestion.QUALITY_SINGLE_ZONE; +import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.MATCH_TYPE_EMULATOR_ZONE_ID; +import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.MATCH_TYPE_TEST_NETWORK_OFFSET_ONLY; +import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS; +import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET; +import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_SINGLE_ZONE; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.timezonedetector.ManualTimeZoneSuggestion; -import android.app.timezonedetector.PhoneTimeZoneSuggestion; +import android.app.timezonedetector.TelephonyTimeZoneSuggestion; import android.content.Context; import android.util.LocalLog; import android.util.Slog; @@ -44,11 +44,11 @@ import java.util.Objects; * suggestions. Suggestions are acted on or ignored as needed, dependent on the current "auto time * zone detection" setting. * - * <p>For automatic detection it keeps track of the most recent suggestion from each phone it uses - * the best suggestion based on a scoring algorithm. If several phones provide the same score then - * the phone with the lowest numeric ID "wins". If the situation changes and it is no longer - * possible to be confident about the time zone, phones must submit an empty suggestion in order to - * "withdraw" their previous suggestion. + * <p>For automatic detection, it keeps track of the most recent telephony suggestion from each + * slotIndex and it uses the best suggestion based on a scoring algorithm. If several slotIndexes + * provide the same score then the slotIndex with the lowest numeric value "wins". If the situation + * changes and it is no longer possible to be confident about the time zone, slotIndexes must have + * an empty suggestion submitted in order to "withdraw" their previous suggestion. * * <p>Most public methods are marked synchronized to ensure thread safety around internal state. */ @@ -91,28 +91,28 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat private static final String LOG_TAG = "TimeZoneDetectorStrategy"; private static final boolean DBG = false; - @IntDef({ ORIGIN_PHONE, ORIGIN_MANUAL }) + @IntDef({ ORIGIN_TELEPHONY, ORIGIN_MANUAL }) @Retention(RetentionPolicy.SOURCE) public @interface Origin {} /** Used when a time value originated from a telephony signal. */ @Origin - private static final int ORIGIN_PHONE = 1; + private static final int ORIGIN_TELEPHONY = 1; /** Used when a time value originated from a user / manual settings. */ @Origin private static final int ORIGIN_MANUAL = 2; /** - * The abstract score for an empty or invalid phone suggestion. + * The abstract score for an empty or invalid telephony suggestion. * - * Used to score phone suggestions where there is no zone. + * Used to score telephony suggestions where there is no zone. */ @VisibleForTesting - public static final int PHONE_SCORE_NONE = 0; + public static final int TELEPHONY_SCORE_NONE = 0; /** - * The abstract score for a low quality phone suggestion. + * The abstract score for a low quality telephony suggestion. * * Used to score suggestions where: * The suggested zone ID is one of several possibilities, and the possibilities have different @@ -121,10 +121,10 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat * You would have to be quite desperate to want to use this choice. */ @VisibleForTesting - public static final int PHONE_SCORE_LOW = 1; + public static final int TELEPHONY_SCORE_LOW = 1; /** - * The abstract score for a medium quality phone suggestion. + * The abstract score for a medium quality telephony suggestion. * * Used for: * The suggested zone ID is one of several possibilities but at least the possibilities have the @@ -132,36 +132,38 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat * switch to DST at the wrong time and (for example) their calendar events. */ @VisibleForTesting - public static final int PHONE_SCORE_MEDIUM = 2; + public static final int TELEPHONY_SCORE_MEDIUM = 2; /** - * The abstract score for a high quality phone suggestion. + * The abstract score for a high quality telephony suggestion. * * Used for: * The suggestion was for one zone ID and the answer was unambiguous and likely correct given * the info available. */ @VisibleForTesting - public static final int PHONE_SCORE_HIGH = 3; + public static final int TELEPHONY_SCORE_HIGH = 3; /** - * The abstract score for a highest quality phone suggestion. + * The abstract score for a highest quality telephony suggestion. * * Used for: * Suggestions that must "win" because they constitute test or emulator zone ID. */ @VisibleForTesting - public static final int PHONE_SCORE_HIGHEST = 4; + public static final int TELEPHONY_SCORE_HIGHEST = 4; /** - * The threshold at which phone suggestions are good enough to use to set the device's time + * The threshold at which telephony suggestions are good enough to use to set the device's time * zone. */ @VisibleForTesting - public static final int PHONE_SCORE_USAGE_THRESHOLD = PHONE_SCORE_MEDIUM; + public static final int TELEPHONY_SCORE_USAGE_THRESHOLD = TELEPHONY_SCORE_MEDIUM; - /** The number of previous phone suggestions to keep for each ID (for use during debugging). */ - private static final int KEEP_PHONE_SUGGESTION_HISTORY_SIZE = 30; + /** + * The number of previous telephony suggestions to keep for each ID (for use during debugging). + */ + private static final int KEEP_TELEPHONY_SUGGESTION_HISTORY_SIZE = 30; @NonNull private final Callback mCallback; @@ -174,13 +176,14 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat private final LocalLog mTimeZoneChangesLog = new LocalLog(30, false /* useLocalTimestamps */); /** - * A mapping from slotIndex to a phone time zone suggestion. We typically expect one or two - * mappings: devices will have a small number of telephony devices and slotIndexs are assumed to - * be stable. + * A mapping from slotIndex to a telephony time zone suggestion. We typically expect one or two + * mappings: devices will have a small number of telephony devices and slotIndexes are assumed + * to be stable. */ @GuardedBy("this") - private ArrayMapWithHistory<Integer, QualifiedPhoneTimeZoneSuggestion> mSuggestionBySlotIndex = - new ArrayMapWithHistory<>(KEEP_PHONE_SUGGESTION_HISTORY_SIZE); + private ArrayMapWithHistory<Integer, QualifiedTelephonyTimeZoneSuggestion> + mSuggestionBySlotIndex = + new ArrayMapWithHistory<>(KEEP_TELEPHONY_SUGGESTION_HISTORY_SIZE); /** * Creates a new instance of {@link TimeZoneDetectorStrategyImpl}. @@ -205,42 +208,43 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat } @Override - public synchronized void suggestPhoneTimeZone(@NonNull PhoneTimeZoneSuggestion suggestion) { + public synchronized void suggestTelephonyTimeZone( + @NonNull TelephonyTimeZoneSuggestion suggestion) { if (DBG) { - Slog.d(LOG_TAG, "Phone suggestion received. newSuggestion=" + suggestion); + Slog.d(LOG_TAG, "Telephony suggestion received. newSuggestion=" + suggestion); } Objects.requireNonNull(suggestion); // Score the suggestion. - int score = scorePhoneSuggestion(suggestion); - QualifiedPhoneTimeZoneSuggestion scoredSuggestion = - new QualifiedPhoneTimeZoneSuggestion(suggestion, score); + int score = scoreTelephonySuggestion(suggestion); + QualifiedTelephonyTimeZoneSuggestion scoredSuggestion = + new QualifiedTelephonyTimeZoneSuggestion(suggestion, score); // Store the suggestion against the correct slotIndex. mSuggestionBySlotIndex.put(suggestion.getSlotIndex(), scoredSuggestion); // Now perform auto time zone detection. The new suggestion may be used to modify the time // zone setting. - String reason = "New phone time suggested. suggestion=" + suggestion; + String reason = "New telephony time suggested. suggestion=" + suggestion; doAutoTimeZoneDetection(reason); } - private static int scorePhoneSuggestion(@NonNull PhoneTimeZoneSuggestion suggestion) { + private static int scoreTelephonySuggestion(@NonNull TelephonyTimeZoneSuggestion suggestion) { int score; if (suggestion.getZoneId() == null) { - score = PHONE_SCORE_NONE; + score = TELEPHONY_SCORE_NONE; } else if (suggestion.getMatchType() == MATCH_TYPE_TEST_NETWORK_OFFSET_ONLY || suggestion.getMatchType() == MATCH_TYPE_EMULATOR_ZONE_ID) { // Handle emulator / test cases : These suggestions should always just be used. - score = PHONE_SCORE_HIGHEST; + score = TELEPHONY_SCORE_HIGHEST; } else if (suggestion.getQuality() == QUALITY_SINGLE_ZONE) { - score = PHONE_SCORE_HIGH; + score = TELEPHONY_SCORE_HIGH; } else if (suggestion.getQuality() == QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET) { // The suggestion may be wrong, but at least the offset should be correct. - score = PHONE_SCORE_MEDIUM; + score = TELEPHONY_SCORE_MEDIUM; } else if (suggestion.getQuality() == QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS) { // The suggestion has a good chance of being wrong. - score = PHONE_SCORE_LOW; + score = TELEPHONY_SCORE_LOW; } else { throw new AssertionError(); } @@ -248,9 +252,9 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat } /** - * Finds the best available time zone suggestion from all phones. If it is high-enough quality - * and automatic time zone detection is enabled then it will be set on the device. The outcome - * can be that this strategy becomes / remains un-opinionated and nothing is set. + * Finds the best available time zone suggestion from all slotIndexes. If it is high-enough + * quality and automatic time zone detection is enabled then it will be set on the device. The + * outcome can be that this strategy becomes / remains un-opinionated and nothing is set. */ @GuardedBy("this") private void doAutoTimeZoneDetection(@NonNull String detectionReason) { @@ -259,35 +263,37 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat return; } - QualifiedPhoneTimeZoneSuggestion bestPhoneSuggestion = findBestPhoneSuggestion(); + QualifiedTelephonyTimeZoneSuggestion bestTelephonySuggestion = + findBestTelephonySuggestion(); // Work out what to do with the best suggestion. - if (bestPhoneSuggestion == null) { - // There is no phone suggestion available at all. Become un-opinionated. + if (bestTelephonySuggestion == null) { + // There is no telephony suggestion available at all. Become un-opinionated. if (DBG) { - Slog.d(LOG_TAG, "Could not determine time zone: No best phone suggestion." + Slog.d(LOG_TAG, "Could not determine time zone: No best telephony suggestion." + " detectionReason=" + detectionReason); } return; } // Special case handling for uninitialized devices. This should only happen once. - String newZoneId = bestPhoneSuggestion.suggestion.getZoneId(); + String newZoneId = bestTelephonySuggestion.suggestion.getZoneId(); if (newZoneId != null && !mCallback.isDeviceTimeZoneInitialized()) { String cause = "Device has no time zone set. Attempting to set the device to the best" + " available suggestion." - + " bestPhoneSuggestion=" + bestPhoneSuggestion + + " bestTelephonySuggestion=" + bestTelephonySuggestion + ", detectionReason=" + detectionReason; Slog.i(LOG_TAG, cause); - setDeviceTimeZoneIfRequired(ORIGIN_PHONE, newZoneId, cause); + setDeviceTimeZoneIfRequired(ORIGIN_TELEPHONY, newZoneId, cause); return; } - boolean suggestionGoodEnough = bestPhoneSuggestion.score >= PHONE_SCORE_USAGE_THRESHOLD; + boolean suggestionGoodEnough = + bestTelephonySuggestion.score >= TELEPHONY_SCORE_USAGE_THRESHOLD; if (!suggestionGoodEnough) { if (DBG) { Slog.d(LOG_TAG, "Best suggestion not good enough." - + " bestPhoneSuggestion=" + bestPhoneSuggestion + + " bestTelephonySuggestion=" + bestTelephonySuggestion + ", detectionReason=" + detectionReason); } return; @@ -297,16 +303,16 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat // zone ID. if (newZoneId == null) { Slog.w(LOG_TAG, "Empty zone suggestion scored higher than expected. This is an error:" - + " bestPhoneSuggestion=" + bestPhoneSuggestion + + " bestTelephonySuggestion=" + bestTelephonySuggestion + " detectionReason=" + detectionReason); return; } - String zoneId = bestPhoneSuggestion.suggestion.getZoneId(); + String zoneId = bestTelephonySuggestion.suggestion.getZoneId(); String cause = "Found good suggestion." - + ", bestPhoneSuggestion=" + bestPhoneSuggestion + + ", bestTelephonySuggestion=" + bestTelephonySuggestion + ", detectionReason=" + detectionReason; - setDeviceTimeZoneIfRequired(ORIGIN_PHONE, zoneId, cause); + setDeviceTimeZoneIfRequired(ORIGIN_TELEPHONY, zoneId, cause); } @GuardedBy("this") @@ -372,15 +378,15 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat @GuardedBy("this") @Nullable - private QualifiedPhoneTimeZoneSuggestion findBestPhoneSuggestion() { - QualifiedPhoneTimeZoneSuggestion bestSuggestion = null; + private QualifiedTelephonyTimeZoneSuggestion findBestTelephonySuggestion() { + QualifiedTelephonyTimeZoneSuggestion bestSuggestion = null; - // Iterate over the latest QualifiedPhoneTimeZoneSuggestion objects received for each phone - // and find the best. Note that we deliberately do not look at age: the caller can + // Iterate over the latest QualifiedTelephonyTimeZoneSuggestion objects received for each + // slotIndex and find the best. Note that we deliberately do not look at age: the caller can // rate-limit so age is not a strong indicator of confidence. Instead, the callers are // expected to withdraw suggestions they no longer have confidence in. for (int i = 0; i < mSuggestionBySlotIndex.size(); i++) { - QualifiedPhoneTimeZoneSuggestion candidateSuggestion = + QualifiedTelephonyTimeZoneSuggestion candidateSuggestion = mSuggestionBySlotIndex.valueAt(i); if (candidateSuggestion == null) { // Unexpected @@ -404,13 +410,13 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat } /** - * Returns the current best phone suggestion. Not intended for general use: it is used during - * tests to check strategy behavior. + * Returns the current best telephony suggestion. Not intended for general use: it is used + * during tests to check strategy behavior. */ @VisibleForTesting @Nullable - public synchronized QualifiedPhoneTimeZoneSuggestion findBestPhoneSuggestionForTests() { - return findBestPhoneSuggestion(); + public synchronized QualifiedTelephonyTimeZoneSuggestion findBestTelephonySuggestionForTests() { + return findBestTelephonySuggestion(); } @Override @@ -447,7 +453,7 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat mTimeZoneChangesLog.dump(ipw); ipw.decreaseIndent(); // level 2 - ipw.println("Phone suggestion history:"); + ipw.println("Telephony suggestion history:"); ipw.increaseIndent(); // level 2 mSuggestionBySlotIndex.dump(ipw); ipw.decreaseIndent(); // level 2 @@ -459,18 +465,19 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat * A method used to inspect strategy state during tests. Not intended for general use. */ @VisibleForTesting - public synchronized QualifiedPhoneTimeZoneSuggestion getLatestPhoneSuggestion(int slotIndex) { + public synchronized QualifiedTelephonyTimeZoneSuggestion getLatestTelephonySuggestion( + int slotIndex) { return mSuggestionBySlotIndex.get(slotIndex); } /** - * A {@link PhoneTimeZoneSuggestion} with additional qualifying metadata. + * A {@link TelephonyTimeZoneSuggestion} with additional qualifying metadata. */ @VisibleForTesting - public static class QualifiedPhoneTimeZoneSuggestion { + public static class QualifiedTelephonyTimeZoneSuggestion { @VisibleForTesting - public final PhoneTimeZoneSuggestion suggestion; + public final TelephonyTimeZoneSuggestion suggestion; /** * The score the suggestion has been given. This can be used to rank against other @@ -480,7 +487,8 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat public final int score; @VisibleForTesting - public QualifiedPhoneTimeZoneSuggestion(PhoneTimeZoneSuggestion suggestion, int score) { + public QualifiedTelephonyTimeZoneSuggestion( + TelephonyTimeZoneSuggestion suggestion, int score) { this.suggestion = suggestion; this.score = score; } @@ -493,7 +501,7 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat if (o == null || getClass() != o.getClass()) { return false; } - QualifiedPhoneTimeZoneSuggestion that = (QualifiedPhoneTimeZoneSuggestion) o; + QualifiedTelephonyTimeZoneSuggestion that = (QualifiedTelephonyTimeZoneSuggestion) o; return score == that.score && suggestion.equals(that.suggestion); } @@ -505,7 +513,7 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat @Override public String toString() { - return "QualifiedPhoneTimeZoneSuggestion{" + return "QualifiedTelephonyTimeZoneSuggestion{" + "suggestion=" + suggestion + ", score=" + score + '}'; diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java index 4f010d5e0f2c..e3b1152cd7b7 100755 --- a/services/core/java/com/android/server/tv/TvInputManagerService.java +++ b/services/core/java/com/android/server/tv/TvInputManagerService.java @@ -383,36 +383,38 @@ public final class TvInputManagerService extends SystemService { if (mCurrentUserId == userId) { return; } - UserState userState = mUserStates.get(mCurrentUserId); - List<SessionState> sessionStatesToRelease = new ArrayList<>(); - for (SessionState sessionState : userState.sessionStateMap.values()) { - if (sessionState.session != null && !sessionState.isRecordingSession) { - sessionStatesToRelease.add(sessionState); + if (mUserStates.contains(mCurrentUserId)) { + UserState userState = mUserStates.get(mCurrentUserId); + List<SessionState> sessionStatesToRelease = new ArrayList<>(); + for (SessionState sessionState : userState.sessionStateMap.values()) { + if (sessionState.session != null && !sessionState.isRecordingSession) { + sessionStatesToRelease.add(sessionState); + } } - } - for (SessionState sessionState : sessionStatesToRelease) { - try { - sessionState.session.release(); - } catch (RemoteException e) { - Slog.e(TAG, "error in release", e); + for (SessionState sessionState : sessionStatesToRelease) { + try { + sessionState.session.release(); + } catch (RemoteException e) { + Slog.e(TAG, "error in release", e); + } + clearSessionAndNotifyClientLocked(sessionState); } - clearSessionAndNotifyClientLocked(sessionState); - } - for (Iterator<ComponentName> it = userState.serviceStateMap.keySet().iterator(); - it.hasNext(); ) { - ComponentName component = it.next(); - ServiceState serviceState = userState.serviceStateMap.get(component); - if (serviceState != null && serviceState.sessionTokens.isEmpty()) { - if (serviceState.callback != null) { - try { - serviceState.service.unregisterCallback(serviceState.callback); - } catch (RemoteException e) { - Slog.e(TAG, "error in unregisterCallback", e); + for (Iterator<ComponentName> it = userState.serviceStateMap.keySet().iterator(); + it.hasNext(); ) { + ComponentName component = it.next(); + ServiceState serviceState = userState.serviceStateMap.get(component); + if (serviceState != null && serviceState.sessionTokens.isEmpty()) { + if (serviceState.callback != null) { + try { + serviceState.service.unregisterCallback(serviceState.callback); + } catch (RemoteException e) { + Slog.e(TAG, "error in unregisterCallback", e); + } } + mContext.unbindService(serviceState.connection); + it.remove(); } - mContext.unbindService(serviceState.connection); - it.remove(); } } @@ -490,6 +492,10 @@ public final class TvInputManagerService extends SystemService { userState.mainSessionToken = null; mUserStates.remove(userId); + + if (userId == mCurrentUserId) { + switchUser(UserHandle.USER_SYSTEM); + } } } diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java index d5961a881c36..d380f8cd7337 100644 --- a/services/core/java/com/android/server/wm/ActivityStack.java +++ b/services/core/java/com/android/server/wm/ActivityStack.java @@ -356,6 +356,8 @@ class ActivityStack extends Task implements BoundsAnimationTarget { // TODO(task-hierarchy): remove when tiles can be actual parents TaskTile mTile = null; + private int mLastTaskOrganizerWindowingMode = -1; + private final Handler mHandler; private class ActivityStackHandler extends Handler { @@ -794,6 +796,13 @@ class ActivityStack extends Task implements BoundsAnimationTarget { } final int windowingMode = getWindowingMode(); + if (windowingMode == mLastTaskOrganizerWindowingMode) { + // If our windowing mode hasn't actually changed, then just stick + // with our old organizer. This lets us implement the semantic + // where SysUI can continue to manage it's old tasks + // while CTS temporarily takes over the registration. + return; + } /* * Different windowing modes may be managed by different task organizers. If * getTaskOrganizer returns null, we still call setTaskOrganizer to @@ -802,6 +811,7 @@ class ActivityStack extends Task implements BoundsAnimationTarget { final ITaskOrganizer org = mWmService.mAtmService.mTaskOrganizerController.getTaskOrganizer(windowingMode); setTaskOrganizer(org); + mLastTaskOrganizerWindowingMode = windowingMode; } @Override diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java index 75d87edbc437..f35ba9e69ed7 100644 --- a/services/core/java/com/android/server/wm/ActivityStartController.java +++ b/services/core/java/com/android/server/wm/ActivityStartController.java @@ -386,6 +386,8 @@ public class ActivityStartController { } else { callingPid = callingUid = -1; } + final int filterCallingUid = ActivityStarter.computeResolveFilterUid( + callingUid, realCallingUid, UserHandle.USER_NULL); final SparseArray<String> startingUidPkgs = new SparseArray<>(); final long origId = Binder.clearCallingIdentity(); try { @@ -408,9 +410,7 @@ public class ActivityStartController { // Collect information about the target of the Intent. ActivityInfo aInfo = mSupervisor.resolveActivity(intent, resolvedTypes[i], - 0 /* startFlags */, null /* profilerInfo */, userId, - ActivityStarter.computeResolveFilterUid( - callingUid, realCallingUid, UserHandle.USER_NULL)); + 0 /* startFlags */, null /* profilerInfo */, userId, filterCallingUid); aInfo = mService.mAmInternal.getActivityInfoForUser(aInfo, userId); if (aInfo != null) { @@ -457,6 +457,7 @@ public class ActivityStartController { Slog.wtf(TAG, sb.toString()); } + final IBinder sourceResultTo = resultTo; final ActivityRecord[] outActivity = new ActivityRecord[1]; // Lock the loop to ensure the activities launched in a sequence. synchronized (mService.mGlobalLock) { @@ -470,7 +471,18 @@ public class ActivityStartController { } return startResult; } - resultTo = outActivity[0] != null ? outActivity[0].appToken : null; + final ActivityRecord started = outActivity[0]; + if (started != null && started.getUid() == filterCallingUid) { + // Only the started activity which has the same uid as the source caller can + // be the caller of next activity. + resultTo = started.appToken; + } else { + resultTo = sourceResultTo; + // Different apps not adjacent to the caller are forced to be new task. + if (i < starters.length - 1) { + starters[i + 1].getIntent().addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + } + } } } } finally { diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 9e3292b59402..c7270f257923 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -2493,7 +2493,6 @@ class ActivityStarter { return this; } - @VisibleForTesting Intent getIntent() { return mRequest.intent; } diff --git a/services/core/java/com/android/server/wm/BLASTSyncEngine.java b/services/core/java/com/android/server/wm/BLASTSyncEngine.java new file mode 100644 index 000000000000..94decc792fd3 --- /dev/null +++ b/services/core/java/com/android/server/wm/BLASTSyncEngine.java @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Slog; +import android.view.ITaskOrganizer; +import android.view.SurfaceControl; + +import java.util.HashMap; + +/** + * Utility class for collecting and merging transactions from various sources asynchronously. + * For example to use to synchronously resize all the children of a window container + * 1. Open a new sync set, and pass the listener that will be invoked + * int id startSyncSet(TransactionReadyListener) + * the returned ID will be eventually passed to the TransactionReadyListener in combination + * with the prepared transaction. You also use it to refer to the operation in future steps. + * 2. Ask each child to participate: + * addToSyncSet(int id, WindowContainer wc) + * if the child thinks it will be affected by a configuration change (a.k.a. has a visible + * window in its sub hierarchy, then we will increment a counter of expected callbacks + * At this point the containers hierarchy will redirect pendingTransaction and sub hierarchy + * updates in to the sync engine. + * 3. Apply your configuration changes to the window containers. + * 4. Tell the engine that the sync set is ready + * setReady(int id) + * 5. If there were no sub windows anywhere in the hierarchy to wait on, then + * transactionReady is immediately invoked, otherwise all the windows are poked + * to redraw and to deliver a buffer to WMS#finishDrawing. + * Once all this drawing is complete the combined transaction of all the buffers + * and pending transaction hierarchy changes will be delivered to the TransactionReadyListener + */ +class BLASTSyncEngine { + private static final String TAG = "BLASTSyncEngine"; + + interface TransactionReadyListener { + void transactionReady(int mSyncId, SurfaceControl.Transaction mergedTransaction); + }; + + // Holds state associated with a single synchronous set of operations. + class SyncState implements TransactionReadyListener { + int mSyncId; + SurfaceControl.Transaction mMergedTransaction; + int mRemainingTransactions; + TransactionReadyListener mListener; + boolean mReady = false; + + private void tryFinish() { + if (mRemainingTransactions == 0 && mReady) { + mListener.transactionReady(mSyncId, mMergedTransaction); + mPendingSyncs.remove(mSyncId); + } + } + + public void transactionReady(int mSyncId, SurfaceControl.Transaction mergedTransaction) { + mRemainingTransactions--; + mMergedTransaction.merge(mergedTransaction); + tryFinish(); + } + + void setReady() { + mReady = true; + tryFinish(); + } + + boolean addToSync(WindowContainer wc) { + if (wc.prepareForSync(this, mSyncId)) { + mRemainingTransactions++; + return true; + } + return false; + } + + SyncState(TransactionReadyListener l, int id) { + mListener = l; + mSyncId = id; + mMergedTransaction = new SurfaceControl.Transaction(); + mRemainingTransactions = 0; + } + }; + + int mNextSyncId = 0; + + final HashMap<Integer, SyncState> mPendingSyncs = new HashMap(); + + BLASTSyncEngine() { + } + + int startSyncSet(TransactionReadyListener listener) { + final int id = mNextSyncId++; + final SyncState s = new SyncState(listener, id); + mPendingSyncs.put(id, s); + return id; + } + + boolean addToSyncSet(int id, WindowContainer wc) { + final SyncState st = mPendingSyncs.get(id); + return st.addToSync(wc); + } + + // TODO(b/148476626): TIMEOUTS! + void setReady(int id) { + final SyncState st = mPendingSyncs.get(id); + st.setReady(); + } +} diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java index efe79b36d645..e71371a477f7 100644 --- a/services/core/java/com/android/server/wm/DisplayRotation.java +++ b/services/core/java/com/android/server/wm/DisplayRotation.java @@ -526,7 +526,9 @@ public class DisplayRotation { mService.mH.removeCallbacks(mDisplayRotationHandlerTimeout); mIsWaitingForRemoteRotation = false; mDisplayContent.sendNewConfiguration(); - mService.mAtmService.mTaskOrganizerController.applyContainerTransaction(t); + if (t != null) { + mService.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null); + } } } diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index 44a6fc936961..096541f57ba5 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -38,6 +38,7 @@ import android.util.ArraySet; import android.util.Slog; import android.view.ITaskOrganizer; import android.view.IWindowContainer; +import android.view.SurfaceControl; import android.view.WindowContainerTransaction; import com.android.internal.util.function.pooled.PooledConsumer; @@ -53,7 +54,8 @@ import java.util.WeakHashMap; * Stores the TaskOrganizers associated with a given windowing mode and * their associated state. */ -class TaskOrganizerController extends ITaskOrganizerController.Stub { +class TaskOrganizerController extends ITaskOrganizerController.Stub + implements BLASTSyncEngine.TransactionReadyListener { private static final String TAG = "TaskOrganizerController"; /** Flag indicating that an applied transaction may have effected lifecycle */ @@ -74,11 +76,10 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { @Override public void binderDied() { synchronized (mGlobalLock) { - final TaskOrganizerState state = mTaskOrganizerStates.get(mTaskOrganizer); - for (int i = 0; i < state.mOrganizedTasks.size(); i++) { - state.mOrganizedTasks.get(i).taskOrganizerDied(); - } - mTaskOrganizerStates.remove(mTaskOrganizer); + final TaskOrganizerState state = + mTaskOrganizerStates.get(mTaskOrganizer.asBinder()); + state.releaseTasks(); + mTaskOrganizerStates.remove(mTaskOrganizer.asBinder()); if (mTaskOrganizersForWindowingMode.get(mWindowingMode) == mTaskOrganizer) { mTaskOrganizersForWindowingMode.remove(mWindowingMode); } @@ -89,32 +90,84 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { class TaskOrganizerState { ITaskOrganizer mOrganizer; DeathRecipient mDeathRecipient; + int mWindowingMode; ArrayList<Task> mOrganizedTasks = new ArrayList<>(); + // Save the TaskOrganizer which we replaced registration for + // so it can be re-registered if we unregister. + TaskOrganizerState mReplacementFor; + boolean mDisposed = false; + + + TaskOrganizerState(ITaskOrganizer organizer, int windowingMode, + TaskOrganizerState replacing) { + mOrganizer = organizer; + mDeathRecipient = new DeathRecipient(organizer, windowingMode); + try { + organizer.asBinder().linkToDeath(mDeathRecipient, 0); + } catch (RemoteException e) { + Slog.e(TAG, "TaskOrganizer failed to register death recipient"); + } + mWindowingMode = windowingMode; + mReplacementFor = replacing; + } + void addTask(Task t) { mOrganizedTasks.add(t); + try { + mOrganizer.taskAppeared(t.getTaskInfo()); + } catch (Exception e) { + Slog.e(TAG, "Exception sending taskAppeared callback" + e); + } } void removeTask(Task t) { + try { + mOrganizer.taskVanished(t.getRemoteToken()); + } catch (Exception e) { + Slog.e(TAG, "Exception sending taskVanished callback" + e); + } mOrganizedTasks.remove(t); } - TaskOrganizerState(ITaskOrganizer organizer, DeathRecipient deathRecipient) { - mOrganizer = organizer; - mDeathRecipient = deathRecipient; + void dispose() { + mDisposed = true; + releaseTasks(); + handleReplacement(); + } + + void releaseTasks() { + for (int i = mOrganizedTasks.size() - 1; i >= 0; i--) { + final Task t = mOrganizedTasks.get(i); + t.taskOrganizerDied(); + removeTask(t); + } + } + + void handleReplacement() { + if (mReplacementFor != null && !mReplacementFor.mDisposed) { + mTaskOrganizersForWindowingMode.put(mWindowingMode, mReplacementFor); + } + } + + void unlinkDeath() { + mDisposed = true; + mOrganizer.asBinder().unlinkToDeath(mDeathRecipient, 0); } }; final HashMap<Integer, TaskOrganizerState> mTaskOrganizersForWindowingMode = new HashMap(); - final HashMap<ITaskOrganizer, TaskOrganizerState> mTaskOrganizerStates = new HashMap(); + final HashMap<IBinder, TaskOrganizerState> mTaskOrganizerStates = new HashMap(); final HashMap<Integer, ITaskOrganizer> mTaskOrganizersByPendingSyncId = new HashMap(); private final WeakHashMap<Task, RunningTaskInfo> mLastSentTaskInfos = new WeakHashMap<>(); private final ArrayList<Task> mPendingTaskInfoChanges = new ArrayList<>(); + private final BLASTSyncEngine mBLASTSyncEngine = new BLASTSyncEngine(); + final ActivityTaskManagerService mService; RunningTaskInfo mTmpTaskInfo; @@ -128,17 +181,10 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { mService.mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, func); } - private void clearIfNeeded(int windowingMode) { - final TaskOrganizerState oldState = mTaskOrganizersForWindowingMode.get(windowingMode); - if (oldState != null) { - oldState.mOrganizer.asBinder().unlinkToDeath(oldState.mDeathRecipient, 0); - } - } - /** * Register a TaskOrganizer to manage tasks as they enter the given windowing mode. * If there was already a TaskOrganizer for this windowing mode it will be evicted - * and receive taskVanished callbacks in the process. + * but will continue to organize it's existing tasks. */ @Override public void registerTaskOrganizer(ITaskOrganizer organizer, int windowingMode) { @@ -153,24 +199,25 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { final long origId = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { - clearIfNeeded(windowingMode); - DeathRecipient dr = new DeathRecipient(organizer, windowingMode); - try { - organizer.asBinder().linkToDeath(dr, 0); - } catch (RemoteException e) { - Slog.e(TAG, "TaskOrganizer failed to register death recipient"); - } - - final TaskOrganizerState state = new TaskOrganizerState(organizer, dr); + final TaskOrganizerState state = new TaskOrganizerState(organizer, windowingMode, + mTaskOrganizersForWindowingMode.get(windowingMode)); mTaskOrganizersForWindowingMode.put(windowingMode, state); - - mTaskOrganizerStates.put(organizer, state); + mTaskOrganizerStates.put(organizer.asBinder(), state); } } finally { Binder.restoreCallingIdentity(origId); } } + void unregisterTaskOrganizer(ITaskOrganizer organizer) { + final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder()); + state.unlinkDeath(); + if (mTaskOrganizersForWindowingMode.get(state.mWindowingMode) == state) { + mTaskOrganizersForWindowingMode.remove(state.mWindowingMode); + } + state.dispose(); + } + ITaskOrganizer getTaskOrganizer(int windowingMode) { final TaskOrganizerState state = mTaskOrganizersForWindowingMode.get(windowingMode); if (state == null) { @@ -179,35 +226,13 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { return state.mOrganizer; } - private void sendTaskAppeared(ITaskOrganizer organizer, Task task) { - try { - organizer.taskAppeared(task.getTaskInfo()); - } catch (Exception e) { - Slog.e(TAG, "Exception sending taskAppeared callback" + e); - } - } - - private void sendTaskVanished(ITaskOrganizer organizer, Task task) { - try { - organizer.taskVanished(task.getRemoteToken()); - } catch (Exception e) { - Slog.e(TAG, "Exception sending taskVanished callback" + e); - } - } - void onTaskAppeared(ITaskOrganizer organizer, Task task) { - TaskOrganizerState state = mTaskOrganizerStates.get(organizer); - + final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder()); state.addTask(task); - sendTaskAppeared(organizer, task); } void onTaskVanished(ITaskOrganizer organizer, Task task) { - final TaskOrganizerState state = mTaskOrganizerStates.get(organizer); - sendTaskVanished(organizer, task); - - // This could trigger TaskAppeared for other tasks in the same stack so make sure - // we do this AFTER sending taskVanished. + final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder()); state.removeTask(task); } @@ -408,15 +433,35 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } @Override - public void applyContainerTransaction(WindowContainerTransaction t) { + public int applyContainerTransaction(WindowContainerTransaction t, ITaskOrganizer organizer) { enforceStackPermission("applyContainerTransaction()"); + int syncId = -1; if (t == null) { - return; + throw new IllegalArgumentException( + "Null transaction passed to applyContainerTransaction"); } long ident = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { int effects = 0; + + /** + * If organizer is non-null we are looking to synchronize this transaction + * by collecting all the results in to a SurfaceFlinger transaction and + * then delivering that to the given organizers transaction ready callback. + * See {@link BLASTSyncEngine} for the details of the operation. But at + * a high level we create a sync operation with a given ID and an associated + * organizer. Then we notify each WindowContainer in this WindowContainer + * transaction that it is participating in a sync operation with that + * ID. Once everything is notified we tell the BLASTSyncEngine + * "setSyncReady" which means that we have added everything + * to the set. At any point after this, all the WindowContainers + * will eventually finish applying their changes and notify the + * BLASTSyncEngine which will deliver the Transaction to the organizer. + */ + if (organizer != null) { + syncId = startSyncWithOrganizer(organizer); + } mService.deferWindowLayout(); try { ArraySet<WindowContainer> haveConfigChanges = new ArraySet<>(); @@ -429,11 +474,15 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { entry.getKey()).getContainer(); int containerEffect = applyWindowContainerChange(wc, entry.getValue()); effects |= containerEffect; + // Lifecycle changes will trigger ensureConfig for everything. if ((effects & TRANSACT_EFFECTS_LIFECYCLE) == 0 && (containerEffect & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) { haveConfigChanges.add(wc); } + if (syncId >= 0) { + mBLASTSyncEngine.addToSyncSet(syncId, wc); + } } if ((effects & TRANSACT_EFFECTS_LIFECYCLE) != 0) { // Already calls ensureActivityConfig @@ -454,10 +503,38 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } } finally { mService.continueWindowLayout(); + if (syncId >= 0) { + setSyncReady(syncId); + } } } } finally { Binder.restoreCallingIdentity(ident); } + return syncId; + } + + @Override + public void transactionReady(int id, SurfaceControl.Transaction sc) { + final ITaskOrganizer organizer = mTaskOrganizersByPendingSyncId.get(id); + if (organizer == null) { + Slog.e(TAG, "Got transaction complete for unexpected ID"); + } + try { + organizer.transactionReady(id, sc); + } catch (RemoteException e) { + } + + mTaskOrganizersByPendingSyncId.remove(id); + } + + int startSyncWithOrganizer(ITaskOrganizer organizer) { + int id = mBLASTSyncEngine.startSyncSet(this); + mTaskOrganizersByPendingSyncId.put(id, organizer); + return id; + } + + void setSyncReady(int id) { + mBLASTSyncEngine.setReady(id); } } diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 86723153c5f6..9acb660967cb 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -94,7 +94,8 @@ import java.util.function.Predicate; * changes are made to this class. */ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<E> - implements Comparable<WindowContainer>, Animatable { + implements Comparable<WindowContainer>, Animatable, + BLASTSyncEngine.TransactionReadyListener { private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowContainer" : TAG_WM; @@ -260,6 +261,12 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< */ RemoteToken mRemoteToken = null; + BLASTSyncEngine mBLASTSyncEngine = new BLASTSyncEngine(); + SurfaceControl.Transaction mBLASTSyncTransaction = new SurfaceControl.Transaction(); + boolean mUsingBLASTSyncTransaction = false; + BLASTSyncEngine.TransactionReadyListener mWaitingListener; + int mWaitingSyncId; + WindowContainer(WindowManagerService wms) { mWmService = wms; mPendingTransaction = wms.mTransactionFactory.get(); @@ -1837,6 +1844,10 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< @Override public Transaction getPendingTransaction() { + if (mUsingBLASTSyncTransaction) { + return mBLASTSyncTransaction; + } + final DisplayContent displayContent = getDisplayContent(); if (displayContent != null && displayContent != this) { return displayContent.getPendingTransaction(); @@ -2316,4 +2327,38 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< return sb.toString(); } } + + @Override + public void transactionReady(int mSyncId, SurfaceControl.Transaction mergedTransaction) { + mergedTransaction.merge(mBLASTSyncTransaction); + mUsingBLASTSyncTransaction = false; + + mWaitingListener.transactionReady(mWaitingSyncId, mergedTransaction); + + mWaitingListener = null; + mWaitingSyncId = -1; + } + + boolean prepareForSync(BLASTSyncEngine.TransactionReadyListener waitingListener, + int waitingId) { + boolean willSync = false; + if (!isVisible()) { + return willSync; + } + mUsingBLASTSyncTransaction = true; + + int localId = mBLASTSyncEngine.startSyncSet(this); + for (int i = 0; i < mChildren.size(); i++) { + final WindowContainer child = mChildren.get(i); + willSync = mBLASTSyncEngine.addToSyncSet(localId, child) | willSync; + } + + // Make sure to set these before we call setReady in case the sync was a no-op + mWaitingSyncId = waitingId; + mWaitingListener = waitingListener; + + mBLASTSyncEngine.setReady(localId); + + return willSync; + } } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 6e1f46bb5e42..633098566b1b 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -2505,7 +2505,7 @@ public class WindowManagerService extends IWindowManager.Stub WindowState win = windowForClientLocked(session, client, false); ProtoLog.d(WM_DEBUG_ADD_REMOVE, "finishDrawingWindow: %s mDrawState=%s", win, (win != null ? win.mWinAnimator.drawStateToString() : "null")); - if (win != null && win.mWinAnimator.finishDrawingLocked(postDrawTransaction)) { + if (win != null && win.finishDrawing(postDrawTransaction)) { if ((win.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0) { win.getDisplayContent().pendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; @@ -5687,6 +5687,12 @@ public class WindowManagerService extends IWindowManager.Stub @Override public void setForceShowSystemBars(boolean show) { + boolean isAutomotive = mContext.getPackageManager().hasSystemFeature( + PackageManager.FEATURE_AUTOMOTIVE); + if (!isAutomotive) { + throw new UnsupportedOperationException("Force showing system bars is only supported" + + "for Automotive use cases."); + } if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("Caller does not hold permission " diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 42e5bbc1c1ee..b336f8d1a1ac 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -5689,4 +5689,30 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP SurfaceControl getDeferTransactionBarrier() { return mWinAnimator.getDeferTransactionBarrier(); } + + @Override + boolean prepareForSync(BLASTSyncEngine.TransactionReadyListener waitingListener, + int waitingId) { + // TODO(b/148871522): Support child window + mWaitingListener = waitingListener; + mWaitingSyncId = waitingId; + mUsingBLASTSyncTransaction = true; + return true; + } + + boolean finishDrawing(SurfaceControl.Transaction postDrawTransaction) { + if (!mUsingBLASTSyncTransaction) { + return mWinAnimator.finishDrawingLocked(postDrawTransaction); + } + if (postDrawTransaction == null) { + postDrawTransaction = new SurfaceControl.Transaction(); + } + postDrawTransaction.merge(mBLASTSyncTransaction); + mWaitingListener.transactionReady(mWaitingSyncId, postDrawTransaction); + mUsingBLASTSyncTransaction = false; + + mWaitingSyncId = 0; + mWaitingListener = null; + return mWinAnimator.finishDrawingLocked(null); + } } diff --git a/services/core/java/com/android/server/wm/utils/DisplayRotationUtil.java b/services/core/java/com/android/server/wm/utils/DisplayRotationUtil.java index 9f307bb0f98f..59abaab0f795 100644 --- a/services/core/java/com/android/server/wm/utils/DisplayRotationUtil.java +++ b/services/core/java/com/android/server/wm/utils/DisplayRotationUtil.java @@ -59,7 +59,7 @@ public class DisplayRotationUtil { } /** - * Compute bounds after rotating teh screen. + * Compute bounds after rotating the screen. * * @param bounds Bounds before the rotation. The array must contain exactly 4 non-null elements. * @param rotation rotation constant defined in android.view.Surface. diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index 390068e1fa75..812bc438246f 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -109,6 +109,7 @@ cc_defaults { "libinputservice", "libprotobuf-cpp-lite", "libprotoutil", + "libstatshidl", "libstatspull", "libstatssocket", "libstatslog", @@ -155,6 +156,7 @@ cc_defaults { "android.hardware.vr@1.0", "android.frameworks.schedulerservice@1.0", "android.frameworks.sensorservice@1.0", + "android.frameworks.stats@1.0", "android.system.suspend@1.0", "service.incremental", "suspend_control_aidl_interface-cpp", diff --git a/services/core/jni/com_android_server_SystemServer.cpp b/services/core/jni/com_android_server_SystemServer.cpp index 67254b811ee0..279ea4b9a790 100644 --- a/services/core/jni/com_android_server_SystemServer.cpp +++ b/services/core/jni/com_android_server_SystemServer.cpp @@ -29,6 +29,7 @@ #include <schedulerservice/SchedulingPolicyService.h> #include <sensorservice/SensorService.h> #include <sensorservicehidl/SensorManager.h> +#include <stats/StatsHal.h> #include <bionic/malloc.h> #include <bionic/reserved_signals.h> @@ -59,6 +60,8 @@ static void android_server_SystemServer_startHidlServices(JNIEnv* env, jobject / using ::android::frameworks::schedulerservice::V1_0::implementation::SchedulingPolicyService; using ::android::frameworks::sensorservice::V1_0::ISensorManager; using ::android::frameworks::sensorservice::V1_0::implementation::SensorManager; + using ::android::frameworks::stats::V1_0::IStats; + using ::android::frameworks::stats::V1_0::implementation::StatsHal; using ::android::hardware::configureRpcThreadpool; status_t err; @@ -75,6 +78,10 @@ static void android_server_SystemServer_startHidlServices(JNIEnv* env, jobject / sp<ISchedulingPolicyService> schedulingService = new SchedulingPolicyService(); err = schedulingService->registerAsService(); ALOGE_IF(err != OK, "Cannot register %s: %d", ISchedulingPolicyService::descriptor, err); + + sp<IStats> statsHal = new StatsHal(); + err = statsHal->registerAsService(); + ALOGE_IF(err != OK, "Cannot register %s: %d", IStats::descriptor, err); } static void android_server_SystemServer_initZygoteChildHeapProfiling(JNIEnv* /* env */, diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 28e44f14d839..553ec4201cc2 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -11598,6 +11598,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { millis, "DevicePolicyManagerService: setTime"); mInjector.binderWithCleanCallingIdentity( () -> mInjector.getTimeDetector().suggestManualTime(manualTimeSuggestion)); + + DevicePolicyEventLogger + .createEvent(DevicePolicyEnums.SET_TIME) + .setAdmin(who) + .write(); return true; } @@ -11614,6 +11619,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { timeZone, "DevicePolicyManagerService: setTimeZone"); mInjector.binderWithCleanCallingIdentity(() -> mInjector.getTimeZoneDetector().suggestManualTimeZone(manualTimeZoneSuggestion)); + + DevicePolicyEventLogger + .createEvent(DevicePolicyEnums.SET_TIME_ZONE) + .setAdmin(who) + .write(); return true; } diff --git a/services/people/java/com/android/server/people/data/CallLogQueryHelper.java b/services/people/java/com/android/server/people/data/CallLogQueryHelper.java index d825b6b2bd8f..45e0aac24ca7 100644 --- a/services/people/java/com/android/server/people/data/CallLogQueryHelper.java +++ b/services/people/java/com/android/server/people/data/CallLogQueryHelper.java @@ -107,7 +107,7 @@ class CallLogQueryHelper { } @Event.EventType int eventType = CALL_TYPE_TO_EVENT_TYPE.get(callType); Event event = new Event.Builder(date, eventType) - .setCallDetails(new Event.CallDetails(durationSeconds)) + .setDurationSeconds((int) durationSeconds) .build(); mEventConsumer.accept(phoneNumber, event); return true; diff --git a/services/people/java/com/android/server/people/data/ConversationStore.java b/services/people/java/com/android/server/people/data/ConversationStore.java index f17e1b91cb5d..364992181f75 100644 --- a/services/people/java/com/android/server/people/data/ConversationStore.java +++ b/services/people/java/com/android/server/people/data/ConversationStore.java @@ -40,6 +40,9 @@ class ConversationStore { // Phone Number -> Shortcut ID private final Map<String, String> mPhoneNumberToShortcutIdMap = new ArrayMap<>(); + // Notification Channel ID -> Shortcut ID + private final Map<String, String> mNotifChannelIdToShortcutIdMap = new ArrayMap<>(); + void addOrUpdate(@NonNull ConversationInfo conversationInfo) { mConversationInfoMap.put(conversationInfo.getShortcutId(), conversationInfo); @@ -57,6 +60,11 @@ class ConversationStore { if (phoneNumber != null) { mPhoneNumberToShortcutIdMap.put(phoneNumber, conversationInfo.getShortcutId()); } + + String notifChannelId = conversationInfo.getNotificationChannelId(); + if (notifChannelId != null) { + mNotifChannelIdToShortcutIdMap.put(notifChannelId, conversationInfo.getShortcutId()); + } } void deleteConversation(@NonNull String shortcutId) { @@ -79,6 +87,11 @@ class ConversationStore { if (phoneNumber != null) { mPhoneNumberToShortcutIdMap.remove(phoneNumber); } + + String notifChannelId = conversationInfo.getNotificationChannelId(); + if (notifChannelId != null) { + mNotifChannelIdToShortcutIdMap.remove(notifChannelId); + } } void forAllConversations(@NonNull Consumer<ConversationInfo> consumer) { @@ -106,4 +119,9 @@ class ConversationStore { ConversationInfo getConversationByPhoneNumber(@NonNull String phoneNumber) { return getConversation(mPhoneNumberToShortcutIdMap.get(phoneNumber)); } + + @Nullable + ConversationInfo getConversationByNotificationChannelId(@NonNull String notifChannelId) { + return getConversation(mNotifChannelIdToShortcutIdMap.get(notifChannelId)); + } } diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java index 79503f797318..7fdcf42c6364 100644 --- a/services/people/java/com/android/server/people/data/DataManager.java +++ b/services/people/java/com/android/server/people/data/DataManager.java @@ -24,8 +24,6 @@ import android.app.Notification; import android.app.Person; import android.app.prediction.AppTarget; import android.app.prediction.AppTargetEvent; -import android.app.usage.UsageEvents; -import android.app.usage.UsageStatsManagerInternal; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; @@ -69,6 +67,7 @@ import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.function.BiConsumer; import java.util.function.Consumer; +import java.util.function.Function; /** * A class manages the lifecycle of the conversations and associated data, and exposes the methods @@ -96,7 +95,6 @@ public class DataManager { private final ContentObserver mMmsSmsContentObserver; private ShortcutServiceInternal mShortcutServiceInternal; - private UsageStatsManagerInternal mUsageStatsManagerInternal; private ShortcutManager mShortcutManager; private UserManager mUserManager; @@ -118,7 +116,6 @@ public class DataManager { /** Initialization. Called when the system services are up running. */ public void initialize() { mShortcutServiceInternal = LocalServices.getService(ShortcutServiceInternal.class); - mUsageStatsManagerInternal = LocalServices.getService(UsageStatsManagerInternal.class); mShortcutManager = mContext.getSystemService(ShortcutManager.class); mUserManager = mContext.getSystemService(UserManager.class); @@ -293,13 +290,14 @@ public class DataManager { | ShortcutQuery.FLAG_MATCH_PINNED | ShortcutQuery.FLAG_MATCH_PINNED_BY_ANY_LAUNCHER; return mShortcutServiceInternal.getShortcuts( mInjector.getCallingUserId(), /*callingPackage=*/ PLATFORM_PACKAGE_NAME, - /*changedSince=*/ 0, packageName, shortcutIds, /*componentName=*/ null, queryFlags, - userId, MY_PID, MY_UID); + /*changedSince=*/ 0, packageName, shortcutIds, /*locusIds=*/ null, + /*componentName=*/ null, queryFlags, userId, MY_PID, MY_UID); } private void forAllUnlockedUsers(Consumer<UserData> consumer) { for (int i = 0; i < mUserDataArray.size(); i++) { - UserData userData = mUserDataArray.get(i); + int userId = mUserDataArray.keyAt(i); + UserData userData = mUserDataArray.get(userId); if (userData.isUnlocked()) { consumer.accept(userData); } @@ -385,36 +383,6 @@ public class DataManager { } @VisibleForTesting - @WorkerThread - void queryUsageStatsService(@UserIdInt int userId, long currentTime, long lastQueryTime) { - UsageEvents usageEvents = mUsageStatsManagerInternal.queryEventsForUser( - userId, lastQueryTime, currentTime, false, false); - if (usageEvents == null) { - return; - } - while (usageEvents.hasNextEvent()) { - UsageEvents.Event e = new UsageEvents.Event(); - usageEvents.getNextEvent(e); - - String packageName = e.getPackageName(); - PackageData packageData = getPackage(packageName, userId); - if (packageData == null) { - continue; - } - if (e.getEventType() == UsageEvents.Event.SHORTCUT_INVOCATION) { - String shortcutId = e.getShortcutId(); - if (packageData.getConversationStore().getConversation(shortcutId) != null) { - EventHistoryImpl eventHistory = - packageData.getEventStore().getOrCreateShortcutEventHistory( - shortcutId); - eventHistory.addEvent( - new Event(e.getTimeStamp(), Event.TYPE_SHORTCUT_INVOCATION)); - } - } - } - } - - @VisibleForTesting ContentObserver getContactsContentObserverForTesting(@UserIdInt int userId) { return mContactsContentObservers.get(userId); } @@ -611,19 +579,20 @@ public class DataManager { */ private class UsageStatsQueryRunnable implements Runnable { - private final int mUserId; - private long mLastQueryTime; + private final UsageStatsQueryHelper mUsageStatsQueryHelper; + private long mLastEventTimestamp; private UsageStatsQueryRunnable(int userId) { - mUserId = userId; - mLastQueryTime = System.currentTimeMillis() - QUERY_EVENTS_MAX_AGE_MS; + mUsageStatsQueryHelper = mInjector.createUsageStatsQueryHelper(userId, + (packageName) -> getPackage(packageName, userId)); + mLastEventTimestamp = System.currentTimeMillis() - QUERY_EVENTS_MAX_AGE_MS; } @Override public void run() { - long currentTime = System.currentTimeMillis(); - queryUsageStatsService(mUserId, currentTime, mLastQueryTime); - mLastQueryTime = currentTime; + if (mUsageStatsQueryHelper.querySince(mLastEventTimestamp)) { + mLastEventTimestamp = mUsageStatsQueryHelper.getLastEventTimestamp(); + } } } @@ -679,6 +648,11 @@ public class DataManager { return new SmsQueryHelper(context, eventConsumer); } + UsageStatsQueryHelper createUsageStatsQueryHelper(@UserIdInt int userId, + Function<String, PackageData> packageDataGetter) { + return new UsageStatsQueryHelper(userId, packageDataGetter); + } + int getCallingUserId() { return Binder.getCallingUserHandle().getIdentifier(); } diff --git a/services/people/java/com/android/server/people/data/Event.java b/services/people/java/com/android/server/people/data/Event.java index c2364a295e30..81411c00db51 100644 --- a/services/people/java/com/android/server/people/data/Event.java +++ b/services/people/java/com/android/server/people/data/Event.java @@ -18,14 +18,12 @@ package com.android.server.people.data; import android.annotation.IntDef; import android.annotation.NonNull; -import android.annotation.Nullable; import android.text.format.DateFormat; import android.util.ArraySet; -import com.android.internal.util.Preconditions; - import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Objects; import java.util.Set; /** An event representing the interaction with a specific conversation or app. */ @@ -55,6 +53,8 @@ public class Event { public static final int TYPE_CALL_MISSED = 12; + public static final int TYPE_IN_APP_CONVERSATION = 13; + @IntDef(prefix = { "TYPE_" }, value = { TYPE_SHORTCUT_INVOCATION, TYPE_NOTIFICATION_POSTED, @@ -68,6 +68,7 @@ public class Event { TYPE_CALL_OUTGOING, TYPE_CALL_INCOMING, TYPE_CALL_MISSED, + TYPE_IN_APP_CONVERSATION, }) @Retention(RetentionPolicy.SOURCE) public @interface EventType {} @@ -95,6 +96,7 @@ public class Event { CALL_EVENT_TYPES.add(TYPE_CALL_MISSED); ALL_EVENT_TYPES.add(TYPE_SHORTCUT_INVOCATION); + ALL_EVENT_TYPES.add(TYPE_IN_APP_CONVERSATION); ALL_EVENT_TYPES.addAll(NOTIFICATION_EVENT_TYPES); ALL_EVENT_TYPES.addAll(SHARE_EVENT_TYPES); ALL_EVENT_TYPES.addAll(SMS_EVENT_TYPES); @@ -105,18 +107,18 @@ public class Event { private final int mType; - private final CallDetails mCallDetails; + private final int mDurationSeconds; Event(long timestamp, @EventType int type) { mTimestamp = timestamp; mType = type; - mCallDetails = null; + mDurationSeconds = 0; } private Event(@NonNull Builder builder) { mTimestamp = builder.mTimestamp; mType = builder.mType; - mCallDetails = builder.mCallDetails; + mDurationSeconds = builder.mDurationSeconds; } public long getTimestamp() { @@ -128,12 +130,35 @@ public class Event { } /** - * Gets the {@link CallDetails} of the event. It is only available if the event type is one of - * {@code CALL_EVENT_TYPES}, otherwise, it's always {@code null}. + * Gets the duration of the event in seconds. It is only available for these events: + * <ul> + * <li>{@link #TYPE_CALL_INCOMING} + * <li>{@link #TYPE_CALL_OUTGOING} + * <li>{@link #TYPE_IN_APP_CONVERSATION} + * </ul> + * <p>For the other event types, it always returns {@code 0}. */ - @Nullable - public CallDetails getCallDetails() { - return mCallDetails; + public int getDurationSeconds() { + return mDurationSeconds; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof Event)) { + return false; + } + Event other = (Event) obj; + return mTimestamp == other.mTimestamp + && mType == other.mType + && mDurationSeconds == other.mDurationSeconds; + } + + @Override + public int hashCode() { + return Objects.hash(mTimestamp, mType, mDurationSeconds); } @Override @@ -142,32 +167,13 @@ public class Event { sb.append("Event {"); sb.append("timestamp=").append(DateFormat.format("yyyy-MM-dd HH:mm:ss", mTimestamp)); sb.append(", type=").append(mType); - if (mCallDetails != null) { - sb.append(", callDetails=").append(mCallDetails); + if (mDurationSeconds > 0) { + sb.append(", durationSeconds=").append(mDurationSeconds); } sb.append("}"); return sb.toString(); } - /** Type-specific details of a call event. */ - public static class CallDetails { - - private final long mDurationSeconds; - - CallDetails(long durationSeconds) { - mDurationSeconds = durationSeconds; - } - - public long getDurationSeconds() { - return mDurationSeconds; - } - - @Override - public String toString() { - return "CallDetails {durationSeconds=" + mDurationSeconds + "}"; - } - } - /** Builder class for {@link Event} objects. */ static class Builder { @@ -175,16 +181,15 @@ public class Event { private final int mType; - private CallDetails mCallDetails; + private int mDurationSeconds; Builder(long timestamp, @EventType int type) { mTimestamp = timestamp; mType = type; } - Builder setCallDetails(CallDetails callDetails) { - Preconditions.checkArgument(CALL_EVENT_TYPES.contains(mType)); - mCallDetails = callDetails; + Builder setDurationSeconds(int durationSeconds) { + mDurationSeconds = durationSeconds; return this; } diff --git a/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java b/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java new file mode 100644 index 000000000000..4e37f47149b4 --- /dev/null +++ b/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2020 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.people.data; + +import android.annotation.NonNull; +import android.annotation.UserIdInt; +import android.app.usage.UsageEvents; +import android.app.usage.UsageStatsManagerInternal; +import android.content.ComponentName; +import android.content.LocusId; +import android.text.format.DateUtils; +import android.util.ArrayMap; + +import com.android.server.LocalServices; + +import java.util.Map; +import java.util.function.Function; + +/** A helper class that queries {@link UsageStatsManagerInternal}. */ +class UsageStatsQueryHelper { + + private final UsageStatsManagerInternal mUsageStatsManagerInternal; + private final int mUserId; + private final Function<String, PackageData> mPackageDataGetter; + // Activity name -> Conversation start event (LOCUS_ID_SET) + private final Map<ComponentName, UsageEvents.Event> mConvoStartEvents = new ArrayMap<>(); + private long mLastEventTimestamp; + + /** + * @param userId The user whose events are to be queried. + * @param packageDataGetter The function to get {@link PackageData} with a package name. + */ + UsageStatsQueryHelper(@UserIdInt int userId, + Function<String, PackageData> packageDataGetter) { + mUsageStatsManagerInternal = LocalServices.getService(UsageStatsManagerInternal.class); + mUserId = userId; + mPackageDataGetter = packageDataGetter; + } + + /** + * Queries {@link UsageStatsManagerInternal} for the recent events occurred since {@code + * sinceTime} and adds the derived {@link Event}s into the corresponding package's event store, + * + * @return true if the query runs successfully and at least one event is found. + */ + boolean querySince(long sinceTime) { + UsageEvents usageEvents = mUsageStatsManagerInternal.queryEventsForUser( + mUserId, sinceTime, System.currentTimeMillis(), false, false); + if (usageEvents == null) { + return false; + } + boolean hasEvents = false; + while (usageEvents.hasNextEvent()) { + UsageEvents.Event e = new UsageEvents.Event(); + usageEvents.getNextEvent(e); + + hasEvents = true; + mLastEventTimestamp = Math.max(mLastEventTimestamp, e.getTimeStamp()); + String packageName = e.getPackageName(); + PackageData packageData = mPackageDataGetter.apply(packageName); + if (packageData == null) { + continue; + } + switch (e.getEventType()) { + case UsageEvents.Event.SHORTCUT_INVOCATION: + addEventByShortcutId(packageData, e.getShortcutId(), + new Event(e.getTimeStamp(), Event.TYPE_SHORTCUT_INVOCATION)); + break; + case UsageEvents.Event.NOTIFICATION_INTERRUPTION: + addEventByNotificationChannelId(packageData, e.getNotificationChannelId(), + new Event(e.getTimeStamp(), Event.TYPE_NOTIFICATION_POSTED)); + break; + case UsageEvents.Event.LOCUS_ID_SET: + onInAppConversationEnded(packageData, e); + LocusId locusId = e.getLocusId() != null ? new LocusId(e.getLocusId()) : null; + if (locusId != null) { + if (packageData.getConversationStore().getConversationByLocusId(locusId) + != null) { + ComponentName activityName = + new ComponentName(packageName, e.getClassName()); + mConvoStartEvents.put(activityName, e); + } + } + break; + case UsageEvents.Event.ACTIVITY_PAUSED: + case UsageEvents.Event.ACTIVITY_STOPPED: + case UsageEvents.Event.ACTIVITY_DESTROYED: + onInAppConversationEnded(packageData, e); + break; + } + } + return hasEvents; + } + + long getLastEventTimestamp() { + return mLastEventTimestamp; + } + + private void onInAppConversationEnded(@NonNull PackageData packageData, + @NonNull UsageEvents.Event endEvent) { + ComponentName activityName = + new ComponentName(endEvent.getPackageName(), endEvent.getClassName()); + UsageEvents.Event startEvent = mConvoStartEvents.remove(activityName); + if (startEvent == null || startEvent.getTimeStamp() >= endEvent.getTimeStamp()) { + return; + } + long durationMillis = endEvent.getTimeStamp() - startEvent.getTimeStamp(); + Event event = new Event.Builder(startEvent.getTimeStamp(), Event.TYPE_IN_APP_CONVERSATION) + .setDurationSeconds((int) (durationMillis / DateUtils.SECOND_IN_MILLIS)) + .build(); + addEventByLocusId(packageData, new LocusId(startEvent.getLocusId()), event); + } + + private void addEventByShortcutId(PackageData packageData, String shortcutId, Event event) { + if (packageData.getConversationStore().getConversation(shortcutId) == null) { + return; + } + EventHistoryImpl eventHistory = packageData.getEventStore().getOrCreateShortcutEventHistory( + shortcutId); + eventHistory.addEvent(event); + } + + private void addEventByLocusId(PackageData packageData, LocusId locusId, Event event) { + if (packageData.getConversationStore().getConversationByLocusId(locusId) == null) { + return; + } + EventHistoryImpl eventHistory = packageData.getEventStore().getOrCreateLocusEventHistory( + locusId); + eventHistory.addEvent(event); + } + + private void addEventByNotificationChannelId(PackageData packageData, + String notificationChannelId, Event event) { + ConversationInfo conversationInfo = + packageData.getConversationStore().getConversationByNotificationChannelId( + notificationChannelId); + if (conversationInfo == null) { + return; + } + EventHistoryImpl eventHistory = packageData.getEventStore().getOrCreateShortcutEventHistory( + conversationInfo.getShortcutId()); + eventHistory.addEvent(event); + } +} diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp index 3d9f11ff6d2f..339ff6b8b526 100644 --- a/services/tests/mockingservicestests/Android.bp +++ b/services/tests/mockingservicestests/Android.bp @@ -22,6 +22,7 @@ android_test { "services.net", "service-jobscheduler", "service-permission", + "service-blobstore", "androidx.test.runner", "mockito-target-extended-minus-junit4", "platform-test-annotations", diff --git a/services/tests/mockingservicestests/AndroidManifest.xml b/services/tests/mockingservicestests/AndroidManifest.xml index 0e24b0314b2d..44eb8285c7db 100644 --- a/services/tests/mockingservicestests/AndroidManifest.xml +++ b/services/tests/mockingservicestests/AndroidManifest.xml @@ -17,6 +17,8 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.frameworks.mockingservicestests"> + <uses-permission android:name="android.permission.LOG_COMPAT_CHANGE"/> + <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG"/> <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" /> <uses-permission android:name="android.permission.HARDWARE_TEST"/> <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" /> diff --git a/services/tests/servicestests/assets/AppOpsUpgradeTest/appops-unversioned.xml b/services/tests/mockingservicestests/assets/AppOpsUpgradeTest/appops-unversioned.xml index a37d84f7a08c..a37d84f7a08c 100644 --- a/services/tests/servicestests/assets/AppOpsUpgradeTest/appops-unversioned.xml +++ b/services/tests/mockingservicestests/assets/AppOpsUpgradeTest/appops-unversioned.xml diff --git a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java index 155de3bc0266..2d5fa237f6b7 100644 --- a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java @@ -101,9 +101,8 @@ public class AppOpsServiceTest { private StaticMockitoSession mMockingSession; private void setupAppOpsService() { - mAppOpsService = new AppOpsService(mAppOpsFile, mHandler); + mAppOpsService = new AppOpsService(mAppOpsFile, mHandler, spy(sContext)); mAppOpsService.mHistoricalRegistry.systemReady(sContext.getContentResolver()); - mAppOpsService.mContext = spy(sContext); // Always approve all permission checks doNothing().when(mAppOpsService.mContext).enforcePermission(anyString(), anyInt(), diff --git a/services/tests/servicestests/src/com/android/server/appop/AppOpsUpgradeTest.java b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java index 66d2baba2909..e48b67167cdf 100644 --- a/services/tests/servicestests/src/com/android/server/appop/AppOpsUpgradeTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 The Android Open Source Project + * Copyright (C) 2020 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. @@ -19,9 +19,17 @@ package com.android.server.appop; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.nullable; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; import android.app.AppOpsManager; import android.content.Context; +import android.content.pm.PackageManager; import android.content.res.AssetManager; import android.os.Handler; import android.os.HandlerThread; @@ -133,10 +141,24 @@ public class AppOpsUpgradeTest { AppOpsDataParser parser = new AppOpsDataParser(mAppOpsFile); assertTrue(parser.parse()); assertEquals(AppOpsDataParser.NO_VERSION, parser.mVersion); - AppOpsService testService = new AppOpsService(mAppOpsFile, mHandler); // trigger upgrade + + // Use mock context and package manager to fake permision package manager calls. + Context testContext = spy(mContext); + + // Pretent everybody has all permissions + doNothing().when(testContext).enforcePermission(anyString(), anyInt(), anyInt(), + nullable(String.class)); + + PackageManager testPM = mock(PackageManager.class); + when(testContext.getPackageManager()).thenReturn(testPM); + + // Stub out package calls to disable AppOpsService#updatePermissionRevokedCompat + when(testPM.getPackagesForUid(anyInt())).thenReturn(null); + + AppOpsService testService = spy( + new AppOpsService(mAppOpsFile, mHandler, testContext)); // trigger upgrade assertSameModes(testService.mUidStates, AppOpsManager.OP_RUN_IN_BACKGROUND, AppOpsManager.OP_RUN_ANY_IN_BACKGROUND); - testService.mContext = mContext; mHandler.removeCallbacks(testService.mWriteRunner); testService.writeState(); assertTrue(parser.parse()); diff --git a/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java new file mode 100644 index 000000000000..16dde4203e91 --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java @@ -0,0 +1,342 @@ +/* + * Copyright 2020 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.blob; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; +import static com.android.server.blob.BlobStoreConfig.SESSION_EXPIRY_TIMEOUT_MILLIS; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +import android.app.blob.BlobHandle; +import android.content.Context; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.UserHandle; +import android.platform.test.annotations.Presubmit; +import android.util.ArrayMap; +import android.util.LongSparseArray; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.server.blob.BlobStoreManagerService.Injector; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoSession; +import org.mockito.quality.Strictness; + +import java.io.File; + +@RunWith(AndroidJUnit4.class) +@SmallTest +@Presubmit +public class BlobStoreManagerServiceTest { + private Context mContext; + private Handler mHandler; + private BlobStoreManagerService mService; + + private MockitoSession mMockitoSession; + + @Mock + private File mBlobsDir; + + private LongSparseArray<BlobStoreSession> mUserSessions; + private ArrayMap<BlobHandle, BlobMetadata> mUserBlobs; + + private static final String TEST_PKG1 = "com.example1"; + private static final String TEST_PKG2 = "com.example2"; + private static final String TEST_PKG3 = "com.example3"; + + private static final int TEST_UID1 = 10001; + private static final int TEST_UID2 = 10002; + private static final int TEST_UID3 = 10003; + + @Before + public void setUp() { + // Share classloader to allow package private access. + System.setProperty("dexmaker.share_classloader", "true"); + + mMockitoSession = mockitoSession() + .initMocks(this) + .strictness(Strictness.LENIENT) + .mockStatic(BlobStoreConfig.class) + .startMocking(); + + doReturn(mBlobsDir).when(() -> BlobStoreConfig.getBlobsDir()); + doReturn(true).when(mBlobsDir).exists(); + doReturn(new File[0]).when(mBlobsDir).listFiles(); + + mContext = InstrumentationRegistry.getTargetContext(); + mHandler = new TestHandler(Looper.getMainLooper()); + mService = new BlobStoreManagerService(mContext, new TestInjector()); + mUserSessions = new LongSparseArray<>(); + mUserBlobs = new ArrayMap<>(); + + mService.addUserSessionsForTest(mUserSessions, UserHandle.myUserId()); + mService.addUserBlobsForTest(mUserBlobs, UserHandle.myUserId()); + } + + @After + public void tearDown() { + if (mMockitoSession != null) { + mMockitoSession.finishMocking(); + } + } + + @Test + public void testHandlePackageRemoved() throws Exception { + // Setup sessions + final File sessionFile1 = mock(File.class); + final long sessionId1 = 11; + final BlobStoreSession session1 = createBlobStoreSessionMock(TEST_PKG1, TEST_UID1, + sessionId1, sessionFile1); + mUserSessions.append(sessionId1, session1); + + final File sessionFile2 = mock(File.class); + final long sessionId2 = 25; + final BlobStoreSession session2 = createBlobStoreSessionMock(TEST_PKG2, TEST_UID2, + sessionId2, sessionFile2); + mUserSessions.append(sessionId2, session2); + + final File sessionFile3 = mock(File.class); + final long sessionId3 = 37; + final BlobStoreSession session3 = createBlobStoreSessionMock(TEST_PKG3, TEST_UID3, + sessionId3, sessionFile3); + mUserSessions.append(sessionId3, session3); + + final File sessionFile4 = mock(File.class); + final long sessionId4 = 48; + final BlobStoreSession session4 = createBlobStoreSessionMock(TEST_PKG1, TEST_UID1, + sessionId4, sessionFile4); + mUserSessions.append(sessionId4, session4); + + // Setup blobs + final long blobId1 = 978; + final File blobFile1 = mock(File.class); + final BlobHandle blobHandle1 = BlobHandle.createWithSha256("digest1".getBytes(), + "label1", System.currentTimeMillis(), "tag1"); + final BlobMetadata blobMetadata1 = createBlobMetadataMock(blobId1, blobFile1, true); + mUserBlobs.put(blobHandle1, blobMetadata1); + + final long blobId2 = 347; + final File blobFile2 = mock(File.class); + final BlobHandle blobHandle2 = BlobHandle.createWithSha256("digest2".getBytes(), + "label2", System.currentTimeMillis(), "tag2"); + final BlobMetadata blobMetadata2 = createBlobMetadataMock(blobId2, blobFile2, false); + mUserBlobs.put(blobHandle2, blobMetadata2); + + mService.addKnownIdsForTest(sessionId1, sessionId2, sessionId3, sessionId4, + blobId1, blobId2); + + // Invoke test method + mService.handlePackageRemoved(TEST_PKG1, TEST_UID1); + + // Verify sessions are removed + verify(sessionFile1).delete(); + verify(sessionFile2, never()).delete(); + verify(sessionFile3, never()).delete(); + verify(sessionFile4).delete(); + + assertThat(mUserSessions.size()).isEqualTo(2); + assertThat(mUserSessions.get(sessionId1)).isNull(); + assertThat(mUserSessions.get(sessionId2)).isNotNull(); + assertThat(mUserSessions.get(sessionId3)).isNotNull(); + assertThat(mUserSessions.get(sessionId4)).isNull(); + + // Verify blobs are removed + verify(blobMetadata1).removeCommitter(TEST_PKG1, TEST_UID1); + verify(blobMetadata1).removeLeasee(TEST_PKG1, TEST_UID1); + verify(blobMetadata2).removeCommitter(TEST_PKG1, TEST_UID1); + verify(blobMetadata2).removeLeasee(TEST_PKG1, TEST_UID1); + + verify(blobFile1, never()).delete(); + verify(blobFile2).delete(); + + assertThat(mUserBlobs.size()).isEqualTo(1); + assertThat(mUserBlobs.get(blobHandle1)).isNotNull(); + assertThat(mUserBlobs.get(blobHandle2)).isNull(); + + assertThat(mService.getKnownIdsForTest()).containsExactly( + sessionId2, sessionId3, blobId1); + } + + @Test + public void testHandleIdleMaintenance_deleteUnknownBlobs() throws Exception { + // Setup blob files + final long testId1 = 286; + final File file1 = mock(File.class); + doReturn(String.valueOf(testId1)).when(file1).getName(); + final long testId2 = 349; + final File file2 = mock(File.class); + doReturn(String.valueOf(testId2)).when(file2).getName(); + final long testId3 = 7355; + final File file3 = mock(File.class); + doReturn(String.valueOf(testId3)).when(file3).getName(); + + doReturn(new File[] {file1, file2, file3}).when(mBlobsDir).listFiles(); + mService.addKnownIdsForTest(testId1, testId3); + + // Invoke test method + mService.handleIdleMaintenanceLocked(); + + // Verify unknown blobs are delete + verify(file1, never()).delete(); + verify(file2).delete(); + verify(file3, never()).delete(); + } + + @Test + public void testHandleIdleMaintenance_deleteStaleSessions() throws Exception { + // Setup sessions + final File sessionFile1 = mock(File.class); + doReturn(System.currentTimeMillis() - SESSION_EXPIRY_TIMEOUT_MILLIS + 1000) + .when(sessionFile1).lastModified(); + final long sessionId1 = 342; + final BlobHandle blobHandle1 = mock(BlobHandle.class); + doReturn(System.currentTimeMillis() - 1000).when(blobHandle1).getExpiryTimeMillis(); + final BlobStoreSession session1 = createBlobStoreSessionMock(TEST_PKG1, TEST_UID1, + sessionId1, sessionFile1, blobHandle1); + mUserSessions.append(sessionId1, session1); + + final File sessionFile2 = mock(File.class); + doReturn(System.currentTimeMillis() - 20000) + .when(sessionFile2).lastModified(); + final long sessionId2 = 4597; + final BlobHandle blobHandle2 = mock(BlobHandle.class); + doReturn(System.currentTimeMillis() + 20000).when(blobHandle2).getExpiryTimeMillis(); + final BlobStoreSession session2 = createBlobStoreSessionMock(TEST_PKG2, TEST_UID2, + sessionId2, sessionFile2, blobHandle2); + mUserSessions.append(sessionId2, session2); + + final File sessionFile3 = mock(File.class); + doReturn(System.currentTimeMillis() - SESSION_EXPIRY_TIMEOUT_MILLIS - 2000) + .when(sessionFile3).lastModified(); + final long sessionId3 = 9484; + final BlobHandle blobHandle3 = mock(BlobHandle.class); + doReturn(System.currentTimeMillis() + 30000).when(blobHandle3).getExpiryTimeMillis(); + final BlobStoreSession session3 = createBlobStoreSessionMock(TEST_PKG3, TEST_UID3, + sessionId3, sessionFile3, blobHandle3); + mUserSessions.append(sessionId3, session3); + + mService.addKnownIdsForTest(sessionId1, sessionId2, sessionId3); + + // Invoke test method + mService.handleIdleMaintenanceLocked(); + + // Verify stale sessions are removed + verify(sessionFile1).delete(); + verify(sessionFile2, never()).delete(); + verify(sessionFile3).delete(); + + assertThat(mUserSessions.size()).isEqualTo(1); + assertThat(mUserSessions.get(sessionId2)).isNotNull(); + + assertThat(mService.getKnownIdsForTest()).containsExactly(sessionId2); + } + + @Test + public void testHandleIdleMaintenance_deleteStaleBlobs() throws Exception { + // Setup blobs + final long blobId1 = 3489; + final File blobFile1 = mock(File.class); + final BlobHandle blobHandle1 = BlobHandle.createWithSha256("digest1".getBytes(), + "label1", System.currentTimeMillis() - 2000, "tag1"); + final BlobMetadata blobMetadata1 = createBlobMetadataMock(blobId1, blobFile1, true); + mUserBlobs.put(blobHandle1, blobMetadata1); + + final long blobId2 = 78974; + final File blobFile2 = mock(File.class); + final BlobHandle blobHandle2 = BlobHandle.createWithSha256("digest2".getBytes(), + "label2", System.currentTimeMillis() + 30000, "tag2"); + final BlobMetadata blobMetadata2 = createBlobMetadataMock(blobId2, blobFile2, true); + mUserBlobs.put(blobHandle2, blobMetadata2); + + final long blobId3 = 97; + final File blobFile3 = mock(File.class); + final BlobHandle blobHandle3 = BlobHandle.createWithSha256("digest3".getBytes(), + "label3", System.currentTimeMillis() + 4400000, "tag3"); + final BlobMetadata blobMetadata3 = createBlobMetadataMock(blobId3, blobFile3, false); + mUserBlobs.put(blobHandle3, blobMetadata3); + + mService.addKnownIdsForTest(blobId1, blobId2, blobId3); + + // Invoke test method + mService.handleIdleMaintenanceLocked(); + + // Verify stale blobs are removed + verify(blobFile1).delete(); + verify(blobFile2, never()).delete(); + verify(blobFile3).delete(); + + assertThat(mUserBlobs.size()).isEqualTo(1); + assertThat(mUserBlobs.get(blobHandle2)).isNotNull(); + + assertThat(mService.getKnownIdsForTest()).containsExactly(blobId2); + } + + private BlobStoreSession createBlobStoreSessionMock(String ownerPackageName, int ownerUid, + long sessionId, File sessionFile) { + return createBlobStoreSessionMock(ownerPackageName, ownerUid, sessionId, sessionFile, + mock(BlobHandle.class)); + } + private BlobStoreSession createBlobStoreSessionMock(String ownerPackageName, int ownerUid, + long sessionId, File sessionFile, BlobHandle blobHandle) { + final BlobStoreSession session = mock(BlobStoreSession.class); + doReturn(ownerPackageName).when(session).getOwnerPackageName(); + doReturn(ownerUid).when(session).getOwnerUid(); + doReturn(sessionId).when(session).getSessionId(); + doReturn(sessionFile).when(session).getSessionFile(); + doReturn(blobHandle).when(session).getBlobHandle(); + return session; + } + + private BlobMetadata createBlobMetadataMock(long blobId, File blobFile, boolean hasLeases) { + final BlobMetadata blobMetadata = mock(BlobMetadata.class); + doReturn(blobId).when(blobMetadata).getBlobId(); + doReturn(blobFile).when(blobMetadata).getBlobFile(); + doReturn(hasLeases).when(blobMetadata).hasLeases(); + return blobMetadata; + } + + private class TestHandler extends Handler { + TestHandler(Looper looper) { + super(looper); + } + + @Override + public void dispatchMessage(Message msg) { + // Ignore all messages + } + } + + private class TestInjector extends Injector { + @Override + public Handler initializeMessageHandler() { + return mHandler; + } + } +} diff --git a/services/tests/mockingservicestests/src/com/android/server/testables/StaticMockFixtureRule.java b/services/tests/mockingservicestests/src/com/android/server/testables/StaticMockFixtureRule.java index 8e9d7ee71df3..3566aee2eba3 100644 --- a/services/tests/mockingservicestests/src/com/android/server/testables/StaticMockFixtureRule.java +++ b/services/tests/mockingservicestests/src/com/android/server/testables/StaticMockFixtureRule.java @@ -21,6 +21,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSess import com.android.dx.mockito.inline.extended.StaticMockitoSession; import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder; +import org.junit.AssumptionViolatedException; import org.junit.rules.TestRule; import org.junit.rules.TestWatcher; import org.junit.runner.Description; @@ -100,6 +101,11 @@ public class StaticMockFixtureRule implements TestRule { } @Override + protected void skipped(AssumptionViolatedException e, Description description) { + tearDown(e); + } + + @Override protected void failed(Throwable e, Description description) { tearDown(e); } diff --git a/services/tests/mockingservicestests/src/com/android/server/testables/StaticMockFixtureRuleTest.java b/services/tests/mockingservicestests/src/com/android/server/testables/StaticMockFixtureRuleTest.java index b7e71ded30ab..8e0ccf01d7a7 100644 --- a/services/tests/mockingservicestests/src/com/android/server/testables/StaticMockFixtureRuleTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/testables/StaticMockFixtureRuleTest.java @@ -16,8 +16,10 @@ package com.android.server.testables; +import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; @@ -30,6 +32,7 @@ import com.android.dx.mockito.inline.extended.StaticMockitoSession; import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder; import org.junit.After; +import org.junit.AssumptionViolatedException; import org.junit.Before; import org.junit.Test; import org.junit.runner.Description; @@ -57,6 +60,8 @@ public class StaticMockFixtureRuleTest { @Mock private Supplier<StaticMockFixture> mSupplyA; @Mock private Supplier<StaticMockFixture> mSupplyB; @Mock private Statement mStatement; + @Mock private Statement mSkipStatement; + @Mock private Statement mThrowStatement; @Mock private Description mDescription; @Before @@ -91,17 +96,22 @@ public class StaticMockFixtureRuleTest { when(mB1.setUpMockedClasses(any())).thenAnswer(invocation -> invocation.getArgument(0)); doNothing().when(mB1).setUpMockBehaviors(); doNothing().when(mStatement).evaluate(); + doThrow(new AssumptionViolatedException("bad assumption, test should be skipped")) + .when(mSkipStatement).evaluate(); + doThrow(new IllegalArgumentException("bad argument, test should be failed")) + .when(mThrowStatement).evaluate(); doNothing().when(mA1).tearDown(); doNothing().when(mB1).tearDown(); } private InOrder mocksInOrder() { - return inOrder(mSessionBuilder, mSession, mSupplyA, mSupplyB, - mA1, mA2, mB1, mB2, mStatement, mDescription); + return inOrder(mSessionBuilder, mSession, mSupplyA, mSupplyB, mA1, mA2, mB1, mB2, + mStatement, mSkipStatement, mThrowStatement, mDescription); } private void verifyNoMoreImportantMockInteractions() { - verifyNoMoreInteractions(mSupplyA, mSupplyB, mA1, mA2, mB1, mB2, mStatement); + verifyNoMoreInteractions(mSupplyA, mSupplyB, mA1, mA2, mB1, mB2, mStatement, + mSkipStatement, mThrowStatement); } @Test @@ -183,4 +193,66 @@ public class StaticMockFixtureRuleTest { verifyNoMoreImportantMockInteractions(); } + + @Test + public void testTearDownOnSkippedTests() throws Throwable { + InOrder inOrder = mocksInOrder(); + + StaticMockFixtureRule rule = new StaticMockFixtureRule(mA1, mB1) { + @Override public StaticMockitoSessionBuilder getSessionBuilder() { + return mSessionBuilder; + } + }; + Statement skipStatement = rule.apply(mSkipStatement, mDescription); + + inOrder.verify(mA1).setUpMockedClasses(any(StaticMockitoSessionBuilder.class)); + inOrder.verify(mB1).setUpMockedClasses(any(StaticMockitoSessionBuilder.class)); + inOrder.verify(mA1).setUpMockBehaviors(); + inOrder.verify(mB1).setUpMockBehaviors(); + + try { + skipStatement.evaluate(); + fail("AssumptionViolatedException should have been thrown"); + } catch (AssumptionViolatedException e) { + // expected + } + + inOrder.verify(mSkipStatement).evaluate(); + // note: tearDown in reverse order + inOrder.verify(mB1).tearDown(); + inOrder.verify(mA1).tearDown(); + + verifyNoMoreImportantMockInteractions(); + } + + @Test + public void testTearDownOnFailedTests() throws Throwable { + InOrder inOrder = mocksInOrder(); + + StaticMockFixtureRule rule = new StaticMockFixtureRule(mA1, mB1) { + @Override public StaticMockitoSessionBuilder getSessionBuilder() { + return mSessionBuilder; + } + }; + Statement failStatement = rule.apply(mThrowStatement, mDescription); + + inOrder.verify(mA1).setUpMockedClasses(any(StaticMockitoSessionBuilder.class)); + inOrder.verify(mB1).setUpMockedClasses(any(StaticMockitoSessionBuilder.class)); + inOrder.verify(mA1).setUpMockBehaviors(); + inOrder.verify(mB1).setUpMockBehaviors(); + + try { + failStatement.evaluate(); + fail("IllegalArgumentException should have been thrown"); + } catch (IllegalArgumentException e) { + // expected + } + + inOrder.verify(mThrowStatement).evaluate(); + // note: tearDown in reverse order + inOrder.verify(mB1).tearDown(); + inOrder.verify(mA1).tearDown(); + + verifyNoMoreImportantMockInteractions(); + } } diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp index 8381205fa48e..f99081024494 100644 --- a/services/tests/servicestests/Android.bp +++ b/services/tests/servicestests/Android.bp @@ -46,7 +46,6 @@ android_test { "service-appsearch", "service-jobscheduler", "service-permission", - "service-blobstore", // TODO: remove once Android migrates to JUnit 4.12, // which provides assertThrows "testng", diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml index d2ddff3627b9..1212f2082404 100644 --- a/services/tests/servicestests/AndroidManifest.xml +++ b/services/tests/servicestests/AndroidManifest.xml @@ -65,6 +65,8 @@ <uses-permission android:name="android.permission.WATCH_APPOPS" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> <uses-permission android:name="android.permission.SUSPEND_APPS"/> + <uses-permission android:name="android.permission.LOG_COMPAT_CHANGE" /> + <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG" /> <uses-permission android:name="android.permission.CONTROL_KEYGUARD"/> <uses-permission android:name="android.permission.MANAGE_BIND_INSTANT_SERVICE"/> <uses-permission android:name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" /> diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java index cf10559c8198..dfe950ea93d6 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java @@ -47,6 +47,7 @@ import static org.hamcrest.Matchers.hasItems; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; @@ -703,14 +704,20 @@ public class AbstractAccessibilityServiceConnectionTest { @Test public void takeScreenshot_returnNull() { - // no canTakeScreenshot, should return null. - when(mMockSecurityPolicy.canTakeScreenshotLocked(mServiceConnection)).thenReturn(false); - assertThat(mServiceConnection.takeScreenshot(Display.DEFAULT_DISPLAY), is(nullValue())); - // no checkAccessibilityAccess, should return null. when(mMockSecurityPolicy.canTakeScreenshotLocked(mServiceConnection)).thenReturn(true); when(mMockSecurityPolicy.checkAccessibilityAccess(mServiceConnection)).thenReturn(false); - assertThat(mServiceConnection.takeScreenshot(Display.DEFAULT_DISPLAY), is(nullValue())); + mServiceConnection.takeScreenshot(Display.DEFAULT_DISPLAY, new RemoteCallback((result) -> { + assertNull(result); + })); + } + + @Test (expected = SecurityException.class) + public void takeScreenshot_throwSecurityException() { + // no canTakeScreenshot, should throw security exception. + when(mMockSecurityPolicy.canTakeScreenshotLocked(mServiceConnection)).thenReturn(false); + mServiceConnection.takeScreenshot(Display.DEFAULT_DISPLAY, new RemoteCallback((result) -> { + })); } private void updateServiceInfo(AccessibilityServiceInfo serviceInfo, int eventType, @@ -852,8 +859,5 @@ public class AbstractAccessibilityServiceConnectionTest { @Override public void onFingerprintGesture(int gesture) {} - - @Override - public void takeScreenshotWithCallback(int displayId, RemoteCallback callback) {} } } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java index d38c80cdabe0..6aa928794244 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java @@ -24,6 +24,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -32,6 +33,9 @@ import android.content.pm.PackageManager; import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback; import android.hardware.biometrics.IBiometricService; import android.hardware.biometrics.IBiometricServiceReceiver; +import android.hardware.face.IFaceService; +import android.hardware.fingerprint.IFingerprintService; +import android.hardware.iris.IIrisService; import android.os.Binder; import android.os.Bundle; @@ -61,6 +65,12 @@ public class AuthServiceTest { AuthService.Injector mInjector; @Mock IBiometricService mBiometricService; + @Mock + IFingerprintService mFingerprintService; + @Mock + IIrisService mIrisService; + @Mock + IFaceService mFaceService; @Before public void setUp() { @@ -76,10 +86,28 @@ public class AuthServiceTest { when(mContext.getPackageManager()).thenReturn(mPackageManager); when(mInjector.getBiometricService()).thenReturn(mBiometricService); when(mInjector.getConfiguration(any())).thenReturn(config); - when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) - .thenReturn(true); - when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_IRIS)).thenReturn(true); - when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(true); + when(mInjector.getFingerprintService()).thenReturn(mFingerprintService); + when(mInjector.getFaceService()).thenReturn(mFaceService); + when(mInjector.getIrisService()).thenReturn(mIrisService); + } + + @Test + public void testRegisterNullService_doesNotRegister() throws Exception { + + // Config contains Fingerprint, Iris, Face, but services are all null + + when(mInjector.getFingerprintService()).thenReturn(null); + when(mInjector.getFaceService()).thenReturn(null); + when(mInjector.getIrisService()).thenReturn(null); + + mAuthService = new AuthService(mContext, mInjector); + mAuthService.onStart(); + + verify(mBiometricService, never()).registerAuthenticator( + anyInt(), + anyInt(), + anyInt(), + any()); } diff --git a/services/tests/servicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java deleted file mode 100644 index ff728e7a4017..000000000000 --- a/services/tests/servicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright 2020 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.blob; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.app.blob.BlobHandle; -import android.content.Context; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.os.UserHandle; -import android.platform.test.annotations.Presubmit; -import android.util.ArrayMap; -import android.util.LongSparseArray; - -import androidx.test.InstrumentationRegistry; -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.server.blob.BlobStoreManagerService.Injector; -import com.android.server.blob.BlobStoreManagerService.SessionStateChangeListener; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.io.File; - -@RunWith(AndroidJUnit4.class) -@SmallTest -@Presubmit -public class BlobStoreManagerServiceTest { - private Context mContext; - private Handler mHandler; - private BlobStoreManagerService mService; - - private LongSparseArray<BlobStoreSession> mUserSessions; - private ArrayMap<BlobHandle, BlobMetadata> mUserBlobs; - - private SessionStateChangeListener mStateChangeListener; - - private static final String TEST_PKG1 = "com.example1"; - private static final String TEST_PKG2 = "com.example2"; - private static final String TEST_PKG3 = "com.example3"; - - private static final int TEST_UID1 = 10001; - private static final int TEST_UID2 = 10002; - private static final int TEST_UID3 = 10003; - - @Before - public void setUp() { - // Share classloader to allow package private access. - System.setProperty("dexmaker.share_classloader", "true"); - - mContext = InstrumentationRegistry.getTargetContext(); - mHandler = new TestHandler(Looper.getMainLooper()); - mService = new BlobStoreManagerService(mContext, new TestInjector()); - mUserSessions = new LongSparseArray<>(); - mUserBlobs = new ArrayMap<>(); - - mService.addUserSessionsForTest(mUserSessions, UserHandle.myUserId()); - mService.addUserBlobsForTest(mUserBlobs, UserHandle.myUserId()); - - mStateChangeListener = mService.new SessionStateChangeListener(); - } - - @Test - public void testHandlePackageRemoved() throws Exception { - // Setup sessions - final File sessionFile1 = mock(File.class); - final long sessionId1 = 11; - final BlobStoreSession session1 = createBlobStoreSessionMock(TEST_PKG1, TEST_UID1, - sessionId1, sessionFile1); - mUserSessions.append(sessionId1, session1); - - final File sessionFile2 = mock(File.class); - final long sessionId2 = 25; - final BlobStoreSession session2 = createBlobStoreSessionMock(TEST_PKG2, TEST_UID2, - sessionId2, sessionFile2); - mUserSessions.append(sessionId2, session2); - - final File sessionFile3 = mock(File.class); - final long sessionId3 = 37; - final BlobStoreSession session3 = createBlobStoreSessionMock(TEST_PKG3, TEST_UID3, - sessionId3, sessionFile3); - mUserSessions.append(sessionId3, session3); - - final File sessionFile4 = mock(File.class); - final long sessionId4 = 48; - final BlobStoreSession session4 = createBlobStoreSessionMock(TEST_PKG1, TEST_UID1, - sessionId4, sessionFile4); - mUserSessions.append(sessionId4, session4); - - // Setup blobs - final File blobFile1 = mock(File.class); - final BlobHandle blobHandle1 = BlobHandle.createWithSha256("digest1".getBytes(), - "label1", System.currentTimeMillis(), "tag1"); - final BlobMetadata blobMetadata1 = createBlobMetadataMock(blobFile1, true); - mUserBlobs.put(blobHandle1, blobMetadata1); - - final File blobFile2 = mock(File.class); - final BlobHandle blobHandle2 = BlobHandle.createWithSha256("digest2".getBytes(), - "label2", System.currentTimeMillis(), "tag2"); - final BlobMetadata blobMetadata2 = createBlobMetadataMock(blobFile2, false); - mUserBlobs.put(blobHandle2, blobMetadata2); - - // Invoke test method - mService.handlePackageRemoved(TEST_PKG1, TEST_UID1); - - // Verify sessions are removed - verify(sessionFile1).delete(); - verify(sessionFile2, never()).delete(); - verify(sessionFile3, never()).delete(); - verify(sessionFile4).delete(); - - assertThat(mUserSessions.size()).isEqualTo(2); - assertThat(mUserSessions.get(sessionId1)).isNull(); - assertThat(mUserSessions.get(sessionId2)).isNotNull(); - assertThat(mUserSessions.get(sessionId3)).isNotNull(); - assertThat(mUserSessions.get(sessionId4)).isNull(); - - // Verify blobs are removed - verify(blobMetadata1).removeCommitter(TEST_PKG1, TEST_UID1); - verify(blobMetadata1).removeLeasee(TEST_PKG1, TEST_UID1); - verify(blobMetadata2).removeCommitter(TEST_PKG1, TEST_UID1); - verify(blobMetadata2).removeLeasee(TEST_PKG1, TEST_UID1); - - verify(blobFile1, never()).delete(); - verify(blobFile2).delete(); - - assertThat(mUserBlobs.size()).isEqualTo(1); - assertThat(mUserBlobs.get(blobHandle1)).isNotNull(); - assertThat(mUserBlobs.get(blobHandle2)).isNull(); - } - - private BlobStoreSession createBlobStoreSessionMock(String ownerPackageName, int ownerUid, - long sessionId, File sessionFile) { - final BlobStoreSession session = mock(BlobStoreSession.class); - when(session.getOwnerPackageName()).thenReturn(ownerPackageName); - when(session.getOwnerUid()).thenReturn(ownerUid); - when(session.getSessionId()).thenReturn(sessionId); - when(session.getSessionFile()).thenReturn(sessionFile); - return session; - } - - private BlobMetadata createBlobMetadataMock(File blobFile, boolean hasLeases) { - final BlobMetadata blobMetadata = mock(BlobMetadata.class); - when(blobMetadata.getBlobFile()).thenReturn(blobFile); - when(blobMetadata.hasLeases()).thenReturn(hasLeases); - return blobMetadata; - } - - private class TestHandler extends Handler { - TestHandler(Looper looper) { - super(looper); - } - - @Override - public void dispatchMessage(Message msg) { - // Ignore all messages - } - } - - private class TestInjector extends Injector { - @Override - public Handler initializeMessageHandler() { - return mHandler; - } - } -} diff --git a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java index 4a7636a179b1..c9ec87427722 100644 --- a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java +++ b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java @@ -423,7 +423,8 @@ public class AppIntegrityManagerServiceImplTest { PackageInfo packageInfo = mRealContext .getPackageManager() - .getPackageInfo(TEST_FRAMEWORK_PACKAGE, PackageManager.GET_SIGNATURES); + .getPackageInfo(TEST_FRAMEWORK_PACKAGE, + PackageManager.GET_SIGNING_CERTIFICATES); doReturn(packageInfo).when(mSpyPackageManager).getPackageInfo(eq(INSTALLER), anyInt()); doReturn(1).when(mSpyPackageManager).getPackageUid(eq(INSTALLER), anyInt()); return makeVerificationIntent(INSTALLER); diff --git a/services/tests/servicestests/src/com/android/server/people/data/CallLogQueryHelperTest.java b/services/tests/servicestests/src/com/android/server/people/data/CallLogQueryHelperTest.java index 7a16d171b475..a54501029712 100644 --- a/services/tests/servicestests/src/com/android/server/people/data/CallLogQueryHelperTest.java +++ b/services/tests/servicestests/src/com/android/server/people/data/CallLogQueryHelperTest.java @@ -92,7 +92,7 @@ public final class CallLogQueryHelperTest { assertEquals(1, events.size()); assertEquals(Event.TYPE_CALL_INCOMING, events.get(0).getType()); assertEquals(100L, events.get(0).getTimestamp()); - assertEquals(30L, events.get(0).getCallDetails().getDurationSeconds()); + assertEquals(30L, events.get(0).getDurationSeconds()); } @Test @@ -108,7 +108,7 @@ public final class CallLogQueryHelperTest { assertEquals(1, events.size()); assertEquals(Event.TYPE_CALL_OUTGOING, events.get(0).getType()); assertEquals(100L, events.get(0).getTimestamp()); - assertEquals(40L, events.get(0).getCallDetails().getDurationSeconds()); + assertEquals(40L, events.get(0).getDurationSeconds()); } @Test @@ -124,7 +124,7 @@ public final class CallLogQueryHelperTest { assertEquals(1, events.size()); assertEquals(Event.TYPE_CALL_MISSED, events.get(0).getType()); assertEquals(100L, events.get(0).getTimestamp()); - assertEquals(0L, events.get(0).getCallDetails().getDurationSeconds()); + assertEquals(0L, events.get(0).getDurationSeconds()); } @Test @@ -145,7 +145,7 @@ public final class CallLogQueryHelperTest { assertEquals(100L, events.get(0).getTimestamp()); assertEquals(Event.TYPE_CALL_OUTGOING, events.get(1).getType()); assertEquals(110L, events.get(1).getTimestamp()); - assertEquals(40L, events.get(1).getCallDetails().getDurationSeconds()); + assertEquals(40L, events.get(1).getDurationSeconds()); } private class EventConsumer implements BiConsumer<String, Event> { diff --git a/services/tests/servicestests/src/com/android/server/people/data/ConversationStoreTest.java b/services/tests/servicestests/src/com/android/server/people/data/ConversationStoreTest.java index a40c6ab90197..bbcb54ef8d3e 100644 --- a/services/tests/servicestests/src/com/android/server/people/data/ConversationStoreTest.java +++ b/services/tests/servicestests/src/com/android/server/people/data/ConversationStoreTest.java @@ -37,6 +37,7 @@ import java.util.Set; public final class ConversationStoreTest { private static final String SHORTCUT_ID = "abc"; + private static final String NOTIFICATION_CHANNEL_ID = "test : abc"; private static final LocusId LOCUS_ID = new LocusId("def"); private static final Uri CONTACT_URI = Uri.parse("tel:+1234567890"); private static final String PHONE_NUMBER = "+1234567890"; @@ -59,16 +60,19 @@ public final class ConversationStoreTest { @Test public void testUpdateConversation() { - ConversationInfo original = - buildConversationInfo(SHORTCUT_ID, LOCUS_ID, CONTACT_URI, PHONE_NUMBER); + ConversationInfo original = buildConversationInfo(SHORTCUT_ID, LOCUS_ID, CONTACT_URI, + PHONE_NUMBER, null); mConversationStore.addOrUpdate(original); assertEquals(LOCUS_ID, mConversationStore.getConversation(SHORTCUT_ID).getLocusId()); + assertNull(mConversationStore.getConversation(SHORTCUT_ID).getNotificationChannelId()); LocusId newLocusId = new LocusId("ghi"); ConversationInfo update = buildConversationInfo( - SHORTCUT_ID, newLocusId, CONTACT_URI, PHONE_NUMBER); + SHORTCUT_ID, newLocusId, CONTACT_URI, PHONE_NUMBER, NOTIFICATION_CHANNEL_ID); mConversationStore.addOrUpdate(update); - assertEquals(newLocusId, mConversationStore.getConversation(SHORTCUT_ID).getLocusId()); + ConversationInfo updated = mConversationStore.getConversation(SHORTCUT_ID); + assertEquals(newLocusId, updated.getLocusId()); + assertEquals(NOTIFICATION_CHANNEL_ID, updated.getNotificationChannelId()); } @Test @@ -97,8 +101,8 @@ public final class ConversationStoreTest { @Test public void testGetConversationByLocusId() { - ConversationInfo in = - buildConversationInfo(SHORTCUT_ID, LOCUS_ID, CONTACT_URI, PHONE_NUMBER); + ConversationInfo in = buildConversationInfo(SHORTCUT_ID, LOCUS_ID, CONTACT_URI, + PHONE_NUMBER, NOTIFICATION_CHANNEL_ID); mConversationStore.addOrUpdate(in); ConversationInfo out = mConversationStore.getConversationByLocusId(LOCUS_ID); assertNotNull(out); @@ -110,8 +114,8 @@ public final class ConversationStoreTest { @Test public void testGetConversationByContactUri() { - ConversationInfo in = - buildConversationInfo(SHORTCUT_ID, LOCUS_ID, CONTACT_URI, PHONE_NUMBER); + ConversationInfo in = buildConversationInfo(SHORTCUT_ID, LOCUS_ID, CONTACT_URI, + PHONE_NUMBER, NOTIFICATION_CHANNEL_ID); mConversationStore.addOrUpdate(in); ConversationInfo out = mConversationStore.getConversationByContactUri(CONTACT_URI); assertNotNull(out); @@ -123,8 +127,8 @@ public final class ConversationStoreTest { @Test public void testGetConversationByPhoneNumber() { - ConversationInfo in = - buildConversationInfo(SHORTCUT_ID, LOCUS_ID, CONTACT_URI, PHONE_NUMBER); + ConversationInfo in = buildConversationInfo(SHORTCUT_ID, LOCUS_ID, CONTACT_URI, + PHONE_NUMBER, NOTIFICATION_CHANNEL_ID); mConversationStore.addOrUpdate(in); ConversationInfo out = mConversationStore.getConversationByPhoneNumber(PHONE_NUMBER); assertNotNull(out); @@ -134,17 +138,34 @@ public final class ConversationStoreTest { assertNull(mConversationStore.getConversationByPhoneNumber(PHONE_NUMBER)); } + @Test + public void testGetConversationByNotificationChannelId() { + ConversationInfo in = buildConversationInfo(SHORTCUT_ID, LOCUS_ID, CONTACT_URI, + PHONE_NUMBER, NOTIFICATION_CHANNEL_ID); + mConversationStore.addOrUpdate(in); + ConversationInfo out = mConversationStore.getConversationByNotificationChannelId( + NOTIFICATION_CHANNEL_ID); + assertNotNull(out); + assertEquals(SHORTCUT_ID, out.getShortcutId()); + + mConversationStore.deleteConversation(SHORTCUT_ID); + assertNull( + mConversationStore.getConversationByNotificationChannelId(NOTIFICATION_CHANNEL_ID)); + } + private static ConversationInfo buildConversationInfo(String shortcutId) { - return buildConversationInfo(shortcutId, null, null, null); + return buildConversationInfo(shortcutId, null, null, null, null); } private static ConversationInfo buildConversationInfo( - String shortcutId, LocusId locusId, Uri contactUri, String phoneNumber) { + String shortcutId, LocusId locusId, Uri contactUri, String phoneNumber, + String notificationChannelId) { return new ConversationInfo.Builder() .setShortcutId(shortcutId) .setLocusId(locusId) .setContactUri(contactUri) .setContactPhoneNumber(phoneNumber) + .setNotificationChannelId(notificationChannelId) .setShortcutFlags(ShortcutInfo.FLAG_LONG_LIVED) .setVip(true) .setBubbled(true) diff --git a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java index 62ea425a54a7..ad5c57dd11bc 100644 --- a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java @@ -16,15 +16,12 @@ package com.android.server.people.data; -import static android.app.usage.UsageEvents.Event.SHORTCUT_INVOCATION; - import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; @@ -40,7 +37,6 @@ import android.app.Person; import android.app.prediction.AppTarget; import android.app.prediction.AppTargetEvent; import android.app.prediction.AppTargetId; -import android.app.usage.UsageEvents; import android.app.usage.UsageStatsManagerInternal; import android.content.ContentResolver; import android.content.Context; @@ -232,7 +228,7 @@ public final class DataManagerTest { mDataManager.getShortcut(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID); verify(mShortcutServiceInternal).getShortcuts(anyInt(), anyString(), anyLong(), eq(TEST_PKG_NAME), eq(Collections.singletonList(TEST_SHORTCUT_ID)), - eq(null), anyInt(), eq(USER_ID_PRIMARY), anyInt(), anyInt()); + eq(null), eq(null), anyInt(), eq(USER_ID_PRIMARY), anyInt(), anyInt()); } @Test @@ -263,6 +259,7 @@ public final class DataManagerTest { @Test public void testContactsChanged() { mDataManager.onUserUnlocked(USER_ID_PRIMARY); + mDataManager.onUserUnlocked(USER_ID_SECONDARY); ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, buildPerson()); @@ -289,6 +286,7 @@ public final class DataManagerTest { @Test public void testNotificationListener() { mDataManager.onUserUnlocked(USER_ID_PRIMARY); + mDataManager.onUserUnlocked(USER_ID_SECONDARY); ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, buildPerson()); @@ -310,37 +308,9 @@ public final class DataManagerTest { } @Test - public void testQueryUsageStatsService() { - UsageEvents.Event e = new UsageEvents.Event(SHORTCUT_INVOCATION, - System.currentTimeMillis()); - e.mPackage = TEST_PKG_NAME; - e.mShortcutId = TEST_SHORTCUT_ID; - List<UsageEvents.Event> events = new ArrayList<>(); - events.add(e); - UsageEvents usageEvents = new UsageEvents(events, new String[]{}); - when(mUsageStatsManagerInternal.queryEventsForUser(anyInt(), anyLong(), anyLong(), - anyBoolean(), anyBoolean())).thenReturn(usageEvents); - - mDataManager.onUserUnlocked(USER_ID_PRIMARY); - - ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, - buildPerson()); - mDataManager.onShortcutAddedOrUpdated(shortcut); - - mDataManager.queryUsageStatsService(USER_ID_PRIMARY, 0L, Long.MAX_VALUE); - - List<Range<Long>> activeShortcutInvocationTimeSlots = new ArrayList<>(); - mDataManager.forAllPackages(packageData -> - activeShortcutInvocationTimeSlots.addAll( - packageData.getEventHistory(TEST_SHORTCUT_ID) - .getEventIndex(Event.TYPE_SHORTCUT_INVOCATION) - .getActiveTimeSlots())); - assertEquals(1, activeShortcutInvocationTimeSlots.size()); - } - - @Test public void testCallLogContentObserver() { mDataManager.onUserUnlocked(USER_ID_PRIMARY); + mDataManager.onUserUnlocked(USER_ID_SECONDARY); ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, buildPerson()); @@ -368,6 +338,7 @@ public final class DataManagerTest { @Test public void testMmsSmsContentObserver() { mDataManager.onUserUnlocked(USER_ID_PRIMARY); + mDataManager.onUserUnlocked(USER_ID_SECONDARY); ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, buildPerson()); diff --git a/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java b/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java new file mode 100644 index 000000000000..e4248a04878d --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java @@ -0,0 +1,295 @@ +/* + * Copyright (C) 2020 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.people.data; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.when; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.app.usage.UsageEvents; +import android.app.usage.UsageStatsManagerInternal; +import android.content.LocusId; + +import com.android.server.LocalServices; + +import org.junit.After; +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.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Set; +import java.util.function.Predicate; + +@RunWith(JUnit4.class) +public final class UsageStatsQueryHelperTest { + + private static final int USER_ID_PRIMARY = 0; + private static final String PKG_NAME = "pkg"; + private static final String ACTIVITY_NAME = "TestActivity"; + private static final String SHORTCUT_ID = "abc"; + private static final String NOTIFICATION_CHANNEL_ID = "test : abc"; + private static final LocusId LOCUS_ID_1 = new LocusId("locus_1"); + private static final LocusId LOCUS_ID_2 = new LocusId("locus_2"); + + @Mock private UsageStatsManagerInternal mUsageStatsManagerInternal; + + private TestPackageData mPackageData; + private UsageStatsQueryHelper mHelper; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + addLocalServiceMock(UsageStatsManagerInternal.class, mUsageStatsManagerInternal); + + mPackageData = new TestPackageData(PKG_NAME, USER_ID_PRIMARY, pkg -> false, pkg -> false); + mPackageData.mConversationStore.mConversationInfo = new ConversationInfo.Builder() + .setShortcutId(SHORTCUT_ID) + .setNotificationChannelId(NOTIFICATION_CHANNEL_ID) + .setLocusId(LOCUS_ID_1) + .build(); + + mHelper = new UsageStatsQueryHelper(USER_ID_PRIMARY, pkg -> mPackageData); + } + + @After + public void tearDown() { + LocalServices.removeServiceForTest(UsageStatsManagerInternal.class); + } + + @Test + public void testQueryNoEvents() { + assertFalse(mHelper.querySince(50L)); + } + + @Test + public void testQueryShortcutInvocationEvent() { + addUsageEvents(createShortcutInvocationEvent(100L)); + + assertTrue(mHelper.querySince(50L)); + assertEquals(100L, mHelper.getLastEventTimestamp()); + Event expectedEvent = new Event(100L, Event.TYPE_SHORTCUT_INVOCATION); + List<Event> events = mPackageData.mEventStore.mShortcutEventHistory.queryEvents( + Event.ALL_EVENT_TYPES, 0L, Long.MAX_VALUE); + assertEquals(1, events.size()); + assertEquals(expectedEvent, events.get(0)); + } + + @Test + public void testQueryNotificationInterruptionEvent() { + addUsageEvents(createNotificationInterruptionEvent(100L)); + + assertTrue(mHelper.querySince(50L)); + assertEquals(100L, mHelper.getLastEventTimestamp()); + Event expectedEvent = new Event(100L, Event.TYPE_NOTIFICATION_POSTED); + List<Event> events = mPackageData.mEventStore.mShortcutEventHistory.queryEvents( + Event.ALL_EVENT_TYPES, 0L, Long.MAX_VALUE); + assertEquals(1, events.size()); + assertEquals(expectedEvent, events.get(0)); + } + + @Test + public void testInAppConversationSwitch() { + addUsageEvents( + createLocusIdSetEvent(100_000L, LOCUS_ID_1.getId()), + createLocusIdSetEvent(110_000L, LOCUS_ID_2.getId())); + + assertTrue(mHelper.querySince(50_000L)); + assertEquals(110_000L, mHelper.getLastEventTimestamp()); + List<Event> events = mPackageData.mEventStore.mLocusEventHistory.queryEvents( + Event.ALL_EVENT_TYPES, 0L, Long.MAX_VALUE); + assertEquals(1, events.size()); + assertEquals(createInAppConversationEvent(100_000L, 10), events.get(0)); + } + + @Test + public void testInAppConversationExplicitlyEnd() { + addUsageEvents( + createLocusIdSetEvent(100_000L, LOCUS_ID_1.getId()), + createLocusIdSetEvent(110_000L, null)); + + assertTrue(mHelper.querySince(50_000L)); + assertEquals(110_000L, mHelper.getLastEventTimestamp()); + List<Event> events = mPackageData.mEventStore.mLocusEventHistory.queryEvents( + Event.ALL_EVENT_TYPES, 0L, Long.MAX_VALUE); + assertEquals(1, events.size()); + assertEquals(createInAppConversationEvent(100_000L, 10), events.get(0)); + } + + @Test + public void testInAppConversationImplicitlyEnd() { + addUsageEvents( + createLocusIdSetEvent(100_000L, LOCUS_ID_1.getId()), + createActivityStoppedEvent(110_000L)); + + assertTrue(mHelper.querySince(50_000L)); + assertEquals(110_000L, mHelper.getLastEventTimestamp()); + List<Event> events = mPackageData.mEventStore.mLocusEventHistory.queryEvents( + Event.ALL_EVENT_TYPES, 0L, Long.MAX_VALUE); + assertEquals(1, events.size()); + assertEquals(createInAppConversationEvent(100_000L, 10), events.get(0)); + } + + @Test + public void testMultipleInAppConversations() { + addUsageEvents( + createLocusIdSetEvent(100_000L, LOCUS_ID_1.getId()), + createLocusIdSetEvent(110_000L, LOCUS_ID_2.getId()), + createLocusIdSetEvent(130_000L, LOCUS_ID_1.getId()), + createActivityStoppedEvent(160_000L)); + + assertTrue(mHelper.querySince(50_000L)); + assertEquals(160_000L, mHelper.getLastEventTimestamp()); + List<Event> events = mPackageData.mEventStore.mLocusEventHistory.queryEvents( + Event.ALL_EVENT_TYPES, 0L, Long.MAX_VALUE); + assertEquals(3, events.size()); + assertEquals(createInAppConversationEvent(100_000L, 10), events.get(0)); + assertEquals(createInAppConversationEvent(110_000L, 20), events.get(1)); + assertEquals(createInAppConversationEvent(130_000L, 30), events.get(2)); + } + + private void addUsageEvents(UsageEvents.Event ... events) { + UsageEvents usageEvents = new UsageEvents(Arrays.asList(events), new String[]{}); + when(mUsageStatsManagerInternal.queryEventsForUser(anyInt(), anyLong(), anyLong(), + anyBoolean(), anyBoolean())).thenReturn(usageEvents); + } + + private static <T> void addLocalServiceMock(Class<T> clazz, T mock) { + LocalServices.removeServiceForTest(clazz); + LocalServices.addService(clazz, mock); + } + + private static UsageEvents.Event createShortcutInvocationEvent(long timestamp) { + UsageEvents.Event e = createUsageEvent(UsageEvents.Event.SHORTCUT_INVOCATION, timestamp); + e.mShortcutId = SHORTCUT_ID; + return e; + } + + private static UsageEvents.Event createNotificationInterruptionEvent(long timestamp) { + UsageEvents.Event e = createUsageEvent(UsageEvents.Event.NOTIFICATION_INTERRUPTION, + timestamp); + e.mNotificationChannelId = NOTIFICATION_CHANNEL_ID; + return e; + } + + private static UsageEvents.Event createLocusIdSetEvent(long timestamp, String locusId) { + UsageEvents.Event e = createUsageEvent(UsageEvents.Event.LOCUS_ID_SET, timestamp); + e.mClass = ACTIVITY_NAME; + e.mLocusId = locusId; + return e; + } + + private static UsageEvents.Event createActivityStoppedEvent(long timestamp) { + UsageEvents.Event e = createUsageEvent(UsageEvents.Event.ACTIVITY_STOPPED, timestamp); + e.mClass = ACTIVITY_NAME; + return e; + } + + private static UsageEvents.Event createUsageEvent(int eventType, long timestamp) { + UsageEvents.Event e = new UsageEvents.Event(eventType, timestamp); + e.mPackage = PKG_NAME; + return e; + } + + private static Event createInAppConversationEvent(long timestamp, int durationSeconds) { + return new Event.Builder(timestamp, Event.TYPE_IN_APP_CONVERSATION) + .setDurationSeconds(durationSeconds) + .build(); + } + + private static class TestConversationStore extends ConversationStore { + + private ConversationInfo mConversationInfo; + + @Override + @Nullable + ConversationInfo getConversation(@Nullable String shortcutId) { + return mConversationInfo; + } + } + + private static class TestPackageData extends PackageData { + + private final TestConversationStore mConversationStore = new TestConversationStore(); + private final TestEventStore mEventStore = new TestEventStore(); + + TestPackageData(@NonNull String packageName, @UserIdInt int userId, + @NonNull Predicate<String> isDefaultDialerPredicate, + @NonNull Predicate<String> isDefaultSmsAppPredicate) { + super(packageName, userId, isDefaultDialerPredicate, isDefaultSmsAppPredicate); + } + + @Override + @NonNull + ConversationStore getConversationStore() { + return mConversationStore; + } + + @Override + @NonNull + EventStore getEventStore() { + return mEventStore; + } + } + + private static class TestEventStore extends EventStore { + + private final EventHistoryImpl mShortcutEventHistory = new TestEventHistoryImpl(); + private final EventHistoryImpl mLocusEventHistory = new TestEventHistoryImpl(); + + @Override + @NonNull + EventHistoryImpl getOrCreateShortcutEventHistory(String shortcutId) { + return mShortcutEventHistory; + } + + @Override + @NonNull + EventHistoryImpl getOrCreateLocusEventHistory(LocusId locusId) { + return mLocusEventHistory; + } + } + + private static class TestEventHistoryImpl extends EventHistoryImpl { + + private final List<Event> mEvents = new ArrayList<>(); + + @Override + @NonNull + public List<Event> queryEvents(Set<Integer> eventTypes, long startTime, long endTime) { + return mEvents; + } + + @Override + void addEvent(Event event) { + mEvents.add(event); + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java index 41416f1352a8..3d190be8888b 100644 --- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java @@ -52,6 +52,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.IntentSender; +import android.content.LocusId; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.ILauncherApps; @@ -1600,6 +1601,22 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { } /** + * Make a shortcut with an ID and a locus ID. + */ + protected ShortcutInfo makeShortcutWithLocusId(String id, LocusId locusId) { + final ShortcutInfo.Builder b = new ShortcutInfo.Builder(mClientContext, id) + .setActivity(new ComponentName(mClientContext.getPackageName(), "main")) + .setShortLabel("title-" + id) + .setIntent(makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class)) + .setLocusId(locusId); + final ShortcutInfo s = b.build(); + + s.setTimestamp(mInjectedCurrentTimeMillis); // HACK + + return s; + } + + /** * Make an intent. */ protected Intent makeIntent(String action, Class<?> clazz, Object... bundleKeysAndValues) { @@ -1618,6 +1635,13 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { } /** + * Make a LocusId. + */ + protected LocusId makeLocusId(String id) { + return new LocusId(id); + } + + /** * Make an component name, with the client context. */ @NonNull @@ -1955,16 +1979,17 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { protected static ShortcutQuery buildQuery(long changedSince, String packageName, ComponentName componentName, /* @ShortcutQuery.QueryFlags */ int flags) { - return buildQuery(changedSince, packageName, null, componentName, flags); + return buildQuery(changedSince, packageName, null, null, componentName, flags); } protected static ShortcutQuery buildQuery(long changedSince, - String packageName, List<String> shortcutIds, ComponentName componentName, - /* @ShortcutQuery.QueryFlags */ int flags) { + String packageName, List<String> shortcutIds, List<LocusId> locusIds, + ComponentName componentName, /* @ShortcutQuery.QueryFlags */ int flags) { final ShortcutQuery q = new ShortcutQuery(); q.setChangedSince(changedSince); q.setPackage(packageName); q.setShortcutIds(shortcutIds); + q.setLocusIds(locusIds); q.setActivity(componentName); q.setQueryFlags(flags); return q; diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java index bfe0c15ef6e8..d4edab44bae3 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java @@ -24,6 +24,7 @@ import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; import static org.xmlpull.v1.XmlPullParser.START_TAG; import android.content.pm.PackageInstaller; +import android.platform.test.annotations.Presubmit; import android.util.AtomicFile; import android.util.Slog; import android.util.Xml; @@ -57,6 +58,7 @@ import java.util.Arrays; import java.util.List; @RunWith(AndroidJUnit4.class) +@Presubmit public class PackageInstallerSessionTest { @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder(); diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java index 798420ee0137..63da5fbab122 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java @@ -1372,7 +1372,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { setCaller(CALLING_PACKAGE_1); final ShortcutInfo s1_1 = makeShortcut("s1"); - final ShortcutInfo s1_2 = makeShortcut("s2"); + final ShortcutInfo s1_2 = makeShortcutWithLocusId("s2", makeLocusId("l1")); assertTrue(mManager.setDynamicShortcuts(list(s1_1, s1_2))); @@ -1394,7 +1394,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { getCallerShortcut("s4").setTimestamp(500); setCaller(CALLING_PACKAGE_3); - final ShortcutInfo s3_2 = makeShortcut("s3"); + final ShortcutInfo s3_2 = makeShortcutWithLocusId("s3", makeLocusId("l2")); assertTrue(mManager.setDynamicShortcuts(list(s3_2))); getCallerShortcut("s3").setTimestamp(START_TIME + 5000); @@ -1446,7 +1446,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { // With ID. assertAllDynamic(assertAllNotHaveTitle(assertAllNotHaveIntents(assertShortcutIds( assertAllKeyFieldsOnly(mLauncherApps.getShortcuts(buildQuery( - /* time =*/ 1000, CALLING_PACKAGE_2, list("s3"), + /* time =*/ 1000, CALLING_PACKAGE_2, list("s3"), /* locusIds =*/ null, /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY), getCallingUser())), @@ -1454,20 +1454,51 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { assertAllDynamic(assertAllNotHaveTitle(assertAllNotHaveIntents(assertShortcutIds( assertAllKeyFieldsOnly(mLauncherApps.getShortcuts(buildQuery( /* time =*/ 1000, CALLING_PACKAGE_2, list("s3", "s2", "ss"), - /* activity =*/ null, + /* locusIds =*/ null, /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY), getCallingUser())), "s2", "s3")))); assertAllDynamic(assertAllNotHaveTitle(assertAllNotHaveIntents(assertShortcutIds( assertAllKeyFieldsOnly(mLauncherApps.getShortcuts(buildQuery( /* time =*/ 1000, CALLING_PACKAGE_2, list("s3x", "s2x"), + /* locusIds =*/ null, /* activity =*/ null, + ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY), + getCallingUser())) + /* empty */)))); + assertAllDynamic(assertAllNotHaveTitle(assertAllNotHaveIntents(assertShortcutIds( + assertAllKeyFieldsOnly(mLauncherApps.getShortcuts(buildQuery( + /* time =*/ 1000, CALLING_PACKAGE_2, list(), /* locusIds =*/ null, + /* activity =*/ null, + ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY), + getCallingUser())) + /* empty */)))); + + // With locus ID. + assertAllDynamic(assertAllNotHaveTitle(assertAllNotHaveIntents(assertShortcutIds( + assertAllKeyFieldsOnly(mLauncherApps.getShortcuts(buildQuery( + /* time =*/ 1000, CALLING_PACKAGE_3, /* shortcutIds =*/ null, + list(makeLocusId("l2")), /* activity =*/ null, + ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY), + getCallingUser())), + "s3")))); + assertAllDynamic(assertAllNotHaveTitle(assertAllNotHaveIntents(assertShortcutIds( + assertAllKeyFieldsOnly(mLauncherApps.getShortcuts(buildQuery( + /* time =*/ 1000, CALLING_PACKAGE_1, /* shortcutIds =*/ null, + list(makeLocusId("l1"), makeLocusId("l2"), makeLocusId("l3")), /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY), + getCallingUser())), + "s2")))); + assertAllDynamic(assertAllNotHaveTitle(assertAllNotHaveIntents(assertShortcutIds( + assertAllKeyFieldsOnly(mLauncherApps.getShortcuts(buildQuery( + /* time =*/ 1000, CALLING_PACKAGE_1, /* shortcutIds =*/ null, + list(makeLocusId("lx1"), makeLocusId("lx2")), /* activity =*/ null, + ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY), getCallingUser())) /* empty */)))); assertAllDynamic(assertAllNotHaveTitle(assertAllNotHaveIntents(assertShortcutIds( assertAllKeyFieldsOnly(mLauncherApps.getShortcuts(buildQuery( - /* time =*/ 1000, CALLING_PACKAGE_2, list(), + /* time =*/ 1000, CALLING_PACKAGE_3, /* shortcutIds =*/ null, list(), /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC | ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY), getCallingUser())) @@ -1498,7 +1529,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { assertExpectException( IllegalArgumentException.class, "package name must also be set", () -> { mLauncherApps.getShortcuts(buildQuery( - /* time =*/ 0, /* package= */ null, list("id"), + /* time =*/ 0, /* package= */ null, list("id"), /* locusIds =*/ null, /* activity =*/ null, /* flags */ 0), getCallingUser()); }); @@ -1537,7 +1568,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { assertExpectException( IllegalArgumentException.class, "package name must also be set", () -> { mLauncherApps.getShortcuts(buildQuery( - /* time =*/ 0, /* package= */ null, list("id"), + /* time =*/ 0, /* package= */ null, list("id"), /* locusIds= */ null, /* activity =*/ null, /* flags */ 0), getCallingUser()); }); diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java index 218f43c9495d..2eeeb3ebaa2a 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java @@ -30,7 +30,7 @@ import static org.mockito.Mockito.when; import android.app.timedetector.ManualTimeSuggestion; import android.app.timedetector.NetworkTimeSuggestion; -import android.app.timedetector.PhoneTimeSuggestion; +import android.app.timedetector.TelephonyTimeSuggestion; import android.content.Context; import android.content.pm.PackageManager; import android.os.HandlerThread; @@ -80,35 +80,35 @@ public class TimeDetectorServiceTest { } @Test(expected = SecurityException.class) - public void testSuggestPhoneTime_withoutPermission() { + public void testSuggestTelephonyTime_withoutPermission() { doThrow(new SecurityException("Mock")) .when(mMockContext).enforceCallingPermission(anyString(), any()); - PhoneTimeSuggestion phoneTimeSuggestion = createPhoneTimeSuggestion(); + TelephonyTimeSuggestion timeSuggestion = createTelephonyTimeSuggestion(); try { - mTimeDetectorService.suggestPhoneTime(phoneTimeSuggestion); + mTimeDetectorService.suggestTelephonyTime(timeSuggestion); fail(); } finally { verify(mMockContext).enforceCallingPermission( - eq(android.Manifest.permission.SUGGEST_PHONE_TIME_AND_ZONE), + eq(android.Manifest.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE), anyString()); } } @Test - public void testSuggestPhoneTime() throws Exception { + public void testSuggestTelephonyTime() throws Exception { doNothing().when(mMockContext).enforceCallingPermission(anyString(), any()); - PhoneTimeSuggestion phoneTimeSuggestion = createPhoneTimeSuggestion(); - mTimeDetectorService.suggestPhoneTime(phoneTimeSuggestion); + TelephonyTimeSuggestion timeSuggestion = createTelephonyTimeSuggestion(); + mTimeDetectorService.suggestTelephonyTime(timeSuggestion); mTestHandler.assertTotalMessagesEnqueued(1); verify(mMockContext).enforceCallingPermission( - eq(android.Manifest.permission.SUGGEST_PHONE_TIME_AND_ZONE), + eq(android.Manifest.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE), anyString()); mTestHandler.waitForMessagesToBeProcessed(); - mStubbedTimeDetectorStrategy.verifySuggestPhoneTimeCalled(phoneTimeSuggestion); + mStubbedTimeDetectorStrategy.verifySuggestTelephonyTimeCalled(timeSuggestion); } @Test(expected = SecurityException.class) @@ -199,10 +199,10 @@ public class TimeDetectorServiceTest { mStubbedTimeDetectorStrategy.verifyHandleAutoTimeDetectionChangedCalled(); } - private static PhoneTimeSuggestion createPhoneTimeSuggestion() { + private static TelephonyTimeSuggestion createTelephonyTimeSuggestion() { int slotIndex = 1234; TimestampedValue<Long> timeValue = new TimestampedValue<>(100L, 1_000_000L); - return new PhoneTimeSuggestion.Builder(slotIndex) + return new TelephonyTimeSuggestion.Builder(slotIndex) .setUtcTime(timeValue) .build(); } @@ -220,7 +220,7 @@ public class TimeDetectorServiceTest { private static class StubbedTimeDetectorStrategy implements TimeDetectorStrategy { // Call tracking. - private PhoneTimeSuggestion mLastPhoneSuggestion; + private TelephonyTimeSuggestion mLastTelephonySuggestion; private ManualTimeSuggestion mLastManualSuggestion; private NetworkTimeSuggestion mLastNetworkSuggestion; private boolean mHandleAutoTimeDetectionChangedCalled; @@ -231,8 +231,8 @@ public class TimeDetectorServiceTest { } @Override - public void suggestPhoneTime(PhoneTimeSuggestion timeSuggestion) { - mLastPhoneSuggestion = timeSuggestion; + public void suggestTelephonyTime(TelephonyTimeSuggestion timeSuggestion) { + mLastTelephonySuggestion = timeSuggestion; } @Override @@ -256,15 +256,15 @@ public class TimeDetectorServiceTest { } void resetCallTracking() { - mLastPhoneSuggestion = null; + mLastTelephonySuggestion = null; mLastManualSuggestion = null; mLastNetworkSuggestion = null; mHandleAutoTimeDetectionChangedCalled = false; mDumpCalled = false; } - void verifySuggestPhoneTimeCalled(PhoneTimeSuggestion expectedSuggestion) { - assertEquals(expectedSuggestion, mLastPhoneSuggestion); + void verifySuggestTelephonyTimeCalled(TelephonyTimeSuggestion expectedSuggestion) { + assertEquals(expectedSuggestion, mLastTelephonySuggestion); } public void verifySuggestManualTimeCalled(ManualTimeSuggestion expectedSuggestion) { diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java index d940a6a320f2..803b245889e7 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java +++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java @@ -24,7 +24,7 @@ import static org.junit.Assert.fail; import android.app.timedetector.ManualTimeSuggestion; import android.app.timedetector.NetworkTimeSuggestion; -import android.app.timedetector.PhoneTimeSuggestion; +import android.app.timedetector.TelephonyTimeSuggestion; import android.icu.util.Calendar; import android.icu.util.GregorianCalendar; import android.icu.util.TimeZone; @@ -52,7 +52,7 @@ public class TimeDetectorStrategyImplTest { */ private static final long ARBITRARY_TEST_TIME_MILLIS = createUtcTime(2018, 1, 1, 12, 0, 0); - private static final int ARBITRARY_PHONE_ID = 123456; + private static final int ARBITRARY_SLOT_INDEX = 123456; private Script mScript; @@ -62,51 +62,51 @@ public class TimeDetectorStrategyImplTest { } @Test - public void testSuggestPhoneTime_autoTimeEnabled() { + public void testSuggestTelephonyTime_autoTimeEnabled() { mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO) .pokeAutoTimeDetectionEnabled(true); - int phoneId = ARBITRARY_PHONE_ID; + int slotIndex = ARBITRARY_SLOT_INDEX; long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS; - PhoneTimeSuggestion timeSuggestion = - mScript.generatePhoneTimeSuggestion(phoneId, testTimeMillis); + TelephonyTimeSuggestion timeSuggestion = + mScript.generateTelephonyTimeSuggestion(slotIndex, testTimeMillis); mScript.simulateTimePassing() - .simulatePhoneTimeSuggestion(timeSuggestion); + .simulateTelephonyTimeSuggestion(timeSuggestion); long expectedSystemClockMillis = mScript.calculateTimeInMillisForNow(timeSuggestion.getUtcTime()); mScript.verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis) - .assertLatestPhoneSuggestion(phoneId, timeSuggestion); + .assertLatestTelephonySuggestion(slotIndex, timeSuggestion); } @Test - public void testSuggestPhoneTime_emptySuggestionIgnored() { + public void testSuggestTelephonyTime_emptySuggestionIgnored() { mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO) .pokeAutoTimeDetectionEnabled(true); - int phoneId = ARBITRARY_PHONE_ID; - PhoneTimeSuggestion timeSuggestion = - mScript.generatePhoneTimeSuggestion(phoneId, null); - mScript.simulatePhoneTimeSuggestion(timeSuggestion) + int slotIndex = ARBITRARY_SLOT_INDEX; + TelephonyTimeSuggestion timeSuggestion = + mScript.generateTelephonyTimeSuggestion(slotIndex, null); + mScript.simulateTelephonyTimeSuggestion(timeSuggestion) .verifySystemClockWasNotSetAndResetCallTracking() - .assertLatestPhoneSuggestion(phoneId, null); + .assertLatestTelephonySuggestion(slotIndex, null); } @Test - public void testSuggestPhoneTime_systemClockThreshold() { + public void testSuggestTelephonyTime_systemClockThreshold() { final int systemClockUpdateThresholdMillis = 1000; final int clockIncrementMillis = 100; mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO) .pokeThresholds(systemClockUpdateThresholdMillis) .pokeAutoTimeDetectionEnabled(true); - int phoneId = ARBITRARY_PHONE_ID; + int slotIndex = ARBITRARY_SLOT_INDEX; // Send the first time signal. It should be used. { - PhoneTimeSuggestion timeSuggestion1 = - mScript.generatePhoneTimeSuggestion(phoneId, ARBITRARY_TEST_TIME_MILLIS); + TelephonyTimeSuggestion timeSuggestion1 = + mScript.generateTelephonyTimeSuggestion(slotIndex, ARBITRARY_TEST_TIME_MILLIS); // Increment the the device clocks to simulate the passage of time. mScript.simulateTimePassing(clockIncrementMillis); @@ -114,151 +114,151 @@ public class TimeDetectorStrategyImplTest { long expectedSystemClockMillis1 = mScript.calculateTimeInMillisForNow(timeSuggestion1.getUtcTime()); - mScript.simulatePhoneTimeSuggestion(timeSuggestion1) + mScript.simulateTelephonyTimeSuggestion(timeSuggestion1) .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis1) - .assertLatestPhoneSuggestion(phoneId, timeSuggestion1); + .assertLatestTelephonySuggestion(slotIndex, timeSuggestion1); } // Now send another time signal, but one that is too similar to the last one and should be // stored, but not used to set the system clock. { int underThresholdMillis = systemClockUpdateThresholdMillis - 1; - PhoneTimeSuggestion timeSuggestion2 = mScript.generatePhoneTimeSuggestion( - phoneId, mScript.peekSystemClockMillis() + underThresholdMillis); + TelephonyTimeSuggestion timeSuggestion2 = mScript.generateTelephonyTimeSuggestion( + slotIndex, mScript.peekSystemClockMillis() + underThresholdMillis); mScript.simulateTimePassing(clockIncrementMillis) - .simulatePhoneTimeSuggestion(timeSuggestion2) + .simulateTelephonyTimeSuggestion(timeSuggestion2) .verifySystemClockWasNotSetAndResetCallTracking() - .assertLatestPhoneSuggestion(phoneId, timeSuggestion2); + .assertLatestTelephonySuggestion(slotIndex, timeSuggestion2); } // Now send another time signal, but one that is on the threshold and so should be used. { - PhoneTimeSuggestion timeSuggestion3 = mScript.generatePhoneTimeSuggestion( - phoneId, + TelephonyTimeSuggestion timeSuggestion3 = mScript.generateTelephonyTimeSuggestion( + slotIndex, mScript.peekSystemClockMillis() + systemClockUpdateThresholdMillis); mScript.simulateTimePassing(clockIncrementMillis); long expectedSystemClockMillis3 = mScript.calculateTimeInMillisForNow(timeSuggestion3.getUtcTime()); - mScript.simulatePhoneTimeSuggestion(timeSuggestion3) + mScript.simulateTelephonyTimeSuggestion(timeSuggestion3) .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis3) - .assertLatestPhoneSuggestion(phoneId, timeSuggestion3); + .assertLatestTelephonySuggestion(slotIndex, timeSuggestion3); } } @Test - public void testSuggestPhoneTime_multiplePhoneIdsAndBucketing() { + public void testSuggestTelephonyTime_multipleSlotIndexsAndBucketing() { mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO) .pokeAutoTimeDetectionEnabled(true); - // There are 2 phones in this test. Phone 2 has a different idea of the current time. - // phone1Id < phone2Id (which is important because the strategy uses the lowest ID when - // multiple phone suggestions are available. - int phone1Id = ARBITRARY_PHONE_ID; - int phone2Id = ARBITRARY_PHONE_ID + 1; - long phone1TimeMillis = ARBITRARY_TEST_TIME_MILLIS; - long phone2TimeMillis = ARBITRARY_TEST_TIME_MILLIS + Duration.ofDays(1).toMillis(); + // There are 2 slotIndexes in this test. slotIndex1 and slotIndex2 have different opinions + // about the current time. slotIndex1 < slotIndex2 (which is important because the strategy + // uses the lowest slotIndex when multiple telephony suggestions are available. + int slotIndex1 = ARBITRARY_SLOT_INDEX; + int slotIndex2 = ARBITRARY_SLOT_INDEX + 1; + long slotIndex1TimeMillis = ARBITRARY_TEST_TIME_MILLIS; + long slotIndex2TimeMillis = ARBITRARY_TEST_TIME_MILLIS + Duration.ofDays(1).toMillis(); - // Make a suggestion with phone2Id. + // Make a suggestion with slotIndex2. { - PhoneTimeSuggestion phone2TimeSuggestion = - mScript.generatePhoneTimeSuggestion(phone2Id, phone2TimeMillis); + TelephonyTimeSuggestion slotIndex2TimeSuggestion = + mScript.generateTelephonyTimeSuggestion(slotIndex2, slotIndex2TimeMillis); mScript.simulateTimePassing(); long expectedSystemClockMillis = - mScript.calculateTimeInMillisForNow(phone2TimeSuggestion.getUtcTime()); + mScript.calculateTimeInMillisForNow(slotIndex2TimeSuggestion.getUtcTime()); - mScript.simulatePhoneTimeSuggestion(phone2TimeSuggestion) + mScript.simulateTelephonyTimeSuggestion(slotIndex2TimeSuggestion) .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis) - .assertLatestPhoneSuggestion(phone1Id, null) - .assertLatestPhoneSuggestion(phone2Id, phone2TimeSuggestion); + .assertLatestTelephonySuggestion(slotIndex1, null) + .assertLatestTelephonySuggestion(slotIndex2, slotIndex2TimeSuggestion); } mScript.simulateTimePassing(); - // Now make a different suggestion with phone1Id. + // Now make a different suggestion with slotIndex1. { - PhoneTimeSuggestion phone1TimeSuggestion = - mScript.generatePhoneTimeSuggestion(phone1Id, phone1TimeMillis); + TelephonyTimeSuggestion slotIndex1TimeSuggestion = + mScript.generateTelephonyTimeSuggestion(slotIndex1, slotIndex1TimeMillis); mScript.simulateTimePassing(); long expectedSystemClockMillis = - mScript.calculateTimeInMillisForNow(phone1TimeSuggestion.getUtcTime()); + mScript.calculateTimeInMillisForNow(slotIndex1TimeSuggestion.getUtcTime()); - mScript.simulatePhoneTimeSuggestion(phone1TimeSuggestion) + mScript.simulateTelephonyTimeSuggestion(slotIndex1TimeSuggestion) .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis) - .assertLatestPhoneSuggestion(phone1Id, phone1TimeSuggestion); + .assertLatestTelephonySuggestion(slotIndex1, slotIndex1TimeSuggestion); } mScript.simulateTimePassing(); - // Make another suggestion with phone2Id. It should be stored but not used because the - // phone1Id suggestion will still "win". + // Make another suggestion with slotIndex2. It should be stored but not used because the + // slotIndex1 suggestion will still "win". { - PhoneTimeSuggestion phone2TimeSuggestion = - mScript.generatePhoneTimeSuggestion(phone2Id, phone2TimeMillis); + TelephonyTimeSuggestion slotIndex2TimeSuggestion = + mScript.generateTelephonyTimeSuggestion(slotIndex2, slotIndex2TimeMillis); mScript.simulateTimePassing(); - mScript.simulatePhoneTimeSuggestion(phone2TimeSuggestion) + mScript.simulateTelephonyTimeSuggestion(slotIndex2TimeSuggestion) .verifySystemClockWasNotSetAndResetCallTracking() - .assertLatestPhoneSuggestion(phone2Id, phone2TimeSuggestion); + .assertLatestTelephonySuggestion(slotIndex2, slotIndex2TimeSuggestion); } - // Let enough time pass that phone1Id's suggestion should now be too old. - mScript.simulateTimePassing(TimeDetectorStrategyImpl.PHONE_BUCKET_SIZE_MILLIS); + // Let enough time pass that slotIndex1's suggestion should now be too old. + mScript.simulateTimePassing(TimeDetectorStrategyImpl.TELEPHONY_BUCKET_SIZE_MILLIS); - // Make another suggestion with phone2Id. It should be used because the phoneId1 + // Make another suggestion with slotIndex2. It should be used because the slotIndex1 // is in an older "bucket". { - PhoneTimeSuggestion phone2TimeSuggestion = - mScript.generatePhoneTimeSuggestion(phone2Id, phone2TimeMillis); + TelephonyTimeSuggestion slotIndex2TimeSuggestion = + mScript.generateTelephonyTimeSuggestion(slotIndex2, slotIndex2TimeMillis); mScript.simulateTimePassing(); long expectedSystemClockMillis = - mScript.calculateTimeInMillisForNow(phone2TimeSuggestion.getUtcTime()); + mScript.calculateTimeInMillisForNow(slotIndex2TimeSuggestion.getUtcTime()); - mScript.simulatePhoneTimeSuggestion(phone2TimeSuggestion) + mScript.simulateTelephonyTimeSuggestion(slotIndex2TimeSuggestion) .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis) - .assertLatestPhoneSuggestion(phone2Id, phone2TimeSuggestion); + .assertLatestTelephonySuggestion(slotIndex2, slotIndex2TimeSuggestion); } } @Test - public void testSuggestPhoneTime_autoTimeDisabled() { + public void testSuggestTelephonyTime_autoTimeDisabled() { mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO) .pokeAutoTimeDetectionEnabled(false); - int phoneId = ARBITRARY_PHONE_ID; - PhoneTimeSuggestion timeSuggestion = - mScript.generatePhoneTimeSuggestion(phoneId, ARBITRARY_TEST_TIME_MILLIS); + int slotIndex = ARBITRARY_SLOT_INDEX; + TelephonyTimeSuggestion timeSuggestion = + mScript.generateTelephonyTimeSuggestion(slotIndex, ARBITRARY_TEST_TIME_MILLIS); mScript.simulateTimePassing() - .simulatePhoneTimeSuggestion(timeSuggestion) + .simulateTelephonyTimeSuggestion(timeSuggestion) .verifySystemClockWasNotSetAndResetCallTracking() - .assertLatestPhoneSuggestion(phoneId, timeSuggestion); + .assertLatestTelephonySuggestion(slotIndex, timeSuggestion); } @Test - public void testSuggestPhoneTime_invalidNitzReferenceTimesIgnored() { + public void testSuggestTelephonyTime_invalidNitzReferenceTimesIgnored() { final int systemClockUpdateThreshold = 2000; mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO) .pokeThresholds(systemClockUpdateThreshold) .pokeAutoTimeDetectionEnabled(true); long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS; - int phoneId = ARBITRARY_PHONE_ID; + int slotIndex = ARBITRARY_SLOT_INDEX; - PhoneTimeSuggestion timeSuggestion1 = - mScript.generatePhoneTimeSuggestion(phoneId, testTimeMillis); + TelephonyTimeSuggestion timeSuggestion1 = + mScript.generateTelephonyTimeSuggestion(slotIndex, testTimeMillis); TimestampedValue<Long> utcTime1 = timeSuggestion1.getUtcTime(); - // Initialize the strategy / device with a time set from a phone suggestion. + // Initialize the strategy / device with a time set from a telephony suggestion. mScript.simulateTimePassing(); long expectedSystemClockMillis1 = mScript.calculateTimeInMillisForNow(utcTime1); - mScript.simulatePhoneTimeSuggestion(timeSuggestion1) + mScript.simulateTelephonyTimeSuggestion(timeSuggestion1) .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis1) - .assertLatestPhoneSuggestion(phoneId, timeSuggestion1); + .assertLatestTelephonySuggestion(slotIndex, timeSuggestion1); // The UTC time increment should be larger than the system clock update threshold so we // know it shouldn't be ignored for other reasons. @@ -269,11 +269,11 @@ public class TimeDetectorStrategyImplTest { long referenceTimeBeforeLastSignalMillis = utcTime1.getReferenceTimeMillis() - 1; TimestampedValue<Long> utcTime2 = new TimestampedValue<>( referenceTimeBeforeLastSignalMillis, validUtcTimeMillis); - PhoneTimeSuggestion timeSuggestion2 = - createPhoneTimeSuggestion(phoneId, utcTime2); - mScript.simulatePhoneTimeSuggestion(timeSuggestion2) + TelephonyTimeSuggestion timeSuggestion2 = + createTelephonyTimeSuggestion(slotIndex, utcTime2); + mScript.simulateTelephonyTimeSuggestion(timeSuggestion2) .verifySystemClockWasNotSetAndResetCallTracking() - .assertLatestPhoneSuggestion(phoneId, timeSuggestion1); + .assertLatestTelephonySuggestion(slotIndex, timeSuggestion1); // Now supply a new signal that has an obviously bogus reference time : substantially in the // future. @@ -281,36 +281,36 @@ public class TimeDetectorStrategyImplTest { utcTime1.getReferenceTimeMillis() + Integer.MAX_VALUE + 1; TimestampedValue<Long> utcTime3 = new TimestampedValue<>( referenceTimeInFutureMillis, validUtcTimeMillis); - PhoneTimeSuggestion timeSuggestion3 = - createPhoneTimeSuggestion(phoneId, utcTime3); - mScript.simulatePhoneTimeSuggestion(timeSuggestion3) + TelephonyTimeSuggestion timeSuggestion3 = + createTelephonyTimeSuggestion(slotIndex, utcTime3); + mScript.simulateTelephonyTimeSuggestion(timeSuggestion3) .verifySystemClockWasNotSetAndResetCallTracking() - .assertLatestPhoneSuggestion(phoneId, timeSuggestion1); + .assertLatestTelephonySuggestion(slotIndex, timeSuggestion1); // Just to prove validUtcTimeMillis is valid. long validReferenceTimeMillis = utcTime1.getReferenceTimeMillis() + 100; TimestampedValue<Long> utcTime4 = new TimestampedValue<>( validReferenceTimeMillis, validUtcTimeMillis); long expectedSystemClockMillis4 = mScript.calculateTimeInMillisForNow(utcTime4); - PhoneTimeSuggestion timeSuggestion4 = - createPhoneTimeSuggestion(phoneId, utcTime4); - mScript.simulatePhoneTimeSuggestion(timeSuggestion4) + TelephonyTimeSuggestion timeSuggestion4 = + createTelephonyTimeSuggestion(slotIndex, utcTime4); + mScript.simulateTelephonyTimeSuggestion(timeSuggestion4) .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis4) - .assertLatestPhoneSuggestion(phoneId, timeSuggestion4); + .assertLatestTelephonySuggestion(slotIndex, timeSuggestion4); } @Test - public void testSuggestPhoneTime_timeDetectionToggled() { + public void testSuggestTelephonyTime_timeDetectionToggled() { final int clockIncrementMillis = 100; final int systemClockUpdateThreshold = 2000; mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO) .pokeThresholds(systemClockUpdateThreshold) .pokeAutoTimeDetectionEnabled(false); - int phoneId = ARBITRARY_PHONE_ID; + int slotIndex = ARBITRARY_SLOT_INDEX; long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS; - PhoneTimeSuggestion timeSuggestion1 = - mScript.generatePhoneTimeSuggestion(phoneId, testTimeMillis); + TelephonyTimeSuggestion timeSuggestion1 = + mScript.generateTelephonyTimeSuggestion(slotIndex, testTimeMillis); TimestampedValue<Long> utcTime1 = timeSuggestion1.getUtcTime(); // Simulate time passing. @@ -318,9 +318,9 @@ public class TimeDetectorStrategyImplTest { // Simulate the time signal being received. It should not be used because auto time // detection is off but it should be recorded. - mScript.simulatePhoneTimeSuggestion(timeSuggestion1) + mScript.simulateTelephonyTimeSuggestion(timeSuggestion1) .verifySystemClockWasNotSetAndResetCallTracking() - .assertLatestPhoneSuggestion(phoneId, timeSuggestion1); + .assertLatestTelephonySuggestion(slotIndex, timeSuggestion1); // Simulate more time passing. mScript.simulateTimePassing(clockIncrementMillis); @@ -330,17 +330,17 @@ public class TimeDetectorStrategyImplTest { // Turn on auto time detection. mScript.simulateAutoTimeDetectionToggle() .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis1) - .assertLatestPhoneSuggestion(phoneId, timeSuggestion1); + .assertLatestTelephonySuggestion(slotIndex, timeSuggestion1); // Turn off auto time detection. mScript.simulateAutoTimeDetectionToggle() .verifySystemClockWasNotSetAndResetCallTracking() - .assertLatestPhoneSuggestion(phoneId, timeSuggestion1); + .assertLatestTelephonySuggestion(slotIndex, timeSuggestion1); // Receive another valid time signal. // It should be on the threshold and accounting for the clock increments. - PhoneTimeSuggestion timeSuggestion2 = mScript.generatePhoneTimeSuggestion( - phoneId, mScript.peekSystemClockMillis() + systemClockUpdateThreshold); + TelephonyTimeSuggestion timeSuggestion2 = mScript.generateTelephonyTimeSuggestion( + slotIndex, mScript.peekSystemClockMillis() + systemClockUpdateThreshold); // Simulate more time passing. mScript.simulateTimePassing(clockIncrementMillis); @@ -350,45 +350,45 @@ public class TimeDetectorStrategyImplTest { // The new time, though valid, should not be set in the system clock because auto time is // disabled. - mScript.simulatePhoneTimeSuggestion(timeSuggestion2) + mScript.simulateTelephonyTimeSuggestion(timeSuggestion2) .verifySystemClockWasNotSetAndResetCallTracking() - .assertLatestPhoneSuggestion(phoneId, timeSuggestion2); + .assertLatestTelephonySuggestion(slotIndex, timeSuggestion2); // Turn on auto time detection. mScript.simulateAutoTimeDetectionToggle() .verifySystemClockWasSetAndResetCallTracking(expectedSystemClockMillis2) - .assertLatestPhoneSuggestion(phoneId, timeSuggestion2); + .assertLatestTelephonySuggestion(slotIndex, timeSuggestion2); } @Test - public void testSuggestPhoneTime_maxSuggestionAge() { + public void testSuggestTelephonyTime_maxSuggestionAge() { mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO) .pokeAutoTimeDetectionEnabled(true); - int phoneId = ARBITRARY_PHONE_ID; + int slotIndex = ARBITRARY_SLOT_INDEX; long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS; - PhoneTimeSuggestion phoneSuggestion = - mScript.generatePhoneTimeSuggestion(phoneId, testTimeMillis); + TelephonyTimeSuggestion telephonySuggestion = + mScript.generateTelephonyTimeSuggestion(slotIndex, testTimeMillis); mScript.simulateTimePassing(); long expectedSystemClockMillis = - mScript.calculateTimeInMillisForNow(phoneSuggestion.getUtcTime()); - mScript.simulatePhoneTimeSuggestion(phoneSuggestion) + mScript.calculateTimeInMillisForNow(telephonySuggestion.getUtcTime()); + mScript.simulateTelephonyTimeSuggestion(telephonySuggestion) .verifySystemClockWasSetAndResetCallTracking( expectedSystemClockMillis /* expectedNetworkBroadcast */) - .assertLatestPhoneSuggestion(phoneId, phoneSuggestion); + .assertLatestTelephonySuggestion(slotIndex, telephonySuggestion); - // Look inside and check what the strategy considers the current best phone suggestion. - assertEquals(phoneSuggestion, mScript.peekBestPhoneSuggestion()); + // Look inside and check what the strategy considers the current best telephony suggestion. + assertEquals(telephonySuggestion, mScript.peekBestTelephonySuggestion()); - // Simulate time passing, long enough that phoneSuggestion is now too old. + // Simulate time passing, long enough that telephonySuggestion is now too old. mScript.simulateTimePassing(TimeDetectorStrategyImpl.MAX_UTC_TIME_AGE_MILLIS); - // Look inside and check what the strategy considers the current best phone suggestion. It - // should still be the, it's just no longer used. - assertNull(mScript.peekBestPhoneSuggestion()); - mScript.assertLatestPhoneSuggestion(phoneId, phoneSuggestion); + // Look inside and check what the strategy considers the current best telephony suggestion. + // It should still be the, it's just no longer used. + assertNull(mScript.peekBestTelephonySuggestion()); + mScript.assertLatestTelephonySuggestion(slotIndex, telephonySuggestion); } @Test @@ -413,21 +413,21 @@ public class TimeDetectorStrategyImplTest { mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO) .pokeAutoTimeDetectionEnabled(true); - int phoneId = ARBITRARY_PHONE_ID; + int slotIndex = ARBITRARY_SLOT_INDEX; - // Simulate a phone suggestion. + // Simulate a telephony suggestion. long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS; - PhoneTimeSuggestion phoneTimeSuggestion = - mScript.generatePhoneTimeSuggestion(phoneId, testTimeMillis); + TelephonyTimeSuggestion telephonyTimeSuggestion = + mScript.generateTelephonyTimeSuggestion(slotIndex, testTimeMillis); // Simulate the passage of time. mScript.simulateTimePassing(); long expectedAutoClockMillis = - mScript.calculateTimeInMillisForNow(phoneTimeSuggestion.getUtcTime()); - mScript.simulatePhoneTimeSuggestion(phoneTimeSuggestion) + mScript.calculateTimeInMillisForNow(telephonyTimeSuggestion.getUtcTime()); + mScript.simulateTelephonyTimeSuggestion(telephonyTimeSuggestion) .verifySystemClockWasSetAndResetCallTracking(expectedAutoClockMillis) - .assertLatestPhoneSuggestion(phoneId, phoneTimeSuggestion); + .assertLatestTelephonySuggestion(slotIndex, telephonyTimeSuggestion); // Simulate the passage of time. mScript.simulateTimePassing(); @@ -435,7 +435,7 @@ public class TimeDetectorStrategyImplTest { // Switch to manual. mScript.simulateAutoTimeDetectionToggle() .verifySystemClockWasNotSetAndResetCallTracking() - .assertLatestPhoneSuggestion(phoneId, phoneTimeSuggestion); + .assertLatestTelephonySuggestion(slotIndex, telephonyTimeSuggestion); // Simulate the passage of time. mScript.simulateTimePassing(); @@ -450,7 +450,7 @@ public class TimeDetectorStrategyImplTest { mScript.calculateTimeInMillisForNow(manualTimeSuggestion.getUtcTime()); mScript.simulateManualTimeSuggestion(manualTimeSuggestion) .verifySystemClockWasSetAndResetCallTracking(expectedManualClockMillis) - .assertLatestPhoneSuggestion(phoneId, phoneTimeSuggestion); + .assertLatestTelephonySuggestion(slotIndex, telephonyTimeSuggestion); // Simulate the passage of time. mScript.simulateTimePassing(); @@ -459,14 +459,14 @@ public class TimeDetectorStrategyImplTest { mScript.simulateAutoTimeDetectionToggle(); expectedAutoClockMillis = - mScript.calculateTimeInMillisForNow(phoneTimeSuggestion.getUtcTime()); + mScript.calculateTimeInMillisForNow(telephonyTimeSuggestion.getUtcTime()); mScript.verifySystemClockWasSetAndResetCallTracking(expectedAutoClockMillis) - .assertLatestPhoneSuggestion(phoneId, phoneTimeSuggestion); + .assertLatestTelephonySuggestion(slotIndex, telephonyTimeSuggestion); // Switch back to manual - nothing should happen to the clock. mScript.simulateAutoTimeDetectionToggle() .verifySystemClockWasNotSetAndResetCallTracking() - .assertLatestPhoneSuggestion(phoneId, phoneTimeSuggestion); + .assertLatestTelephonySuggestion(slotIndex, telephonyTimeSuggestion); } /** @@ -515,19 +515,19 @@ public class TimeDetectorStrategyImplTest { } @Test - public void testSuggestNetworkTime_phoneSuggestionsBeatNetworkSuggestions() { + public void testSuggestNetworkTime_telephonySuggestionsBeatNetworkSuggestions() { mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO) .pokeAutoTimeDetectionEnabled(true); // Three obviously different times that could not be mistaken for each other. long networkTimeMillis1 = ARBITRARY_TEST_TIME_MILLIS; long networkTimeMillis2 = ARBITRARY_TEST_TIME_MILLIS + Duration.ofDays(30).toMillis(); - long phoneTimeMillis = ARBITRARY_TEST_TIME_MILLIS + Duration.ofDays(60).toMillis(); + long telephonyTimeMillis = ARBITRARY_TEST_TIME_MILLIS + Duration.ofDays(60).toMillis(); // A small increment used to simulate the passage of time, but not enough to interfere with // macro-level time changes associated with suggestion age. final long smallTimeIncrementMillis = 101; - // A network suggestion is made. It should be used because there is no phone suggestion. + // A network suggestion is made. It should be used because there is no telephony suggestion. NetworkTimeSuggestion networkTimeSuggestion1 = mScript.generateNetworkTimeSuggestion(networkTimeMillis1); mScript.simulateTimePassing(smallTimeIncrementMillis) @@ -536,37 +536,37 @@ public class TimeDetectorStrategyImplTest { mScript.calculateTimeInMillisForNow(networkTimeSuggestion1.getUtcTime())); // Check internal state. - mScript.assertLatestPhoneSuggestion(ARBITRARY_PHONE_ID, null) + mScript.assertLatestTelephonySuggestion(ARBITRARY_SLOT_INDEX, null) .assertLatestNetworkSuggestion(networkTimeSuggestion1); assertEquals(networkTimeSuggestion1, mScript.peekLatestValidNetworkSuggestion()); - assertNull(mScript.peekBestPhoneSuggestion()); + assertNull(mScript.peekBestTelephonySuggestion()); // Simulate a little time passing. mScript.simulateTimePassing(smallTimeIncrementMillis) .verifySystemClockWasNotSetAndResetCallTracking(); - // Now a phone suggestion is made. Phone suggestions are prioritized over network + // Now a telephony suggestion is made. Telephony suggestions are prioritized over network // suggestions so it should "win". - PhoneTimeSuggestion phoneTimeSuggestion = - mScript.generatePhoneTimeSuggestion(ARBITRARY_PHONE_ID, phoneTimeMillis); + TelephonyTimeSuggestion telephonyTimeSuggestion = + mScript.generateTelephonyTimeSuggestion(ARBITRARY_SLOT_INDEX, telephonyTimeMillis); mScript.simulateTimePassing(smallTimeIncrementMillis) - .simulatePhoneTimeSuggestion(phoneTimeSuggestion) + .simulateTelephonyTimeSuggestion(telephonyTimeSuggestion) .verifySystemClockWasSetAndResetCallTracking( - mScript.calculateTimeInMillisForNow(phoneTimeSuggestion.getUtcTime())); + mScript.calculateTimeInMillisForNow(telephonyTimeSuggestion.getUtcTime())); // Check internal state. - mScript.assertLatestPhoneSuggestion(ARBITRARY_PHONE_ID, phoneTimeSuggestion) + mScript.assertLatestTelephonySuggestion(ARBITRARY_SLOT_INDEX, telephonyTimeSuggestion) .assertLatestNetworkSuggestion(networkTimeSuggestion1); assertEquals(networkTimeSuggestion1, mScript.peekLatestValidNetworkSuggestion()); - assertEquals(phoneTimeSuggestion, mScript.peekBestPhoneSuggestion()); + assertEquals(telephonyTimeSuggestion, mScript.peekBestTelephonySuggestion()); // Simulate some significant time passing: half the time allowed before a time signal // becomes "too old to use". mScript.simulateTimePassing(TimeDetectorStrategyImpl.MAX_UTC_TIME_AGE_MILLIS / 2) .verifySystemClockWasNotSetAndResetCallTracking(); - // Now another network suggestion is made. Phone suggestions are prioritized over network - // suggestions so the latest phone suggestion should still "win". + // Now another network suggestion is made. Telephony suggestions are prioritized over + // network suggestions so the latest telephony suggestion should still "win". NetworkTimeSuggestion networkTimeSuggestion2 = mScript.generateNetworkTimeSuggestion(networkTimeMillis2); mScript.simulateTimePassing(smallTimeIncrementMillis) @@ -574,14 +574,14 @@ public class TimeDetectorStrategyImplTest { .verifySystemClockWasNotSetAndResetCallTracking(); // Check internal state. - mScript.assertLatestPhoneSuggestion(ARBITRARY_PHONE_ID, phoneTimeSuggestion) + mScript.assertLatestTelephonySuggestion(ARBITRARY_SLOT_INDEX, telephonyTimeSuggestion) .assertLatestNetworkSuggestion(networkTimeSuggestion2); assertEquals(networkTimeSuggestion2, mScript.peekLatestValidNetworkSuggestion()); - assertEquals(phoneTimeSuggestion, mScript.peekBestPhoneSuggestion()); + assertEquals(telephonyTimeSuggestion, mScript.peekBestTelephonySuggestion()); // Simulate some significant time passing: half the time allowed before a time signal - // becomes "too old to use". This should mean that phoneTimeSuggestion is now too old to be - // used but networkTimeSuggestion2 is not. + // becomes "too old to use". This should mean that telephonyTimeSuggestion is now too old to + // be used but networkTimeSuggestion2 is not. mScript.simulateTimePassing(TimeDetectorStrategyImpl.MAX_UTC_TIME_AGE_MILLIS / 2); // NOTE: The TimeDetectorStrategyImpl doesn't set an alarm for the point when the last @@ -591,10 +591,10 @@ public class TimeDetectorStrategyImplTest { mScript.verifySystemClockWasNotSetAndResetCallTracking(); // Check internal state. - mScript.assertLatestPhoneSuggestion(ARBITRARY_PHONE_ID, phoneTimeSuggestion) + mScript.assertLatestTelephonySuggestion(ARBITRARY_SLOT_INDEX, telephonyTimeSuggestion) .assertLatestNetworkSuggestion(networkTimeSuggestion2); assertEquals(networkTimeSuggestion2, mScript.peekLatestValidNetworkSuggestion()); - assertNull(mScript.peekBestPhoneSuggestion()); + assertNull(mScript.peekBestTelephonySuggestion()); // Toggle auto-time off and on to force the detection logic to run. mScript.simulateAutoTimeDetectionToggle() @@ -606,10 +606,10 @@ public class TimeDetectorStrategyImplTest { mScript.calculateTimeInMillisForNow(networkTimeSuggestion2.getUtcTime())); // Check internal state. - mScript.assertLatestPhoneSuggestion(ARBITRARY_PHONE_ID, phoneTimeSuggestion) + mScript.assertLatestTelephonySuggestion(ARBITRARY_SLOT_INDEX, telephonyTimeSuggestion) .assertLatestNetworkSuggestion(networkTimeSuggestion2); assertEquals(networkTimeSuggestion2, mScript.peekLatestValidNetworkSuggestion()); - assertNull(mScript.peekBestPhoneSuggestion()); + assertNull(mScript.peekBestTelephonySuggestion()); } /** @@ -760,8 +760,8 @@ public class TimeDetectorStrategyImplTest { return mFakeCallback.peekSystemClockMillis(); } - Script simulatePhoneTimeSuggestion(PhoneTimeSuggestion timeSuggestion) { - mTimeDetectorStrategy.suggestPhoneTime(timeSuggestion); + Script simulateTelephonyTimeSuggestion(TelephonyTimeSuggestion timeSuggestion) { + mTimeDetectorStrategy.suggestTelephonyTime(timeSuggestion); return this; } @@ -806,10 +806,10 @@ public class TimeDetectorStrategyImplTest { } /** - * White box test info: Asserts the latest suggestion for the phone ID is as expected. + * White box test info: Asserts the latest suggestion for the slotIndex is as expected. */ - Script assertLatestPhoneSuggestion(int phoneId, PhoneTimeSuggestion expected) { - assertEquals(expected, mTimeDetectorStrategy.getLatestPhoneSuggestion(phoneId)); + Script assertLatestTelephonySuggestion(int slotIndex, TelephonyTimeSuggestion expected) { + assertEquals(expected, mTimeDetectorStrategy.getLatestTelephonySuggestion(slotIndex)); return this; } @@ -822,11 +822,11 @@ public class TimeDetectorStrategyImplTest { } /** - * White box test info: Returns the phone suggestion that would be used, if any, given the - * current elapsed real time clock and regardless of origin prioritization. + * White box test info: Returns the telephony suggestion that would be used, if any, given + * the current elapsed real time clock and regardless of origin prioritization. */ - PhoneTimeSuggestion peekBestPhoneSuggestion() { - return mTimeDetectorStrategy.findBestPhoneSuggestionForTests(); + TelephonyTimeSuggestion peekBestTelephonySuggestion() { + return mTimeDetectorStrategy.findBestTelephonySuggestionForTests(); } /** @@ -848,15 +848,15 @@ public class TimeDetectorStrategyImplTest { } /** - * Generates a PhoneTimeSuggestion using the current elapsed realtime clock for the - * reference time. + * Generates a {@link TelephonyTimeSuggestion} using the current elapsed realtime clock for + * the reference time. */ - PhoneTimeSuggestion generatePhoneTimeSuggestion(int phoneId, Long timeMillis) { + TelephonyTimeSuggestion generateTelephonyTimeSuggestion(int slotIndex, Long timeMillis) { TimestampedValue<Long> time = null; if (timeMillis != null) { time = new TimestampedValue<>(peekElapsedRealtimeMillis(), timeMillis); } - return createPhoneTimeSuggestion(phoneId, time); + return createTelephonyTimeSuggestion(slotIndex, time); } /** @@ -878,9 +878,9 @@ public class TimeDetectorStrategyImplTest { } } - private static PhoneTimeSuggestion createPhoneTimeSuggestion(int phoneId, + private static TelephonyTimeSuggestion createTelephonyTimeSuggestion(int slotIndex, TimestampedValue<Long> utcTime) { - return new PhoneTimeSuggestion.Builder(phoneId) + return new TelephonyTimeSuggestion.Builder(slotIndex) .setUtcTime(utcTime) .build(); } diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java index 3e7d40a0335a..039c2b4933e9 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java @@ -29,7 +29,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.timezonedetector.ManualTimeZoneSuggestion; -import android.app.timezonedetector.PhoneTimeZoneSuggestion; +import android.app.timezonedetector.TelephonyTimeZoneSuggestion; import android.content.Context; import android.content.pm.PackageManager; import android.os.HandlerThread; @@ -76,35 +76,35 @@ public class TimeZoneDetectorServiceTest { } @Test(expected = SecurityException.class) - public void testSuggestPhoneTime_withoutPermission() { + public void testSuggestTelephonyTime_withoutPermission() { doThrow(new SecurityException("Mock")) .when(mMockContext).enforceCallingPermission(anyString(), any()); - PhoneTimeZoneSuggestion timeZoneSuggestion = createPhoneTimeZoneSuggestion(); + TelephonyTimeZoneSuggestion timeZoneSuggestion = createTelephonyTimeZoneSuggestion(); try { - mTimeZoneDetectorService.suggestPhoneTimeZone(timeZoneSuggestion); + mTimeZoneDetectorService.suggestTelephonyTimeZone(timeZoneSuggestion); fail(); } finally { verify(mMockContext).enforceCallingPermission( - eq(android.Manifest.permission.SUGGEST_PHONE_TIME_AND_ZONE), + eq(android.Manifest.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE), anyString()); } } @Test - public void testSuggestPhoneTimeZone() throws Exception { + public void testSuggestTelephonyTimeZone() throws Exception { doNothing().when(mMockContext).enforceCallingPermission(anyString(), any()); - PhoneTimeZoneSuggestion timeZoneSuggestion = createPhoneTimeZoneSuggestion(); - mTimeZoneDetectorService.suggestPhoneTimeZone(timeZoneSuggestion); + TelephonyTimeZoneSuggestion timeZoneSuggestion = createTelephonyTimeZoneSuggestion(); + mTimeZoneDetectorService.suggestTelephonyTimeZone(timeZoneSuggestion); mTestHandler.assertTotalMessagesEnqueued(1); verify(mMockContext).enforceCallingPermission( - eq(android.Manifest.permission.SUGGEST_PHONE_TIME_AND_ZONE), + eq(android.Manifest.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE), anyString()); mTestHandler.waitForMessagesToBeProcessed(); - mStubbedTimeZoneDetectorStrategy.verifySuggestPhoneTimeZoneCalled(timeZoneSuggestion); + mStubbedTimeZoneDetectorStrategy.verifySuggestTelephonyTimeZoneCalled(timeZoneSuggestion); } @Test(expected = SecurityException.class) @@ -165,12 +165,12 @@ public class TimeZoneDetectorServiceTest { mStubbedTimeZoneDetectorStrategy.verifyHandleAutoTimeZoneDetectionChangedCalled(); } - private static PhoneTimeZoneSuggestion createPhoneTimeZoneSuggestion() { + private static TelephonyTimeZoneSuggestion createTelephonyTimeZoneSuggestion() { int slotIndex = 1234; - return new PhoneTimeZoneSuggestion.Builder(slotIndex) + return new TelephonyTimeZoneSuggestion.Builder(slotIndex) .setZoneId("TestZoneId") - .setMatchType(PhoneTimeZoneSuggestion.MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET) - .setQuality(PhoneTimeZoneSuggestion.QUALITY_SINGLE_ZONE) + .setMatchType(TelephonyTimeZoneSuggestion.MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET) + .setQuality(TelephonyTimeZoneSuggestion.QUALITY_SINGLE_ZONE) .build(); } @@ -181,14 +181,14 @@ public class TimeZoneDetectorServiceTest { private static class StubbedTimeZoneDetectorStrategy implements TimeZoneDetectorStrategy { // Call tracking. - private PhoneTimeZoneSuggestion mLastPhoneSuggestion; + private TelephonyTimeZoneSuggestion mLastTelephonySuggestion; private ManualTimeZoneSuggestion mLastManualSuggestion; private boolean mHandleAutoTimeZoneDetectionChangedCalled; private boolean mDumpCalled; @Override - public void suggestPhoneTimeZone(PhoneTimeZoneSuggestion timeZoneSuggestion) { - mLastPhoneSuggestion = timeZoneSuggestion; + public void suggestTelephonyTimeZone(TelephonyTimeZoneSuggestion timeZoneSuggestion) { + mLastTelephonySuggestion = timeZoneSuggestion; } @Override @@ -207,14 +207,14 @@ public class TimeZoneDetectorServiceTest { } void resetCallTracking() { - mLastPhoneSuggestion = null; + mLastTelephonySuggestion = null; mLastManualSuggestion = null; mHandleAutoTimeZoneDetectionChangedCalled = false; mDumpCalled = false; } - void verifySuggestPhoneTimeZoneCalled(PhoneTimeZoneSuggestion expectedSuggestion) { - assertEquals(expectedSuggestion, mLastPhoneSuggestion); + void verifySuggestTelephonyTimeZoneCalled(TelephonyTimeZoneSuggestion expectedSuggestion) { + assertEquals(expectedSuggestion, mLastTelephonySuggestion); } public void verifySuggestManualTimeZoneCalled(ManualTimeZoneSuggestion expectedSuggestion) { diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java index 1e387110ed43..ba309679e47a 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java @@ -16,20 +16,20 @@ package com.android.server.timezonedetector; -import static android.app.timezonedetector.PhoneTimeZoneSuggestion.MATCH_TYPE_EMULATOR_ZONE_ID; -import static android.app.timezonedetector.PhoneTimeZoneSuggestion.MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET; -import static android.app.timezonedetector.PhoneTimeZoneSuggestion.MATCH_TYPE_NETWORK_COUNTRY_ONLY; -import static android.app.timezonedetector.PhoneTimeZoneSuggestion.MATCH_TYPE_TEST_NETWORK_OFFSET_ONLY; -import static android.app.timezonedetector.PhoneTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS; -import static android.app.timezonedetector.PhoneTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET; -import static android.app.timezonedetector.PhoneTimeZoneSuggestion.QUALITY_SINGLE_ZONE; - -import static com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.PHONE_SCORE_HIGH; -import static com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.PHONE_SCORE_HIGHEST; -import static com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.PHONE_SCORE_LOW; -import static com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.PHONE_SCORE_MEDIUM; -import static com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.PHONE_SCORE_NONE; -import static com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.PHONE_SCORE_USAGE_THRESHOLD; +import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.MATCH_TYPE_EMULATOR_ZONE_ID; +import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET; +import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.MATCH_TYPE_NETWORK_COUNTRY_ONLY; +import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.MATCH_TYPE_TEST_NETWORK_OFFSET_ONLY; +import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS; +import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET; +import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_SINGLE_ZONE; + +import static com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.TELEPHONY_SCORE_HIGH; +import static com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.TELEPHONY_SCORE_HIGHEST; +import static com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.TELEPHONY_SCORE_LOW; +import static com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.TELEPHONY_SCORE_MEDIUM; +import static com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.TELEPHONY_SCORE_NONE; +import static com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.TELEPHONY_SCORE_USAGE_THRESHOLD; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -37,11 +37,11 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import android.app.timezonedetector.ManualTimeZoneSuggestion; -import android.app.timezonedetector.PhoneTimeZoneSuggestion; -import android.app.timezonedetector.PhoneTimeZoneSuggestion.MatchType; -import android.app.timezonedetector.PhoneTimeZoneSuggestion.Quality; +import android.app.timezonedetector.TelephonyTimeZoneSuggestion; +import android.app.timezonedetector.TelephonyTimeZoneSuggestion.MatchType; +import android.app.timezonedetector.TelephonyTimeZoneSuggestion.Quality; -import com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.QualifiedPhoneTimeZoneSuggestion; +import com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.QualifiedTelephonyTimeZoneSuggestion; import org.junit.Before; import org.junit.Test; @@ -58,24 +58,24 @@ public class TimeZoneDetectorStrategyImplTest { /** A time zone used for initialization that does not occur elsewhere in tests. */ private static final String ARBITRARY_TIME_ZONE_ID = "Etc/UTC"; - private static final int PHONE1_ID = 10000; - private static final int PHONE2_ID = 20000; + private static final int SLOT_INDEX1 = 10000; + private static final int SLOT_INDEX2 = 20000; // Suggestion test cases are ordered so that each successive one is of the same or higher score // than the previous. private static final SuggestionTestCase[] TEST_CASES = new SuggestionTestCase[] { newTestCase(MATCH_TYPE_NETWORK_COUNTRY_ONLY, - QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS, PHONE_SCORE_LOW), + QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS, TELEPHONY_SCORE_LOW), newTestCase(MATCH_TYPE_NETWORK_COUNTRY_ONLY, QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET, - PHONE_SCORE_MEDIUM), + TELEPHONY_SCORE_MEDIUM), newTestCase(MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET, - QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET, PHONE_SCORE_MEDIUM), - newTestCase(MATCH_TYPE_NETWORK_COUNTRY_ONLY, QUALITY_SINGLE_ZONE, PHONE_SCORE_HIGH), + QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET, TELEPHONY_SCORE_MEDIUM), + newTestCase(MATCH_TYPE_NETWORK_COUNTRY_ONLY, QUALITY_SINGLE_ZONE, TELEPHONY_SCORE_HIGH), newTestCase(MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET, QUALITY_SINGLE_ZONE, - PHONE_SCORE_HIGH), + TELEPHONY_SCORE_HIGH), newTestCase(MATCH_TYPE_TEST_NETWORK_OFFSET_ONLY, - QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET, PHONE_SCORE_HIGHEST), - newTestCase(MATCH_TYPE_EMULATOR_ZONE_ID, QUALITY_SINGLE_ZONE, PHONE_SCORE_HIGHEST), + QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET, TELEPHONY_SCORE_HIGHEST), + newTestCase(MATCH_TYPE_EMULATOR_ZONE_ID, QUALITY_SINGLE_ZONE, TELEPHONY_SCORE_HIGHEST), }; private TimeZoneDetectorStrategyImpl mTimeZoneDetectorStrategy; @@ -89,76 +89,82 @@ public class TimeZoneDetectorStrategyImplTest { } @Test - public void testEmptyPhoneSuggestions() { - PhoneTimeZoneSuggestion phone1TimeZoneSuggestion = createEmptyPhone1Suggestion(); - PhoneTimeZoneSuggestion phone2TimeZoneSuggestion = createEmptyPhone2Suggestion(); + public void testEmptyTelephonySuggestions() { + TelephonyTimeZoneSuggestion slotIndex1TimeZoneSuggestion = + createEmptySlotIndex1Suggestion(); + TelephonyTimeZoneSuggestion slotIndex2TimeZoneSuggestion = + createEmptySlotIndex2Suggestion(); Script script = new Script() .initializeAutoTimeZoneDetection(true) .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID); - script.suggestPhoneTimeZone(phone1TimeZoneSuggestion) + script.suggestTelephonyTimeZone(slotIndex1TimeZoneSuggestion) .verifyTimeZoneNotSet(); // Assert internal service state. - QualifiedPhoneTimeZoneSuggestion expectedPhone1ScoredSuggestion = - new QualifiedPhoneTimeZoneSuggestion(phone1TimeZoneSuggestion, PHONE_SCORE_NONE); - assertEquals(expectedPhone1ScoredSuggestion, - mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE1_ID)); - assertNull(mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE2_ID)); - assertEquals(expectedPhone1ScoredSuggestion, - mTimeZoneDetectorStrategy.findBestPhoneSuggestionForTests()); - - script.suggestPhoneTimeZone(phone2TimeZoneSuggestion) + QualifiedTelephonyTimeZoneSuggestion expectedSlotIndex1ScoredSuggestion = + new QualifiedTelephonyTimeZoneSuggestion(slotIndex1TimeZoneSuggestion, + TELEPHONY_SCORE_NONE); + assertEquals(expectedSlotIndex1ScoredSuggestion, + mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX1)); + assertNull(mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX2)); + assertEquals(expectedSlotIndex1ScoredSuggestion, + mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests()); + + script.suggestTelephonyTimeZone(slotIndex2TimeZoneSuggestion) .verifyTimeZoneNotSet(); // Assert internal service state. - QualifiedPhoneTimeZoneSuggestion expectedPhone2ScoredSuggestion = - new QualifiedPhoneTimeZoneSuggestion(phone2TimeZoneSuggestion, PHONE_SCORE_NONE); - assertEquals(expectedPhone1ScoredSuggestion, - mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE1_ID)); - assertEquals(expectedPhone2ScoredSuggestion, - mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE2_ID)); - // Phone 1 should always beat phone 2, all other things being equal. - assertEquals(expectedPhone1ScoredSuggestion, - mTimeZoneDetectorStrategy.findBestPhoneSuggestionForTests()); + QualifiedTelephonyTimeZoneSuggestion expectedSlotIndex2ScoredSuggestion = + new QualifiedTelephonyTimeZoneSuggestion(slotIndex2TimeZoneSuggestion, + TELEPHONY_SCORE_NONE); + assertEquals(expectedSlotIndex1ScoredSuggestion, + mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX1)); + assertEquals(expectedSlotIndex2ScoredSuggestion, + mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX2)); + // SlotIndex1 should always beat slotIndex2, all other things being equal. + assertEquals(expectedSlotIndex1ScoredSuggestion, + mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests()); } @Test - public void testFirstPlausiblePhoneSuggestionAcceptedWhenTimeZoneUninitialized() { + public void testFirstPlausibleTelephonySuggestionAcceptedWhenTimeZoneUninitialized() { SuggestionTestCase testCase = newTestCase(MATCH_TYPE_NETWORK_COUNTRY_ONLY, - QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS, PHONE_SCORE_LOW); - PhoneTimeZoneSuggestion lowQualitySuggestion = - testCase.createSuggestion(PHONE1_ID, "America/New_York"); + QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS, TELEPHONY_SCORE_LOW); + TelephonyTimeZoneSuggestion lowQualitySuggestion = + testCase.createSuggestion(SLOT_INDEX1, "America/New_York"); // The device time zone setting is left uninitialized. Script script = new Script() .initializeAutoTimeZoneDetection(true); // The very first suggestion will be taken. - script.suggestPhoneTimeZone(lowQualitySuggestion) + script.suggestTelephonyTimeZone(lowQualitySuggestion) .verifyTimeZoneSetAndReset(lowQualitySuggestion); // Assert internal service state. - QualifiedPhoneTimeZoneSuggestion expectedScoredSuggestion = - new QualifiedPhoneTimeZoneSuggestion(lowQualitySuggestion, testCase.expectedScore); + QualifiedTelephonyTimeZoneSuggestion expectedScoredSuggestion = + new QualifiedTelephonyTimeZoneSuggestion( + lowQualitySuggestion, testCase.expectedScore); assertEquals(expectedScoredSuggestion, - mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE1_ID)); + mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX1)); assertEquals(expectedScoredSuggestion, - mTimeZoneDetectorStrategy.findBestPhoneSuggestionForTests()); + mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests()); // Another low quality suggestion will be ignored now that the setting is initialized. - PhoneTimeZoneSuggestion lowQualitySuggestion2 = - testCase.createSuggestion(PHONE1_ID, "America/Los_Angeles"); - script.suggestPhoneTimeZone(lowQualitySuggestion2) + TelephonyTimeZoneSuggestion lowQualitySuggestion2 = + testCase.createSuggestion(SLOT_INDEX1, "America/Los_Angeles"); + script.suggestTelephonyTimeZone(lowQualitySuggestion2) .verifyTimeZoneNotSet(); // Assert internal service state. - QualifiedPhoneTimeZoneSuggestion expectedScoredSuggestion2 = - new QualifiedPhoneTimeZoneSuggestion(lowQualitySuggestion2, testCase.expectedScore); + QualifiedTelephonyTimeZoneSuggestion expectedScoredSuggestion2 = + new QualifiedTelephonyTimeZoneSuggestion( + lowQualitySuggestion2, testCase.expectedScore); assertEquals(expectedScoredSuggestion2, - mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE1_ID)); + mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX1)); assertEquals(expectedScoredSuggestion2, - mTimeZoneDetectorStrategy.findBestPhoneSuggestionForTests()); + mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests()); } /** @@ -174,28 +180,28 @@ public class TimeZoneDetectorStrategyImplTest { script.initializeAutoTimeZoneDetection(false) .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID); - PhoneTimeZoneSuggestion suggestion = - testCase.createSuggestion(PHONE1_ID, "Europe/London"); - script.suggestPhoneTimeZone(suggestion); + TelephonyTimeZoneSuggestion suggestion = + testCase.createSuggestion(SLOT_INDEX1, "Europe/London"); + script.suggestTelephonyTimeZone(suggestion); // When time zone detection is not enabled, the time zone suggestion will not be set // regardless of the score. script.verifyTimeZoneNotSet(); // Assert internal service state. - QualifiedPhoneTimeZoneSuggestion expectedScoredSuggestion = - new QualifiedPhoneTimeZoneSuggestion(suggestion, testCase.expectedScore); + QualifiedTelephonyTimeZoneSuggestion expectedScoredSuggestion = + new QualifiedTelephonyTimeZoneSuggestion(suggestion, testCase.expectedScore); assertEquals(expectedScoredSuggestion, - mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE1_ID)); + mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX1)); assertEquals(expectedScoredSuggestion, - mTimeZoneDetectorStrategy.findBestPhoneSuggestionForTests()); + mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests()); // Toggling the time zone setting on should cause the device setting to be set. script.autoTimeZoneDetectionEnabled(true); // When time zone detection is already enabled the suggestion (if it scores highly // enough) should be set immediately. - if (testCase.expectedScore >= PHONE_SCORE_USAGE_THRESHOLD) { + if (testCase.expectedScore >= TELEPHONY_SCORE_USAGE_THRESHOLD) { script.verifyTimeZoneSetAndReset(suggestion); } else { script.verifyTimeZoneNotSet(); @@ -203,9 +209,9 @@ public class TimeZoneDetectorStrategyImplTest { // Assert internal service state. assertEquals(expectedScoredSuggestion, - mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE1_ID)); + mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX1)); assertEquals(expectedScoredSuggestion, - mTimeZoneDetectorStrategy.findBestPhoneSuggestionForTests()); + mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests()); // Toggling the time zone setting should off should do nothing. script.autoTimeZoneDetectionEnabled(false) @@ -213,20 +219,20 @@ public class TimeZoneDetectorStrategyImplTest { // Assert internal service state. assertEquals(expectedScoredSuggestion, - mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE1_ID)); + mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX1)); assertEquals(expectedScoredSuggestion, - mTimeZoneDetectorStrategy.findBestPhoneSuggestionForTests()); + mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests()); } } @Test - public void testPhoneSuggestionsSinglePhone() { + public void testTelephonySuggestionsSingleSlotId() { Script script = new Script() .initializeAutoTimeZoneDetection(true) .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID); for (SuggestionTestCase testCase : TEST_CASES) { - makePhone1SuggestionAndCheckState(script, testCase); + makeSlotIndex1SuggestionAndCheckState(script, testCase); } /* @@ -241,125 +247,128 @@ public class TimeZoneDetectorStrategyImplTest { Collections.reverse(descendingCasesByScore); for (SuggestionTestCase testCase : descendingCasesByScore) { - makePhone1SuggestionAndCheckState(script, testCase); + makeSlotIndex1SuggestionAndCheckState(script, testCase); } } - private void makePhone1SuggestionAndCheckState(Script script, SuggestionTestCase testCase) { + private void makeSlotIndex1SuggestionAndCheckState(Script script, SuggestionTestCase testCase) { // Give the next suggestion a different zone from the currently set device time zone; String currentZoneId = mFakeTimeZoneDetectorStrategyCallback.getDeviceTimeZone(); String suggestionZoneId = "Europe/London".equals(currentZoneId) ? "Europe/Paris" : "Europe/London"; - PhoneTimeZoneSuggestion zonePhone1Suggestion = - testCase.createSuggestion(PHONE1_ID, suggestionZoneId); - QualifiedPhoneTimeZoneSuggestion expectedZonePhone1ScoredSuggestion = - new QualifiedPhoneTimeZoneSuggestion(zonePhone1Suggestion, testCase.expectedScore); - - script.suggestPhoneTimeZone(zonePhone1Suggestion); - if (testCase.expectedScore >= PHONE_SCORE_USAGE_THRESHOLD) { - script.verifyTimeZoneSetAndReset(zonePhone1Suggestion); + TelephonyTimeZoneSuggestion zoneSlotIndex1Suggestion = + testCase.createSuggestion(SLOT_INDEX1, suggestionZoneId); + QualifiedTelephonyTimeZoneSuggestion expectedZoneSlotIndex1ScoredSuggestion = + new QualifiedTelephonyTimeZoneSuggestion( + zoneSlotIndex1Suggestion, testCase.expectedScore); + + script.suggestTelephonyTimeZone(zoneSlotIndex1Suggestion); + if (testCase.expectedScore >= TELEPHONY_SCORE_USAGE_THRESHOLD) { + script.verifyTimeZoneSetAndReset(zoneSlotIndex1Suggestion); } else { script.verifyTimeZoneNotSet(); } // Assert internal service state. - assertEquals(expectedZonePhone1ScoredSuggestion, - mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE1_ID)); - assertEquals(expectedZonePhone1ScoredSuggestion, - mTimeZoneDetectorStrategy.findBestPhoneSuggestionForTests()); + assertEquals(expectedZoneSlotIndex1ScoredSuggestion, + mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX1)); + assertEquals(expectedZoneSlotIndex1ScoredSuggestion, + mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests()); } /** - * Tries a set of test cases to see if the phone with the lowest ID is given preference. This - * test also confirms that the time zone setting would only be set if a suggestion is of - * sufficient quality. + * Tries a set of test cases to see if the slotIndex with the lowest numeric value is given + * preference. This test also confirms that the time zone setting would only be set if a + * suggestion is of sufficient quality. */ @Test - public void testMultiplePhoneSuggestionScoringAndPhoneIdBias() { + public void testMultipleSlotIndexSuggestionScoringAndSlotIndexBias() { String[] zoneIds = { "Europe/London", "Europe/Paris" }; - PhoneTimeZoneSuggestion emptyPhone1Suggestion = createEmptyPhone1Suggestion(); - PhoneTimeZoneSuggestion emptyPhone2Suggestion = createEmptyPhone2Suggestion(); - QualifiedPhoneTimeZoneSuggestion expectedEmptyPhone1ScoredSuggestion = - new QualifiedPhoneTimeZoneSuggestion(emptyPhone1Suggestion, PHONE_SCORE_NONE); - QualifiedPhoneTimeZoneSuggestion expectedEmptyPhone2ScoredSuggestion = - new QualifiedPhoneTimeZoneSuggestion(emptyPhone2Suggestion, PHONE_SCORE_NONE); + TelephonyTimeZoneSuggestion emptySlotIndex1Suggestion = createEmptySlotIndex1Suggestion(); + TelephonyTimeZoneSuggestion emptySlotIndex2Suggestion = createEmptySlotIndex2Suggestion(); + QualifiedTelephonyTimeZoneSuggestion expectedEmptySlotIndex1ScoredSuggestion = + new QualifiedTelephonyTimeZoneSuggestion(emptySlotIndex1Suggestion, + TELEPHONY_SCORE_NONE); + QualifiedTelephonyTimeZoneSuggestion expectedEmptySlotIndex2ScoredSuggestion = + new QualifiedTelephonyTimeZoneSuggestion(emptySlotIndex2Suggestion, + TELEPHONY_SCORE_NONE); Script script = new Script() .initializeAutoTimeZoneDetection(true) .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID) // Initialize the latest suggestions as empty so we don't need to worry about nulls // below for the first loop. - .suggestPhoneTimeZone(emptyPhone1Suggestion) - .suggestPhoneTimeZone(emptyPhone2Suggestion) + .suggestTelephonyTimeZone(emptySlotIndex1Suggestion) + .suggestTelephonyTimeZone(emptySlotIndex2Suggestion) .resetState(); for (SuggestionTestCase testCase : TEST_CASES) { - PhoneTimeZoneSuggestion zonePhone1Suggestion = - testCase.createSuggestion(PHONE1_ID, zoneIds[0]); - PhoneTimeZoneSuggestion zonePhone2Suggestion = - testCase.createSuggestion(PHONE2_ID, zoneIds[1]); - QualifiedPhoneTimeZoneSuggestion expectedZonePhone1ScoredSuggestion = - new QualifiedPhoneTimeZoneSuggestion(zonePhone1Suggestion, + TelephonyTimeZoneSuggestion zoneSlotIndex1Suggestion = + testCase.createSuggestion(SLOT_INDEX1, zoneIds[0]); + TelephonyTimeZoneSuggestion zoneSlotIndex2Suggestion = + testCase.createSuggestion(SLOT_INDEX2, zoneIds[1]); + QualifiedTelephonyTimeZoneSuggestion expectedZoneSlotIndex1ScoredSuggestion = + new QualifiedTelephonyTimeZoneSuggestion(zoneSlotIndex1Suggestion, testCase.expectedScore); - QualifiedPhoneTimeZoneSuggestion expectedZonePhone2ScoredSuggestion = - new QualifiedPhoneTimeZoneSuggestion(zonePhone2Suggestion, + QualifiedTelephonyTimeZoneSuggestion expectedZoneSlotIndex2ScoredSuggestion = + new QualifiedTelephonyTimeZoneSuggestion(zoneSlotIndex2Suggestion, testCase.expectedScore); - // Start the test by making a suggestion for phone 1. - script.suggestPhoneTimeZone(zonePhone1Suggestion); - if (testCase.expectedScore >= PHONE_SCORE_USAGE_THRESHOLD) { - script.verifyTimeZoneSetAndReset(zonePhone1Suggestion); + // Start the test by making a suggestion for slotIndex1. + script.suggestTelephonyTimeZone(zoneSlotIndex1Suggestion); + if (testCase.expectedScore >= TELEPHONY_SCORE_USAGE_THRESHOLD) { + script.verifyTimeZoneSetAndReset(zoneSlotIndex1Suggestion); } else { script.verifyTimeZoneNotSet(); } // Assert internal service state. - assertEquals(expectedZonePhone1ScoredSuggestion, - mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE1_ID)); - assertEquals(expectedEmptyPhone2ScoredSuggestion, - mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE2_ID)); - assertEquals(expectedZonePhone1ScoredSuggestion, - mTimeZoneDetectorStrategy.findBestPhoneSuggestionForTests()); - - // Phone 2 then makes an alternative suggestion with an identical score. Phone 1's + assertEquals(expectedZoneSlotIndex1ScoredSuggestion, + mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX1)); + assertEquals(expectedEmptySlotIndex2ScoredSuggestion, + mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX2)); + assertEquals(expectedZoneSlotIndex1ScoredSuggestion, + mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests()); + + // SlotIndex2 then makes an alternative suggestion with an identical score. SlotIndex1's // suggestion should still "win" if it is above the required threshold. - script.suggestPhoneTimeZone(zonePhone2Suggestion); + script.suggestTelephonyTimeZone(zoneSlotIndex2Suggestion); script.verifyTimeZoneNotSet(); // Assert internal service state. - assertEquals(expectedZonePhone1ScoredSuggestion, - mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE1_ID)); - assertEquals(expectedZonePhone2ScoredSuggestion, - mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE2_ID)); - // Phone 1 should always beat phone 2, all other things being equal. - assertEquals(expectedZonePhone1ScoredSuggestion, - mTimeZoneDetectorStrategy.findBestPhoneSuggestionForTests()); - - // Withdrawing phone 1's suggestion should leave phone 2 as the new winner. Since the - // zoneId is different, the time zone setting should be updated if the score is high + assertEquals(expectedZoneSlotIndex1ScoredSuggestion, + mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX1)); + assertEquals(expectedZoneSlotIndex2ScoredSuggestion, + mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX2)); + // SlotIndex1 should always beat slotIndex2, all other things being equal. + assertEquals(expectedZoneSlotIndex1ScoredSuggestion, + mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests()); + + // Withdrawing slotIndex1's suggestion should leave slotIndex2 as the new winner. Since + // the zoneId is different, the time zone setting should be updated if the score is high // enough. - script.suggestPhoneTimeZone(emptyPhone1Suggestion); - if (testCase.expectedScore >= PHONE_SCORE_USAGE_THRESHOLD) { - script.verifyTimeZoneSetAndReset(zonePhone2Suggestion); + script.suggestTelephonyTimeZone(emptySlotIndex1Suggestion); + if (testCase.expectedScore >= TELEPHONY_SCORE_USAGE_THRESHOLD) { + script.verifyTimeZoneSetAndReset(zoneSlotIndex2Suggestion); } else { script.verifyTimeZoneNotSet(); } // Assert internal service state. - assertEquals(expectedEmptyPhone1ScoredSuggestion, - mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE1_ID)); - assertEquals(expectedZonePhone2ScoredSuggestion, - mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE2_ID)); - assertEquals(expectedZonePhone2ScoredSuggestion, - mTimeZoneDetectorStrategy.findBestPhoneSuggestionForTests()); + assertEquals(expectedEmptySlotIndex1ScoredSuggestion, + mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX1)); + assertEquals(expectedZoneSlotIndex2ScoredSuggestion, + mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX2)); + assertEquals(expectedZoneSlotIndex2ScoredSuggestion, + mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests()); // Reset the state for the next loop. - script.suggestPhoneTimeZone(emptyPhone2Suggestion) + script.suggestTelephonyTimeZone(emptySlotIndex2Suggestion) .verifyTimeZoneNotSet(); - assertEquals(expectedEmptyPhone1ScoredSuggestion, - mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE1_ID)); - assertEquals(expectedEmptyPhone2ScoredSuggestion, - mTimeZoneDetectorStrategy.getLatestPhoneSuggestion(PHONE2_ID)); + assertEquals(expectedEmptySlotIndex1ScoredSuggestion, + mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX1)); + assertEquals(expectedEmptySlotIndex2ScoredSuggestion, + mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX2)); } } @@ -375,21 +384,21 @@ public class TimeZoneDetectorStrategyImplTest { SuggestionTestCase testCase = newTestCase(MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET, QUALITY_SINGLE_ZONE, - PHONE_SCORE_HIGH); - PhoneTimeZoneSuggestion losAngelesSuggestion = - testCase.createSuggestion(PHONE1_ID, "America/Los_Angeles"); - PhoneTimeZoneSuggestion newYorkSuggestion = - testCase.createSuggestion(PHONE1_ID, "America/New_York"); + TELEPHONY_SCORE_HIGH); + TelephonyTimeZoneSuggestion losAngelesSuggestion = + testCase.createSuggestion(SLOT_INDEX1, "America/Los_Angeles"); + TelephonyTimeZoneSuggestion newYorkSuggestion = + testCase.createSuggestion(SLOT_INDEX1, "America/New_York"); // Initialization. - script.suggestPhoneTimeZone(losAngelesSuggestion) + script.suggestTelephonyTimeZone(losAngelesSuggestion) .verifyTimeZoneSetAndReset(losAngelesSuggestion); // Suggest it again - it should not be set because it is already set. - script.suggestPhoneTimeZone(losAngelesSuggestion) + script.suggestTelephonyTimeZone(losAngelesSuggestion) .verifyTimeZoneNotSet(); // Toggling time zone detection should set the device time zone only if the current setting - // value is different from the most recent phone suggestion. + // value is different from the most recent telephony suggestion. script.autoTimeZoneDetectionEnabled(false) .verifyTimeZoneNotSet() .autoTimeZoneDetectionEnabled(true) @@ -398,7 +407,7 @@ public class TimeZoneDetectorStrategyImplTest { // Simulate a user turning auto detection off, a new suggestion being made while auto // detection is off, and the user turning it on again. script.autoTimeZoneDetectionEnabled(false) - .suggestPhoneTimeZone(newYorkSuggestion) + .suggestTelephonyTimeZone(newYorkSuggestion) .verifyTimeZoneNotSet(); // Latest suggestion should be used. script.autoTimeZoneDetectionEnabled(true) @@ -433,12 +442,12 @@ public class TimeZoneDetectorStrategyImplTest { return new ManualTimeZoneSuggestion(zoneId); } - private static PhoneTimeZoneSuggestion createEmptyPhone1Suggestion() { - return new PhoneTimeZoneSuggestion.Builder(PHONE1_ID).build(); + private static TelephonyTimeZoneSuggestion createEmptySlotIndex1Suggestion() { + return new TelephonyTimeZoneSuggestion.Builder(SLOT_INDEX1).build(); } - private static PhoneTimeZoneSuggestion createEmptyPhone2Suggestion() { - return new PhoneTimeZoneSuggestion.Builder(PHONE2_ID).build(); + private static TelephonyTimeZoneSuggestion createEmptySlotIndex2Suggestion() { + return new TelephonyTimeZoneSuggestion.Builder(SLOT_INDEX2).build(); } static class FakeTimeZoneDetectorStrategyCallback @@ -565,9 +574,11 @@ public class TimeZoneDetectorStrategyImplTest { return this; } - /** Simulates the time zone detection strategy receiving a phone-originated suggestion. */ - Script suggestPhoneTimeZone(PhoneTimeZoneSuggestion phoneTimeZoneSuggestion) { - mTimeZoneDetectorStrategy.suggestPhoneTimeZone(phoneTimeZoneSuggestion); + /** + * Simulates the time zone detection strategy receiving a telephony-originated suggestion. + */ + Script suggestTelephonyTimeZone(TelephonyTimeZoneSuggestion timeZoneSuggestion) { + mTimeZoneDetectorStrategy.suggestTelephonyTimeZone(timeZoneSuggestion); return this; } @@ -582,7 +593,7 @@ public class TimeZoneDetectorStrategyImplTest { return this; } - Script verifyTimeZoneSetAndReset(PhoneTimeZoneSuggestion suggestion) { + Script verifyTimeZoneSetAndReset(TelephonyTimeZoneSuggestion suggestion) { mFakeTimeZoneDetectorStrategyCallback.assertTimeZoneSet(suggestion.getZoneId()); mFakeTimeZoneDetectorStrategyCallback.commitAllChanges(); return this; @@ -611,8 +622,8 @@ public class TimeZoneDetectorStrategyImplTest { this.expectedScore = expectedScore; } - private PhoneTimeZoneSuggestion createSuggestion(int phoneId, String zoneId) { - return new PhoneTimeZoneSuggestion.Builder(phoneId) + private TelephonyTimeZoneSuggestion createSuggestion(int slotIndex, String zoneId) { + return new TelephonyTimeZoneSuggestion.Builder(slotIndex) .setZoneId(zoneId) .setMatchType(matchType) .setQuality(quality) diff --git a/services/tests/uiservicestests/AndroidManifest.xml b/services/tests/uiservicestests/AndroidManifest.xml index 180deb5c4dcc..dab0a5f0e279 100644 --- a/services/tests/uiservicestests/AndroidManifest.xml +++ b/services/tests/uiservicestests/AndroidManifest.xml @@ -28,6 +28,8 @@ <uses-permission android:name="android.permission.ACCESS_VOICE_INTERACTION_SERVICE" /> <uses-permission android:name="android.permission.DEVICE_POWER" /> <uses-permission android:name="android.permission.ACCESS_CONTENT_PROVIDERS_EXTERNALLY" /> + <uses-permission android:name="android.permission.LOG_COMPAT_CHANGE"/> + <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.OBSERVE_ROLE_HOLDERS" /> <uses-permission android:name="android.permission.GET_INTENT_SENDER_INTENT"/> diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml index 8c4544249b5c..123bb079dbbb 100644 --- a/services/tests/wmtests/AndroidManifest.xml +++ b/services/tests/wmtests/AndroidManifest.xml @@ -37,6 +37,7 @@ <uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.REORDER_TASKS" /> <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" /> + <uses-permission android:name="android.permission.STATUS_BAR" /> <!-- TODO: Remove largeHeap hack when memory leak is fixed (b/123984854) --> <application android:debuggable="true" diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java index 078347e96a07..7172a1b14244 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java @@ -26,16 +26,19 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityManager.StackInfo; @@ -139,6 +142,52 @@ public class TaskOrganizerTests extends WindowTestsBase { } @Test + public void testUnregisterOrganizer() throws RemoteException { + final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent); + final Task task = createTaskInStack(stack, 0 /* userId */); + final ITaskOrganizer organizer = registerMockOrganizer(); + + stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + verify(organizer).taskAppeared(any()); + assertTrue(stack.isControlledByTaskOrganizer()); + + mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer); + verify(organizer).taskVanished(any()); + assertFalse(stack.isControlledByTaskOrganizer()); + } + + @Test + public void testUnregisterOrganizerReturnsRegistrationToPrevious() throws RemoteException { + final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent); + final Task task = createTaskInStack(stack, 0 /* userId */); + final ActivityStack stack2 = createTaskStackOnDisplay(mDisplayContent); + final Task task2 = createTaskInStack(stack2, 0 /* userId */); + final ActivityStack stack3 = createTaskStackOnDisplay(mDisplayContent); + final Task task3 = createTaskInStack(stack3, 0 /* userId */); + final ITaskOrganizer organizer = registerMockOrganizer(WINDOWING_MODE_MULTI_WINDOW); + + // First organizer is registered, verify a task appears when changing windowing mode + stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + verify(organizer, times(1)).taskAppeared(any()); + assertTrue(stack.isControlledByTaskOrganizer()); + + // Now we replace the registration and1 verify the new organizer receives tasks + // newly entering the windowing mode. + final ITaskOrganizer organizer2 = registerMockOrganizer(WINDOWING_MODE_MULTI_WINDOW); + stack2.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + verify(organizer2).taskAppeared(any()); + assertTrue(stack2.isControlledByTaskOrganizer()); + + // Now we unregister the second one, the first one should automatically be reregistered + // so we verify that it's now seeing changes. + mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer2); + + stack3.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + verify(organizer, times(2)).taskAppeared(any()); + assertTrue(stack3.isControlledByTaskOrganizer()); + } + + @Test public void testRegisterTaskOrganizerStackWindowingModeChanges() throws RemoteException { final ITaskOrganizer organizer = registerMockOrganizer(WINDOWING_MODE_PINNED); @@ -161,7 +210,7 @@ public class TaskOrganizerTests extends WindowTestsBase { WindowContainerTransaction t = new WindowContainerTransaction(); Rect newBounds = new Rect(10, 10, 100, 100); t.setBounds(task.mRemoteToken, new Rect(10, 10, 100, 100)); - mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t); + mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null); assertEquals(newBounds, task.getBounds()); } @@ -176,7 +225,7 @@ public class TaskOrganizerTests extends WindowTestsBase { assertEquals(stack.mRemoteToken, info.stackToken); Rect newBounds = new Rect(10, 10, 100, 100); t.setBounds(info.stackToken, new Rect(10, 10, 100, 100)); - mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t); + mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null); assertEquals(newBounds, stack.getBounds()); } @@ -189,7 +238,7 @@ public class TaskOrganizerTests extends WindowTestsBase { WindowContainerTransaction t = new WindowContainerTransaction(); assertTrue(task.isFocusable()); t.setFocusable(stack.mRemoteToken, false); - mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t); + mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null); assertFalse(task.isFocusable()); } @@ -318,4 +367,46 @@ public class TaskOrganizerTests extends WindowTestsBase { } return out; } + + @Test + public void testTrivialBLASTCallback() throws RemoteException { + final ActivityStack stackController1 = createTaskStackOnDisplay(mDisplayContent); + final Task task = createTaskInStack(stackController1, 0 /* userId */); + final ITaskOrganizer organizer = registerMockOrganizer(); + + BLASTSyncEngine bse = new BLASTSyncEngine(); + + BLASTSyncEngine.TransactionReadyListener transactionListener = + mock(BLASTSyncEngine.TransactionReadyListener.class); + + int id = bse.startSyncSet(transactionListener); + bse.addToSyncSet(id, task); + bse.setReady(id); + // Since this task has no windows the sync is trivial and completes immediately. + verify(transactionListener) + .transactionReady(anyInt(), any()); + } + + @Test + public void testBLASTCallbackWithWindow() { + final ActivityStack stackController1 = createTaskStackOnDisplay(mDisplayContent); + final Task task = createTaskInStack(stackController1, 0 /* userId */); + final ITaskOrganizer organizer = registerMockOrganizer(); + final WindowState w = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window"); + + BLASTSyncEngine bse = new BLASTSyncEngine(); + + BLASTSyncEngine.TransactionReadyListener transactionListener = + mock(BLASTSyncEngine.TransactionReadyListener.class); + + int id = bse.startSyncSet(transactionListener); + bse.addToSyncSet(id, task); + bse.setReady(id); + // Since we have a window we have to wait for it to draw to finish sync. + verify(transactionListener, never()) + .transactionReady(anyInt(), any()); + w.finishDrawing(null); + verify(transactionListener) + .transactionReady(anyInt(), any()); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java new file mode 100644 index 000000000000..35723abb4310 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + +import android.content.pm.PackageManager; +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; + +@SmallTest +@Presubmit +@RunWith(WindowTestRunner.class) +public class WindowManagerServiceTests extends WindowTestsBase { + @Rule + public ExpectedException mExpectedException = ExpectedException.none(); + + @Test + public void testForceShowSystemBarsThrowsExceptionForNonAutomotive() { + if (!isAutomotive()) { + mExpectedException.expect(UnsupportedOperationException.class); + + mWm.setForceShowSystemBars(true); + } + } + + @Test + public void testForceShowSystemBarsDoesNotThrowExceptionForAutomotiveWithStatusBarPermission() { + if (isAutomotive()) { + mExpectedException.none(); + + mWm.setForceShowSystemBars(true); + } + } + + private boolean isAutomotive() { + return getInstrumentation().getTargetContext().getPackageManager().hasSystemFeature( + PackageManager.FEATURE_AUTOMOTIVE); + } +} diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 672352211089..e520a02c2721 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -7761,9 +7761,8 @@ public class TelephonyManager { * * @hide */ - @SystemApi public static final int DEFAULT_PREFERRED_NETWORK_MODE = - RILConstants.DEFAULT_PREFERRED_NETWORK_MODE; + RILConstants.PREFERRED_NETWORK_MODE; /** * Get the preferred network type. @@ -11290,14 +11289,6 @@ public class TelephonyManager { */ public static final int INDICATION_UPDATE_MODE_IGNORE_SCREEN_OFF = 2; - /** @hide */ - @IntDef(prefix = { "INDICATION_UPDATE_MODE_" }, value = { - INDICATION_UPDATE_MODE_NORMAL, - INDICATION_UPDATE_MODE_IGNORE_SCREEN_OFF - }) - @Retention(RetentionPolicy.SOURCE) - public @interface IndicationUpdateMode{} - /** * The indication for signal strength update. * @hide @@ -11328,51 +11319,6 @@ public class TelephonyManager { */ public static final int INDICATION_FILTER_PHYSICAL_CHANNEL_CONFIG = 0x10; - /** @hide */ - @IntDef(flag = true, prefix = { "INDICATION_FILTER_" }, value = { - INDICATION_FILTER_SIGNAL_STRENGTH, - INDICATION_FILTER_FULL_NETWORK_STATE, - INDICATION_FILTER_DATA_CALL_DORMANCY_CHANGED, - INDICATION_FILTER_LINK_CAPACITY_ESTIMATE, - INDICATION_FILTER_PHYSICAL_CHANNEL_CONFIG - }) - @Retention(RetentionPolicy.SOURCE) - public @interface IndicationFilters{} - - /** - * Sets radio indication update mode. This can be used to control the behavior of indication - * update from modem to Android frameworks. For example, by default several indication updates - * are turned off when screen is off, but in some special cases (e.g. carkit is connected but - * screen is off) we want to turn on those indications even when the screen is off. - * - * <p>Requires Permission: - * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} - * - * @param filters Indication filters. Should be a bitmask of INDICATION_FILTER_XXX. - * @see #INDICATION_FILTER_SIGNAL_STRENGTH - * @see #INDICATION_FILTER_FULL_NETWORK_STATE - * @see #INDICATION_FILTER_DATA_CALL_DORMANCY_CHANGED - * @param updateMode The voice activation state - * @see #INDICATION_UPDATE_MODE_NORMAL - * @see #INDICATION_UPDATE_MODE_IGNORE_SCREEN_OFF - * @hide - */ - @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) - public void setRadioIndicationUpdateMode(@IndicationFilters int filters, - @IndicationUpdateMode int updateMode) { - try { - ITelephony telephony = getITelephony(); - if (telephony != null) { - telephony.setRadioIndicationUpdateMode(getSubId(), filters, updateMode); - } - } catch (RemoteException ex) { - // This could happen if binder process crashes. - if (!isSystemProcess()) { - ex.rethrowAsRuntimeException(); - } - } - } - /** * A test API to override carrier information including mccmnc, imsi, iccid, gid1, gid2, * plmn and spn. This would be handy for, eg, forcing a particular carrier id, carrier's config diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index beb3c8cac41e..168c8b64c194 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -1827,14 +1827,6 @@ interface ITelephony { boolean switchSlots(in int[] physicalSlots); /** - * Sets radio indication update mode. This can be used to control the behavior of indication - * update from modem to Android frameworks. For example, by default several indication updates - * are turned off when screen is off, but in some special cases (e.g. carkit is connected but - * screen is off) we want to turn on those indications even when the screen is off. - */ - void setRadioIndicationUpdateMode(int subId, int filters, int mode); - - /** * Returns whether mobile data roaming is enabled on the subscription with id {@code subId}. * * @param subId the subscription id diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java index 9ac8cb136c6b..c40573b25068 100644 --- a/telephony/java/com/android/internal/telephony/RILConstants.java +++ b/telephony/java/com/android/internal/telephony/RILConstants.java @@ -233,14 +233,11 @@ public interface RILConstants { /** NR 5G, LTE, TD-SCDMA, CDMA, EVDO, GSM and WCDMA */ int NETWORK_MODE_NR_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA = 33; - /** Default preferred network mode */ - int DEFAULT_PREFERRED_NETWORK_MODE = NETWORK_MODE_WCDMA_PREF; - @UnsupportedAppUsage int PREFERRED_NETWORK_MODE = Optional.of(TelephonyProperties.default_network()) .filter(list -> !list.isEmpty()) .map(list -> list.get(0)) - .orElse(DEFAULT_PREFERRED_NETWORK_MODE); + .orElse(NETWORK_MODE_WCDMA_PREF); int BAND_MODE_UNSPECIFIED = 0; //"unspecified" (selected by baseband automatically) int BAND_MODE_EURO = 1; //"EURO band" (GSM-900 / DCS-1800 / WCDMA-IMT-2000) diff --git a/tests/BlobStoreTestUtils/Android.bp b/tests/BlobStoreTestUtils/Android.bp new file mode 100644 index 000000000000..edd2b435f1da --- /dev/null +++ b/tests/BlobStoreTestUtils/Android.bp @@ -0,0 +1,20 @@ +// Copyright (C) 2020 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 { + name: "BlobStoreTestUtils", + srcs: ["src/**/*.java"], + static_libs: ["truth-prebuilt"], + platform_apis: true +}
\ No newline at end of file diff --git a/tests/BlobStoreTestUtils/src/com/android/utils/blob/DummyBlobData.java b/tests/BlobStoreTestUtils/src/com/android/utils/blob/DummyBlobData.java new file mode 100644 index 000000000000..f96766a1d3ad --- /dev/null +++ b/tests/BlobStoreTestUtils/src/com/android/utils/blob/DummyBlobData.java @@ -0,0 +1,227 @@ +/* + * Copyright 2020 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.utils.blob; + +import static com.google.common.truth.Truth.assertThat; + +import android.app.blob.BlobHandle; +import android.app.blob.BlobStoreManager; +import android.content.Context; +import android.os.FileUtils; +import android.os.ParcelFileDescriptor; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.RandomAccessFile; +import java.nio.file.Files; +import java.security.MessageDigest; +import java.util.Random; +import java.util.concurrent.TimeUnit; + +public class DummyBlobData { + private static final long DEFAULT_SIZE_BYTES = 10 * 1024L * 1024L; + private static final int BUFFER_SIZE_BYTES = 16 * 1024; + + private final Context mContext; + private final Random mRandom; + private final File mFile; + private final long mFileSize; + private final String mLabel; + + byte[] mFileDigest; + long mExpiryTimeMs; + + public DummyBlobData(Context context) { + this(context, new Random(0), "blob_" + System.nanoTime()); + } + + public DummyBlobData(Context context, long fileSize) { + this(context, fileSize, new Random(0), "blob_" + System.nanoTime(), "Test label"); + } + + public DummyBlobData(Context context, Random random, String fileName) { + this(context, DEFAULT_SIZE_BYTES, random, fileName, "Test label"); + } + + public DummyBlobData(Context context, Random random, String fileName, String label) { + this(context, DEFAULT_SIZE_BYTES, random, fileName, label); + } + + public DummyBlobData(Context context, long fileSize, Random random, String fileName, + String label) { + mContext = context; + mRandom = random; + mFile = new File(mContext.getFilesDir(), fileName); + mFileSize = fileSize; + mLabel = label; + } + + public void prepare() throws Exception { + try (RandomAccessFile file = new RandomAccessFile(mFile, "rw")) { + writeRandomData(file, mFileSize); + } + mFileDigest = FileUtils.digest(mFile, "SHA-256"); + mExpiryTimeMs = System.currentTimeMillis() + TimeUnit.DAYS.toMillis(1); + } + + public BlobHandle getBlobHandle() throws Exception { + return BlobHandle.createWithSha256(createSha256Digest(mFile), mLabel, + mExpiryTimeMs, "test_tag"); + } + + public long getFileSize() throws Exception { + return mFileSize; + } + + public long getExpiryTimeMillis() { + return mExpiryTimeMs; + } + + public void delete() { + mFile.delete(); + } + + public void writeToSession(BlobStoreManager.Session session) throws Exception { + writeToSession(session, 0, mFileSize); + } + + public void writeToSession(BlobStoreManager.Session session, + long offsetBytes, long lengthBytes) throws Exception { + try (FileInputStream in = new FileInputStream(mFile)) { + in.getChannel().position(offsetBytes); + try (FileOutputStream out = new ParcelFileDescriptor.AutoCloseOutputStream( + session.openWrite(offsetBytes, lengthBytes))) { + copy(in, out, lengthBytes); + } + } + } + + public void writeToFd(FileDescriptor fd, long offsetBytes, long lengthBytes) throws Exception { + try (FileInputStream in = new FileInputStream(mFile)) { + in.getChannel().position(offsetBytes); + try (FileOutputStream out = new FileOutputStream(fd)) { + copy(in, out, lengthBytes); + } + } + } + + private void copy(InputStream in, OutputStream out, long lengthBytes) throws Exception { + final byte[] buffer = new byte[BUFFER_SIZE_BYTES]; + long bytesWrittern = 0; + while (bytesWrittern < lengthBytes) { + final int toWrite = (bytesWrittern + buffer.length <= lengthBytes) + ? buffer.length : (int) (lengthBytes - bytesWrittern); + in.read(buffer, 0, toWrite); + out.write(buffer, 0, toWrite); + bytesWrittern += toWrite; + } + } + + public void readFromSessionAndVerifyBytes(BlobStoreManager.Session session, + long offsetBytes, int lengthBytes) throws Exception { + final byte[] expectedBytes = new byte[lengthBytes]; + try (FileInputStream in = new FileInputStream(mFile)) { + read(in, expectedBytes, offsetBytes, lengthBytes); + } + + final byte[] actualBytes = new byte[lengthBytes]; + try (FileInputStream in = new ParcelFileDescriptor.AutoCloseInputStream( + session.openWrite(0L, 0L))) { + read(in, actualBytes, offsetBytes, lengthBytes); + } + + assertThat(actualBytes).isEqualTo(expectedBytes); + + } + + private void read(FileInputStream in, byte[] buffer, + long offsetBytes, int lengthBytes) throws Exception { + in.getChannel().position(offsetBytes); + in.read(buffer, 0, lengthBytes); + } + + public void readFromSessionAndVerifyDigest(BlobStoreManager.Session session) + throws Exception { + readFromSessionAndVerifyDigest(session, 0, mFile.length()); + } + + public void readFromSessionAndVerifyDigest(BlobStoreManager.Session session, + long offsetBytes, long lengthBytes) throws Exception { + final byte[] actualDigest; + try (FileInputStream in = new ParcelFileDescriptor.AutoCloseInputStream( + session.openWrite(0L, 0L))) { + actualDigest = createSha256Digest(in, offsetBytes, lengthBytes); + } + + assertThat(actualDigest).isEqualTo(mFileDigest); + } + + public void verifyBlob(ParcelFileDescriptor pfd) throws Exception { + final byte[] actualDigest; + try (FileInputStream in = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) { + actualDigest = FileUtils.digest(in, "SHA-256"); + } + assertThat(actualDigest).isEqualTo(mFileDigest); + } + + private byte[] createSha256Digest(FileInputStream in, long offsetBytes, long lengthBytes) + throws Exception { + final MessageDigest digest = MessageDigest.getInstance("SHA-256"); + in.getChannel().position(offsetBytes); + final byte[] buffer = new byte[BUFFER_SIZE_BYTES]; + long bytesRead = 0; + while (bytesRead < lengthBytes) { + int toRead = (bytesRead + buffer.length <= lengthBytes) + ? buffer.length : (int) (lengthBytes - bytesRead); + toRead = in.read(buffer, 0, toRead); + digest.update(buffer, 0, toRead); + bytesRead += toRead; + } + return digest.digest(); + } + + private byte[] createSha256Digest(File file) throws Exception { + final MessageDigest digest = MessageDigest.getInstance("SHA-256"); + try (BufferedInputStream in = new BufferedInputStream( + Files.newInputStream(file.toPath()))) { + final byte[] buffer = new byte[BUFFER_SIZE_BYTES]; + int bytesRead; + while ((bytesRead = in.read(buffer)) > 0) { + digest.update(buffer, 0, bytesRead); + } + } + return digest.digest(); + } + + private void writeRandomData(RandomAccessFile file, long fileSize) + throws Exception { + long bytesWritten = 0; + final byte[] buffer = new byte[BUFFER_SIZE_BYTES]; + while (bytesWritten < fileSize) { + mRandom.nextBytes(buffer); + final int toWrite = (bytesWritten + buffer.length <= fileSize) + ? buffer.length : (int) (fileSize - bytesWritten); + file.seek(bytesWritten); + file.write(buffer, 0, toWrite); + bytesWritten += toWrite; + } + } +} diff --git a/tests/PlatformCompatGating/Android.bp b/tests/PlatformCompatGating/Android.bp index 5e9ef8efc402..74dfde848191 100644 --- a/tests/PlatformCompatGating/Android.bp +++ b/tests/PlatformCompatGating/Android.bp @@ -18,14 +18,11 @@ android_test { name: "PlatformCompatGating", // Only compile source java files in this apk. srcs: ["src/**/*.java"], - certificate: "platform", - libs: [ - "android.test.runner", - "android.test.base", - ], static_libs: [ "junit", - "android-support-test", + "androidx.test.runner", + "androidx.test.core", + "androidx.test.ext.junit", "mockito-target-minus-junit4", "truth-prebuilt", "platform-compat-test-rules" diff --git a/tests/PlatformCompatGating/AndroidManifest.xml b/tests/PlatformCompatGating/AndroidManifest.xml index 7f14b83fbc75..c24dc31b7bf3 100644 --- a/tests/PlatformCompatGating/AndroidManifest.xml +++ b/tests/PlatformCompatGating/AndroidManifest.xml @@ -6,6 +6,6 @@ <uses-library android:name="android.test.runner" /> </application> - <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner" + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" android:targetPackage="com.android.tests.gating"/> </manifest> diff --git a/tests/PlatformCompatGating/AndroidTest.xml b/tests/PlatformCompatGating/AndroidTest.xml index c62684837332..0c7485b27fb8 100644 --- a/tests/PlatformCompatGating/AndroidTest.xml +++ b/tests/PlatformCompatGating/AndroidTest.xml @@ -24,7 +24,6 @@ <test class="com.android.tradefed.testtype.AndroidJUnitTest"> <option name="package" value="com.android.tests.gating"/> - <option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/> <option name="hidden-api-checks" value="false"/> </test> </configuration> diff --git a/tests/PlatformCompatGating/src/com/android/tests/gating/PlatformCompatGatingTest.java b/tests/PlatformCompatGating/src/com/android/tests/gating/PlatformCompatGatingTest.java index dc317f1941c7..c1ce0e99640c 100644 --- a/tests/PlatformCompatGating/src/com/android/tests/gating/PlatformCompatGatingTest.java +++ b/tests/PlatformCompatGating/src/com/android/tests/gating/PlatformCompatGatingTest.java @@ -18,8 +18,9 @@ package com.android.tests.gating; import static com.google.common.truth.Truth.assertThat; import android.compat.testing.PlatformCompatChangeRule; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.platform.app.InstrumentationRegistry; import com.android.compat.testing.DummyApi; @@ -81,14 +82,14 @@ public class PlatformCompatGatingTest { @Test @EnableCompatChanges({DummyApi.CHANGE_SYSTEM_SERVER}) public void testDummyGatingPositiveSystemServer() { - assertThat( - DummyApi.dummySystemServer(InstrumentationRegistry.getTargetContext())).isTrue(); + assertThat(DummyApi.dummySystemServer( + InstrumentationRegistry.getInstrumentation().getTargetContext())).isTrue(); } @Test @DisableCompatChanges({DummyApi.CHANGE_SYSTEM_SERVER}) public void testDummyGatingNegativeSystemServer() { - assertThat( - DummyApi.dummySystemServer(InstrumentationRegistry.getTargetContext())).isFalse(); + assertThat(DummyApi.dummySystemServer( + InstrumentationRegistry.getInstrumentation().getTargetContext())).isFalse(); } } diff --git a/tests/PlatformCompatGating/src/com/android/tests/gating/PlatformCompatPermissionsTest.java b/tests/PlatformCompatGating/src/com/android/tests/gating/PlatformCompatPermissionsTest.java new file mode 100644 index 000000000000..9b9e5815a588 --- /dev/null +++ b/tests/PlatformCompatGating/src/com/android/tests/gating/PlatformCompatPermissionsTest.java @@ -0,0 +1,319 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.tests.gating; + +import static android.Manifest.permission.LOG_COMPAT_CHANGE; +import static android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG; +import static android.Manifest.permission.READ_COMPAT_CHANGE_CONFIG; + +import android.app.Instrumentation; +import android.app.UiAutomation; +import android.compat.Compatibility.ChangeConfig; +import android.content.Context; +import android.content.pm.PackageManager; +import android.os.Process; +import android.os.ServiceManager; + +import androidx.test.platform.app.InstrumentationRegistry; + +import com.android.internal.compat.CompatibilityChangeConfig; +import com.android.internal.compat.IPlatformCompat; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.util.HashSet; +import java.util.Set; + +@RunWith(JUnit4.class) +public final class PlatformCompatPermissionsTest { + + // private Context mContext; + private IPlatformCompat mPlatformCompat; + + @Rule + public final ExpectedException thrown = ExpectedException.none(); + private Context mContext; + private UiAutomation mUiAutomation; + private PackageManager mPackageManager; + + @Before + public void setUp() { + // mContext; + mPlatformCompat = IPlatformCompat.Stub + .asInterface(ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)); + Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); + mUiAutomation = instrumentation.getUiAutomation(); + mContext = instrumentation.getTargetContext(); + + mPackageManager = mContext.getPackageManager(); + } + + @After + public void tearDown() { + + mUiAutomation.dropShellPermissionIdentity(); + } + + @Test + public void reportChange_noLogCompatChangePermission_throwsSecurityException() + throws Throwable { + thrown.expect(SecurityException.class); + final String packageName = mContext.getPackageName(); + + mPlatformCompat.reportChange(1, mPackageManager.getApplicationInfo(packageName, 0)); + } + + @Test + public void reportChange_logCompatChangePermission_noThrow() + throws Throwable { + mUiAutomation.adoptShellPermissionIdentity(LOG_COMPAT_CHANGE); + final String packageName = mContext.getPackageName(); + + mPlatformCompat.reportChange(1, mPackageManager.getApplicationInfo(packageName, 0)); + } + + @Test + public void reportChangeByPackageName_noLogCompatChangePermission_throwsSecurityException() + throws Throwable { + thrown.expect(SecurityException.class); + final String packageName = mContext.getPackageName(); + + mPlatformCompat.reportChangeByPackageName(1, packageName, 0); + } + + @Test + public void reportChangeByPackageName_logCompatChangePermission_noThrow() + throws Throwable { + mUiAutomation.adoptShellPermissionIdentity(LOG_COMPAT_CHANGE); + final String packageName = mContext.getPackageName(); + + mPlatformCompat.reportChangeByPackageName(1, packageName, 0); + } + + @Test + public void reportChangeByUid_noLogCompatChangePermission_throwsSecurityException() + throws Throwable { + thrown.expect(SecurityException.class); + + mPlatformCompat.reportChangeByUid(1, Process.myUid()); + } + + @Test + public void reportChangeByUid_logCompatChangePermission_noThrow() + throws Throwable { + mUiAutomation.adoptShellPermissionIdentity(LOG_COMPAT_CHANGE); + + mPlatformCompat.reportChangeByUid(1, Process.myUid()); + } + + @Test + public void isChangeEnabled_noReadCompatConfigPermission_throwsSecurityException() + throws Throwable { + thrown.expect(SecurityException.class); + final String packageName = mContext.getPackageName(); + + mPlatformCompat.isChangeEnabled(1, mPackageManager.getApplicationInfo(packageName, 0)); + } + + @Test + public void isChangeEnabled_noLogCompatChangeConfigPermission_throwsSecurityException() + throws Throwable { + thrown.expect(SecurityException.class); + mUiAutomation.adoptShellPermissionIdentity(READ_COMPAT_CHANGE_CONFIG); + final String packageName = mContext.getPackageName(); + + mPlatformCompat.isChangeEnabled(1, mPackageManager.getApplicationInfo(packageName, 0)); + } + + @Test + public void isChangeEnabled_readAndLogCompatChangeConfigPermission_noThrow() + throws Throwable { + mUiAutomation.adoptShellPermissionIdentity(READ_COMPAT_CHANGE_CONFIG, LOG_COMPAT_CHANGE); + final String packageName = mContext.getPackageName(); + + mPlatformCompat.isChangeEnabled(1, mPackageManager.getApplicationInfo(packageName, 0)); + } + + @Test + public void isChangeEnabledByPackageName_noReadCompatConfigPermission_throwsSecurityException() + throws Throwable { + thrown.expect(SecurityException.class); + final String packageName = mContext.getPackageName(); + + mPlatformCompat.isChangeEnabledByPackageName(1, packageName, 0); + } + + @Test + public void isChangeEnabledByPackageName_noLogompatConfigPermission_throwsSecurityException() + throws Throwable { + thrown.expect(SecurityException.class); + mUiAutomation.adoptShellPermissionIdentity(READ_COMPAT_CHANGE_CONFIG); + final String packageName = mContext.getPackageName(); + + mPlatformCompat.isChangeEnabledByPackageName(1, packageName, 0); + } + + @Test + public void isChangeEnabledByPackageName_readAndLogCompatChangeConfigPermission_noThrow() + throws Throwable { + mUiAutomation.adoptShellPermissionIdentity(READ_COMPAT_CHANGE_CONFIG, LOG_COMPAT_CHANGE); + final String packageName = mContext.getPackageName(); + + mPlatformCompat.isChangeEnabledByPackageName(1, packageName, 0); + } + + @Test + public void isChangeEnabledByUid_noReadCompatConfigPermission_throwsSecurityException() + throws Throwable { + thrown.expect(SecurityException.class); + + mPlatformCompat.isChangeEnabledByUid(1, Process.myUid()); + } + + @Test + public void isChangeEnabledByUid_noLogCompatChangePermission_throwsSecurityException() + throws Throwable { + thrown.expect(SecurityException.class); + mUiAutomation.adoptShellPermissionIdentity(READ_COMPAT_CHANGE_CONFIG); + + mPlatformCompat.isChangeEnabledByUid(1, Process.myUid()); + } + + @Test + public void isChangeEnabledByUid_readAndLogCompatChangeConfigPermission_noThrow() + throws Throwable { + mUiAutomation.adoptShellPermissionIdentity(READ_COMPAT_CHANGE_CONFIG, LOG_COMPAT_CHANGE); + + mPlatformCompat.isChangeEnabledByUid(1, Process.myUid()); + } + + @Test + public void setOverrides_noOverridesPermission_throwsSecurityException() + throws Throwable { + thrown.expect(SecurityException.class); + Set<Long> enabled = new HashSet<>(); + Set<Long> disabled = new HashSet<>(); + ChangeConfig changeConfig = new ChangeConfig(enabled, disabled); + CompatibilityChangeConfig compatibilityChangeConfig = + new CompatibilityChangeConfig(changeConfig); + + mPlatformCompat.setOverrides(compatibilityChangeConfig, "foo.bar"); + } + @Test + public void setOverrides_overridesPermission_noThrow() + throws Throwable { + mUiAutomation.adoptShellPermissionIdentity(OVERRIDE_COMPAT_CHANGE_CONFIG); + Set<Long> enabled = new HashSet<>(); + Set<Long> disabled = new HashSet<>(); + ChangeConfig changeConfig = new ChangeConfig(enabled, disabled); + CompatibilityChangeConfig compatibilityChangeConfig = + new CompatibilityChangeConfig(changeConfig); + + mPlatformCompat.setOverrides(compatibilityChangeConfig, "foo.bar"); + } + + @Test + public void setOverridesForTest_noOverridesPermission_throwsSecurityException() + throws Throwable { + thrown.expect(SecurityException.class); + Set<Long> enabled = new HashSet<>(); + Set<Long> disabled = new HashSet<>(); + ChangeConfig changeConfig = new ChangeConfig(enabled, disabled); + CompatibilityChangeConfig compatibilityChangeConfig = + new CompatibilityChangeConfig(changeConfig); + + mPlatformCompat.setOverridesForTest(compatibilityChangeConfig, "foo.bar"); + } + @Test + public void setOverridesForTest_overridesPermission_noThrow() + throws Throwable { + mUiAutomation.adoptShellPermissionIdentity(OVERRIDE_COMPAT_CHANGE_CONFIG); + Set<Long> enabled = new HashSet<>(); + Set<Long> disabled = new HashSet<>(); + ChangeConfig changeConfig = new ChangeConfig(enabled, disabled); + CompatibilityChangeConfig compatibilityChangeConfig = + new CompatibilityChangeConfig(changeConfig); + + mPlatformCompat.setOverridesForTest(compatibilityChangeConfig, "foo.bar"); + } + + @Test + public void clearOverrides_noOverridesPermission_throwsSecurityException() + throws Throwable { + thrown.expect(SecurityException.class); + + mPlatformCompat.clearOverrides("foo.bar"); + } + @Test + public void clearOverrides_overridesPermission_noThrow() + throws Throwable { + mUiAutomation.adoptShellPermissionIdentity(OVERRIDE_COMPAT_CHANGE_CONFIG); + + mPlatformCompat.clearOverrides("foo.bar"); + } + + @Test + public void clearOverridesForTest_noOverridesPermission_throwsSecurityException() + throws Throwable { + thrown.expect(SecurityException.class); + + mPlatformCompat.clearOverridesForTest("foo.bar"); + } + @Test + public void clearOverridesForTest_overridesPermission_noThrow() + throws Throwable { + mUiAutomation.adoptShellPermissionIdentity(OVERRIDE_COMPAT_CHANGE_CONFIG); + + mPlatformCompat.clearOverridesForTest("foo.bar"); + } + + @Test + public void clearOverride_noOverridesPermission_throwsSecurityException() + throws Throwable { + thrown.expect(SecurityException.class); + + mPlatformCompat.clearOverride(1, "foo.bar"); + } + @Test + public void clearOverride_overridesPermission_noThrow() + throws Throwable { + mUiAutomation.adoptShellPermissionIdentity(OVERRIDE_COMPAT_CHANGE_CONFIG); + + mPlatformCompat.clearOverride(1, "foo.bar"); + } + + @Test + public void listAllChanges_noReadCompatConfigPermission_throwsSecurityException() + throws Throwable { + thrown.expect(SecurityException.class); + + mPlatformCompat.listAllChanges(); + } + @Test + public void listAllChanges_readCompatConfigPermission_noThrow() + throws Throwable { + mUiAutomation.adoptShellPermissionIdentity(READ_COMPAT_CHANGE_CONFIG); + + mPlatformCompat.listAllChanges(); + } +} diff --git a/tests/PlatformCompatGating/test-rules/Android.bp b/tests/PlatformCompatGating/test-rules/Android.bp index 8211ef523ee7..10fa2dc0d7c6 100644 --- a/tests/PlatformCompatGating/test-rules/Android.bp +++ b/tests/PlatformCompatGating/test-rules/Android.bp @@ -19,7 +19,7 @@ java_library { srcs: ["src/**/*.java"], static_libs: [ "junit", - "android-support-test", + "androidx.test.core", "truth-prebuilt", "core-compat-test-rules" ], diff --git a/tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java b/tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java index 932ec643d478..d6846faa5c00 100644 --- a/tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java +++ b/tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java @@ -16,13 +16,17 @@ package android.compat.testing; +import android.Manifest; import android.app.Instrumentation; +import android.app.UiAutomation; import android.compat.Compatibility; import android.compat.Compatibility.ChangeConfig; import android.content.Context; import android.os.RemoteException; import android.os.ServiceManager; -import android.support.test.InstrumentationRegistry; + +import androidx.test.platform.app.InstrumentationRegistry; + import com.android.internal.compat.CompatibilityChangeConfig; import com.android.internal.compat.IPlatformCompat; @@ -83,12 +87,17 @@ public class PlatformCompatChangeRule extends CoreCompatChangeRule { @Override public void evaluate() throws Throwable { Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); + UiAutomation uiAutomation = instrumentation.getUiAutomation(); String packageName = instrumentation.getTargetContext().getPackageName(); IPlatformCompat platformCompat = IPlatformCompat.Stub .asInterface(ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)); if (platformCompat == null) { throw new IllegalStateException("Could not get IPlatformCompat service!"); } + uiAutomation.adoptShellPermissionIdentity( + Manifest.permission.LOG_COMPAT_CHANGE, + Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG, + Manifest.permission.READ_COMPAT_CHANGE_CONFIG); Compatibility.setOverrides(mConfig); try { platformCompat.setOverridesForTest(new CompatibilityChangeConfig(mConfig), @@ -101,6 +110,7 @@ public class PlatformCompatChangeRule extends CoreCompatChangeRule { } catch (RemoteException e) { throw new RuntimeException("Could not call IPlatformCompat binder method!", e); } finally { + uiAutomation.dropShellPermissionIdentity(); Compatibility.clearOverrides(); } } diff --git a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java index 82a524be5c1e..032f18240a55 100644 --- a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java +++ b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java @@ -23,6 +23,8 @@ import static org.junit.Assume.assumeTrue; import static org.testng.Assert.assertThrows; import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper; +import com.android.tradefed.device.LogcatReceiver; +import com.android.tradefed.result.InputStreamSource; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; @@ -31,11 +33,18 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.io.BufferedReader; import java.io.File; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.TimeUnit; /** * Runs the staged rollback tests. + * + * TODO(gavincorkery): Support the verification of logging parents in Watchdog metrics. */ @RunWith(DeviceJUnit4ClassRunner.class) public class StagedRollbackTest extends BaseHostJUnit4Test { @@ -54,6 +63,7 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { } private static final String APK_IN_APEX_TESTAPEX_NAME = "com.android.apex.apkrollback.test"; + private static final String TESTAPP_A = "com.android.cts.install.lib.testapp.A"; private static final String TEST_SUBDIR = "/subdir/"; @@ -66,8 +76,19 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { private static final String TEST_FILENAME_4 = "one_more.test"; private static final String TEST_STRING_4 = "once more unto the test"; + private static final String REASON_APP_CRASH = "REASON_APP_CRASH"; + private static final String REASON_NATIVE_CRASH = "REASON_NATIVE_CRASH"; + private static final String REASON_EXPLICIT_HEALTH_CHECK = "REASON_EXPLICIT_HEALTH_CHECK"; + + private static final String ROLLBACK_INITIATE = "ROLLBACK_INITIATE"; + private static final String ROLLBACK_BOOT_TRIGGERED = "ROLLBACK_BOOT_TRIGGERED"; + + private LogcatReceiver mReceiver; + @Before public void setUp() throws Exception { + mReceiver = new LogcatReceiver(getDevice(), "logcat -s WatchdogRollbackLogger", + getDevice().getOptions().getMaxLogcatDataSize(), 0); if (!getDevice().isAdbRoot()) { getDevice().enableAdbRoot(); } @@ -77,10 +98,13 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { + "/data/apex/active/" + APK_IN_APEX_TESTAPEX_NAME + "*.apex"); getDevice().reboot(); runPhase("testCleanUp"); + mReceiver.start(); } @After public void tearDown() throws Exception { + mReceiver.stop(); + mReceiver.clear(); runPhase("testCleanUp"); if (!getDevice().isAdbRoot()) { @@ -110,6 +134,16 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { getDevice().waitForDeviceAvailable(); runPhase("testBadApkOnly_Phase4"); + InputStreamSource logcatStream = mReceiver.getLogcatData(); + try { + List<String> watchdogEvents = getWatchdogLoggingEvents(logcatStream); + assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_INITIATE, null, + REASON_APP_CRASH, TESTAPP_A)); + assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_BOOT_TRIGGERED, null, + null, null)); + } finally { + logcatStream.close(); + } } @Test @@ -137,6 +171,16 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { // verify rollback committed runPhase("testNativeWatchdogTriggersRollback_Phase3"); + InputStreamSource logcatStream = mReceiver.getLogcatData(); + try { + List<String> watchdogEvents = getWatchdogLoggingEvents(logcatStream); + assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_INITIATE, null, + REASON_NATIVE_CRASH, null)); + assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_BOOT_TRIGGERED, null, + null, null)); + } finally { + logcatStream.close(); + } } @Test @@ -171,6 +215,16 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { // verify all available rollbacks have been committed runPhase("testNativeWatchdogTriggersRollbackForAll_Phase4"); + InputStreamSource logcatStream = mReceiver.getLogcatData(); + try { + List<String> watchdogEvents = getWatchdogLoggingEvents(logcatStream); + assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_INITIATE, null, + REASON_NATIVE_CRASH, null)); + assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_BOOT_TRIGGERED, null, + null, null)); + } finally { + logcatStream.close(); + } } /** @@ -194,6 +248,16 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { getDevice().waitForDeviceAvailable(); // Verify rollback was executed after health check deadline runPhase("testNetworkFailedRollback_Phase4"); + InputStreamSource logcatStream = mReceiver.getLogcatData(); + try { + List<String> watchdogEvents = getWatchdogLoggingEvents(logcatStream); + assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_INITIATE, null, + REASON_EXPLICIT_HEALTH_CHECK, null)); + assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_BOOT_TRIGGERED, null, + null, null)); + } finally { + logcatStream.close(); + } } finally { // Reconnect internet again so we won't break tests which assume internet available getDevice().executeShellCommand("svc wifi enable"); @@ -223,6 +287,15 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { // Verify rollback was not executed after health check deadline runPhase("testNetworkPassedDoesNotRollback_Phase3"); + InputStreamSource logcatStream = mReceiver.getLogcatData(); + try { + List<String> watchdogEvents = getWatchdogLoggingEvents(logcatStream); + assertEquals(watchdogEventOccurred(watchdogEvents, null, null, + REASON_EXPLICIT_HEALTH_CHECK, null), false); + } finally { + logcatStream.close(); + } + } /** @@ -288,6 +361,16 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { getDevice().waitForDeviceAvailable(); // Verify rollback occurred due to crash of apk-in-apex runPhase("testRollbackApexWithApkCrashing_Phase3"); + InputStreamSource logcatStream = mReceiver.getLogcatData(); + try { + List<String> watchdogEvents = getWatchdogLoggingEvents(logcatStream); + assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_INITIATE, null, + REASON_APP_CRASH, TESTAPP_A)); + assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_BOOT_TRIGGERED, null, + null, null)); + } finally { + logcatStream.close(); + } } /** @@ -457,4 +540,57 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { return false; } } + + /** + * Returns a list of all Watchdog logging events which have occurred. + */ + private List<String> getWatchdogLoggingEvents(InputStreamSource inputStreamSource) + throws Exception { + List<String> watchdogEvents = new ArrayList<>(); + InputStream inputStream = inputStreamSource.createInputStream(); + BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); + String line; + while ((line = reader.readLine()) != null) { + if (line.contains("Watchdog event occurred")) { + watchdogEvents.add(line); + } + } + return watchdogEvents; + } + + /** + * Returns whether a Watchdog event has occurred that matches the given criteria. + * + * Check the value of all non-null parameters against the list of Watchdog events that have + * occurred, and return {@code true} if an event exists which matches all criteria. + */ + private boolean watchdogEventOccurred(List<String> loggingEvents, + String type, String logPackage, + String rollbackReason, String failedPackageName) throws Exception { + List<String> eventCriteria = new ArrayList<>(); + if (type != null) { + eventCriteria.add("type: " + type); + } + if (logPackage != null) { + eventCriteria.add("logPackage: " + logPackage); + } + if (rollbackReason != null) { + eventCriteria.add("rollbackReason: " + rollbackReason); + } + if (failedPackageName != null) { + eventCriteria.add("failedPackageName: " + failedPackageName); + } + for (String loggingEvent: loggingEvents) { + boolean matchesCriteria = true; + for (String criterion: eventCriteria) { + if (!loggingEvent.contains(criterion)) { + matchesCriteria = false; + } + } + if (matchesCriteria) { + return true; + } + } + return false; + } } diff --git a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java index 8f3cb3442f5a..bdfaaa8f11ea 100644 --- a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java +++ b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java @@ -45,7 +45,7 @@ public class TaskOrganizerPipTest extends Service { final WindowContainerTransaction wct = new WindowContainerTransaction(); wct.scheduleFinishEnterPip(ti.token, new Rect(0, 0, PIP_WIDTH, PIP_HEIGHT)); try { - ActivityTaskManager.getTaskOrganizerController().applyContainerTransaction(wct); + ActivityTaskManager.getTaskOrganizerController().applyContainerTransaction(wct, null); } catch (Exception e) { } } diff --git a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java index 3e4f3d818840..efea91ab91f0 100644 --- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java +++ b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java @@ -272,10 +272,24 @@ public class NetworkCapabilitiesTest { netCap.setOwnerUid(123); assertParcelingIsLossless(netCap); netCap.setSSID(TEST_SSID); - assertParcelSane(netCap, 13); + assertParcelSane(netCap, 15); } @Test + public void testParcelNetworkCapabilitiesWithRequestorUidAndPackageName() { + final NetworkCapabilities netCap = new NetworkCapabilities() + .addCapability(NET_CAPABILITY_INTERNET) + .setRequestorUid(9304) + .setRequestorPackageName("com.android.test") + .addCapability(NET_CAPABILITY_EIMS) + .addCapability(NET_CAPABILITY_NOT_METERED); + assertParcelingIsLossless(netCap); + netCap.setSSID(TEST_SSID); + assertParcelSane(netCap, 15); + } + + + @Test public void testOemPaid() { NetworkCapabilities nc = new NetworkCapabilities(); // By default OEM_PAID is neither in the unwanted or required lists and the network is not diff --git a/tests/net/java/android/net/ConnectivityManagerTest.java b/tests/net/java/android/net/ConnectivityManagerTest.java index 7ede14428a4f..d6bf334ee56a 100644 --- a/tests/net/java/android/net/ConnectivityManagerTest.java +++ b/tests/net/java/android/net/ConnectivityManagerTest.java @@ -212,7 +212,8 @@ public class ConnectivityManagerTest { ArgumentCaptor<Messenger> captor = ArgumentCaptor.forClass(Messenger.class); // register callback - when(mService.requestNetwork(any(), captor.capture(), anyInt(), any(), anyInt())) + when(mService.requestNetwork( + any(), captor.capture(), anyInt(), any(), anyInt(), any())) .thenReturn(request); manager.requestNetwork(request, callback, handler); @@ -240,7 +241,8 @@ public class ConnectivityManagerTest { ArgumentCaptor<Messenger> captor = ArgumentCaptor.forClass(Messenger.class); // register callback - when(mService.requestNetwork(any(), captor.capture(), anyInt(), any(), anyInt())) + when(mService.requestNetwork( + any(), captor.capture(), anyInt(), any(), anyInt(), any())) .thenReturn(req1); manager.requestNetwork(req1, callback, handler); @@ -258,7 +260,8 @@ public class ConnectivityManagerTest { verify(callback, timeout(100).times(0)).onLosing(any(), anyInt()); // callback can be registered again - when(mService.requestNetwork(any(), captor.capture(), anyInt(), any(), anyInt())) + when(mService.requestNetwork( + any(), captor.capture(), anyInt(), any(), anyInt(), any())) .thenReturn(req2); manager.requestNetwork(req2, callback, handler); @@ -282,7 +285,8 @@ public class ConnectivityManagerTest { info.targetSdkVersion = VERSION_CODES.N_MR1 + 1; when(mCtx.getApplicationInfo()).thenReturn(info); - when(mService.requestNetwork(any(), any(), anyInt(), any(), anyInt())).thenReturn(request); + when(mService.requestNetwork(any(), any(), anyInt(), any(), anyInt(), any())) + .thenReturn(request); Handler handler = new Handler(Looper.getMainLooper()); manager.requestNetwork(request, callback, handler); diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index b4f32e75fd01..968f5523d818 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -107,6 +107,7 @@ import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -305,6 +306,7 @@ public class ConnectivityServiceTest { private static final String MOBILE_IFNAME = "test_rmnet_data0"; private static final String WIFI_IFNAME = "test_wlan0"; private static final String WIFI_WOL_IFNAME = "test_wlan_wol"; + private static final String TEST_PACKAGE_NAME = "com.android.test.package"; private static final String[] EMPTY_STRING_ARRAY = new String[0]; private MockContext mServiceContext; @@ -654,7 +656,7 @@ public class ConnectivityServiceTest { if (mNmValidationRedirectUrl != null) { mNmCallbacks.showProvisioningNotification( - "test_provisioning_notif_action", "com.android.test.package"); + "test_provisioning_notif_action", TEST_PACKAGE_NAME); mNmProvNotificationRequested = true; } } @@ -2972,7 +2974,7 @@ public class ConnectivityServiceTest { networkCapabilities.addTransportType(TRANSPORT_WIFI) .setNetworkSpecifier(new MatchAllNetworkSpecifier()); mService.requestNetwork(networkCapabilities, null, 0, null, - ConnectivityManager.TYPE_WIFI); + ConnectivityManager.TYPE_WIFI, TEST_PACKAGE_NAME); }); class NonParcelableSpecifier extends NetworkSpecifier { @@ -3011,31 +3013,12 @@ public class ConnectivityServiceTest { } @Test - public void testNetworkSpecifierUidSpoofSecurityException() throws Exception { - class UidAwareNetworkSpecifier extends NetworkSpecifier implements Parcelable { - @Override - public boolean satisfiedBy(NetworkSpecifier other) { - return true; - } - - @Override - public void assertValidFromUid(int requestorUid) { - throw new SecurityException("failure"); - } - - @Override - public int describeContents() { return 0; } - @Override - public void writeToParcel(Parcel dest, int flags) {} - } - + public void testNetworkRequestUidSpoofSecurityException() throws Exception { mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(false); - - UidAwareNetworkSpecifier networkSpecifier = new UidAwareNetworkSpecifier(); - NetworkRequest networkRequest = newWifiRequestBuilder().setNetworkSpecifier( - networkSpecifier).build(); + NetworkRequest networkRequest = newWifiRequestBuilder().build(); TestNetworkCallback networkCallback = new TestNetworkCallback(); + doThrow(new SecurityException()).when(mAppOpsManager).checkPackage(anyInt(), anyString()); assertThrows(SecurityException.class, () -> { mCm.requestNetwork(networkRequest, networkCallback); }); @@ -6446,14 +6429,16 @@ public class ConnectivityServiceTest { public void testRegisterUnregisterConnectivityDiagnosticsCallback() throws Exception { final NetworkRequest wifiRequest = new NetworkRequest.Builder().addTransportType(TRANSPORT_WIFI).build(); - when(mConnectivityDiagnosticsCallback.asBinder()).thenReturn(mIBinder); mService.registerConnectivityDiagnosticsCallback( mConnectivityDiagnosticsCallback, wifiRequest, mContext.getPackageName()); - verify(mIBinder, timeout(TIMEOUT_MS)) - .linkToDeath(any(ConnectivityDiagnosticsCallbackInfo.class), anyInt()); + // Block until all other events are done processing. + HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS); + + verify(mIBinder).linkToDeath(any(ConnectivityDiagnosticsCallbackInfo.class), anyInt()); + verify(mConnectivityDiagnosticsCallback).asBinder(); assertTrue( mService.mConnectivityDiagnosticsCallbacks.containsKey( mConnectivityDiagnosticsCallback)); @@ -6476,8 +6461,10 @@ public class ConnectivityServiceTest { mService.registerConnectivityDiagnosticsCallback( mConnectivityDiagnosticsCallback, wifiRequest, mContext.getPackageName()); - verify(mIBinder, timeout(TIMEOUT_MS)) - .linkToDeath(any(ConnectivityDiagnosticsCallbackInfo.class), anyInt()); + // Block until all other events are done processing. + HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS); + + verify(mIBinder).linkToDeath(any(ConnectivityDiagnosticsCallbackInfo.class), anyInt()); verify(mConnectivityDiagnosticsCallback).asBinder(); assertTrue( mService.mConnectivityDiagnosticsCallbacks.containsKey( @@ -6635,8 +6622,11 @@ public class ConnectivityServiceTest { public void testConnectivityDiagnosticsCallbackOnConnectivityReport() throws Exception { setUpConnectivityDiagnosticsCallback(); - // Wait for onConnectivityReport to fire - verify(mConnectivityDiagnosticsCallback, timeout(TIMEOUT_MS)) + // Block until all other events are done processing. + HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS); + + // Verify onConnectivityReport fired + verify(mConnectivityDiagnosticsCallback) .onConnectivityReport(any(ConnectivityReport.class)); } @@ -6648,9 +6638,11 @@ public class ConnectivityServiceTest { // cellular network agent mCellNetworkAgent.notifyDataStallSuspected(); - // Wait for onDataStallSuspected to fire - verify(mConnectivityDiagnosticsCallback, timeout(TIMEOUT_MS)) - .onDataStallSuspected(any(DataStallReport.class)); + // Block until all other events are done processing. + HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS); + + // Verify onDataStallSuspected fired + verify(mConnectivityDiagnosticsCallback).onDataStallSuspected(any(DataStallReport.class)); } @Test @@ -6661,15 +6653,21 @@ public class ConnectivityServiceTest { final boolean hasConnectivity = true; mService.reportNetworkConnectivity(n, hasConnectivity); - // Wait for onNetworkConnectivityReported to fire - verify(mConnectivityDiagnosticsCallback, timeout(TIMEOUT_MS)) + // Block until all other events are done processing. + HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS); + + // Verify onNetworkConnectivityReported fired + verify(mConnectivityDiagnosticsCallback) .onNetworkConnectivityReported(eq(n), eq(hasConnectivity)); final boolean noConnectivity = false; mService.reportNetworkConnectivity(n, noConnectivity); + // Block until all other events are done processing. + HandlerUtilsKt.waitForIdle(mCsHandlerThread, TIMEOUT_MS); + // Wait for onNetworkConnectivityReported to fire - verify(mConnectivityDiagnosticsCallback, timeout(TIMEOUT_MS)) + verify(mConnectivityDiagnosticsCallback) .onNetworkConnectivityReported(eq(n), eq(noConnectivity)); } } diff --git a/tools/stats_log_api_gen/Android.bp b/tools/stats_log_api_gen/Android.bp index d3958a65c704..a251c053e004 100644 --- a/tools/stats_log_api_gen/Android.bp +++ b/tools/stats_log_api_gen/Android.bp @@ -30,7 +30,7 @@ cc_binary_host { "utils.cpp", ], cflags: [ - "-DSTATS_SCHEMA_LEGACY", + //"-DSTATS_SCHEMA_LEGACY", "-Wall", "-Werror", ], diff --git a/tools/stats_log_api_gen/java_writer_q.cpp b/tools/stats_log_api_gen/java_writer_q.cpp index a68c3a208869..12c050d8ef8d 100644 --- a/tools/stats_log_api_gen/java_writer_q.cpp +++ b/tools/stats_log_api_gen/java_writer_q.cpp @@ -175,9 +175,7 @@ int write_java_methods_q_schema( indent.c_str()); fprintf(out, "%s android.util.SparseArray<Float> floatMap = null;\n", indent.c_str()); - fprintf(out, - "%s int keyValuePairSize = LIST_TYPE_OVERHEAD * 5;\n", - indent.c_str()); + fprintf(out, "%s int keyValuePairSize = LIST_TYPE_OVERHEAD;\n", indent.c_str()); fprintf(out, "%s for (int i = 0; i < count; i++) {\n", indent.c_str()); fprintf(out, @@ -360,8 +358,9 @@ int write_java_methods_q_schema( requiredHelpers |= JAVA_MODULE_REQUIRES_FLOAT; requiredHelpers |= JAVA_MODULE_REQUIRES_KEY_VALUE_PAIRS; fprintf(out, - "%s writeKeyValuePairs(buff, pos, intMap, longMap, stringMap, " - "floatMap);\n", indent.c_str()); + "%s writeKeyValuePairs(buff, pos, (byte) count, intMap, longMap, " + "stringMap, floatMap);\n", + indent.c_str()); fprintf(out, "%s pos += keyValuePairSize;\n", indent.c_str()); break; default: @@ -472,7 +471,8 @@ void write_java_helpers_for_q_schema_methods( } if (requiredHelpers & JAVA_MODULE_REQUIRES_KEY_VALUE_PAIRS) { - fprintf(out, "%sprivate static void writeKeyValuePairs(byte[] buff, int pos,\n", + fprintf(out, + "%sprivate static void writeKeyValuePairs(byte[] buff, int pos, byte numPairs,\n", indent.c_str()); fprintf(out, "%s final android.util.SparseIntArray intMap,\n", indent.c_str()); fprintf(out, "%s final android.util.SparseLongArray longMap,\n", indent.c_str()); @@ -483,15 +483,12 @@ void write_java_helpers_for_q_schema_methods( // Start list of lists. fprintf(out, "%s buff[pos] = LIST_TYPE;\n", indent.c_str()); - fprintf(out, "%s buff[pos + 1] = (byte) 4;\n", indent.c_str()); + fprintf(out, "%s buff[pos + 1] = (byte) numPairs;\n", indent.c_str()); fprintf(out, "%s pos += LIST_TYPE_OVERHEAD;\n", indent.c_str()); // Write integers. fprintf(out, "%s final int intMapSize = null == intMap ? 0 : intMap.size();\n", indent.c_str()); - fprintf(out, "%s buff[pos] = LIST_TYPE;\n", indent.c_str()); - fprintf(out, "%s buff[pos + 1] = (byte) intMapSize;\n", indent.c_str()); - fprintf(out, "%s pos += LIST_TYPE_OVERHEAD;\n", indent.c_str()); fprintf(out, "%s for (int i = 0; i < intMapSize; i++) {\n", indent.c_str()); fprintf(out, "%s buff[pos] = LIST_TYPE;\n", indent.c_str()); fprintf(out, "%s buff[pos + 1] = (byte) 2;\n", indent.c_str()); @@ -509,9 +506,6 @@ void write_java_helpers_for_q_schema_methods( // Write longs. fprintf(out, "%s final int longMapSize = null == longMap ? 0 : longMap.size();\n", indent.c_str()); - fprintf(out, "%s buff[pos] = LIST_TYPE;\n", indent.c_str()); - fprintf(out, "%s buff[pos + 1] = (byte) longMapSize;\n", indent.c_str()); - fprintf(out, "%s pos += LIST_TYPE_OVERHEAD;\n", indent.c_str()); fprintf(out, "%s for (int i = 0; i < longMapSize; i++) {\n", indent.c_str()); fprintf(out, "%s buff[pos] = LIST_TYPE;\n", indent.c_str()); fprintf(out, "%s buff[pos + 1] = (byte) 2;\n", indent.c_str()); @@ -529,9 +523,6 @@ void write_java_helpers_for_q_schema_methods( // Write Strings. fprintf(out, "%s final int stringMapSize = null == stringMap ? 0 : stringMap.size();\n", indent.c_str()); - fprintf(out, "%s buff[pos] = LIST_TYPE;\n", indent.c_str()); - fprintf(out, "%s buff[pos + 1] = (byte) stringMapSize;\n", indent.c_str()); - fprintf(out, "%s pos += LIST_TYPE_OVERHEAD;\n", indent.c_str()); fprintf(out, "%s for (int i = 0; i < stringMapSize; i++) {\n", indent.c_str()); fprintf(out, "%s buff[pos] = LIST_TYPE;\n", indent.c_str()); fprintf(out, "%s buff[pos + 1] = (byte) 2;\n", indent.c_str()); @@ -556,9 +547,6 @@ void write_java_helpers_for_q_schema_methods( // Write floats. fprintf(out, "%s final int floatMapSize = null == floatMap ? 0 : floatMap.size();\n", indent.c_str()); - fprintf(out, "%s buff[pos] = LIST_TYPE;\n", indent.c_str()); - fprintf(out, "%s buff[pos + 1] = (byte) floatMapSize;\n", indent.c_str()); - fprintf(out, "%s pos += LIST_TYPE_OVERHEAD;\n", indent.c_str()); fprintf(out, "%s for (int i = 0; i < floatMapSize; i++) {\n", indent.c_str()); fprintf(out, "%s buff[pos] = LIST_TYPE;\n", indent.c_str()); fprintf(out, "%s buff[pos + 1] = (byte) 2;\n", indent.c_str()); diff --git a/tools/streaming_proto/cpp/main.cpp b/tools/streaming_proto/cpp/main.cpp index d6b9d81137ac..fe9a438d81d7 100644 --- a/tools/streaming_proto/cpp/main.cpp +++ b/tools/streaming_proto/cpp/main.cpp @@ -33,13 +33,13 @@ write_enum(stringstream& text, const EnumDescriptorProto& enu, const string& ind if (GENERATE_MAPPING) { string name = make_constant_name(enu.name()); string prefix = name + "_"; - text << indent << "const int _ENUM_" << name << "_COUNT = " << N << ";" << endl; - text << indent << "const char* _ENUM_" << name << "_NAMES[" << N << "] = {" << endl; + text << indent << "static const int _ENUM_" << name << "_COUNT = " << N << ";" << endl; + text << indent << "static const char* _ENUM_" << name << "_NAMES[" << N << "] = {" << endl; for (int i=0; i<N; i++) { text << indent << INDENT << "\"" << stripPrefix(enu.value(i).name(), prefix) << "\"," << endl; } text << indent << "};" << endl; - text << indent << "const int _ENUM_" << name << "_VALUES[" << N << "] = {" << endl; + text << indent << "static const int _ENUM_" << name << "_VALUES[" << N << "] = {" << endl; for (int i=0; i<N; i++) { text << indent << INDENT << make_constant_name(enu.value(i).name()) << "," << endl; } @@ -102,13 +102,13 @@ write_message(stringstream& text, const DescriptorProto& message, const string& if (GENERATE_MAPPING) { N = message.field_size(); - text << indented << "const int _FIELD_COUNT = " << N << ";" << endl; - text << indented << "const char* _FIELD_NAMES[" << N << "] = {" << endl; + text << indented << "static const int _FIELD_COUNT = " << N << ";" << endl; + text << indented << "static const char* _FIELD_NAMES[" << N << "] = {" << endl; for (int i=0; i<N; i++) { text << indented << INDENT << "\"" << message.field(i).name() << "\"," << endl; } text << indented << "};" << endl; - text << indented << "const uint64_t _FIELD_IDS[" << N << "] = {" << endl; + text << indented << "static const uint64_t _FIELD_IDS[" << N << "] = {" << endl; for (int i=0; i<N; i++) { text << indented << INDENT << make_constant_name(message.field(i).name()) << "," << endl; } @@ -152,7 +152,7 @@ write_header_file(CodeGeneratorResponse* response, const FileDescriptorProto& fi write_message(text, file_descriptor.message_type(i), ""); } - for (vector<string>::iterator it = namespaces.begin(); it != namespaces.end(); it++) { + for (vector<string>::reverse_iterator it = namespaces.rbegin(); it != namespaces.rend(); it++) { text << "} // " << *it << endl; } diff --git a/wifi/Android.bp b/wifi/Android.bp index dae04c6c3a25..6a29b1c36052 100644 --- a/wifi/Android.bp +++ b/wifi/Android.bp @@ -63,7 +63,6 @@ test_access_hidden_api_whitelist = [ "//frameworks/base/wifi/tests", "//frameworks/opt/net/wifi/tests/wifitests:__subpackages__", - "//frameworks/opt/net/wifi/libs/WifiTrackerLib/tests", "//external/robolectric-shadows:__subpackages__", "//frameworks/base/packages/SettingsLib/tests/integ", "//external/sl4a:__subpackages__", diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java index 62d6067c86f3..7c3d0b92dd0a 100644 --- a/wifi/java/android/net/wifi/WifiConfiguration.java +++ b/wifi/java/android/net/wifi/WifiConfiguration.java @@ -1301,10 +1301,34 @@ public class WifiConfiguration implements Parcelable { public static final int DISABLED_BY_WRONG_PASSWORD = 8; /** This network is disabled because service is not subscribed. */ public static final int DISABLED_AUTHENTICATION_NO_SUBSCRIPTION = 9; - /** All other disable reasons should be strictly less than this value. */ + /** + * All other disable reasons should be strictly less than this value. + * @hide + */ public static final int NETWORK_SELECTION_DISABLED_MAX = 10; /** + * Get an integer that is equal to the maximum integer value of all the + * DISABLED_* reasons + * e.g. {@link #DISABLED_NONE}, {@link #DISABLED_ASSOCIATION_REJECTION}, etc. + * + * All DISABLED_* constants will be contiguous in the range + * 0, 1, 2, 3, ..., getMaxNetworkSelectionDisableReasons() + * + * <br /> + * For example, this can be used to iterate through all the network selection + * disable reasons like so: + * <pre>{@code + * for (int reason = 0; reason <= getMaxNetworkSelectionDisableReasons(); reason++) { + * ... + * } + * }</pre> + */ + public static int getMaxNetworkSelectionDisableReason() { + return NETWORK_SELECTION_DISABLED_MAX - 1; + } + + /** * Contains info about disable reasons. * @hide */ @@ -1706,7 +1730,10 @@ public class WifiConfiguration implements Parcelable { return mStatus; } - /** True if the current network is enabled to join network selection, false otherwise. */ + /** + * True if the current network is enabled to join network selection, false otherwise. + * @hide + */ public boolean isNetworkEnabled() { return mStatus == NETWORK_SELECTION_ENABLED; } @@ -1719,7 +1746,10 @@ public class WifiConfiguration implements Parcelable { return mStatus == NETWORK_SELECTION_TEMPORARY_DISABLED; } - /** True if the current network is permanently disabled, false otherwise. */ + /** + * True if the current network is permanently disabled, false otherwise. + * @hide + */ public boolean isNetworkPermanentlyDisabled() { return mStatus == NETWORK_SELECTION_PERMANENTLY_DISABLED; } diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java index 7c031eaaeaf4..24b2a8e8994f 100644 --- a/wifi/java/android/net/wifi/WifiInfo.java +++ b/wifi/java/android/net/wifi/WifiInfo.java @@ -449,9 +449,8 @@ public class WifiInfo implements Parcelable { * <p> * If the SSID can be decoded as UTF-8, it will be returned surrounded by double * quotation marks. Otherwise, it is returned as a string of hex digits. - * The SSID may be - * <lt><unknown ssid>, if there is no network currently connected or if the caller has - * insufficient permissions to access the SSID.<lt> + * The SSID may be {@link WifiManager#UNKNOWN_SSID}, if there is no network currently connected + * or if the caller has insufficient permissions to access the SSID. * </p> * <p> * Prior to {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}, this method diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index 1dc4a069fe2f..a3cce7c00b3a 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -2625,8 +2625,8 @@ public class WifiManager { public void getWifiActivityEnergyInfoAsync( @NonNull @CallbackExecutor Executor executor, @NonNull OnWifiActivityEnergyInfoListener listener) { - if (executor == null) throw new IllegalArgumentException("executor cannot be null"); - if (listener == null) throw new IllegalArgumentException("listener cannot be null"); + Objects.requireNonNull(executor, "executor cannot be null"); + Objects.requireNonNull(listener, "listener cannot be null"); try { mService.getWifiActivityEnergyInfoAsync( new OnWifiActivityEnergyInfoProxy(executor, listener)); diff --git a/wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java b/wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java index 04d2e1a8b5dd..6632c162fcf9 100644 --- a/wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java +++ b/wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java @@ -27,7 +27,6 @@ import android.net.NetworkRequest; import android.net.NetworkSpecifier; import android.os.Parcel; import android.os.Parcelable; -import android.text.TextUtils; import java.util.Objects; @@ -41,33 +40,10 @@ public final class WifiNetworkAgentSpecifier extends NetworkSpecifier implements */ private final WifiConfiguration mWifiConfiguration; - /** - * The UID of the app that requested a specific wifi network using {@link WifiNetworkSpecifier}. - * - * Will only be filled when the device connects to a wifi network as a result of a - * {@link NetworkRequest} with {@link WifiNetworkSpecifier}. Will be set to -1 if the device - * auto-connected to a wifi network. - */ - private final int mOriginalRequestorUid; - - /** - * The package name of the app that requested a specific wifi network using - * {@link WifiNetworkSpecifier}. - * - * Will only be filled when the device connects to a wifi network as a result of a - * {@link NetworkRequest} with {@link WifiNetworkSpecifier}. Will be set to null if the device - * auto-connected to a wifi network. - */ - private final String mOriginalRequestorPackageName; - - public WifiNetworkAgentSpecifier(@NonNull WifiConfiguration wifiConfiguration, - int originalRequestorUid, - @Nullable String originalRequestorPackageName) { + public WifiNetworkAgentSpecifier(@NonNull WifiConfiguration wifiConfiguration) { checkNotNull(wifiConfiguration); mWifiConfiguration = wifiConfiguration; - mOriginalRequestorUid = originalRequestorUid; - mOriginalRequestorPackageName = originalRequestorPackageName; } /** @@ -78,10 +54,7 @@ public final class WifiNetworkAgentSpecifier extends NetworkSpecifier implements @Override public WifiNetworkAgentSpecifier createFromParcel(@NonNull Parcel in) { WifiConfiguration wifiConfiguration = in.readParcelable(null); - int originalRequestorUid = in.readInt(); - String originalRequestorPackageName = in.readString(); - return new WifiNetworkAgentSpecifier( - wifiConfiguration, originalRequestorUid, originalRequestorPackageName); + return new WifiNetworkAgentSpecifier(wifiConfiguration); } @Override @@ -98,8 +71,6 @@ public final class WifiNetworkAgentSpecifier extends NetworkSpecifier implements @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeParcelable(mWifiConfiguration, flags); - dest.writeInt(mOriginalRequestorUid); - dest.writeString(mOriginalRequestorPackageName); } @Override @@ -149,12 +120,6 @@ public final class WifiNetworkAgentSpecifier extends NetworkSpecifier implements this.mWifiConfiguration.allowedKeyManagement)) { return false; } - if (ns.requestorUid != this.mOriginalRequestorUid) { - return false; - } - if (!TextUtils.equals(ns.requestorPackageName, this.mOriginalRequestorPackageName)) { - return false; - } return true; } @@ -163,9 +128,7 @@ public final class WifiNetworkAgentSpecifier extends NetworkSpecifier implements return Objects.hash( mWifiConfiguration.SSID, mWifiConfiguration.BSSID, - mWifiConfiguration.allowedKeyManagement, - mOriginalRequestorUid, - mOriginalRequestorPackageName); + mWifiConfiguration.allowedKeyManagement); } @Override @@ -180,10 +143,7 @@ public final class WifiNetworkAgentSpecifier extends NetworkSpecifier implements return Objects.equals(this.mWifiConfiguration.SSID, lhs.mWifiConfiguration.SSID) && Objects.equals(this.mWifiConfiguration.BSSID, lhs.mWifiConfiguration.BSSID) && Objects.equals(this.mWifiConfiguration.allowedKeyManagement, - lhs.mWifiConfiguration.allowedKeyManagement) - && mOriginalRequestorUid == lhs.mOriginalRequestorUid - && TextUtils.equals(mOriginalRequestorPackageName, - lhs.mOriginalRequestorPackageName); + lhs.mWifiConfiguration.allowedKeyManagement); } @Override @@ -192,19 +152,11 @@ public final class WifiNetworkAgentSpecifier extends NetworkSpecifier implements sb.append("WifiConfiguration=") .append(", SSID=").append(mWifiConfiguration.SSID) .append(", BSSID=").append(mWifiConfiguration.BSSID) - .append(", mOriginalRequestorUid=").append(mOriginalRequestorUid) - .append(", mOriginalRequestorPackageName=").append(mOriginalRequestorPackageName) .append("]"); return sb.toString(); } @Override - public void assertValidFromUid(int requestorUid) { - throw new IllegalStateException("WifiNetworkAgentSpecifier should never be used " - + "for requests."); - } - - @Override public NetworkSpecifier redact() { return null; } diff --git a/wifi/java/android/net/wifi/WifiNetworkSpecifier.java b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java index 444e1ef041e8..3d946c9f887d 100644 --- a/wifi/java/android/net/wifi/WifiNetworkSpecifier.java +++ b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java @@ -20,7 +20,6 @@ import static com.android.internal.util.Preconditions.checkNotNull; import android.annotation.NonNull; import android.annotation.Nullable; -import android.app.Application; import android.net.MacAddress; import android.net.MatchAllNetworkSpecifier; import android.net.NetworkRequest; @@ -28,13 +27,9 @@ import android.net.NetworkSpecifier; import android.os.Parcel; import android.os.Parcelable; import android.os.PatternMatcher; -import android.os.Process; import android.text.TextUtils; -import android.util.Log; import android.util.Pair; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.nio.charset.CharsetEncoder; import java.nio.charset.StandardCharsets; import java.util.Objects; @@ -438,24 +433,7 @@ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parc return new WifiNetworkSpecifier( mSsidPatternMatcher, mBssidPatternMatcher, - buildWifiConfiguration(), - Process.myUid(), - getCurrentApplicationReflectively().getApplicationContext().getOpPackageName()); - } - - // TODO(b/144102365): Remove once refactor is complete - private static Application getCurrentApplicationReflectively() { - try { - // reflection for static method android.app.ActivityThread#currentApplication() - Class<?> klass = Class.forName("android.app.ActivityThread"); - Method currentApplicationMethod = klass.getDeclaredMethod("currentApplication"); - Object result = currentApplicationMethod.invoke(null); - return (Application) result; - } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException - | InvocationTargetException e) { - Log.e(TAG, "Failed to call ActivityThread#currentApplication() reflectively!", e); - throw new RuntimeException(e); - } + buildWifiConfiguration()); } } @@ -483,20 +461,6 @@ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parc */ public final WifiConfiguration wifiConfiguration; - /** - * The UID of the process initializing this network specifier. Validated by receiver using - * checkUidIfNecessary() and is used by satisfiedBy() to determine whether the specifier - * matches the offered network. - * @hide - */ - public final int requestorUid; - - /** - * The package name of the app initializing this network specifier. - * @hide - */ - public final String requestorPackageName; - /** @hide */ public WifiNetworkSpecifier() throws IllegalAccessException { throw new IllegalAccessException("Use the builder to create an instance"); @@ -505,18 +469,14 @@ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parc /** @hide */ public WifiNetworkSpecifier(@NonNull PatternMatcher ssidPatternMatcher, @NonNull Pair<MacAddress, MacAddress> bssidPatternMatcher, - @NonNull WifiConfiguration wifiConfiguration, - int requestorUid, @NonNull String requestorPackageName) { + @NonNull WifiConfiguration wifiConfiguration) { checkNotNull(ssidPatternMatcher); checkNotNull(bssidPatternMatcher); checkNotNull(wifiConfiguration); - checkNotNull(requestorPackageName); this.ssidPatternMatcher = ssidPatternMatcher; this.bssidPatternMatcher = bssidPatternMatcher; this.wifiConfiguration = wifiConfiguration; - this.requestorUid = requestorUid; - this.requestorPackageName = requestorPackageName; } public static final @NonNull Creator<WifiNetworkSpecifier> CREATOR = @@ -529,10 +489,8 @@ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parc Pair<MacAddress, MacAddress> bssidPatternMatcher = Pair.create(baseAddress, mask); WifiConfiguration wifiConfiguration = in.readParcelable(null); - int requestorUid = in.readInt(); - String requestorPackageName = in.readString(); return new WifiNetworkSpecifier(ssidPatternMatcher, bssidPatternMatcher, - wifiConfiguration, requestorUid, requestorPackageName); + wifiConfiguration); } @Override @@ -552,18 +510,13 @@ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parc dest.writeParcelable(bssidPatternMatcher.first, flags); dest.writeParcelable(bssidPatternMatcher.second, flags); dest.writeParcelable(wifiConfiguration, flags); - dest.writeInt(requestorUid); - dest.writeString(requestorPackageName); } @Override public int hashCode() { return Objects.hash( - ssidPatternMatcher.getPath(), - ssidPatternMatcher.getType(), - bssidPatternMatcher, - wifiConfiguration.allowedKeyManagement, - requestorUid, requestorPackageName); + ssidPatternMatcher.getPath(), ssidPatternMatcher.getType(), bssidPatternMatcher, + wifiConfiguration.allowedKeyManagement); } @Override @@ -582,9 +535,7 @@ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parc && Objects.equals(this.bssidPatternMatcher, lhs.bssidPatternMatcher) && Objects.equals(this.wifiConfiguration.allowedKeyManagement, - lhs.wifiConfiguration.allowedKeyManagement) - && requestorUid == lhs.requestorUid - && TextUtils.equals(requestorPackageName, lhs.requestorPackageName); + lhs.wifiConfiguration.allowedKeyManagement); } @Override @@ -595,8 +546,6 @@ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parc .append(", BSSID Match pattern=").append(bssidPatternMatcher) .append(", SSID=").append(wifiConfiguration.SSID) .append(", BSSID=").append(wifiConfiguration.BSSID) - .append(", requestorUid=").append(requestorUid) - .append(", requestorPackageName=").append(requestorPackageName) .append("]") .toString(); } @@ -618,12 +567,4 @@ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parc // not make much sense! return equals(other); } - - /** @hide */ - @Override - public void assertValidFromUid(int requestorUid) { - if (this.requestorUid != requestorUid) { - throw new SecurityException("mismatched UIDs"); - } - } } diff --git a/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java b/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java index c66733472d0e..a4b3e86398a8 100644 --- a/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java +++ b/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java @@ -143,12 +143,6 @@ public class WifiAwareAgentNetworkSpecifier extends NetworkSpecifier implements } @Override - public void assertValidFromUid(int requestorUid) { - throw new SecurityException( - "WifiAwareAgentNetworkSpecifier should not be used in network requests"); - } - - @Override public NetworkSpecifier redact() { return null; } diff --git a/wifi/java/android/net/wifi/aware/WifiAwareManager.java b/wifi/java/android/net/wifi/aware/WifiAwareManager.java index 81bf81e40199..2ebaa1805b2b 100644 --- a/wifi/java/android/net/wifi/aware/WifiAwareManager.java +++ b/wifi/java/android/net/wifi/aware/WifiAwareManager.java @@ -34,7 +34,6 @@ import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Message; -import android.os.Process; import android.os.RemoteException; import android.util.Log; @@ -447,8 +446,7 @@ public class WifiAwareManager { pmk, passphrase, 0, // no port info for deprecated IB APIs - -1, // no transport info for deprecated IB APIs - Process.myUid()); + -1); // no transport info for deprecated IB APIs } /** @hide */ @@ -488,8 +486,7 @@ public class WifiAwareManager { pmk, passphrase, 0, // no port info for OOB APIs - -1, // no transport protocol info for OOB APIs - Process.myUid()); + -1); // no transport protocol info for OOB APIs } private static class WifiAwareEventCallbackProxy extends IWifiAwareEventCallback.Stub { diff --git a/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java b/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java index 5a4ed3c2f5e3..65ac1ab26064 100644 --- a/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java +++ b/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java @@ -23,7 +23,6 @@ import android.annotation.NonNull; import android.net.NetworkSpecifier; import android.os.Parcel; import android.os.Parcelable; -import android.os.Process; import android.text.TextUtils; import java.util.Arrays; @@ -144,19 +143,9 @@ public final class WifiAwareNetworkSpecifier extends NetworkSpecifier implements */ public final int transportProtocol; - /** - * The UID of the process initializing this network specifier. Validated by receiver using - * checkUidIfNecessary() and is used by satisfiedBy() to determine whether matches the - * offered network. - * - * @hide - */ - public final int requestorUid; - /** @hide */ public WifiAwareNetworkSpecifier(int type, int role, int clientId, int sessionId, int peerId, - byte[] peerMac, byte[] pmk, String passphrase, int port, int transportProtocol, - int requestorUid) { + byte[] peerMac, byte[] pmk, String passphrase, int port, int transportProtocol) { this.type = type; this.role = role; this.clientId = clientId; @@ -167,7 +156,6 @@ public final class WifiAwareNetworkSpecifier extends NetworkSpecifier implements this.passphrase = passphrase; this.port = port; this.transportProtocol = transportProtocol; - this.requestorUid = requestorUid; } public static final @android.annotation.NonNull Creator<WifiAwareNetworkSpecifier> CREATOR = @@ -184,8 +172,7 @@ public final class WifiAwareNetworkSpecifier extends NetworkSpecifier implements in.createByteArray(), // pmk in.readString(), // passphrase in.readInt(), // port - in.readInt(), // transportProtocol - in.readInt()); // requestorUid + in.readInt()); // transportProtocol } @Override @@ -221,7 +208,6 @@ public final class WifiAwareNetworkSpecifier extends NetworkSpecifier implements dest.writeString(passphrase); dest.writeInt(port); dest.writeInt(transportProtocol); - dest.writeInt(requestorUid); } /** @hide */ @@ -238,7 +224,7 @@ public final class WifiAwareNetworkSpecifier extends NetworkSpecifier implements @Override public int hashCode() { return Objects.hash(type, role, clientId, sessionId, peerId, Arrays.hashCode(peerMac), - Arrays.hashCode(pmk), passphrase, port, transportProtocol, requestorUid); + Arrays.hashCode(pmk), passphrase, port, transportProtocol); } /** @hide */ @@ -263,8 +249,7 @@ public final class WifiAwareNetworkSpecifier extends NetworkSpecifier implements && Arrays.equals(pmk, lhs.pmk) && Objects.equals(passphrase, lhs.passphrase) && port == lhs.port - && transportProtocol == lhs.transportProtocol - && requestorUid == lhs.requestorUid; + && transportProtocol == lhs.transportProtocol; } /** @hide */ @@ -283,19 +268,11 @@ public final class WifiAwareNetworkSpecifier extends NetworkSpecifier implements // masking PII .append(", passphrase=").append((passphrase == null) ? "<null>" : "<non-null>") .append(", port=").append(port).append(", transportProtocol=") - .append(transportProtocol).append(", requestorUid=").append(requestorUid) + .append(transportProtocol) .append("]"); return sb.toString(); } - /** @hide */ - @Override - public void assertValidFromUid(int requestorUid) { - if (this.requestorUid != requestorUid) { - throw new SecurityException("mismatched UIDs"); - } - } - /** * A builder class for a Wi-Fi Aware network specifier to set up an Aware connection with a * peer. @@ -463,7 +440,7 @@ public final class WifiAwareNetworkSpecifier extends NetworkSpecifier implements return new WifiAwareNetworkSpecifier( WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_IB, role, mDiscoverySession.mClientId, mDiscoverySession.mSessionId, mPeerHandle.peerId, - null, mPmk, mPskPassphrase, mPort, mTransportProtocol, Process.myUid()); + null, mPmk, mPskPassphrase, mPort, mTransportProtocol); } } } diff --git a/wifi/java/android/net/wifi/wificond/NativeScanResult.java b/wifi/java/android/net/wifi/wificond/NativeScanResult.java index 85251e8b1d42..7cc617d61b00 100644 --- a/wifi/java/android/net/wifi/wificond/NativeScanResult.java +++ b/wifi/java/android/net/wifi/wificond/NativeScanResult.java @@ -24,7 +24,6 @@ import android.os.Parcelable; import com.android.internal.annotations.VisibleForTesting; import java.util.ArrayList; -import java.util.BitSet; import java.util.List; /** @@ -34,8 +33,6 @@ import java.util.List; */ @SystemApi public final class NativeScanResult implements Parcelable { - private static final int CAPABILITY_SIZE = 16; - /** @hide */ @VisibleForTesting public byte[] ssid; @@ -56,7 +53,7 @@ public final class NativeScanResult implements Parcelable { public long tsf; /** @hide */ @VisibleForTesting - public BitSet capability; + public int capability; /** @hide */ @VisibleForTesting public boolean associated; @@ -134,7 +131,7 @@ public final class NativeScanResult implements Parcelable { * Returns the capabilities of the AP repseresented by this scan result as advertised in the * received probe response or beacon. * - * This is a bit mask describing the capabilities of a BSS. See IEEE Std 802.11: 8.4.1.4: + * This is a bit mask describing the capabilities of a BSS. See IEEE Std 802.11: 9.4.1.4: * Bit 0 - ESS * Bit 1 - IBSS * Bit 2 - CF Pollable @@ -143,7 +140,7 @@ public final class NativeScanResult implements Parcelable { * Bit 5 - Short Preamble * Bit 6 - PBCC * Bit 7 - Channel Agility - * Bit 8 - Spectrum Mgmt + * Bit 8 - Spectrum Management * Bit 9 - QoS * Bit 10 - Short Slot Time * Bit 11 - APSD @@ -154,7 +151,7 @@ public final class NativeScanResult implements Parcelable { * * @return a bit mask of capabilities. */ - @NonNull public BitSet getCapabilities() { + @NonNull public int getCapabilities() { return capability; } @@ -188,13 +185,7 @@ public final class NativeScanResult implements Parcelable { out.writeInt(frequency); out.writeInt(signalMbm); out.writeLong(tsf); - int capabilityInt = 0; - for (int i = 0; i < CAPABILITY_SIZE; i++) { - if (capability.get(i)) { - capabilityInt |= 1 << i; - } - } - out.writeInt(capabilityInt); + out.writeInt(capability); out.writeInt(associated ? 1 : 0); out.writeTypedList(radioChainInfos); } @@ -220,13 +211,7 @@ public final class NativeScanResult implements Parcelable { result.frequency = in.readInt(); result.signalMbm = in.readInt(); result.tsf = in.readLong(); - int capabilityInt = in.readInt(); - result.capability = new BitSet(CAPABILITY_SIZE); - for (int i = 0; i < CAPABILITY_SIZE; i++) { - if ((capabilityInt & (1 << i)) != 0) { - result.capability.set(i); - } - } + result.capability = in.readInt(); result.associated = (in.readInt() != 0); result.radioChainInfos = new ArrayList<>(); in.readTypedList(result.radioChainInfos, RadioChainInfo.CREATOR); diff --git a/wifi/java/android/net/wifi/wificond/PnoSettings.java b/wifi/java/android/net/wifi/wificond/PnoSettings.java index 57c9ca5fd302..533d37d3a23a 100644 --- a/wifi/java/android/net/wifi/wificond/PnoSettings.java +++ b/wifi/java/android/net/wifi/wificond/PnoSettings.java @@ -16,6 +16,7 @@ package android.net.wifi.wificond; +import android.annotation.DurationMillisLong; import android.annotation.NonNull; import android.annotation.SystemApi; import android.os.Parcel; @@ -33,7 +34,7 @@ import java.util.Objects; */ @SystemApi public final class PnoSettings implements Parcelable { - private int mIntervalMs; + private long mIntervalMs; private int mMin2gRssi; private int mMin5gRssi; private int mMin6gRssi; @@ -47,17 +48,17 @@ public final class PnoSettings implements Parcelable { * * @return An interval in milliseconds. */ - public int getIntervalMillis() { + public @DurationMillisLong long getIntervalMillis() { return mIntervalMs; } /** * Set the requested PNO scan interval in milliseconds. * - * @param intervalMs An interval in milliseconds. + * @param intervalMillis An interval in milliseconds. */ - public void setIntervalMillis(int intervalMs) { - this.mIntervalMs = intervalMs; + public void setIntervalMillis(@DurationMillisLong long intervalMillis) { + this.mIntervalMs = intervalMillis; } /** @@ -176,7 +177,7 @@ public final class PnoSettings implements Parcelable { **/ @Override public void writeToParcel(@NonNull Parcel out, int flags) { - out.writeInt(mIntervalMs); + out.writeLong(mIntervalMs); out.writeInt(mMin2gRssi); out.writeInt(mMin5gRssi); out.writeInt(mMin6gRssi); @@ -189,7 +190,7 @@ public final class PnoSettings implements Parcelable { @Override public PnoSettings createFromParcel(Parcel in) { PnoSettings result = new PnoSettings(); - result.mIntervalMs = in.readInt(); + result.mIntervalMs = in.readLong(); result.mMin2gRssi = in.readInt(); result.mMin5gRssi = in.readInt(); result.mMin6gRssi = in.readInt(); diff --git a/wifi/java/android/net/wifi/wificond/WifiCondManager.java b/wifi/java/android/net/wifi/wificond/WifiCondManager.java index 43aa1b64efbc..7a31a5afab05 100644 --- a/wifi/java/android/net/wifi/wificond/WifiCondManager.java +++ b/wifi/java/android/net/wifi/wificond/WifiCondManager.java @@ -496,22 +496,17 @@ public class WifiCondManager { } /** - * Initializes WifiCondManager & registers a death notification for the WifiCondManager which - * acts as a proxy for the wificond daemon (i.e. the death listener will be called when and if - * the wificond daemon dies). - * - * Note: This method clears any existing state in wificond daemon. + * Register a death notification for the WifiCondManager which acts as a proxy for the + * wificond daemon (i.e. the death listener will be called when and if the wificond daemon + * dies). * * @param deathEventHandler A {@link Runnable} to be called whenever the wificond daemon dies. - * @return Returns true on success. */ - public boolean initialize(@NonNull Runnable deathEventHandler) { + public void setOnServiceDeadCallback(@NonNull Runnable deathEventHandler) { if (mDeathEventHandler != null) { Log.e(TAG, "Death handler already present"); } mDeathEventHandler = deathEventHandler; - tearDownInterfaces(); - return true; } /** @@ -603,11 +598,12 @@ public class WifiCondManager { } /** - * Tear down a specific client (STA) interface, initially configured using + * Tear down a specific client (STA) interface configured using * {@link #setupInterfaceForClientMode(String, Executor, ScanEventCallback, ScanEventCallback)}. * * @param ifaceName Name of the interface to tear down. - * @return Returns true on success. + * @return Returns true on success, false on failure (e.g. when called before an interface was + * set up). */ public boolean tearDownClientInterface(@NonNull String ifaceName) { if (getClientInterface(ifaceName) == null) { @@ -681,11 +677,12 @@ public class WifiCondManager { } /** - * Tear down a Soft AP interface initially configured using + * Tear down a Soft AP interface configured using * {@link #setupInterfaceForSoftApMode(String)}. * * @param ifaceName Name of the interface to tear down. - * @return Returns true on success. + * @return Returns true on success, false on failure (e.g. when called before an interface was + * set up). */ public boolean tearDownSoftApInterface(@NonNull String ifaceName) { if (getApInterface(ifaceName) == null) { @@ -750,9 +747,13 @@ public class WifiCondManager { /** * Request signal polling. * - * @param ifaceName Name of the interface on which to poll. + * @param ifaceName Name of the interface on which to poll. The interface must have been + * already set up using + *{@link #setupInterfaceForClientMode(String, Executor, ScanEventCallback, ScanEventCallback)} + * or {@link #setupInterfaceForSoftApMode(String)}. + * * @return A {@link SignalPollResult} object containing interface statistics, or a null on - * error. + * error (e.g. the interface hasn't been set up yet). */ @Nullable public SignalPollResult signalPoll(@NonNull String ifaceName) { IClientInterface iface = getClientInterface(ifaceName); @@ -776,10 +777,14 @@ public class WifiCondManager { } /** - * Get current transmit (Tx) packet counters of the specified interface. + * Get current transmit (Tx) packet counters of the specified interface. The interface must + * have been already set up using + * {@link #setupInterfaceForClientMode(String, Executor, ScanEventCallback, ScanEventCallback)} + * or {@link #setupInterfaceForSoftApMode(String)}. * * @param ifaceName Name of the interface. - * @return {@link TxPacketCounters} of the current interface or null on error. + * @return {@link TxPacketCounters} of the current interface or null on error (e.g. when + * called before the interface has been set up). */ @Nullable public TxPacketCounters getTxPacketCounters(@NonNull String ifaceName) { IClientInterface iface = getClientInterface(ifaceName); @@ -813,10 +818,15 @@ public class WifiCondManager { * be done using {@link #startScan(String, int, Set, List)} or * {@link #startPnoScan(String, PnoSettings, Executor, PnoScanRequestCallback)}. * + * Note: The interface must have been already set up using + * {@link #setupInterfaceForClientMode(String, Executor, ScanEventCallback, ScanEventCallback)} + * or {@link #setupInterfaceForSoftApMode(String)}. + * * @param ifaceName Name of the interface. * @param scanType The type of scan result to be returned, can be * {@link #SCAN_TYPE_SINGLE_SCAN} or {@link #SCAN_TYPE_PNO_SCAN}. - * @return Returns an array of {@link NativeScanResult} or an empty array on failure. + * @return Returns an array of {@link NativeScanResult} or an empty array on failure (e.g. when + * called before the interface has been set up). */ @NonNull public List<NativeScanResult> getScanResults(@NonNull String ifaceName, @ScanResultType int scanType) { @@ -869,13 +879,19 @@ public class WifiCondManager { * The latest scans can be obtained using {@link #getScanResults(String, int)} and using a * {@link #SCAN_TYPE_SINGLE_SCAN} for the {@code scanType}. * + * Note: The interface must have been already set up using + * {@link #setupInterfaceForClientMode(String, Executor, ScanEventCallback, ScanEventCallback)} + * or {@link #setupInterfaceForSoftApMode(String)}. + * * @param ifaceName Name of the interface on which to initiate the scan. * @param scanType Type of scan to perform, can be any of * {@link WifiScanner#SCAN_TYPE_HIGH_ACCURACY}, {@link WifiScanner#SCAN_TYPE_LOW_POWER}, or * {@link WifiScanner#SCAN_TYPE_LOW_LATENCY}. * @param freqs list of frequencies to scan for, if null scan all supported channels. - * @param hiddenNetworkSSIDs List of hidden networks to be scanned for. - * @return Returns true on success. + * @param hiddenNetworkSSIDs List of hidden networks to be scanned for, a null indicates that + * no hidden frequencies will be scanned for. + * @return Returns true on success, false on failure (e.g. when called before the interface + * has been set up). */ public boolean startScan(@NonNull String ifaceName, @WifiAnnotations.ScanType int scanType, @Nullable Set<Integer> freqs, @Nullable List<byte[]> hiddenNetworkSSIDs) { @@ -931,11 +947,16 @@ public class WifiCondManager { * The latest PNO scans can be obtained using {@link #getScanResults(String, int)} with the * {@code scanType} set to {@link #SCAN_TYPE_PNO_SCAN}. * + * Note: The interface must have been already set up using + * {@link #setupInterfaceForClientMode(String, Executor, ScanEventCallback, ScanEventCallback)} + * or {@link #setupInterfaceForSoftApMode(String)}. + * * @param ifaceName Name of the interface on which to request a PNO. * @param pnoSettings PNO scan configuration. * @param executor The Executor on which to execute the callback. * @param callback Callback for the results of the offload request. - * @return true on success. + * @return true on success, false on failure (e.g. when called before the interface has been set + * up). */ public boolean startPnoScan(@NonNull String ifaceName, @NonNull PnoSettings pnoSettings, @NonNull @CallbackExecutor Executor executor, @@ -969,8 +990,13 @@ public class WifiCondManager { * Stop PNO scan configured with * {@link #startPnoScan(String, PnoSettings, Executor, PnoScanRequestCallback)}. * + * Note: The interface must have been already set up using + * {@link #setupInterfaceForClientMode(String, Executor, ScanEventCallback, ScanEventCallback)} + * or {@link #setupInterfaceForSoftApMode(String)}. + * * @param ifaceName Name of the interface on which the PNO scan was configured. - * @return true on success. + * @return true on success, false on failure (e.g. when called before the interface has been + * set up). */ public boolean stopPnoScan(@NonNull String ifaceName) { IWifiScannerImpl scannerImpl = getScannerImpl(ifaceName); @@ -987,7 +1013,13 @@ public class WifiCondManager { } /** - * Abort ongoing single scan started with {@link #startScan(String, int, Set, List)}. + * Abort ongoing single scan started with {@link #startScan(String, int, Set, List)}. No failure + * callback, e.g. {@link ScanEventCallback#onScanFailed()}, is triggered by this operation. + * + * Note: The interface must have been already set up using + * {@link #setupInterfaceForClientMode(String, Executor, ScanEventCallback, ScanEventCallback)} + * or {@link #setupInterfaceForSoftApMode(String)}. If the interface has not been set up then + * this method has no impact. * * @param ifaceName Name of the interface on which the scan was started. */ @@ -1055,7 +1087,14 @@ public class WifiCondManager { } /** - * Get the device phy capabilities for a given interface + * Get the device phy capabilities for a given interface. + * + * Note: The interface must have been already set up using + * {@link #setupInterfaceForClientMode(String, Executor, ScanEventCallback, ScanEventCallback)} + * or {@link #setupInterfaceForSoftApMode(String)}. + * + * @return DeviceWiphyCapabilities or null on error (e.g. when called on an interface which has + * not been set up). */ @Nullable public DeviceWiphyCapabilities getDeviceWiphyCapabilities(@NonNull String ifaceName) { if (mWificond == null) { @@ -1071,13 +1110,19 @@ public class WifiCondManager { } /** - * Register the provided callback handler for SoftAp events. Note that the Soft AP itself is - * configured using {@link #setupInterfaceForSoftApMode(String)}. + * Register the provided callback handler for SoftAp events. The interface must first be created + * using {@link #setupInterfaceForSoftApMode(String)}. The callback registration is valid until + * the interface is deleted using {@link #tearDownSoftApInterface(String)} (no deregistration + * method is provided). + * <p> + * Note that only one callback can be registered at a time - any registration overrides previous + * registrations. * * @param ifaceName Name of the interface on which to register the callback. * @param executor The Executor on which to execute the callbacks. * @param callback Callback for AP events. - * @return true on success, false otherwise. + * @return true on success, false on failure (e.g. when called on an interface which has not + * been set up). */ public boolean registerApCallback(@NonNull String ifaceName, @NonNull @CallbackExecutor Executor executor, @@ -1113,6 +1158,10 @@ public class WifiCondManager { * Send a management frame on the specified interface at the specified rate. Useful for probing * the link with arbitrary frames. * + * Note: The interface must have been already set up using + * {@link #setupInterfaceForClientMode(String, Executor, ScanEventCallback, ScanEventCallback)} + * or {@link #setupInterfaceForSoftApMode(String)}. + * * @param ifaceName The interface on which to send the frame. * @param frame The raw byte array of the management frame to tramit. * @param mcs The MCS (modulation and coding scheme), i.e. rate, at which to transmit the diff --git a/wifi/java/com/android/server/wifi/BaseWifiService.java b/wifi/java/com/android/server/wifi/BaseWifiService.java deleted file mode 100644 index 060c85cac209..000000000000 --- a/wifi/java/com/android/server/wifi/BaseWifiService.java +++ /dev/null @@ -1,641 +0,0 @@ -/** - * Copyright (c) 2018, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License") { - * throw new UnsupportedOperationException(); - } - * 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.wifi; - -import android.content.pm.ParceledListSlice; -import android.net.DhcpInfo; -import android.net.Network; -import android.net.wifi.IActionListener; -import android.net.wifi.IDppCallback; -import android.net.wifi.ILocalOnlyHotspotCallback; -import android.net.wifi.INetworkRequestMatchCallback; -import android.net.wifi.IOnWifiActivityEnergyInfoListener; -import android.net.wifi.IOnWifiUsabilityStatsListener; -import android.net.wifi.IScanResultsCallback; -import android.net.wifi.ISoftApCallback; -import android.net.wifi.ISuggestionConnectionStatusListener; -import android.net.wifi.ITrafficStateCallback; -import android.net.wifi.ITxPacketCountListener; -import android.net.wifi.IWifiConnectedNetworkScorer; -import android.net.wifi.IWifiManager; -import android.net.wifi.ScanResult; -import android.net.wifi.SoftApConfiguration; -import android.net.wifi.WifiConfiguration; -import android.net.wifi.WifiInfo; -import android.net.wifi.WifiManager; -import android.net.wifi.WifiNetworkSuggestion; -import android.net.wifi.hotspot2.IProvisioningCallback; -import android.net.wifi.hotspot2.OsuProvider; -import android.net.wifi.hotspot2.PasspointConfiguration; -import android.os.IBinder; -import android.os.RemoteException; -import android.os.ResultReceiver; -import android.os.WorkSource; -import android.os.connectivity.WifiActivityEnergyInfo; - -import java.util.List; -import java.util.Map; - -/** - * Empty concrete class implementing IWifiManager with stub methods throwing runtime exceptions. - * - * This class is meant to be extended by real implementations of IWifiManager in order to facilitate - * cross-repo changes to WiFi internal APIs, including the introduction of new APIs, the removal of - * deprecated APIs, or the migration of existing API signatures. - * - * When an existing API is scheduled for removal, it can be removed from IWifiManager.aidl - * immediately and marked as @Deprecated first in this class. Children inheriting this class are - * then given a short grace period to update themselves before the @Deprecated stub is removed for - * good. If the API scheduled for removal has a replacement or an overload (signature change), - * these should be introduced before the stub is removed to allow children to migrate. - * - * When a new API is added to IWifiManager.aidl, a stub should be added in BaseWifiService as - * well otherwise compilation will fail. - */ -public class BaseWifiService extends IWifiManager.Stub { - - private static final String TAG = BaseWifiService.class.getSimpleName(); - - @Override - public long getSupportedFeatures() { - throw new UnsupportedOperationException(); - } - - /** @deprecated use {@link #getWifiActivityEnergyInfoAsync} instead */ - @Deprecated - public WifiActivityEnergyInfo reportActivityInfo() { - throw new UnsupportedOperationException(); - } - - /** @deprecated use {@link #getWifiActivityEnergyInfoAsync} instead */ - @Deprecated - public void requestActivityInfo(ResultReceiver result) { - throw new UnsupportedOperationException(); - } - - @Override - public void getWifiActivityEnergyInfoAsync(IOnWifiActivityEnergyInfoListener listener) { - throw new UnsupportedOperationException(); - } - - @Override - public ParceledListSlice getConfiguredNetworks(String packageName, String featureId) { - throw new UnsupportedOperationException(); - } - - @Override - public ParceledListSlice getPrivilegedConfiguredNetworks(String packageName, String featureId) { - throw new UnsupportedOperationException(); - } - - @Override - public Map<String, Map<Integer, List<ScanResult>>> getAllMatchingFqdnsForScanResults( - List<ScanResult> scanResults) { - throw new UnsupportedOperationException(); - } - - @Override - public Map<OsuProvider, List<ScanResult>> getMatchingOsuProviders( - List<ScanResult> scanResults) { - throw new UnsupportedOperationException(); - } - - @Override - public Map<OsuProvider, PasspointConfiguration> getMatchingPasspointConfigsForOsuProviders( - List<OsuProvider> osuProviders) { - throw new UnsupportedOperationException(); - } - - @Override - public int addOrUpdateNetwork(WifiConfiguration config, String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean addOrUpdatePasspointConfiguration( - PasspointConfiguration config, String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean removePasspointConfiguration(String fqdn, String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public List<PasspointConfiguration> getPasspointConfigurations(String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public List<WifiConfiguration> getWifiConfigsForPasspointProfiles(List<String> fqdnList) { - throw new UnsupportedOperationException(); - } - - @Override - public void queryPasspointIcon(long bssid, String fileName) { - throw new UnsupportedOperationException(); - } - - @Override - public int matchProviderWithCurrentNetwork(String fqdn) { - throw new UnsupportedOperationException(); - } - - @Override - public void deauthenticateNetwork(long holdoff, boolean ess) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean removeNetwork(int netId, String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean enableNetwork(int netId, boolean disableOthers, String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean disableNetwork(int netId, String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public void allowAutojoinGlobal(boolean choice) { - throw new UnsupportedOperationException(); - } - - @Override - public void allowAutojoin(int netId, boolean choice) { - throw new UnsupportedOperationException(); - } - - @Override - public void allowAutojoinPasspoint(String fqdn, boolean enableAutoJoin) { - throw new UnsupportedOperationException(); - } - - @Override - public void setMacRandomizationSettingPasspointEnabled(String fqdn, boolean enable) { - throw new UnsupportedOperationException(); - } - - @Override - public void setMeteredOverridePasspoint(String fqdn, int meteredOverride) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean startScan(String packageName, String featureId) { - throw new UnsupportedOperationException(); - } - - @Override - public List<ScanResult> getScanResults(String callingPackage, String callingFeatureId) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean disconnect(String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean reconnect(String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean reassociate(String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public WifiInfo getConnectionInfo(String callingPackage, String callingFeatureId) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean setWifiEnabled(String packageName, boolean enable) { - throw new UnsupportedOperationException(); - } - - @Override - public int getWifiEnabledState() { - throw new UnsupportedOperationException(); - } - - @Override - public String getCountryCode() { - throw new UnsupportedOperationException(); - } - - /** @deprecated use {@link #is5GHzBandSupported} instead */ - @Deprecated - public boolean isDualBandSupported() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean is5GHzBandSupported() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean is6GHzBandSupported() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isWifiStandardSupported(int standard) { - throw new UnsupportedOperationException(); - } - - /** @deprecated use {@link WifiManager#isStaApConcurrencySupported()} */ - @Deprecated - public boolean needs5GHzToAnyApBandConversion() { - throw new UnsupportedOperationException(); - } - - @Override - public DhcpInfo getDhcpInfo() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isScanAlwaysAvailable() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean acquireWifiLock(IBinder lock, int lockType, String tag, WorkSource ws) { - throw new UnsupportedOperationException(); - } - - @Override - public void updateWifiLockWorkSource(IBinder lock, WorkSource ws) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean releaseWifiLock(IBinder lock) { - throw new UnsupportedOperationException(); - } - - @Override - public void initializeMulticastFiltering() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isMulticastEnabled() { - throw new UnsupportedOperationException(); - } - - @Override - public void acquireMulticastLock(IBinder binder, String tag) { - throw new UnsupportedOperationException(); - } - - @Override - public void releaseMulticastLock(String tag) { - throw new UnsupportedOperationException(); - } - - @Override - public void updateInterfaceIpState(String ifaceName, int mode) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean startSoftAp(WifiConfiguration wifiConfig) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean startTetheredHotspot(SoftApConfiguration softApConfig) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean stopSoftAp() { - throw new UnsupportedOperationException(); - } - - @Override - public int startLocalOnlyHotspot(ILocalOnlyHotspotCallback callback, String packageName, - String featureId, SoftApConfiguration customConfig) { - throw new UnsupportedOperationException(); - } - - @Override - public void stopLocalOnlyHotspot() { - throw new UnsupportedOperationException(); - } - - @Override - public void startWatchLocalOnlyHotspot(ILocalOnlyHotspotCallback callback) { - throw new UnsupportedOperationException(); - } - - @Override - public void stopWatchLocalOnlyHotspot() { - throw new UnsupportedOperationException(); - } - - @Override - public int getWifiApEnabledState() { - throw new UnsupportedOperationException(); - } - - @Override - public WifiConfiguration getWifiApConfiguration() { - throw new UnsupportedOperationException(); - } - - @Override - public SoftApConfiguration getSoftApConfiguration() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean setWifiApConfiguration(WifiConfiguration wifiConfig, String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean setSoftApConfiguration(SoftApConfiguration softApConfig, String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public void notifyUserOfApBandConversion(String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public void enableTdls(String remoteIPAddress, boolean enable) { - throw new UnsupportedOperationException(); - } - - @Override - public void enableTdlsWithMacAddress(String remoteMacAddress, boolean enable) { - throw new UnsupportedOperationException(); - } - - @Override - public String getCurrentNetworkWpsNfcConfigurationToken() { - throw new UnsupportedOperationException(); - } - - @Override - public void enableVerboseLogging(int verbose) { - throw new UnsupportedOperationException(); - } - - @Override - public int getVerboseLoggingLevel() { - throw new UnsupportedOperationException(); - } - - /** @deprecated use {@link #allowAutojoinGlobal(boolean)} instead */ - @Deprecated - public void enableWifiConnectivityManager(boolean enabled) { - throw new UnsupportedOperationException(); - } - - @Override - public void disableEphemeralNetwork(String SSID, String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public void factoryReset(String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public Network getCurrentNetwork() { - throw new UnsupportedOperationException(); - } - - @Override - public byte[] retrieveBackupData() { - throw new UnsupportedOperationException(); - } - - @Override - public void restoreBackupData(byte[] data) { - throw new UnsupportedOperationException(); - } - - @Override - public byte[] retrieveSoftApBackupData() { - throw new UnsupportedOperationException(); - } - - @Override - public SoftApConfiguration restoreSoftApBackupData(byte[] data) { - throw new UnsupportedOperationException(); - } - - @Override - public void restoreSupplicantBackupData(byte[] supplicantData, byte[] ipConfigData) { - throw new UnsupportedOperationException(); - } - - @Override - public void startSubscriptionProvisioning( - OsuProvider provider, IProvisioningCallback callback) { - throw new UnsupportedOperationException(); - } - - @Override - public void registerSoftApCallback( - IBinder binder, ISoftApCallback callback, int callbackIdentifier) { - throw new UnsupportedOperationException(); - } - - @Override - public void unregisterSoftApCallback(int callbackIdentifier) { - throw new UnsupportedOperationException(); - } - - @Override - public void registerTrafficStateCallback( - IBinder binder, ITrafficStateCallback callback, int callbackIdentifier) { - throw new UnsupportedOperationException(); - } - - @Override - public void unregisterTrafficStateCallback(int callbackIdentifier) { - throw new UnsupportedOperationException(); - } - - @Override - public void registerNetworkRequestMatchCallback( - IBinder binder, INetworkRequestMatchCallback callback, int callbackIdentifier) { - throw new UnsupportedOperationException(); - } - - @Override - public void unregisterNetworkRequestMatchCallback(int callbackIdentifier) { - throw new UnsupportedOperationException(); - } - - @Override - public int addNetworkSuggestions( - List<WifiNetworkSuggestion> networkSuggestions, String callingPackageName, - String callingFeatureId) { - throw new UnsupportedOperationException(); - } - - @Override - public int removeNetworkSuggestions( - List<WifiNetworkSuggestion> networkSuggestions, String callingPackageName) { - throw new UnsupportedOperationException(); - } - - @Override - public List<WifiNetworkSuggestion> getNetworkSuggestions(String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public String[] getFactoryMacAddresses() { - throw new UnsupportedOperationException(); - } - - @Override - public void setDeviceMobilityState(int state) { - throw new UnsupportedOperationException(); - } - - @Override - public void startDppAsConfiguratorInitiator(IBinder binder, String enrolleeUri, - int selectedNetworkId, int netRole, IDppCallback callback) { - throw new UnsupportedOperationException(); - } - - @Override - public void startDppAsEnrolleeInitiator(IBinder binder, String configuratorUri, - IDppCallback callback) { - throw new UnsupportedOperationException(); - } - - @Override - public void stopDppSession() throws RemoteException { - throw new UnsupportedOperationException(); - } - - @Override - public void addOnWifiUsabilityStatsListener( - IBinder binder, IOnWifiUsabilityStatsListener listener, int listenerIdentifier) { - throw new UnsupportedOperationException(); - } - - @Override - public void removeOnWifiUsabilityStatsListener(int listenerIdentifier) { - throw new UnsupportedOperationException(); - } - - @Override - public void updateWifiUsabilityScore(int seqNum, int score, int predictionHorizonSec) { - throw new UnsupportedOperationException(); - } - - @Override - public void connect(WifiConfiguration config, int netId, IBinder binder, - IActionListener callback, int callbackIdentifier) { - throw new UnsupportedOperationException(); - } - - @Override - public void save(WifiConfiguration config, IBinder binder, IActionListener callback, - int callbackIdentifier) { - throw new UnsupportedOperationException(); - } - - @Override - public void forget(int netId, IBinder binder, IActionListener callback, - int callbackIdentifier) { - throw new UnsupportedOperationException(); - } - - @Override - public void getTxPacketCount(String packageName, IBinder binder, - ITxPacketCountListener callback, int callbackIdentifier) { - throw new UnsupportedOperationException(); - } - - @Override - public void registerScanResultsCallback(IScanResultsCallback callback) { - throw new UnsupportedOperationException(); - } - - @Override - public void unregisterScanResultsCallback(IScanResultsCallback callback) { - throw new UnsupportedOperationException(); - } - - @Override - public void registerSuggestionConnectionStatusListener(IBinder binder, - ISuggestionConnectionStatusListener listener, - int listenerIdentifier, String packageName, String featureId) { - throw new UnsupportedOperationException(); - } - - @Override - public void unregisterSuggestionConnectionStatusListener(int listenerIdentifier, - String packageName) { - throw new UnsupportedOperationException(); - } - - @Override - public int calculateSignalLevel(int rssi) { - throw new UnsupportedOperationException(); - } - - @Override - public List<WifiConfiguration> getWifiConfigForMatchedNetworkSuggestionsSharedWithUser( - List<ScanResult> scanResults) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean setWifiConnectedNetworkScorer(IBinder binder, - IWifiConnectedNetworkScorer scorer) { - throw new UnsupportedOperationException(); - } - - @Override - public void clearWifiConnectedNetworkScorer() { - throw new UnsupportedOperationException(); - } - - @Override - public Map<WifiNetworkSuggestion, List<ScanResult>> getMatchingScanResults( - List<WifiNetworkSuggestion> networkSuggestions, - List<ScanResult> scanResults, - String callingPackage, String callingFeatureId) { - throw new UnsupportedOperationException(); - } -} diff --git a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java index 8023160a811e..05a3dce44022 100644 --- a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java +++ b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java @@ -336,6 +336,20 @@ public class WifiConfigurationTest { } /** + * Ensure that {@link NetworkSelectionStatus#getMaxNetworkSelectionDisableReason()} returns + * the maximum disable reason. + */ + @Test + public void testNetworkSelectionGetMaxNetworkSelectionDisableReason() { + int maxReason = Integer.MIN_VALUE; + for (int i = 0; i < NetworkSelectionStatus.DISABLE_REASON_INFOS.size(); i++) { + int reason = NetworkSelectionStatus.DISABLE_REASON_INFOS.keyAt(i); + maxReason = Math.max(maxReason, reason); + } + assertEquals(maxReason, NetworkSelectionStatus.getMaxNetworkSelectionDisableReason()); + } + + /** * Ensure that {@link WifiConfiguration#setSecurityParams(int)} sets up the * {@link WifiConfiguration} object correctly for SAE security type. * @throws Exception diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java index a189d507a32a..6320f85eb974 100644 --- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java +++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java @@ -1867,7 +1867,7 @@ public class WifiManagerTest { * Tests that passing a null Executor to {@link WifiManager#getWifiActivityEnergyInfoAsync} * throws an exception. */ - @Test(expected = IllegalArgumentException.class) + @Test(expected = NullPointerException.class) public void testGetWifiActivityInfoNullExecutor() throws Exception { mWifiManager.getWifiActivityEnergyInfoAsync(null, mOnWifiActivityEnergyInfoListener); } @@ -1876,7 +1876,7 @@ public class WifiManagerTest { * Tests that passing a null listener to {@link WifiManager#getWifiActivityEnergyInfoAsync} * throws an exception. */ - @Test(expected = IllegalArgumentException.class) + @Test(expected = NullPointerException.class) public void testGetWifiActivityInfoNullListener() throws Exception { mWifiManager.getWifiActivityEnergyInfoAsync(mExecutor, null); } diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java index adc41f0df4b4..0233ee2e2785 100644 --- a/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java +++ b/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java @@ -22,7 +22,6 @@ import static org.junit.Assert.assertTrue; import android.net.MacAddress; import android.net.MatchAllNetworkSpecifier; -import android.net.NetworkRequest; import android.os.Parcel; import android.os.PatternMatcher; import android.util.Pair; @@ -36,10 +35,6 @@ import org.junit.Test; */ @SmallTest public class WifiNetworkAgentSpecifierTest { - private static final int TEST_UID = 5; - private static final int TEST_UID_1 = 8; - private static final String TEST_PACKAGE = "com.test"; - private static final String TEST_PACKAGE_1 = "com.test.1"; private static final String TEST_SSID = "Test123"; private static final String TEST_SSID_PATTERN = "Test"; private static final String TEST_SSID_1 = "456test"; @@ -71,16 +66,6 @@ public class WifiNetworkAgentSpecifierTest { } /** - * Validate that the NetworkAgentSpecifier cannot be used in a {@link NetworkRequest} by apps. - */ - @Test(expected = IllegalStateException.class) - public void testWifiNetworkAgentSpecifierNotUsedInNetworkRequest() { - WifiNetworkAgentSpecifier specifier = createDefaultNetworkAgentSpecifier(); - - specifier.assertValidFromUid(TEST_UID); - } - - /** * Validate NetworkAgentSpecifier equals with itself. * a) Create network agent specifier 1 for WPA_PSK network * b) Create network agent specifier 2 with the same params as specifier 1. @@ -105,15 +90,13 @@ public class WifiNetworkAgentSpecifierTest { WifiConfiguration wifiConfiguration1 = createDefaultWifiConfiguration(); WifiNetworkAgentSpecifier specifier1 = new WifiNetworkAgentSpecifier( - wifiConfiguration1, - TEST_UID, TEST_PACKAGE); + wifiConfiguration1); WifiConfiguration wifiConfiguration2 = new WifiConfiguration(wifiConfiguration1); wifiConfiguration2.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); WifiNetworkAgentSpecifier specifier2 = new WifiNetworkAgentSpecifier( - wifiConfiguration2, - TEST_UID, TEST_PACKAGE); + wifiConfiguration2); assertFalse(specifier2.equals(specifier1)); } @@ -129,15 +112,13 @@ public class WifiNetworkAgentSpecifierTest { WifiConfiguration wifiConfiguration1 = createDefaultWifiConfiguration(); WifiNetworkAgentSpecifier specifier1 = new WifiNetworkAgentSpecifier( - wifiConfiguration1, - TEST_UID, TEST_PACKAGE); + wifiConfiguration1); WifiConfiguration wifiConfiguration2 = new WifiConfiguration(wifiConfiguration1); wifiConfiguration2.SSID = TEST_SSID_1; WifiNetworkAgentSpecifier specifier2 = new WifiNetworkAgentSpecifier( - wifiConfiguration2, - TEST_UID, TEST_PACKAGE); + wifiConfiguration2); assertFalse(specifier2.equals(specifier1)); } @@ -153,15 +134,13 @@ public class WifiNetworkAgentSpecifierTest { WifiConfiguration wifiConfiguration1 = createDefaultWifiConfiguration(); WifiNetworkAgentSpecifier specifier1 = new WifiNetworkAgentSpecifier( - wifiConfiguration1, - TEST_UID, TEST_PACKAGE); + wifiConfiguration1); WifiConfiguration wifiConfiguration2 = new WifiConfiguration(wifiConfiguration1); wifiConfiguration2.BSSID = TEST_BSSID_1; WifiNetworkAgentSpecifier specifier2 = new WifiNetworkAgentSpecifier( - wifiConfiguration2, - TEST_UID, TEST_PACKAGE); + wifiConfiguration2); assertFalse(specifier2.equals(specifier1)); } @@ -215,8 +194,7 @@ public class WifiNetworkAgentSpecifierTest { WifiNetworkSpecifier wifiNetworkSpecifier = new WifiNetworkSpecifier( ssidPattern, bssidPattern, - wificonfigurationNetworkSpecifier, - TEST_UID, TEST_PACKAGE); + wificonfigurationNetworkSpecifier); assertTrue(wifiNetworkSpecifier.satisfiedBy(wifiNetworkAgentSpecifier)); assertTrue(wifiNetworkAgentSpecifier.satisfiedBy(wifiNetworkSpecifier)); @@ -244,8 +222,7 @@ public class WifiNetworkAgentSpecifierTest { WifiNetworkSpecifier wifiNetworkSpecifier = new WifiNetworkSpecifier( ssidPattern, bssidPattern, - wificonfigurationNetworkSpecifier, - TEST_UID, TEST_PACKAGE); + wificonfigurationNetworkSpecifier); assertTrue(wifiNetworkSpecifier.satisfiedBy(wifiNetworkAgentSpecifier)); assertTrue(wifiNetworkAgentSpecifier.satisfiedBy(wifiNetworkSpecifier)); @@ -273,8 +250,7 @@ public class WifiNetworkAgentSpecifierTest { WifiNetworkSpecifier wifiNetworkSpecifier = new WifiNetworkSpecifier( ssidPattern, bssidPattern, - wificonfigurationNetworkSpecifier, - TEST_UID, TEST_PACKAGE); + wificonfigurationNetworkSpecifier); assertTrue(wifiNetworkSpecifier.satisfiedBy(wifiNetworkAgentSpecifier)); assertTrue(wifiNetworkAgentSpecifier.satisfiedBy(wifiNetworkSpecifier)); @@ -293,8 +269,7 @@ public class WifiNetworkAgentSpecifierTest { wifiConfigurationNetworkAgent.SSID = "\"" + TEST_SSID_1 + "\""; WifiNetworkAgentSpecifier wifiNetworkAgentSpecifier = new WifiNetworkAgentSpecifier( - wifiConfigurationNetworkAgent, - TEST_UID, TEST_PACKAGE); + wifiConfigurationNetworkAgent); PatternMatcher ssidPattern = new PatternMatcher(TEST_SSID_PATTERN, PatternMatcher.PATTERN_PREFIX); @@ -306,8 +281,7 @@ public class WifiNetworkAgentSpecifierTest { WifiNetworkSpecifier wifiNetworkSpecifier = new WifiNetworkSpecifier( ssidPattern, bssidPattern, - wificonfigurationNetworkSpecifier, - TEST_UID, TEST_PACKAGE); + wificonfigurationNetworkSpecifier); assertFalse(wifiNetworkSpecifier.satisfiedBy(wifiNetworkAgentSpecifier)); assertFalse(wifiNetworkAgentSpecifier.satisfiedBy(wifiNetworkSpecifier)); @@ -326,8 +300,7 @@ public class WifiNetworkAgentSpecifierTest { wifiConfigurationNetworkAgent.BSSID = TEST_BSSID_1; WifiNetworkAgentSpecifier wifiNetworkAgentSpecifier = new WifiNetworkAgentSpecifier( - wifiConfigurationNetworkAgent, - TEST_UID, TEST_PACKAGE); + wifiConfigurationNetworkAgent); PatternMatcher ssidPattern = new PatternMatcher(".*", PatternMatcher.PATTERN_SIMPLE_GLOB); @@ -340,8 +313,7 @@ public class WifiNetworkAgentSpecifierTest { WifiNetworkSpecifier wifiNetworkSpecifier = new WifiNetworkSpecifier( ssidPattern, bssidPattern, - wificonfigurationNetworkSpecifier, - TEST_UID, TEST_PACKAGE); + wificonfigurationNetworkSpecifier); assertFalse(wifiNetworkSpecifier.satisfiedBy(wifiNetworkAgentSpecifier)); assertFalse(wifiNetworkAgentSpecifier.satisfiedBy(wifiNetworkSpecifier)); @@ -360,8 +332,7 @@ public class WifiNetworkAgentSpecifierTest { wifiConfigurationNetworkAgent.BSSID = TEST_BSSID_1; WifiNetworkAgentSpecifier wifiNetworkAgentSpecifier = new WifiNetworkAgentSpecifier( - wifiConfigurationNetworkAgent, - TEST_UID, TEST_PACKAGE); + wifiConfigurationNetworkAgent); PatternMatcher ssidPattern = new PatternMatcher(TEST_SSID_PATTERN, PatternMatcher.PATTERN_PREFIX); @@ -374,8 +345,7 @@ public class WifiNetworkAgentSpecifierTest { WifiNetworkSpecifier wifiNetworkSpecifier = new WifiNetworkSpecifier( ssidPattern, bssidPattern, - wificonfigurationNetworkSpecifier, - TEST_UID, TEST_PACKAGE); + wificonfigurationNetworkSpecifier); assertFalse(wifiNetworkSpecifier.satisfiedBy(wifiNetworkAgentSpecifier)); assertFalse(wifiNetworkAgentSpecifier.satisfiedBy(wifiNetworkSpecifier)); @@ -402,41 +372,12 @@ public class WifiNetworkAgentSpecifierTest { WifiNetworkSpecifier wifiNetworkSpecifier = new WifiNetworkSpecifier( ssidPattern, bssidPattern, - wificonfigurationNetworkSpecifier, - TEST_UID, TEST_PACKAGE); + wificonfigurationNetworkSpecifier); assertFalse(wifiNetworkSpecifier.satisfiedBy(wifiNetworkAgentSpecifier)); assertFalse(wifiNetworkAgentSpecifier.satisfiedBy(wifiNetworkSpecifier)); } - /** - * Validate {@link WifiNetworkAgentSpecifier} with {@link WifiNetworkSpecifier} matching. - * a) Create network agent specifier for WPA_PSK network - * b) Create network specifier with matching SSID and BSSID pattern, but different UID. - * c) Ensure that the agent specifier is not satisfied by specifier. - */ - @Test - public void - testWifiNetworkAgentSpecifierDoesNotSatisfyNetworkSpecifierWithDifferentUid() { - WifiNetworkAgentSpecifier wifiNetworkAgentSpecifier = createDefaultNetworkAgentSpecifier(); - - PatternMatcher ssidPattern = - new PatternMatcher(TEST_SSID_PATTERN, PatternMatcher.PATTERN_PREFIX); - Pair<MacAddress, MacAddress> bssidPattern = - Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS), - MacAddress.fromString(TEST_BSSID_OUI_MASK)); - WifiConfiguration wificonfigurationNetworkSpecifier = new WifiConfiguration(); - wificonfigurationNetworkSpecifier.allowedKeyManagement - .set(WifiConfiguration.KeyMgmt.WPA_PSK); - WifiNetworkSpecifier wifiNetworkSpecifier = new WifiNetworkSpecifier( - ssidPattern, - bssidPattern, - wificonfigurationNetworkSpecifier, - TEST_UID_1, TEST_PACKAGE_1); - - assertFalse(wifiNetworkSpecifier.satisfiedBy(wifiNetworkAgentSpecifier)); - assertFalse(wifiNetworkAgentSpecifier.satisfiedBy(wifiNetworkSpecifier)); - } private WifiConfiguration createDefaultWifiConfiguration() { WifiConfiguration wifiConfiguration = new WifiConfiguration(); @@ -448,8 +389,7 @@ public class WifiNetworkAgentSpecifierTest { } private WifiNetworkAgentSpecifier createDefaultNetworkAgentSpecifier() { - return new WifiNetworkAgentSpecifier(createDefaultWifiConfiguration(), TEST_UID, - TEST_PACKAGE); + return new WifiNetworkAgentSpecifier(createDefaultWifiConfiguration()); } } diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java index 16197443b9d9..3b6723613c50 100644 --- a/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java +++ b/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java @@ -29,7 +29,6 @@ import android.net.MatchAllNetworkSpecifier; import android.net.NetworkSpecifier; import android.os.Parcel; import android.os.PatternMatcher; -import android.os.Process; import android.util.Pair; import androidx.test.filters.SmallTest; @@ -41,8 +40,6 @@ import org.junit.Test; */ @SmallTest public class WifiNetworkSpecifierTest { - private static final int TEST_UID = 5; - private static final String TEST_PACKAGE_NAME = "com.test"; private static final String TEST_SSID = "Test123"; private static final String TEST_BSSID_OUI_BASE_ADDRESS = "12:12:12:00:00:00"; private static final String TEST_BSSID_OUI_MASK = "ff:ff:ff:00:00:00"; @@ -62,7 +59,6 @@ public class WifiNetworkSpecifierTest { assertTrue(specifier instanceof WifiNetworkSpecifier); WifiNetworkSpecifier wifiNetworkSpecifier = (WifiNetworkSpecifier) specifier; - assertEquals(Process.myUid(), wifiNetworkSpecifier.requestorUid); assertEquals(TEST_SSID, wifiNetworkSpecifier.ssidPatternMatcher.getPath()); assertEquals(PATTERN_PREFIX, wifiNetworkSpecifier.ssidPatternMatcher.getType()); assertEquals(WifiManager.ALL_ZEROS_MAC_ADDRESS, @@ -367,8 +363,7 @@ public class WifiNetworkSpecifierTest { new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL), Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS), MacAddress.fromString(TEST_BSSID_OUI_MASK)), - wifiConfiguration, - TEST_UID, TEST_PACKAGE_NAME); + wifiConfiguration); Parcel parcelW = Parcel.obtain(); specifier.writeToParcel(parcelW, 0); @@ -399,8 +394,7 @@ public class WifiNetworkSpecifierTest { new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL), Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS), MacAddress.fromString(TEST_BSSID_OUI_MASK)), - wifiConfiguration, - TEST_UID, TEST_PACKAGE_NAME); + wifiConfiguration); assertTrue(specifier.satisfiedBy(null)); assertTrue(specifier.satisfiedBy(new MatchAllNetworkSpecifier())); @@ -422,15 +416,13 @@ public class WifiNetworkSpecifierTest { new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL), Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS), MacAddress.fromString(TEST_BSSID_OUI_MASK)), - wifiConfiguration, - TEST_UID, TEST_PACKAGE_NAME); + wifiConfiguration); WifiNetworkSpecifier specifier2 = new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL), Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS), MacAddress.fromString(TEST_BSSID_OUI_MASK)), - wifiConfiguration, - TEST_UID, TEST_PACKAGE_NAME); + wifiConfiguration); assertTrue(specifier2.satisfiedBy(specifier1)); } @@ -451,8 +443,7 @@ public class WifiNetworkSpecifierTest { new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL), Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS), MacAddress.fromString(TEST_BSSID_OUI_MASK)), - wifiConfiguration1, - TEST_UID, TEST_PACKAGE_NAME); + wifiConfiguration1); WifiConfiguration wifiConfiguration2 = new WifiConfiguration(); wifiConfiguration2.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); @@ -460,8 +451,7 @@ public class WifiNetworkSpecifierTest { new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL), Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS), MacAddress.fromString(TEST_BSSID_OUI_MASK)), - wifiConfiguration2, - TEST_UID, TEST_PACKAGE_NAME); + wifiConfiguration2); assertFalse(specifier2.satisfiedBy(specifier1)); } @@ -482,15 +472,13 @@ public class WifiNetworkSpecifierTest { new WifiNetworkSpecifier(new PatternMatcher("", PATTERN_LITERAL), Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS), MacAddress.fromString(TEST_BSSID_OUI_MASK)), - wifiConfiguration, - TEST_UID, TEST_PACKAGE_NAME); + wifiConfiguration); WifiNetworkSpecifier specifier2 = new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL), Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS), MacAddress.fromString(TEST_BSSID_OUI_MASK)), - wifiConfiguration, - TEST_UID, TEST_PACKAGE_NAME); + wifiConfiguration); assertFalse(specifier2.satisfiedBy(specifier1)); } @@ -511,44 +499,13 @@ public class WifiNetworkSpecifierTest { new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL), Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS), MacAddress.fromString(TEST_BSSID_OUI_MASK)), - wifiConfiguration, - TEST_UID, TEST_PACKAGE_NAME); + wifiConfiguration); WifiNetworkSpecifier specifier2 = new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL), Pair.create(WifiManager.ALL_ZEROS_MAC_ADDRESS, WifiManager.ALL_ZEROS_MAC_ADDRESS), - wifiConfiguration, - TEST_UID, TEST_PACKAGE_NAME); - - assertFalse(specifier2.satisfiedBy(specifier1)); - } - - /** - * Validate NetworkSpecifier matching. - * a) Create network specifier 1 for WPA_PSK network - * b) Create network specifier 2 with different package name . - * c) Ensure that the specifier 2 is not satisfied by specifier 1. - */ - @Test - public void testWifiNetworkSpecifierDoesNotSatisfyWhenPackageNameDifferent() { - WifiConfiguration wifiConfiguration = new WifiConfiguration(); - wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK); - wifiConfiguration.preSharedKey = TEST_PRESHARED_KEY; - - WifiNetworkSpecifier specifier1 = - new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL), - Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS), - MacAddress.fromString(TEST_BSSID_OUI_MASK)), - wifiConfiguration, - TEST_UID, TEST_PACKAGE_NAME); - - WifiNetworkSpecifier specifier2 = - new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL), - Pair.create(MacAddress.fromString(TEST_BSSID_OUI_BASE_ADDRESS), - MacAddress.fromString(TEST_BSSID_OUI_MASK)), - wifiConfiguration, - TEST_UID, TEST_PACKAGE_NAME + "blah"); + wifiConfiguration); assertFalse(specifier2.satisfiedBy(specifier1)); } diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java index c3b62854f12c..81b02fa5f801 100644 --- a/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java +++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java @@ -162,17 +162,6 @@ public class WifiAwareAgentNetworkSpecifierTest { collector.checkThat("Match unexpected", oldNs.satisfiedBy(newNs), equalTo(false)); } - /** - * Validate that agent network specifier cannot be used as in network requests - i.e. that - * throws an exception when queried for UID validity. - */ - @Test(expected = SecurityException.class) - public void testNoUsageInRequest() { - WifiAwareAgentNetworkSpecifier dut = new WifiAwareAgentNetworkSpecifier(); - - dut.assertValidFromUid(0); - } - // utilities /** @@ -182,6 +171,6 @@ public class WifiAwareAgentNetworkSpecifierTest { WifiAwareNetworkSpecifier getDummyNetworkSpecifier(int clientId) { return new WifiAwareNetworkSpecifier(WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_OOB, WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR, clientId, 0, 0, new byte[6], - null, null, 10, 5, 0); + null, null, 10, 5); } } diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java index 65fbf5b099d4..c5f98045082b 100644 --- a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java +++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java @@ -1564,7 +1564,7 @@ public class WifiAwareManagerTest { WifiAwareNetworkSpecifier ns = new WifiAwareNetworkSpecifier(NETWORK_SPECIFIER_TYPE_IB, WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER, 5, 568, 334, HexEncoding.decode("000102030405".toCharArray(), false), - "01234567890123456789012345678901".getBytes(), "blah blah", 666, 4, 10001); + "01234567890123456789012345678901".getBytes(), "blah blah", 666, 4); Parcel parcelW = Parcel.obtain(); ns.writeToParcel(parcelW, 0); diff --git a/wifi/tests/src/android/net/wifi/wificond/NativeScanResultTest.java b/wifi/tests/src/android/net/wifi/wificond/NativeScanResultTest.java index 06f12f7f37ea..0df170f8786c 100644 --- a/wifi/tests/src/android/net/wifi/wificond/NativeScanResultTest.java +++ b/wifi/tests/src/android/net/wifi/wificond/NativeScanResultTest.java @@ -28,7 +28,6 @@ import org.junit.Test; import java.util.ArrayList; import java.util.Arrays; -import java.util.BitSet; /** * Unit tests for {@link android.net.wifi.wificond.NativeScanResult}. @@ -46,7 +45,7 @@ public class NativeScanResultTest { private static final int TEST_FREQUENCY = 2456; private static final int TEST_SIGNAL_MBM = -45; private static final long TEST_TSF = 34455441; - private static final BitSet TEST_CAPABILITY = new BitSet(16) {{ set(2); set(5); }}; + private static final int TEST_CAPABILITY = (0x1 << 2) | (0x1 << 5); private static final boolean TEST_ASSOCIATED = true; private static final int[] RADIO_CHAIN_IDS = { 0, 1 }; private static final int[] RADIO_CHAIN_LEVELS = { -56, -65 }; diff --git a/wifi/tests/src/android/net/wifi/wificond/WifiCondManagerTest.java b/wifi/tests/src/android/net/wifi/wificond/WifiCondManagerTest.java index 5ba02a779663..32105be6ae4c 100644 --- a/wifi/tests/src/android/net/wifi/wificond/WifiCondManagerTest.java +++ b/wifi/tests/src/android/net/wifi/wificond/WifiCondManagerTest.java @@ -720,8 +720,7 @@ public class WifiCondManagerTest { @Test public void testRegisterDeathHandler() throws Exception { Runnable deathHandler = mock(Runnable.class); - assertTrue(mWificondControl.initialize(deathHandler)); - verify(mWificond).tearDownInterfaces(); + mWificondControl.setOnServiceDeadCallback(deathHandler); mWificondControl.binderDied(); mLooper.dispatchAll(); verify(deathHandler).run(); @@ -734,7 +733,7 @@ public class WifiCondManagerTest { @Test public void testDeathHandling() throws Exception { Runnable deathHandler = mock(Runnable.class); - assertTrue(mWificondControl.initialize(deathHandler)); + mWificondControl.setOnServiceDeadCallback(deathHandler); testSetupInterfaceForClientMode(); |